HTTP(S) API connection plugin (#39224)
* HTTPAPI connection * Punt run_commands to cliconf or httpapi * Fake enable_mode on eapi * Pull changes to nxos * Move load_config to edit_config for future-preparedness * Don't fail on lldp disabled * Re-enable check_rc on nxos' run_commands * Reorganize nxos httpapi plugin for compatibility * draft docs for connection: httpapi * restores docs for connection:local for eapi * Add _remote_is_local to httpapi
This commit is contained in:
parent
cc61c86049
commit
e9d7fa0418
277 changed files with 1325 additions and 1676 deletions
|
@ -11,11 +11,7 @@ Execution on the Control Node
|
|||
|
||||
Unlike most Ansible modules, network modules do not run on the managed nodes. From a user's point of view, network modules work like any other modules. They work with ad-hoc commands, playbooks, and roles. Behind the scenes, however, network modules use a different methodology than the other (Linux/Unix and Windows) modules use. Ansible is written and executed in Python. Because the majority of network devices can not run Python, the Ansible network modules are executed on the Ansible control node, where ``ansible`` or ``ansible-playbook`` runs.
|
||||
|
||||
Execution on the control node shapes two other differences in how network modules function:
|
||||
|
||||
- Network modules do not run every task in a playbook. They request current config first, compare current config to the state described by the task or playbook, and execute a task only if it changes the state of the managed node.
|
||||
|
||||
- Network modules that offer a backup option write the backup files onto the control node. With Linux/Unix modules, where a configuration file already exists on the managed node(s), the backup file gets written by default in the same directory as the new, changed file. Network modules do not update configuration files on the managed nodes, because network configuration is not written in files. Network modules write backup files on the control node, in the `backup` directory under the playbook root directory.
|
||||
Network modules also use the control node as a destination for backup files, for those modules that offer a ``backup`` option. With Linux/Unix modules, where a configuration file already exists on the managed node(s), the backup file gets written by default in the same directory as the new, changed file. Network modules do not update configuration files on the managed nodes, because network configuration is not written in files. Network modules write backup files on the control node, usually in the `backup` directory under the playbook root directory.
|
||||
|
||||
Multiple Communication Protocols
|
||||
================================================================================
|
||||
|
@ -23,14 +19,15 @@ Multiple Communication Protocols
|
|||
Because network modules execute on the control node instead of on the managed nodes, they can support multiple communication protocols. The communication protocol (XML over SSH, CLI over SSH, API over HTTPS) selected for each network module depends on the platform and the purpose of the module. Some network modules support only one protocol; some offer a choice. The most common protocol is CLI over SSH. You set the communication protocol with the ``ansible_connection`` variable:
|
||||
|
||||
.. csv-table::
|
||||
:header: "Value of ansible_connection", "Protocol", "Requires"
|
||||
:widths: 30, 10, 10
|
||||
:header: "Value of ansible_connection", "Protocol", "Requires", "Persistent?"
|
||||
:widths: 30, 10, 10, 10
|
||||
|
||||
"network_cli", "CLI over SSH", "network_os setting"
|
||||
"netconf", "XML over SSH", "network_os setting"
|
||||
"local", "depends on provider", "provider setting"
|
||||
"network_cli", "CLI over SSH", "network_os setting", "yes"
|
||||
"netconf", "XML over SSH", "network_os setting", "yes"
|
||||
"httpapi", "API over HTTP/HTTPS", "network_os setting", "yes"
|
||||
"local", "depends on provider", "provider setting", "no"
|
||||
|
||||
Beginning with Ansible 2.5, we recommend using ``network_cli`` or ``netconf`` for ``ansible_connection`` whenever possible. For details on using API over HTTPS connections, see the :ref:`platform-specific <platform_options>` pages.
|
||||
Beginning with Ansible 2.6, we recommend using one of the persistent connection types listed above instead of ``local``. With persistent connections, you can define the hosts and credentials only once, rather than in every task. For more details on using each connection type on various platforms, see the :ref:`platform-specific <platform_options>` pages.
|
||||
|
||||
|
||||
Modules Organized by Network Platform
|
||||
|
@ -46,12 +43,15 @@ A network platform is a set of network devices with a common operating system th
|
|||
All modules within a network platform share certain requirements. Some network platforms have specific differences - see the :ref:`platform-specific <platform_options>` documentation for details.
|
||||
|
||||
|
||||
Privilege Escalation: `authorize` and `become`
|
||||
Privilege Escalation: ``enable`` mode, ``become``, and ``authorize``
|
||||
================================================================================
|
||||
|
||||
Several network platforms support privilege escalation, where certain tasks must be done by a privileged user. This is generally known as ``enable`` mode (the equivalent of ``sudo`` in \*nix administration). Ansible network modules offer privilege escalation for those network devices that support it. However, different platforms use privilege escalation in different ways.
|
||||
Several network platforms support privilege escalation, where certain tasks must be done by a privileged user. On network devices this is called ``enable`` mode (the equivalent of ``sudo`` in \*nix administration). Ansible network modules offer privilege escalation for those network devices that support it. For details of which platforms support ``enable`` mode, with examples of how to use it, see the :ref:`platform-specific <platform_options>` documentation.
|
||||
|
||||
Network platforms that support ``connection: network_cli`` and privilege escalation use the top-level Ansible parameter ``become: yes`` with ``become_method: enable``. For modules in these platforms, a ``group_vars`` file would look like:
|
||||
Using ``become`` for privilege escalation
|
||||
-----------------------------------------
|
||||
|
||||
As of Ansible 2.6, you can use the top-level Ansible parameter ``become: yes`` with ``become_method: enable`` to run a task, play, or playbook with escalated privileges on any network platform that supports privilege escalation. You must use either ``connection: network_cli`` or ``connection: httpapi`` with ``become: yes`` with ``become_method: enable``. If you are using ``network_cli`` to connect Ansible to your network devices, a ``group_vars`` file would look like:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -60,9 +60,10 @@ Network platforms that support ``connection: network_cli`` and privilege escalat
|
|||
ansible_become: yes
|
||||
ansible_become_method: enable
|
||||
|
||||
We recommend using ``network_cli`` connections whenever possible.
|
||||
Legacy playbooks: ``authorize`` for privilege escalation
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Some network platforms support privilege escalation but cannot use ``network_cli`` connections yet. This includes all platforms in older versions of Ansible (< 2.5) and HTTPS connections using ``eapi`` in version 2.5. With these connections, you must use a ``provider`` dictionary and include ``authorize: yes`` and ``auth_pass: my_enable_password``. For that use case, a ``group_vars`` file looks like:
|
||||
If you are running Ansible 2.5 or older, some network platforms support privilege escalation but not ``network_cli`` or ``httpapi`` connections. This includes all platforms in versions 2.4 and older, and HTTPS connections using ``eapi`` in version 2.5. With a ``local`` connection, you must use a ``provider`` dictionary and include ``authorize: yes`` and ``auth_pass: my_enable_password``. For that use case, a ``group_vars`` file looks like:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -76,7 +77,7 @@ Some network platforms support privilege escalation but cannot use ``network_cli
|
|||
transport: eapi
|
||||
use_ssl: no
|
||||
|
||||
And you use the ``eapi`` variable in your play(s) or task(s):
|
||||
And you use the ``eapi`` variable in your task(s):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -91,4 +92,6 @@ And you use the ``eapi`` variable in your play(s) or task(s):
|
|||
state: present
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
Note that while Ansible 2.6 supports the use of ``connection: local`` with ``provider`` dictionaries, this usage will be deprecated in future and eventually removed.
|
||||
|
||||
For more information, see :ref:`Become and Networks<become-network>`
|
||||
|
|
|
@ -11,27 +11,35 @@ Arista EOS supports multiple connections. This page offers details on how each c
|
|||
Connections Available
|
||||
================================================================================
|
||||
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
|.. | CLI | eAPI |
|
||||
+===========================+===============================================+=========================================+
|
||||
| **Protocol** | SSH | HTTP(S) |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| | **Credentials** | | uses SSH keys / SSH-agent if present | | uses HTTPS certificates if present |
|
||||
| | | | accepts ``-u myuser -k`` if using password | | |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| **Indirect Access** | via a bastion (jump host) | via a web proxy |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| | **Connection Settings** | | ``ansible_connection: network_cli`` | | ``ansible_connection: local`` |
|
||||
| | | | | | Requires ``transport: eapi`` |
|
||||
| | | | | | in the ``provider`` dictionary |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| | **Enable Mode** | | supported - use ``ansible_become: yes`` | | supported - use ``authorize: yes`` |
|
||||
| | (Privilege Escalation) | | with ``ansible_become_method: enable`` | | and ``auth_pass:`` in the |
|
||||
| | | | and ``ansible_become_pass:`` | | ``provider`` dictionary |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| **Returned Data Format** | ``stdout[0].`` | ``stdout[0].messages[0].`` |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
|.. | CLI | eAPI |
|
||||
+===========================+===============================================+=============================================+
|
||||
| **Protocol** | SSH | HTTP(S) |
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
| | **Credentials** | | uses SSH keys / SSH-agent if present | | uses HTTPS certificates if present |
|
||||
| | | | accepts ``-u myuser -k`` if using password | | |
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
| **Indirect Access** | via a bastion (jump host) | via a web proxy |
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
| | **Connection Settings** | | ``ansible_connection: network_cli`` | | ``ansible_connection: httpapi`` |
|
||||
| | | | | | OR |
|
||||
| | | | | | ``ansible_connection: local`` |
|
||||
| | | | | | with ``transport: eapi`` |
|
||||
| | | | | | in the ``provider`` dictionary |
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
| | **Enable Mode** | | supported - use ``ansible_become: yes`` | | supported: |
|
||||
| | (Privilege Escalation) | | with ``ansible_become_method: enable`` | | ``httpapi`` |
|
||||
| | | | | | uses ``ansible_become: yes`` |
|
||||
| | | | | | with ``ansible_become_method: enable`` |
|
||||
| | | | | | ``local`` |
|
||||
| | | | | | uses ``authorize: yes`` |
|
||||
| | | | | | and ``auth_pass:`` |
|
||||
| | | | | | in the ``provider`` dictionary |
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
| **Returned Data Format** | ``stdout[0].`` | ``stdout[0].messages[0].`` |
|
||||
+---------------------------+-----------------------------------------------+---------------------------------------------+
|
||||
|
||||
For legacy playbooks, EOS still supports ``ansible_connection: local``. We recommend modernizing to use ``ansible_connection: network_cli`` or ``ansible_connection: httpapi`` as soon as possible.
|
||||
|
||||
Using CLI in Ansible 2.5
|
||||
================================================================================
|
||||
|
@ -86,13 +94,47 @@ Before you can use eAPI to connect to a switch, you must enable eAPI. To enable
|
|||
become_method: enable
|
||||
when: ansible_network_os == 'eos'
|
||||
|
||||
You can find more options for enabling HTTP/HTTPS and local http in the :ref:`eos_eapi <eos_eapi_module>` module documentation.
|
||||
You can find more options for enabling HTTP/HTTPS connections in the :ref:`eos_eapi <eos_eapi_module>` module documentation.
|
||||
|
||||
Once eAPI is enabled, change your ``group_vars/eos.yml`` to use the eAPI connection.
|
||||
|
||||
Example eAPI ``group_vars/eos.yml``
|
||||
-----------------------------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ansible_connection: httpapi
|
||||
ansible_network_os: eos
|
||||
ansible_user: myuser
|
||||
ansible_ssh_pass: !vault...
|
||||
become: yes
|
||||
become_method: enable
|
||||
proxy_env:
|
||||
http_proxy: http://proxy.example.com:8080
|
||||
|
||||
- If you are accessing your host directly (not through a web proxy) you can remove the ``proxy_env`` configuration.
|
||||
- If you are accessing your host through a web proxy using ``https``, change ``http_proxy`` to ``https_proxy``.
|
||||
|
||||
|
||||
Example eAPI Task
|
||||
-----------------
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Backup current switch config (eos)
|
||||
eos_config:
|
||||
backup: yes
|
||||
register: backup_eos_location
|
||||
environment: "{{ proxy_env }}"
|
||||
when: ansible_network_os == 'eos'
|
||||
|
||||
In this example the ``proxy_env`` variable defined in ``group_vars`` gets passed to the ``environment`` option of the module in the task.
|
||||
|
||||
eAPI examples with ``connection: local``
|
||||
-----------------------------------------
|
||||
|
||||
``group_vars/eos.yml``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ansible_connection: local
|
||||
|
@ -107,12 +149,7 @@ Example eAPI ``group_vars/eos.yml``
|
|||
proxy_env:
|
||||
http_proxy: http://proxy.example.com:8080
|
||||
|
||||
- If you are accessing your host directly (not through a web proxy) you can remove the ``proxy_env`` configuration.
|
||||
- If you are accessing your host through a web proxy using ``https``, change ``http_proxy`` to ``https_proxy``.
|
||||
|
||||
|
||||
Example eAPI Task
|
||||
-----------------
|
||||
eAPI task:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -129,5 +166,4 @@ In this example two variables defined in ``group_vars`` get passed to the module
|
|||
- the ``eapi`` variable gets passed to the ``provider`` option of the module
|
||||
- the ``proxy_env`` variable gets passed to the ``environment`` option of the module
|
||||
|
||||
|
||||
.. include:: shared_snippets/SSH_warning.rst
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
Platform Options
|
||||
****************
|
||||
|
||||
Some Ansible Network platforms support multiple connection types, privilege escalation, or other options. The pages in this section offer standardized guides to understanding available options on each network platform. We welcome contributions from community-maintained platforms to this section.
|
||||
Some Ansible Network platforms support multiple connection types, privilege escalation (``enable`` mode), or other options. The pages in this section offer standardized guides to understanding available options on each network platform. We welcome contributions from community-maintained platforms to this section.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
|
@ -21,9 +21,7 @@ Connections Available
|
|||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| **Indirect Access** | via a bastion (jump host) | via a web proxy |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| | **Connection Settings** | | ``ansible_connection: network_cli`` | | ``ansible_connection: local`` |
|
||||
| | | | | | Requires ``transport: nxapi`` |
|
||||
| | | | | | in the ``provider`` dictionary |
|
||||
| | **Connection Settings** | | ``ansible_connection: network_cli`` | | ``ansible_connection: httpapi`` |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
| | **Enable Mode** | | supported - use ``ansible_become: yes`` | | not supported by NX-API |
|
||||
| | (Privilege Escalation) | | with ``ansible_become_method: enable`` | | |
|
||||
|
@ -32,6 +30,7 @@ Connections Available
|
|||
| **Returned Data Format** | ``stdout[0].`` | ``stdout[0].messages[0].`` |
|
||||
+---------------------------+-----------------------------------------------+-----------------------------------------+
|
||||
|
||||
For legacy playbooks, NXOS still supports ``ansible_connection: local``. We recommend modernizing to use ``ansible_connection: network_cli`` or ``ansible_connection: httpapi`` as soon as possible.
|
||||
|
||||
Using CLI in Ansible 2.5
|
||||
================================================================================
|
||||
|
@ -93,13 +92,10 @@ Example NX-API ``group_vars/nxos.yml``
|
|||
|
||||
.. code-block:: yaml
|
||||
|
||||
ansible_connection: local
|
||||
ansible_connection: httpapi
|
||||
ansible_network_os: nxos
|
||||
ansible_user: myuser
|
||||
ansible_ssh_pass: !vault...
|
||||
nxapi:
|
||||
host: "{{ inventory_hostname }}"
|
||||
transport: nxapi
|
||||
proxy_env:
|
||||
http_proxy: http://proxy.example.com:8080
|
||||
|
||||
|
@ -115,15 +111,10 @@ Example NX-API Task
|
|||
- name: Backup current switch config (nxos)
|
||||
nxos_config:
|
||||
backup: yes
|
||||
provider: "{{ nxapi }}"
|
||||
register: backup_nxos_location
|
||||
environment: "{{ proxy_env }}"
|
||||
when: ansible_network_os == 'nxos'
|
||||
|
||||
In this example two variables defined in ``group_vars`` get passed to the module of the task:
|
||||
|
||||
- the ``nxapi`` variable gets passed to the ``provider`` option of the module
|
||||
- the ``proxy_env`` variable gets passed to the ``environment`` option of the module
|
||||
|
||||
In this example the ``proxy_env`` variable defined in ``group_vars`` gets passed to the ``environment`` option of the module used in the task.
|
||||
|
||||
.. include:: shared_snippets/SSH_warning.rst
|
||||
|
|
|
@ -213,15 +213,11 @@ module.
|
|||
Become and Networks
|
||||
===================
|
||||
|
||||
As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all :ref:`Ansible-maintained platforms<network_supported>` that support ``enable`` mode: `eos``, ``ios``, and ``nxos``. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
|
||||
|
||||
network_cli and become
|
||||
----------------------
|
||||
You must set the connection type to either ``connection: network_cli`` or ``connection: httpapi`` to use ``become`` for privilege escalation on network devices. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
|
||||
|
||||
Ansible 2.5 added support for ``become`` to be used to enter ``enable`` mode (Privileged EXEC mode) on network devices that support it. This replaces the previous ``authorize`` and ``auth_pass`` options in ``provider``.
|
||||
|
||||
You must set the host connection type to ``connection: network_cli`` to use ``become`` for privilege escalation on network devices. Ansible 2.5.3 supports ``become`` for privilege escalation on ``eos``, ``ios``, and ``nxos``.
|
||||
|
||||
You can use escalated privileges on only the specific tasks that need them, on an entire play, or on all plays. Adding ``become: yes`` and ``become_method: enable`` instructs Ansible to enter ``enable`` mode before executing the task, play, or playbook.
|
||||
You can use escalated privileges on only the specific tasks that need them, on an entire play, or on all plays. Adding ``become: yes`` and ``become_method: enable`` instructs Ansible to enter ``enable`` mode before executing the task, play, or playbook where those parameters are set.
|
||||
|
||||
If you see this error message, the task that generated it requires ``enable`` mode to succeed:
|
||||
|
||||
|
@ -281,13 +277,10 @@ If you need a password to enter ``enable`` mode, you can specify it in one of tw
|
|||
|
||||
As a reminder passwords should never be stored in plain text. For information on encrypting your passwords and other secrets with Ansible Vault, see :doc:`playbooks_vault`.
|
||||
|
||||
|
||||
.. _become-network-auth-and-auth-password:
|
||||
|
||||
authorize and auth_pass
|
||||
-----------------------
|
||||
|
||||
For HTTPS connections that cannot use ``connection: network_cli``, you can enter ``enable`` mode using the module options ``authorize`` and ``auth_pass``:
|
||||
Ansible still supports ``enable`` mode with ``connection: local`` for legacy playbooks. To enter ``enable`` mode with ``connection: local``, use the module options ``authorize`` and ``auth_pass``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -302,7 +295,7 @@ For HTTPS connections that cannot use ``connection: network_cli``, you can enter
|
|||
authorize: yes
|
||||
auth_pass: " {{ secret_auth_pass }}"
|
||||
|
||||
Note that over time more platforms and connections will support ``become``. As this happens, the use of ``authorize`` and of ``provider`` dictionaries will be deprecated. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
|
||||
We recommend updating your playbooks to use ``become`` for network-device ``enable`` mode consistently. The use of ``authorize`` and of ``provider`` dictionaries will be deprecated in future. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
|
||||
|
||||
.. _become-windows:
|
||||
|
||||
|
|
|
@ -655,6 +655,14 @@ DEFAULT_HOST_LIST:
|
|||
section: defaults
|
||||
type: pathlist
|
||||
yaml: {key: defaults.inventory}
|
||||
DEFAULT_HTTPAPI_PLUGIN_PATH:
|
||||
name: HttpApi Plugins Path
|
||||
default: ~/.ansible/plugins/httpapi:/usr/share/ansible/plugins/httpapi
|
||||
description: Colon separated paths in which Ansible will search for HttpApi Plugins.
|
||||
env: [{name: ANSIBLE_HTTPAPI_PLUGINS}]
|
||||
ini:
|
||||
- {key: httpapi_plugins, section: defaults}
|
||||
type: pathspec
|
||||
DEFAULT_INTERNAL_POLL_INTERVAL:
|
||||
name: Internal poll interval
|
||||
default: 0.001
|
||||
|
|
|
@ -163,46 +163,8 @@ class Cli:
|
|||
def run_commands(self, commands, check_rc=True):
|
||||
"""Run list of commands on remote device and return results
|
||||
"""
|
||||
responses = list()
|
||||
connection = self._get_connection()
|
||||
|
||||
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)
|
||||
out = to_text(out, errors='surrogate_or_strict')
|
||||
|
||||
try:
|
||||
out = self._module.from_json(out)
|
||||
except ValueError:
|
||||
out = str(out).strip()
|
||||
|
||||
responses.append(out)
|
||||
|
||||
return responses
|
||||
|
||||
def send_config(self, commands):
|
||||
conn = self._get_connection()
|
||||
|
||||
multiline = False
|
||||
rc = 0
|
||||
for command in to_list(commands):
|
||||
if command == 'end':
|
||||
continue
|
||||
|
||||
if command.startswith('banner') or multiline:
|
||||
multiline = True
|
||||
elif command == 'EOF' and multiline:
|
||||
multiline = False
|
||||
|
||||
conn.get(command, None, None, multiline)
|
||||
return connection.run_commands(commands, check_rc)
|
||||
|
||||
def configure(self, commands):
|
||||
"""Sends configuration commands to the remote device
|
||||
|
@ -239,32 +201,12 @@ class Cli:
|
|||
return result
|
||||
|
||||
conn = self._get_connection()
|
||||
session = 'ansible_%s' % int(time.time())
|
||||
result = {'session': session}
|
||||
|
||||
out = conn.get('configure session %s' % session)
|
||||
|
||||
if replace:
|
||||
out = conn.get('rollback clean-config')
|
||||
|
||||
try:
|
||||
self.send_config(commands)
|
||||
return conn.load_config(commands, commit, replace)
|
||||
except ConnectionError as exc:
|
||||
self.close_session(session)
|
||||
message = getattr(exc, 'err', exc)
|
||||
self._module.fail_json(msg="Error on executing commands %s" % commands, data=to_text(message, errors='surrogate_then_replace'))
|
||||
|
||||
out = conn.get('show session-config diffs')
|
||||
if out:
|
||||
result['diff'] = to_text(out, errors='surrogate_then_replace').strip()
|
||||
|
||||
if commit:
|
||||
conn.get('commit')
|
||||
else:
|
||||
self.close_session(session)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Eapi:
|
||||
|
||||
|
|
|
@ -146,47 +146,8 @@ class Cli:
|
|||
def run_commands(self, commands, check_rc=True):
|
||||
"""Run list of commands on remote device and return results
|
||||
"""
|
||||
responses = list()
|
||||
connection = self._get_connection()
|
||||
|
||||
for item in to_list(commands):
|
||||
if item['output'] == 'json' and not is_json(item['command']):
|
||||
cmd = '%s | json' % item['command']
|
||||
elif item['output'] == 'text' and is_json(item['command']):
|
||||
cmd = item['command'].rsplit('|', 1)[0]
|
||||
else:
|
||||
cmd = item['command']
|
||||
|
||||
out = ''
|
||||
try:
|
||||
out = connection.get(cmd)
|
||||
code = 0
|
||||
except ConnectionError as e:
|
||||
code = getattr(e, 'code', 1)
|
||||
message = getattr(e, 'err', e)
|
||||
err = to_text(message, errors='surrogate_then_replace')
|
||||
|
||||
try:
|
||||
out = to_text(out, errors='surrogate_or_strict')
|
||||
except UnicodeError:
|
||||
self._module.fail_json(msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out)))
|
||||
|
||||
if check_rc and code != 0:
|
||||
self._module.fail_json(msg=err)
|
||||
|
||||
if not check_rc and code != 0:
|
||||
try:
|
||||
out = self._module.from_json(err)
|
||||
except ValueError:
|
||||
out = to_text(message).strip()
|
||||
else:
|
||||
try:
|
||||
out = self._module.from_json(out)
|
||||
except ValueError:
|
||||
out = to_text(out).strip()
|
||||
|
||||
responses.append(out)
|
||||
return responses
|
||||
return connection.run_commands(commands, check_rc)
|
||||
|
||||
def load_config(self, config, return_error=False, opts=None):
|
||||
"""Sends configuration commands to the remote device
|
||||
|
|
|
@ -258,7 +258,8 @@ class Interfaces(FactsBase):
|
|||
self.facts['interfaces'] = self.populate_interfaces(data)
|
||||
|
||||
data = self.responses[1]
|
||||
self.facts['neighbors'] = self.populate_neighbors(data['lldpNeighbors'])
|
||||
if data:
|
||||
self.facts['neighbors'] = self.populate_neighbors(data['lldpNeighbors'])
|
||||
|
||||
def populate_interfaces(self, data):
|
||||
facts = dict()
|
||||
|
|
|
@ -43,13 +43,13 @@ class ActionModule(_ActionModule):
|
|||
|
||||
socket_path = None
|
||||
|
||||
if self._play_context.connection == 'network_cli':
|
||||
if self._play_context.connection in ('network_cli', 'httpapi'):
|
||||
provider = self._task.args.get('provider', {})
|
||||
if any(provider.values()):
|
||||
display.warning('provider is unnecessary when using network_cli and will be ignored')
|
||||
display.warning('provider is unnecessary when using %s and will be ignored' % self._play_context.connection)
|
||||
del self._task.args['provider']
|
||||
if self._task.args.get('transport'):
|
||||
display.warning('transport is unnecessary when using network_cli and will be ignored')
|
||||
display.warning('transport is unnecessary when using %s and will be ignored' % self._play_context.connection)
|
||||
del self._task.args['transport']
|
||||
elif self._play_context.connection == 'local':
|
||||
provider = load_provider(eos_provider_spec, self._task.args)
|
||||
|
|
|
@ -43,16 +43,17 @@ class ActionModule(_ActionModule):
|
|||
|
||||
socket_path = None
|
||||
|
||||
if self._task.args.get('provider', {}).get('transport') == 'nxapi' and self._task.action == 'nxos_nxapi':
|
||||
if (self._play_context.connection == 'httpapi' or self._task.args.get('provider', {}).get('transport') == 'nxapi') \
|
||||
and self._task.action == 'nxos_nxapi':
|
||||
return {'failed': True, 'msg': "Transport type 'nxapi' is not valid for '%s' module." % (self._task.action)}
|
||||
|
||||
if self._play_context.connection == 'network_cli':
|
||||
if self._play_context.connection in ('network_cli', 'httpapi'):
|
||||
provider = self._task.args.get('provider', {})
|
||||
if any(provider.values()):
|
||||
display.warning('provider is unnecessary when using network_cli and will be ignored')
|
||||
display.warning('provider is unnecessary when using %s and will be ignored' % self._play_context.connection)
|
||||
del self._task.args['provider']
|
||||
if self._task.args.get('transport'):
|
||||
display.warning('transport is unnecessary when using network_cli and will be ignored')
|
||||
display.warning('transport is unnecessary when using %s and will be ignored' % self._play_context.connection)
|
||||
del self._task.args['transport']
|
||||
elif self._play_context.connection == 'local':
|
||||
provider = load_provider(nxos_provider_spec, self._task.args)
|
||||
|
|
|
@ -20,15 +20,37 @@ from __future__ import (absolute_import, division, print_function)
|
|||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
from ansible.plugins.connection.network_cli import Connection as NetworkCli
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def send_command(self, command, prompt=None, answer=None, sendonly=False, newline=True, prompt_retry_check=False):
|
||||
"""Executes a cli command and returns the results
|
||||
This method will execute the CLI command on the connection and return
|
||||
the results to the caller. The command output will be returned as a
|
||||
string
|
||||
"""
|
||||
kwargs = {'command': to_bytes(command), 'sendonly': sendonly,
|
||||
'newline': newline, 'prompt_retry_check': prompt_retry_check}
|
||||
if prompt is not None:
|
||||
kwargs['prompt'] = to_bytes(prompt)
|
||||
if answer is not None:
|
||||
kwargs['answer'] = to_bytes(answer)
|
||||
|
||||
if isinstance(self._connection, NetworkCli):
|
||||
resp = self._connection.send(**kwargs)
|
||||
else:
|
||||
resp = self._connection.send_request(command, **kwargs)
|
||||
return resp
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
|
@ -74,3 +96,72 @@ class Cliconf(CliconfBase):
|
|||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
||||
|
||||
# Imported from module_utils
|
||||
def close_session(self, session):
|
||||
# to close session gracefully execute abort in top level session prompt.
|
||||
self.get('end')
|
||||
self.get('configure session %s' % session)
|
||||
self.get('abort')
|
||||
|
||||
def run_commands(self, commands, check_rc=True):
|
||||
"""Run list of commands on remote device and return results
|
||||
"""
|
||||
responses = list()
|
||||
multiline = False
|
||||
|
||||
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
|
||||
|
||||
if command == 'end':
|
||||
continue
|
||||
elif command.startswith('banner') or multiline:
|
||||
multiline = True
|
||||
elif command == 'EOF' and multiline:
|
||||
multiline = False
|
||||
|
||||
out = self.get(command, prompt, answer, multiline)
|
||||
|
||||
if out is not None:
|
||||
try:
|
||||
out = json.loads(out)
|
||||
except ValueError:
|
||||
out = str(out).strip()
|
||||
|
||||
responses.append(out)
|
||||
|
||||
return responses
|
||||
|
||||
def load_config(self, commands, commit=False, replace=False):
|
||||
"""Loads the config commands onto the remote device
|
||||
"""
|
||||
session = 'ansible_%s' % int(time.time())
|
||||
result = {'session': session}
|
||||
|
||||
self.get('configure session %s' % session)
|
||||
if replace:
|
||||
self.get('rollback clean-config')
|
||||
|
||||
try:
|
||||
self.run_commands(commands)
|
||||
except ConnectionError:
|
||||
self.close_session(session)
|
||||
raise
|
||||
|
||||
out = self.get('show session-config diffs')
|
||||
if out:
|
||||
result['diff'] = out.strip()
|
||||
|
||||
if commit:
|
||||
self.get('commit')
|
||||
else:
|
||||
self.close_session(session)
|
||||
|
||||
return result
|
||||
|
|
|
@ -23,12 +23,33 @@ import json
|
|||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase
|
||||
from ansible.plugins.connection.network_cli import Connection as NetworkCli
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def send_command(self, command, prompt=None, answer=None, sendonly=False, newline=True, prompt_retry_check=False):
|
||||
"""Executes a cli command and returns the results
|
||||
This method will execute the CLI command on the connection and return
|
||||
the results to the caller. The command output will be returned as a
|
||||
string
|
||||
"""
|
||||
kwargs = {'command': to_bytes(command), 'sendonly': sendonly,
|
||||
'newline': newline, 'prompt_retry_check': prompt_retry_check}
|
||||
if prompt is not None:
|
||||
kwargs['prompt'] = to_bytes(prompt)
|
||||
if answer is not None:
|
||||
kwargs['answer'] = to_bytes(answer)
|
||||
|
||||
if isinstance(self._connection, NetworkCli):
|
||||
resp = self._connection.send(**kwargs)
|
||||
else:
|
||||
resp = self._connection.send_request(command, **kwargs)
|
||||
return resp
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
|
@ -76,3 +97,37 @@ class Cliconf(CliconfBase):
|
|||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
||||
|
||||
# Migrated from module_utils
|
||||
def run_commands(self, commands, check_rc=True):
|
||||
"""Run list of commands on remote device and return results
|
||||
"""
|
||||
responses = list()
|
||||
|
||||
for item in to_list(commands):
|
||||
if item['output'] == 'json' and not item['command'].endswith('| json'):
|
||||
cmd = '%s | json' % item['command']
|
||||
elif item['output'] == 'text' and item['command'].endswith('| json'):
|
||||
cmd = item['command'].rsplit('|', 1)[0]
|
||||
else:
|
||||
cmd = item['command']
|
||||
|
||||
try:
|
||||
out = self.get(cmd)
|
||||
except ConnectionError as e:
|
||||
if check_rc:
|
||||
raise
|
||||
out = e
|
||||
|
||||
try:
|
||||
out = to_text(out, errors='surrogate_or_strict').strip()
|
||||
except UnicodeError:
|
||||
raise ConnectionError(msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out)))
|
||||
|
||||
try:
|
||||
out = json.loads(out)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
responses.append(out)
|
||||
return responses
|
||||
|
|
303
lib/ansible/plugins/connection/httpapi.py
Normal file
303
lib/ansible/plugins/connection/httpapi.py
Normal file
|
@ -0,0 +1,303 @@
|
|||
# (c) 2018 Red Hat Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
author: Ansible Networking Team
|
||||
connection: httpapi
|
||||
short_description: Use httpapi to run command on network appliances
|
||||
description:
|
||||
- This connection plugin provides a connection to remote devices over a
|
||||
HTTP(S)-based api.
|
||||
version_added: "2.6"
|
||||
options:
|
||||
host:
|
||||
description:
|
||||
- Specifies the remote device FQDN or IP address to establish the SSH
|
||||
connection to.
|
||||
default: inventory_hostname
|
||||
vars:
|
||||
- name: ansible_host
|
||||
port:
|
||||
type: int
|
||||
description:
|
||||
- Specifies the port on the remote device to listening for connections
|
||||
when establishing the SSH connection.
|
||||
ini:
|
||||
- section: defaults
|
||||
key: remote_port
|
||||
env:
|
||||
- name: ANSIBLE_REMOTE_PORT
|
||||
vars:
|
||||
- name: ansible_port
|
||||
network_os:
|
||||
description:
|
||||
- Configures the device platform network operating system. This value is
|
||||
used to load the correct httpapi and cliconf plugins to communicate
|
||||
with the remote device
|
||||
vars:
|
||||
- name: ansible_network_os
|
||||
remote_user:
|
||||
description:
|
||||
- The username used to authenticate to the remote device when the API
|
||||
connection is first established. If the remote_user is not specified,
|
||||
the connection will use the username of the logged in user.
|
||||
- Can be configured form the CLI via the C(--user) or C(-u) options
|
||||
ini:
|
||||
- section: defaults
|
||||
key: remote_user
|
||||
env:
|
||||
- name: ANSIBLE_REMOTE_USER
|
||||
vars:
|
||||
- name: ansible_user
|
||||
password:
|
||||
description:
|
||||
- Secret used to authenticate
|
||||
vars:
|
||||
- name: ansible_password
|
||||
- name: ansible_httpapi_pass
|
||||
use_ssl:
|
||||
description:
|
||||
- Whether to connect using SSL (HTTPS) or not (HTTP)
|
||||
default: False
|
||||
vars:
|
||||
- name: ansible_httpapi_use_ssl
|
||||
timeout:
|
||||
type: int
|
||||
description:
|
||||
- Sets the connection time, in seconds, for the communicating with the
|
||||
remote device. This timeout is used as the default timeout value for
|
||||
commands when issuing a command to the network CLI. If the command
|
||||
does not return in timeout seconds, the an error is generated.
|
||||
default: 120
|
||||
become:
|
||||
type: boolean
|
||||
description:
|
||||
- The become option will instruct the CLI session to attempt privilege
|
||||
escalation on platforms that support it. Normally this means
|
||||
transitioning from user mode to C(enable) mode in the CLI session.
|
||||
If become is set to True and the remote device does not support
|
||||
privilege escalation or the privilege has already been elevated, then
|
||||
this option is silently ignored
|
||||
- Can be configured form the CLI via the C(--become) or C(-b) options
|
||||
default: False
|
||||
ini:
|
||||
section: privilege_escalation
|
||||
key: become
|
||||
env:
|
||||
- name: ANSIBLE_BECOME
|
||||
vars:
|
||||
- name: ansible_become
|
||||
become_method:
|
||||
description:
|
||||
- This option allows the become method to be specified in for handling
|
||||
privilege escalation. Typically the become_method value is set to
|
||||
C(enable) but could be defined as other values.
|
||||
default: sudo
|
||||
ini:
|
||||
section: privilege_escalation
|
||||
key: become_method
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_METHOD
|
||||
vars:
|
||||
- name: ansible_become_method
|
||||
persistent_connect_timeout:
|
||||
type: int
|
||||
description:
|
||||
- Configures, in seconds, the amount of time to wait when trying to
|
||||
initially establish a persistent connection. If this value expires
|
||||
before the connection to the remote device is completed, the connection
|
||||
will fail
|
||||
default: 30
|
||||
ini:
|
||||
section: persistent_connection
|
||||
key: persistent_connect_timeout
|
||||
env:
|
||||
- name: ANSIBLE_PERSISTENT_CONNECT_TIMEOUT
|
||||
persistent_command_timeout:
|
||||
type: int
|
||||
description:
|
||||
- Configures, in seconds, the amount of time to wait for a command to
|
||||
return from the remote device. If this timer is exceeded before the
|
||||
command returns, the connection plugin will raise an exception and
|
||||
close
|
||||
default: 10
|
||||
ini:
|
||||
section: persistent_connection
|
||||
key: persistent_command_timeout
|
||||
env:
|
||||
- name: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.six import PY3
|
||||
from ansible.module_utils.six.moves import cPickle
|
||||
from ansible.module_utils.urls import open_url
|
||||
from ansible.playbook.play_context import PlayContext
|
||||
from ansible.plugins.loader import cliconf_loader, connection_loader, httpapi_loader
|
||||
from ansible.plugins.connection import ConnectionBase
|
||||
from ansible.utils.path import unfrackpath
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class Connection(ConnectionBase):
|
||||
'''Network API connection'''
|
||||
|
||||
transport = 'httpapi'
|
||||
has_pipelining = True
|
||||
force_persistence = True
|
||||
# Do not use _remote_is_local in other connections
|
||||
_remote_is_local = True
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
||||
self._matched_prompt = None
|
||||
self._matched_pattern = None
|
||||
self._last_response = None
|
||||
self._history = list()
|
||||
|
||||
self._local = connection_loader.get('local', play_context, '/dev/null')
|
||||
self._local.set_options()
|
||||
|
||||
self._cliconf = None
|
||||
|
||||
self._ansible_playbook_pid = kwargs.get('ansible_playbook_pid')
|
||||
|
||||
network_os = self._play_context.network_os
|
||||
if not network_os:
|
||||
raise AnsibleConnectionFailure(
|
||||
'Unable to automatically determine host network os. Please '
|
||||
'manually configure ansible_network_os value for this host'
|
||||
)
|
||||
|
||||
self._httpapi = httpapi_loader.get(network_os, self)
|
||||
if self._httpapi:
|
||||
display.vvvv('loaded API plugin for network_os %s' % network_os, host=self._play_context.remote_addr)
|
||||
else:
|
||||
raise AnsibleConnectionFailure('unable to load API plugin for network_os %s' % network_os)
|
||||
|
||||
self._url = None
|
||||
self._auth = None
|
||||
|
||||
# reconstruct the socket_path and set instance values accordingly
|
||||
self._update_connection_state()
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self.__dict__[name]
|
||||
except KeyError:
|
||||
if not name.startswith('_'):
|
||||
for plugin in (self._httpapi, self._cliconf):
|
||||
method = getattr(plugin, name, None)
|
||||
if method:
|
||||
return method
|
||||
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
return self._local.exec_command(cmd, in_data, sudoable)
|
||||
|
||||
def put_file(self, in_path, out_path):
|
||||
return self._local.put_file(in_path, out_path)
|
||||
|
||||
def fetch_file(self, in_path, out_path):
|
||||
return self._local.fetch_file(in_path, out_path)
|
||||
|
||||
def update_play_context(self, pc_data):
|
||||
"""Updates the play context information for the connection"""
|
||||
pc_data = to_bytes(pc_data)
|
||||
if PY3:
|
||||
pc_data = cPickle.loads(pc_data, encoding='bytes')
|
||||
else:
|
||||
pc_data = cPickle.loads(pc_data)
|
||||
play_context = PlayContext()
|
||||
play_context.deserialize(pc_data)
|
||||
|
||||
messages = ['updating play_context for connection']
|
||||
if self._play_context.become is False and play_context.become is True:
|
||||
self._enable = True
|
||||
messages.append('authorizing connection')
|
||||
|
||||
elif self._play_context.become is True and not play_context.become:
|
||||
self._enable = False
|
||||
messages.append('deauthorizing connection')
|
||||
|
||||
self._play_context = play_context
|
||||
return messages
|
||||
|
||||
def _connect(self):
|
||||
if self.connected:
|
||||
return
|
||||
network_os = self._play_context.network_os
|
||||
|
||||
protocol = 'https' if self.get_option('use_ssl') else 'http'
|
||||
host = self._play_context.remote_addr
|
||||
port = self._play_context.port or 443 if protocol == 'https' else 80
|
||||
self._url = '%s://%s:%s' % (protocol, host, port)
|
||||
|
||||
self._cliconf = cliconf_loader.get(network_os, self)
|
||||
if self._cliconf:
|
||||
display.vvvv('loaded cliconf plugin for network_os %s' % network_os, host=self._play_context.remote_addr)
|
||||
else:
|
||||
display.vvvv('unable to load cliconf for network_os %s' % network_os)
|
||||
|
||||
self._connected = True
|
||||
|
||||
def _update_connection_state(self):
|
||||
'''
|
||||
Reconstruct the connection socket_path and check if it exists
|
||||
|
||||
If the socket path exists then the connection is active and set
|
||||
both the _socket_path value to the path and the _connected value
|
||||
to True. If the socket path doesn't exist, leave the socket path
|
||||
value to None and the _connected value to False
|
||||
'''
|
||||
ssh = connection_loader.get('ssh', class_only=True)
|
||||
cp = ssh._create_control_path(
|
||||
self._play_context.remote_addr, self._play_context.port,
|
||||
self._play_context.remote_user, self._play_context.connection,
|
||||
self._ansible_playbook_pid
|
||||
)
|
||||
|
||||
tmp_path = unfrackpath(C.PERSISTENT_CONTROL_PATH_DIR)
|
||||
socket_path = unfrackpath(cp % dict(directory=tmp_path))
|
||||
|
||||
if os.path.exists(socket_path):
|
||||
self._connected = True
|
||||
self._socket_path = socket_path
|
||||
|
||||
def reset(self):
|
||||
'''
|
||||
Reset the connection
|
||||
'''
|
||||
if self._socket_path:
|
||||
display.vvvv('resetting persistent connection for socket_path %s' % self._socket_path, host=self._play_context.remote_addr)
|
||||
self.close()
|
||||
display.vvvv('reset call on connection instance', host=self._play_context.remote_addr)
|
||||
|
||||
def close(self):
|
||||
if self._connected:
|
||||
self._connected = False
|
||||
|
||||
def send(self, path, data, **kwargs):
|
||||
'''
|
||||
Sends the command to the device over api
|
||||
'''
|
||||
url_kwargs = dict(url_username=self._play_context.remote_user, url_password=self._play_context.password)
|
||||
url_kwargs.update(kwargs)
|
||||
response = open_url(self._url + path, data=data, **url_kwargs)
|
||||
self._auth = response.info().get('Set-Cookie')
|
||||
|
||||
return response
|
0
lib/ansible/plugins/httpapi/__init__.py
Normal file
0
lib/ansible/plugins/httpapi/__init__.py
Normal file
158
lib/ansible/plugins/httpapi/eos.py
Normal file
158
lib/ansible/plugins/httpapi/eos.py
Normal file
|
@ -0,0 +1,158 @@
|
|||
# (c) 2018 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 json
|
||||
import time
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.connection import ConnectionError
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class HttpApi:
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def send_request(self, data, **message_kwargs):
|
||||
if 'become' in message_kwargs:
|
||||
display.vvvv('firing event: on_become')
|
||||
# TODO ??? self._terminal.on_become(passwd=auth_pass)
|
||||
|
||||
output = message_kwargs.get('output', 'text')
|
||||
request = request_builder(data, output)
|
||||
headers = {'Content-Type': 'application/json-rpc'}
|
||||
|
||||
response = self.connection.send('/command-api', request, headers=headers, method='POST')
|
||||
response = json.loads(to_text(response.read()))
|
||||
return handle_response(response)
|
||||
|
||||
def get_prompt(self):
|
||||
# Hack to keep @enable_mode working
|
||||
return '#'
|
||||
|
||||
# Imported from module_utils
|
||||
def edit_config(self, config, commit=False, replace=False):
|
||||
"""Loads the configuration onto the remote devices
|
||||
|
||||
If the device doesn't support configuration sessions, this will
|
||||
fallback to using configure() to load the commands. If that happens,
|
||||
there will be no returned diff or session values
|
||||
"""
|
||||
session = 'ansible_%s' % int(time.time())
|
||||
result = {'session': session}
|
||||
banner_cmd = None
|
||||
banner_input = []
|
||||
|
||||
commands = ['configure session %s' % session]
|
||||
if replace:
|
||||
commands.append('rollback clean-config')
|
||||
|
||||
for command in config:
|
||||
if command.startswith('banner'):
|
||||
banner_cmd = command
|
||||
banner_input = []
|
||||
elif banner_cmd:
|
||||
if command == 'EOF':
|
||||
command = {'cmd': banner_cmd, 'input': '\n'.join(banner_input)}
|
||||
banner_cmd = None
|
||||
commands.append(command)
|
||||
else:
|
||||
banner_input.append(command)
|
||||
continue
|
||||
else:
|
||||
commands.append(command)
|
||||
|
||||
response = self.send_request(commands)
|
||||
|
||||
commands = ['configure session %s' % session, 'show session-config diffs']
|
||||
if commit:
|
||||
commands.append('commit')
|
||||
else:
|
||||
commands.append('abort')
|
||||
|
||||
response = self.send_request(commands, output='text')
|
||||
diff = response[1].strip()
|
||||
if diff:
|
||||
result['diff'] = diff
|
||||
|
||||
return result
|
||||
|
||||
def run_commands(self, commands, check_rc=True):
|
||||
"""Runs list of commands on remote device and returns results
|
||||
"""
|
||||
output = None
|
||||
queue = list()
|
||||
responses = list()
|
||||
|
||||
def run_queue(queue, output):
|
||||
response = to_list(self.send_request(queue, output=output))
|
||||
if output == 'json':
|
||||
response = [json.loads(item) for item in response]
|
||||
return response
|
||||
|
||||
for item in to_list(commands):
|
||||
cmd_output = None
|
||||
if isinstance(item, dict):
|
||||
command = item['command']
|
||||
if command.endswith('| json'):
|
||||
command = command.replace('| json', '')
|
||||
cmd_output = 'json'
|
||||
elif 'output' in item:
|
||||
cmd_output = item['output']
|
||||
else:
|
||||
command = item
|
||||
cmd_output = 'json'
|
||||
|
||||
if output and output != cmd_output:
|
||||
responses.extend(run_queue(queue, output))
|
||||
queue = list()
|
||||
|
||||
output = cmd_output or 'json'
|
||||
queue.append(command)
|
||||
|
||||
if queue:
|
||||
responses.extend(run_queue(queue, output))
|
||||
|
||||
return responses
|
||||
|
||||
def load_config(self, config, commit=False, replace=False):
|
||||
"""Loads the configuration onto the remote devices
|
||||
|
||||
If the device doesn't support configuration sessions, this will
|
||||
fallback to using configure() to load the commands. If that happens,
|
||||
there will be no returned diff or session values
|
||||
"""
|
||||
return self.edit_config(config, commit, replace)
|
||||
|
||||
|
||||
def handle_response(response):
|
||||
if 'error' in response:
|
||||
error = response['error']
|
||||
raise ConnectionError(error['message'], code=error['code'])
|
||||
|
||||
results = []
|
||||
for result in response['result']:
|
||||
if 'messages' in result:
|
||||
results.append(result['messages'][0])
|
||||
elif 'output' in result:
|
||||
results.append(result['output'].strip())
|
||||
else:
|
||||
results.append(json.dumps(result))
|
||||
|
||||
if len(results) == 1:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
|
||||
def request_builder(commands, output, reqid=None):
|
||||
params = dict(version=1, cmds=to_list(commands), format=output)
|
||||
return json.dumps(dict(jsonrpc='2.0', id=reqid, method='runCmds', params=params))
|
135
lib/ansible/plugins/httpapi/nxos.py
Normal file
135
lib/ansible/plugins/httpapi/nxos.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
# (c) 2018 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 json
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import ConnectionError
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
class HttpApi:
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def _run_queue(self, queue, output):
|
||||
request = request_builder(queue, output)
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
response = self.connection.send('/ins', request, headers=headers, method='POST')
|
||||
response = json.loads(to_text(response.read()))
|
||||
return handle_response(response)
|
||||
|
||||
def send_request(self, data, **message_kwargs):
|
||||
output = None
|
||||
queue = list()
|
||||
responses = list()
|
||||
|
||||
for item in to_list(data):
|
||||
cmd_output = message_kwargs.get('output', 'json')
|
||||
if isinstance(item, dict):
|
||||
command = item['command']
|
||||
if command.endswith('| json'):
|
||||
command = command.rsplit('|', 1)[0]
|
||||
cmd_output = 'json'
|
||||
elif 'output' in item:
|
||||
cmd_output = item['output']
|
||||
else:
|
||||
command = item
|
||||
|
||||
if output and output != cmd_output:
|
||||
responses.extend(self._run_queue(queue, output))
|
||||
queue = list()
|
||||
|
||||
output = cmd_output or 'json'
|
||||
queue.append(command)
|
||||
|
||||
if queue:
|
||||
responses.extend(self._run_queue(queue, output))
|
||||
|
||||
if len(responses) == 1:
|
||||
return responses[0]
|
||||
return responses
|
||||
|
||||
# Migrated from module_utils
|
||||
def edit_config(self, command):
|
||||
responses = self.send_request(command, output='config')
|
||||
return json.dumps(responses)
|
||||
|
||||
def run_commands(self, commands, check_rc=True):
|
||||
"""Runs list of commands on remote device and returns results
|
||||
"""
|
||||
try:
|
||||
out = self.send_request(commands)
|
||||
except ConnectionError as exc:
|
||||
if check_rc:
|
||||
raise
|
||||
out = to_text(exc)
|
||||
|
||||
out = to_list(out)
|
||||
for index, response in enumerate(out):
|
||||
if response[0] == '{':
|
||||
out[index] = json.loads(response)
|
||||
return out
|
||||
|
||||
|
||||
def handle_response(response):
|
||||
results = []
|
||||
|
||||
if response['ins_api'].get('outputs'):
|
||||
for output in to_list(response['ins_api']['outputs']['output']):
|
||||
if output['code'] != '200':
|
||||
raise ConnectionError('%s: %s' % (output['input'], output['msg']))
|
||||
elif 'body' in output:
|
||||
result = output['body']
|
||||
if isinstance(result, dict):
|
||||
result = json.dumps(result)
|
||||
|
||||
results.append(result.strip())
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def request_builder(commands, output, version='1.0', chunk='0', sid=None):
|
||||
"""Encodes a NXAPI JSON request message
|
||||
"""
|
||||
output_to_command_type = {
|
||||
'text': 'cli_show_ascii',
|
||||
'json': 'cli_show',
|
||||
'bash': 'bash',
|
||||
'config': 'cli_conf'
|
||||
}
|
||||
|
||||
maybe_output = commands[0].split('|')[-1].strip()
|
||||
if maybe_output in output_to_command_type:
|
||||
command_type = output_to_command_type[maybe_output]
|
||||
commands = [command.split('|')[0].strip() for command in commands]
|
||||
else:
|
||||
try:
|
||||
command_type = output_to_command_type[output]
|
||||
except KeyError:
|
||||
msg = 'invalid format, received %s, expected one of %s' % \
|
||||
(output, ','.join(output_to_command_type.keys()))
|
||||
raise ConnectionError(msg)
|
||||
|
||||
if isinstance(commands, (list, set, tuple)):
|
||||
commands = ' ;'.join(commands)
|
||||
|
||||
msg = {
|
||||
'version': version,
|
||||
'type': command_type,
|
||||
'chunk': chunk,
|
||||
'sid': sid,
|
||||
'input': commands,
|
||||
'output_format': 'json'
|
||||
}
|
||||
return json.dumps(dict(ins_api=msg))
|
|
@ -762,3 +762,10 @@ inventory_loader = PluginLoader(
|
|||
C.DEFAULT_INVENTORY_PLUGIN_PATH,
|
||||
'inventory_plugins'
|
||||
)
|
||||
|
||||
httpapi_loader = PluginLoader(
|
||||
'HttpApi',
|
||||
'ansible.plugins.httpapi',
|
||||
C.DEFAULT_HTTPAPI_PLUGIN_PATH,
|
||||
'httpapi_plugins',
|
||||
)
|
||||
|
|
|
@ -14,9 +14,3 @@
|
|||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local ansible_become=no"
|
||||
with_first_found: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "result.session_name is not defined"
|
||||
|
||||
- name: remove login
|
||||
- name: Remove login
|
||||
eos_banner:
|
||||
banner: login
|
||||
state: absent
|
||||
|
@ -62,7 +62,7 @@
|
|||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
- name: remove login (idempotent)
|
||||
- name: Remove login again (idempotent)
|
||||
eos_banner:
|
||||
banner: login
|
||||
state: absent
|
||||
|
@ -78,7 +78,6 @@
|
|||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "result.session_name is not defined"
|
||||
|
||||
|
||||
# FIXME add in tests for everything defined in docs
|
||||
# FIXME Test state:absent + test:
|
||||
# FIXME Without powers ensure "privileged mode required"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
- debug: msg="START eapi/basic-login.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: Remove previous login banner
|
||||
eos_config:
|
||||
|
@ -6,7 +7,7 @@
|
|||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: Set login
|
||||
- name: Create login banner
|
||||
eos_banner:
|
||||
banner: login
|
||||
text: |
|
||||
|
@ -21,12 +22,11 @@
|
|||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.commands.0.cmd == 'banner login'"
|
||||
- "result.commands.0.input == 'this is my login banner\nthat has a multiline\nstring'"
|
||||
- "'banner login' in result.commands[0]" # does this break due to "contains?"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
- name: Set login again (idempotent)
|
||||
- name: Create login banner again (idempotent)
|
||||
eos_banner:
|
||||
banner: login
|
||||
text: |
|
||||
|
@ -60,7 +60,7 @@
|
|||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.commands.0.cmd == 'no banner login'"
|
||||
- "'no banner login' in result.commands" # does this break due to "contains?"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.commands.0.cmd == 'banner motd'"
|
||||
- "result.commands.0.input == 'this is my motd banner\nthat has a multiline\nstring'"
|
||||
- "'this is my motd banner' in result.commands"
|
||||
- "'that has a multiline' in result.commands"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
|
@ -60,7 +60,7 @@
|
|||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.commands.0.cmd == 'no banner motd'"
|
||||
- "'no banner motd' in result.commands"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.commands.0.cmd == 'banner motd'"
|
||||
- "result.commands.0.input == 'this is my motd banner configure by net_banner'"
|
||||
- "'this is my motd banner configure by net_banner' in result.commands"
|
||||
# Ensure sessions contains epoc. Will fail after 18th May 2033
|
||||
- "'ansible_1' in result.session_name"
|
||||
|
||||
|
|
|
@ -14,9 +14,3 @@
|
|||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local ansible_become=no"
|
||||
with_first_found: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name foo Management"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interface Management1 | json
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[0] contains EOS"
|
||||
- "result[1].interfaces.Management1.name contains Manage"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name eq Management1"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -23,7 +22,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name == Management1"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu gt 0"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -23,7 +22,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu > 0"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu ge 0"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -23,7 +22,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu >= 0"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
- name: run invalid command
|
||||
eos_command:
|
||||
commands: ['show foo']
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
@ -18,7 +17,6 @@
|
|||
commands:
|
||||
- show version
|
||||
- show foo
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu lt 1600"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -23,7 +22,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu < 1600"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu le 1600"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -23,7 +22,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu <= 1600"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name neq Ethernet"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -23,7 +22,6 @@
|
|||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name != Ethernet"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
- name: get output for single command
|
||||
eos_command:
|
||||
commands: ['show version']
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -17,7 +16,6 @@
|
|||
commands:
|
||||
- show version
|
||||
- show interfaces
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
- show version
|
||||
wait_for:
|
||||
- "result[0] contains bad_value_string"
|
||||
provider: "{{ cli }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
- show interfaces Management1
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name foo Management"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
---
|
||||
- debug: msg="START eapi/contains.yaml"
|
||||
- debug: msg="START eapi/contains.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test contains operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[0].modelName contains EOS"
|
||||
- "result[1].interfaces.Management1.name contains Management"
|
||||
provider: "{{ eapi }}"
|
||||
- "result[0] contains EOS"
|
||||
- "result[1].interfaces.Management1.name contains Manage"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -17,4 +16,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/contains.yaml"
|
||||
- debug: msg="END eapi/contains.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
---
|
||||
- debug: msg="START eapi/equal.yaml"
|
||||
- debug: msg="START eapi/equal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test eq operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name eq Management1"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -20,10 +19,9 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name == Management1"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -31,4 +29,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/equal.yaml"
|
||||
- debug: msg="END eapi/equal.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
---
|
||||
- debug: msg="START eapi/greaterthan.yaml"
|
||||
- debug: msg="START eapi/greaterthan.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test gt operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu gt 0"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -20,10 +19,9 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu >= 0"
|
||||
provider: "{{ eapi }}"
|
||||
- "result[1].interfaces.Management1.mtu > 0"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -31,4 +29,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/greaterthan.yaml"
|
||||
- debug: msg="END eapi/greaterthan.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
---
|
||||
- debug: msg="START eapi/greaterthanorequal.yaml"
|
||||
- debug: msg="START eapi/greaterthanorequal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test ge operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu ge 0"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -20,10 +19,9 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu >= 0"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -31,4 +29,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/greaterthanorequal.yaml"
|
||||
- debug: msg="END eapi/greaterthanorequal.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
- name: run invalid command
|
||||
eos_command:
|
||||
commands: ['show foo']
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
@ -18,7 +17,6 @@
|
|||
commands:
|
||||
- show version
|
||||
- show foo
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
---
|
||||
- debug: msg="START eapi/lessthan.yaml"
|
||||
- debug: msg="START eapi/lessthan.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test lt operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu lt 9100"
|
||||
provider: "{{ eapi }}"
|
||||
- "result[1].interfaces.Management1.mtu lt 1600"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -20,10 +19,9 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu < 9100"
|
||||
provider: "{{ eapi }}"
|
||||
- "result[1].interfaces.Management1.mtu < 1600"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -31,4 +29,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/lessthan.yaml"
|
||||
- debug: msg="END eapi/lessthan.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
---
|
||||
- debug: msg="START eapi/lessthanorequal.yaml"
|
||||
- debug: msg="START eapi/lessthanorequal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test le operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu le 9100"
|
||||
provider: "{{ eapi }}"
|
||||
- "result[1].interfaces.Management1.mtu le 1600"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -20,10 +19,9 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.mtu <= 9100"
|
||||
provider: "{{ eapi }}"
|
||||
- "result[1].interfaces.Management1.mtu <= 1600"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -31,4 +29,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/lessthanorequal.yaml"
|
||||
- debug: msg="END eapi/lessthanorequal.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
---
|
||||
- debug: msg="START eapi/notequal.yaml"
|
||||
- debug: msg="START eapi/notequal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test neq operator
|
||||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name neq Ethernet"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -20,10 +19,9 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces Management1
|
||||
- show interfaces Management1 | json
|
||||
wait_for:
|
||||
- "result[1].interfaces.Management1.name != Ethernet"
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -31,4 +29,4 @@
|
|||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- debug: msg="END eapi/notequal.yaml"
|
||||
- debug: msg="END eapi/notequal.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
- name: get output for single command
|
||||
eos_command:
|
||||
commands: ['show version']
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -17,7 +16,6 @@
|
|||
commands:
|
||||
- show version
|
||||
- show interfaces
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
wait_for:
|
||||
- "result[0].version foo 4.15"
|
||||
retries: 1
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -14,9 +14,3 @@
|
|||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local ansible_become=no"
|
||||
with_first_found: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: collect any backup files
|
||||
|
@ -29,7 +28,6 @@
|
|||
eos_config:
|
||||
src: basic/config.j2
|
||||
backup: yes
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
lines:
|
||||
- ip address 119.31.1.1 255.255.255.256
|
||||
parents: interface Loopback911
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
check_mode: 1
|
||||
environment:
|
||||
|
@ -25,7 +24,6 @@
|
|||
before:
|
||||
- "no ip access-list test"
|
||||
src: basic/cmds.j2
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
check_mode: yes
|
||||
register: config
|
||||
|
@ -34,7 +32,6 @@
|
|||
eos_command:
|
||||
commands:
|
||||
- show configuration sessions | json
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -47,7 +44,6 @@
|
|||
lines:
|
||||
- ip address 119.31.1.1 255.255.255.256
|
||||
parents: interface Loopback911
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
check_mode: 1
|
||||
environment:
|
||||
|
@ -64,7 +60,6 @@
|
|||
lines:
|
||||
- ip address 119.31.1.1 255.255.255.255
|
||||
parents: interface Loopback911
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
check_mode: yes
|
||||
register: result
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
eos_config:
|
||||
lines: hostname {{ inventory_hostname_short }}
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: get current running-config
|
||||
eos_command:
|
||||
commands: show running-config
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: config
|
||||
|
||||
|
@ -19,7 +17,6 @@
|
|||
eos_config:
|
||||
lines: hostname foo
|
||||
config: "{{ config.stdout[0] }}"
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -31,7 +28,6 @@
|
|||
- name: get current running-config
|
||||
eos_command:
|
||||
commands: show running-config
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: config
|
||||
|
||||
|
@ -39,7 +35,6 @@
|
|||
eos_config:
|
||||
lines: hostname foo
|
||||
config: "{{ config.stdout[0] }}"
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -51,7 +46,6 @@
|
|||
eos_config:
|
||||
lines: hostname {{ inventory_hostname_short }}
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/config.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure device with defaults included
|
||||
eos_config:
|
||||
src: defaults/config.j2
|
||||
defaults: yes
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -31,7 +29,6 @@
|
|||
eos_config:
|
||||
src: defaults/config.j2
|
||||
defaults: yes
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
|
||||
- name: save config always
|
||||
eos_config:
|
||||
save_when: always
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -27,7 +25,6 @@
|
|||
- name: save always again (not idempotent)
|
||||
eos_config:
|
||||
save_when: always
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure device with config
|
||||
eos_config:
|
||||
src: basic/config.j2
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -29,7 +27,6 @@
|
|||
eos_config:
|
||||
src: basic/config.j2
|
||||
defaults: yes
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
- name: configure with invalid src
|
||||
eos_config:
|
||||
src: basic/foobar.j2
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure device with config
|
||||
eos_config:
|
||||
src: basic/config.j2
|
||||
provider: "{{ cli }}"
|
||||
match: none
|
||||
become: yes
|
||||
register: result
|
||||
|
@ -30,7 +28,6 @@
|
|||
eos_config:
|
||||
src: basic/config.j2
|
||||
defaults: yes
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
eos_config:
|
||||
lines: no vlan 10
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure sub level command
|
||||
eos_config:
|
||||
lines: name test
|
||||
parents: vlan 10
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -26,7 +24,6 @@
|
|||
eos_config:
|
||||
lines: name test
|
||||
parents: vlan 10
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -38,7 +35,6 @@
|
|||
eos_config:
|
||||
lines: no vlan 10
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
before: no ip access-list test
|
||||
after: exit
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure sub level command using block resplace
|
||||
|
@ -24,7 +23,6 @@
|
|||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -47,7 +45,6 @@
|
|||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -59,7 +56,6 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/sublevel_block.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
before: no ip access-list test
|
||||
after: exit
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure sub level command using exact match
|
||||
|
@ -28,7 +27,6 @@
|
|||
after: exit
|
||||
match: exact
|
||||
replace: block
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -51,7 +49,6 @@
|
|||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
match: exact
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -63,7 +60,6 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/sublevel_exact.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
before: no ip access-list test
|
||||
after: exit
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure sub level command using strict match
|
||||
|
@ -28,7 +27,6 @@
|
|||
after: exit
|
||||
match: strict
|
||||
replace: block
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -51,7 +49,6 @@
|
|||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
match: strict
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -63,7 +60,6 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/sublevel_strict.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
eos_config:
|
||||
lines: hostname veos01
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure top level command
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -23,7 +21,6 @@
|
|||
- name: configure top level command idempotent check
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -35,7 +32,6 @@
|
|||
eos_config:
|
||||
lines: hostname veos01
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/toplevel.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -7,14 +7,12 @@
|
|||
- snmp-server contact ansible
|
||||
- hostname veos01
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure top level command with before
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
after: snmp-server contact bar
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -28,7 +26,6 @@
|
|||
eos_config:
|
||||
lines: hostname foo
|
||||
after: snmp-server contact foo
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -42,7 +39,6 @@
|
|||
- no snmp-server contact
|
||||
- hostname veos01
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/toplevel_after.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -7,14 +7,12 @@
|
|||
- snmp-server contact ansible
|
||||
- hostname veos01
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: configure top level command with before
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
before: snmp-server contact bar
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -28,7 +26,6 @@
|
|||
eos_config:
|
||||
lines: hostname foo
|
||||
before: snmp-server contact foo
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
@ -42,7 +39,6 @@
|
|||
- hostname veos01
|
||||
- no snmp-server contact
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/toplevel_before.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
- debug: msg="START eapi/backup.yaml"
|
||||
- debug: msg="START eapi/backup.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: setup
|
||||
eos_config:
|
||||
|
@ -9,14 +9,14 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
become: yes
|
||||
|
||||
- name: collect any backup files
|
||||
find:
|
||||
paths: "{{ role_path }}/backup"
|
||||
pattern: "{{ inventory_hostname_short }}_config*"
|
||||
register: backup_files
|
||||
delegate_to: localhost
|
||||
connection: local
|
||||
|
||||
- name: delete backup files
|
||||
file:
|
||||
|
@ -28,7 +28,7 @@
|
|||
eos_config:
|
||||
src: basic/config.j2
|
||||
backup: yes
|
||||
provider: "{{ eapi }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -41,10 +41,10 @@
|
|||
paths: "{{ role_path }}/backup"
|
||||
pattern: "{{ inventory_hostname_short }}_config*"
|
||||
register: backup_files
|
||||
delegate_to: localhost
|
||||
connection: local
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "backup_files.files is defined"
|
||||
|
||||
- debug: msg="END eapi/backup.yaml"
|
||||
- debug: msg="END eapi/backup.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure device with defaults included
|
||||
eos_config:
|
||||
src: defaults/config.j2
|
||||
defaults: yes
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- debug: var=result
|
||||
|
@ -29,7 +27,6 @@
|
|||
eos_config:
|
||||
src: defaults/config.j2
|
||||
defaults: yes
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- debug: var=result
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
|
||||
- name: save config
|
||||
eos_config:
|
||||
save_when: always
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -25,7 +23,6 @@
|
|||
- name: save should always run
|
||||
eos_config:
|
||||
save_when: always
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -9,12 +9,10 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure device with config
|
||||
eos_config:
|
||||
src: basic/config.j2
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -26,7 +24,6 @@
|
|||
- name: check device with config
|
||||
eos_config:
|
||||
src: basic/config.j2
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
- name: configure with invalid src
|
||||
eos_config:
|
||||
src: basic/foobar.j2
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
|
|
|
@ -9,12 +9,10 @@
|
|||
parents:
|
||||
- interface Ethernet2
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure device with config
|
||||
eos_config:
|
||||
src: basic/config.j2
|
||||
provider: "{{ eapi }}"
|
||||
match: none
|
||||
register: result
|
||||
|
||||
|
@ -27,7 +25,6 @@
|
|||
- name: check device with config
|
||||
eos_config:
|
||||
src: basic/config.j2
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure sub level command
|
||||
eos_config:
|
||||
lines: 10 permit ip any any log
|
||||
parents: ip access-list test
|
||||
after: exit
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -25,7 +23,6 @@
|
|||
eos_config:
|
||||
lines: 10 permit ip any any log
|
||||
parents: ip access-list test
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -36,6 +33,5 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
|
||||
- debug: msg="END eapi/sublevel.yaml"
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
parents: ip access-list test
|
||||
before: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure sub level command using block resplace
|
||||
eos_config:
|
||||
|
@ -22,7 +21,6 @@
|
|||
parents: ip access-list test
|
||||
after: end
|
||||
replace: block
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -43,7 +41,6 @@
|
|||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -54,6 +51,5 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/sublevel_block.yaml"
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
before: no ip access-list test
|
||||
after: exit
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure sub level command using exact match
|
||||
eos_config:
|
||||
|
@ -26,7 +25,6 @@
|
|||
parents: ip access-list test
|
||||
after: exit
|
||||
match: exact
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -49,7 +47,6 @@
|
|||
before: no ip access-list test
|
||||
parents: ip access-list test
|
||||
match: exact
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -60,6 +57,5 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/sublevel_exact.yaml"
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
- 50 permit ip host 5.5.5.5 any log
|
||||
parents: ip access-list test
|
||||
before: no ip access-list test
|
||||
provider: "{{ eapi }}"
|
||||
match: none
|
||||
|
||||
- name: configure sub level command using strict match
|
||||
|
@ -26,7 +25,6 @@
|
|||
after: exit
|
||||
match: strict
|
||||
replace: block
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -48,7 +46,6 @@
|
|||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
match: strict
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -59,6 +56,5 @@
|
|||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/sublevel_strict.yaml"
|
||||
|
|
|
@ -5,12 +5,10 @@
|
|||
eos_config:
|
||||
lines: hostname {{ inventory_hostname_short }}
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure top level command
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -21,7 +19,6 @@
|
|||
- name: configure top level command idempotent check
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -32,6 +29,5 @@
|
|||
eos_config:
|
||||
lines: hostname {{ inventory_hostname_short }}
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/toplevel.yaml"
|
||||
|
|
|
@ -7,13 +7,11 @@
|
|||
- "snmp-server contact ansible"
|
||||
- "hostname {{ inventory_hostname_short }}"
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure top level command with before
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
after: snmp-server contact bar
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -26,7 +24,6 @@
|
|||
eos_config:
|
||||
lines: hostname foo
|
||||
after: snmp-server contact foo
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -39,6 +36,5 @@
|
|||
- no snmp-server contact
|
||||
- hostname {{ inventory_hostname_short }}
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/toplevel_after.yaml"
|
||||
|
|
|
@ -7,13 +7,11 @@
|
|||
- "snmp-server contact ansible"
|
||||
- "hostname {{ inventory_hostname_short }}"
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: configure top level command with before
|
||||
eos_config:
|
||||
lines: hostname foo
|
||||
before: snmp-server contact bar
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -26,7 +24,6 @@
|
|||
eos_config:
|
||||
lines: hostname foo
|
||||
before: snmp-server contact foo
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -39,6 +36,5 @@
|
|||
- no snmp-server contact ansible
|
||||
- hostname {{ inventory_hostname_short }}
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/toplevel_before.yaml"
|
||||
|
|
|
@ -14,9 +14,3 @@
|
|||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local ansible_become=no"
|
||||
with_first_found: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
- name: test getting all facts
|
||||
eos_facts:
|
||||
provider: "{{ cli }}"
|
||||
gather_subset:
|
||||
- all
|
||||
become: yes
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
- name: test getting default facts
|
||||
eos_facts:
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
- name: test invalid subset (foobar)
|
||||
eos_facts:
|
||||
provider: "{{ cli }}"
|
||||
gather_subset:
|
||||
- "foobar"
|
||||
register: result
|
||||
|
@ -26,7 +25,6 @@
|
|||
|
||||
- name: test subset specified multiple times
|
||||
eos_facts:
|
||||
provider: "{{ cli }}"
|
||||
gather_subset:
|
||||
- "!hardware"
|
||||
- "hardware"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
- name: test not hardware
|
||||
eos_facts:
|
||||
provider: "{{ cli }}"
|
||||
gather_subset:
|
||||
- "!hardware"
|
||||
become: yes
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
eos_config:
|
||||
lines: lldp run
|
||||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: test getting all facts
|
||||
eos_facts:
|
||||
provider: "{{ eapi }}"
|
||||
gather_subset:
|
||||
- all
|
||||
register: result
|
||||
|
@ -35,6 +33,5 @@
|
|||
eos_config:
|
||||
lines: no lldp run
|
||||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/all_facts.yaml"
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
eos_config:
|
||||
lines: lldp run
|
||||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: test getting default facts
|
||||
eos_facts:
|
||||
provider: "{{ eapi }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
@ -37,6 +35,5 @@
|
|||
eos_config:
|
||||
lines: lldp run
|
||||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/default.yaml"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
- name: test invalid subset (foobar)
|
||||
eos_facts:
|
||||
provider: "{{ eapi }}"
|
||||
gather_subset:
|
||||
- "foobar"
|
||||
register: result
|
||||
|
@ -26,7 +25,6 @@
|
|||
|
||||
- name: test subset specified multiple times
|
||||
eos_facts:
|
||||
provider: "{{ eapi }}"
|
||||
gather_subset:
|
||||
- "!hardware"
|
||||
- "hardware"
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
eos_config:
|
||||
lines: lldp run
|
||||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: test not hardware
|
||||
eos_facts:
|
||||
provider: "{{ eapi }}"
|
||||
gather_subset:
|
||||
- "!hardware"
|
||||
register: result
|
||||
|
@ -36,6 +34,5 @@
|
|||
eos_config:
|
||||
lines: no lldp run
|
||||
authorize: yes
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END eapi/not_hardware_facts.yaml"
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -17,6 +17,6 @@
|
|||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local ansible_become=no"
|
||||
with_first_found: "{{ test_items }}"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
with_items: "{{ test_items }}"
|
||||
|
|
|
@ -45,64 +45,65 @@
|
|||
become: yes
|
||||
|
||||
# hit block and diffs
|
||||
- block:
|
||||
- name: setup
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
parents: ip access-list test
|
||||
before: no ip access-list test
|
||||
after: exit
|
||||
match: strict
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- name: setup
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
parents: ip access-list test
|
||||
before: no ip access-list test
|
||||
after: exit
|
||||
match: strict
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
- name: configure sub level command using block replace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ cli }}"
|
||||
match: line
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
- name: configure sub level command using block resplace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ cli }}"
|
||||
match: line
|
||||
become: yes
|
||||
register: result
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'ip access-list test' in result.updates"
|
||||
- "'10 permit ip host 1.1.1.1 any log' in result.updates"
|
||||
- "'20 permit ip host 2.2.2.2 any log' in result.updates"
|
||||
- "'30 permit ip host 3.3.3.3 any log' in result.updates"
|
||||
- "'40 permit ip host 4.4.4.4 any log' in result.updates"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'ip access-list test' in result.updates"
|
||||
- "'10 permit ip host 1.1.1.1 any log' in result.updates"
|
||||
- "'20 permit ip host 2.2.2.2 any log' in result.updates"
|
||||
- "'30 permit ip host 3.3.3.3 any log' in result.updates"
|
||||
- "'40 permit ip host 4.4.4.4 any log' in result.updates"
|
||||
- name: check sub level command using block replace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ cli }}"
|
||||
match: exact
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
- name: check sub level command using block replace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ cli }}"
|
||||
match: exact
|
||||
become: yes
|
||||
register: result
|
||||
|
||||
- name: teardown
|
||||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
always:
|
||||
- name: teardown
|
||||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ cli }}"
|
||||
become: yes
|
||||
|
||||
- debug: msg="END cli/common_config.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -40,60 +40,61 @@
|
|||
provider: "{{ eapi }}"
|
||||
|
||||
# hit block and diffs
|
||||
- block:
|
||||
- name: setup
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
parents: ip access-list test
|
||||
before: no ip access-list test
|
||||
after: exit
|
||||
match: strict
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- name: setup
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
parents: ip access-list test
|
||||
before: no ip access-list test
|
||||
after: exit
|
||||
match: strict
|
||||
provider: "{{ eapi }}"
|
||||
- name: configure sub level command using block replace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ eapi }}"
|
||||
match: line
|
||||
register: result
|
||||
|
||||
- name: configure sub level command using block resplace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ eapi }}"
|
||||
match: line
|
||||
register: result
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'ip access-list test' in result.updates"
|
||||
- "'10 permit ip host 1.1.1.1 any log' in result.updates"
|
||||
- "'20 permit ip host 2.2.2.2 any log' in result.updates"
|
||||
- "'30 permit ip host 3.3.3.3 any log' in result.updates"
|
||||
- "'40 permit ip host 4.4.4.4 any log' in result.updates"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'ip access-list test' in result.updates"
|
||||
- "'10 permit ip host 1.1.1.1 any log' in result.updates"
|
||||
- "'20 permit ip host 2.2.2.2 any log' in result.updates"
|
||||
- "'30 permit ip host 3.3.3.3 any log' in result.updates"
|
||||
- "'40 permit ip host 4.4.4.4 any log' in result.updates"
|
||||
- name: check sub level command using block replace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ eapi }}"
|
||||
match: exact
|
||||
register: result
|
||||
|
||||
- name: check sub level command using block replace
|
||||
eos_config:
|
||||
lines:
|
||||
- 10 permit ip host 1.1.1.1 any log
|
||||
- 20 permit ip host 2.2.2.2 any log
|
||||
- 30 permit ip host 3.3.3.3 any log
|
||||
- 40 permit ip host 4.4.4.4 any log
|
||||
parents: ip access-list test
|
||||
replace: block
|
||||
after: exit
|
||||
provider: "{{ eapi }}"
|
||||
match: exact
|
||||
register: result
|
||||
|
||||
- name: teardown
|
||||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
always:
|
||||
- name: teardown
|
||||
eos_config:
|
||||
lines: no ip access-list test
|
||||
match: none
|
||||
provider: "{{ eapi }}"
|
||||
|
||||
- debug: msg="END cli/common_config.yaml on connection={{ ansible_connection }}"
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
- debug: msg="START cli/misc_tests.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
|
||||
# test become and unbecome
|
||||
- block:
|
||||
- name: command that does require become (should fail)
|
||||
eos_command:
|
||||
commands: show running-config
|
||||
provider: "{{ eapi }}"
|
||||
become: no
|
||||
ignore_errors: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- 'result.failed == true'
|
||||
- '"privileged mode required" in result.module_stderr'
|
||||
|
||||
- name: command that doesn't require become
|
||||
eos_command:
|
||||
commands: show uptime
|
||||
provider: "{{ eapi }}"
|
||||
become: no
|
||||
|
||||
when: "ansible_connection != 'local'"
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
- name: run test cases (connection=httpapi)
|
||||
include: "{{ test_case_to_run }} ansible_connection=httpapi"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue