Skip to content
This repository has been archived by the owner on Jan 18, 2022. It is now read-only.

Commit

Permalink
New module: routeros — manage MikroTik RouterOS (ansible#41155)
Browse files Browse the repository at this point in the history
* Implement initial RouterOS support

* Correct matchers for license prompts

* Documentation updates & mild refactor

* Remove one last Cisco function

* Sanity test fixes

* Move imports to the beginning

* Remove authorize property

* Handle ANSI codes

* Revert to_lines function

* CR fixes

* test(routeros): add unit tests

* Added another test (with ANSI colors and banner in fixture).

* Ignore CRLF line endings in system_package_print file

* fix: review by ganeshrn
  • Loading branch information
heuels authored and ganeshrn committed Jul 30, 2018
1 parent 21dcaa4 commit 249a6aa
Show file tree
Hide file tree
Showing 15 changed files with 941 additions and 27 deletions.
10 changes: 10 additions & 0 deletions .github/BOTMETA.yml
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ files:
$modules/network/panos/: ivanbojer jtschichold
$modules/network/panos/panos_address.py: itdependsnetworks ivanbojer jtschichold
$modules/network/protocol/: $team_networking
$modules/network/routeros/: heuels
$modules/network/routing/: $team_networking
$modules/network/slxos/: $team_extreme
$modules/network/sros/: privateip
Expand Down Expand Up @@ -895,6 +896,9 @@ files:
$module_utils/network/onyx:
maintainers: $team_onyx
labels: networking
$module_utils/network/routeros:
maintainers: heuels
labels: networking
$module_utils/network/slxos:
maintainers: $team_extreme
labels: networking
Expand Down Expand Up @@ -1021,6 +1025,9 @@ files:
lib/ansible/plugins/cliconf/onyx.py:
maintainers: $team_onyx
labels: networking
lib/ansible/plugins/cliconf/routeros.py:
maintainers: heuels
labels: networking
lib/ansible/plugins/cliconf/slxos.py:
maintainers: $team_extreme
labels: networking
Expand Down Expand Up @@ -1128,6 +1135,9 @@ files:
lib/ansible/plugins/terminal/onyx.py:
maintainers: $team_onyx
labels: networking
lib/ansible/plugins/terminal/routeros.py:
maintainers: heuels
labels: networking
lib/ansible/plugins/terminal/slxos.py:
maintainers: $team_extreme
labels: networking
Expand Down
57 changes: 30 additions & 27 deletions docs/docsite/rst/network/user_guide/platform_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,41 @@ Some Ansible Network platforms support multiple connection types, privilege esca
platform_ironware
platform_junos
platform_nxos
platform_routeros

.. _settings_by_platform:

Settings by Platform
================================

+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
|.. | | ``ansible_connection:`` settings available |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Network OS | ``ansible_network_os:`` | network_cli | netconf | httpapi | local |
+==================+=========================+======================+======================+==================+==================+
| Arista EOS* | ``eos`` | in v. >=2.5 | N/A | in v. >=2.6 | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco ASA | ``asa`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco IOS* | ``ios`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco IOS XR* | ``iosxr`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco NX-OS* | ``nxos`` | in v. >=2.5 | N/A | in v. >=2.6 | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Extreme IronWare | ``ironware`` | in v. >=2.5 | N/A | N/A | in v. >=2.5 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| F5 BIG-IP | N/A | N/A | N/A | N/A | in v. >=2.0 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| F5 BIG-IQ | N/A | N/A | N/A | N/A | in v. >=2.0 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Junos OS* | ``junos`` | in v. >=2.5 | in v. >=2.5 | N/A | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Nokia SR OS | ``sros`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| VyOS* | ``vyos`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+------------------+-------------------------+----------------------+----------------------+------------------+------------------+
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
|.. | | ``ansible_connection:`` settings available |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Network OS | ``ansible_network_os:`` | network_cli | netconf | httpapi | local |
+===================+=========================+======================+======================+==================+==================+
| Arista EOS* | ``eos`` | in v. >=2.5 | N/A | in v. >=2.6 | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco ASA | ``asa`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco IOS* | ``ios`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco IOS XR* | ``iosxr`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Cisco NX-OS* | ``nxos`` | in v. >=2.5 | N/A | in v. >=2.6 | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Extreme IronWare | ``ironware`` | in v. >=2.5 | N/A | N/A | in v. >=2.5 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| F5 BIG-IP | N/A | N/A | N/A | N/A | in v. >=2.0 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| F5 BIG-IQ | N/A | N/A | N/A | N/A | in v. >=2.0 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Junos OS* | ``junos`` | in v. >=2.5 | in v. >=2.5 | N/A | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| MikroTik RouterOS | ``routeros`` | in v. >=2.7 | N/A | N/A | N/A |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| Nokia SR OS | ``sros`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+
| VyOS* | ``vyos`` | in v. >=2.5 | N/A | N/A | in v. >=2.4 |
+-------------------+-------------------------+----------------------+----------------------+------------------+------------------+

`*` Maintained by Ansible Network Team
65 changes: 65 additions & 0 deletions docs/docsite/rst/network/user_guide/platform_routeros.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.. _routeros_platform_options:

***************************************
RouterOS Platform Options
***************************************

.. contents:: Topics

Connections Available
================================================================================

+---------------------------+-----------------------------------------------+
|.. | CLI |
+===========================+===============================================+
| **Protocol** | SSH |
+---------------------------+-----------------------------------------------+
| | **Credentials** | | uses SSH keys / SSH-agent if present |
| | | | accepts ``-u myuser -k`` if using password |
+---------------------------+-----------------------------------------------+
| **Indirect Access** | via a bastion (jump host) |
+---------------------------+-----------------------------------------------+
| | **Connection Settings** | | ``ansible_connection: network_cli`` |
| | | | |
| | | | |
+---------------------------+-----------------------------------------------+
| | **Enable Mode** | | not supported by RouterOS |
| | (Privilege Escalation) | | |
+---------------------------+-----------------------------------------------+
| **Returned Data Format** | ``stdout[0].`` |
+---------------------------+-----------------------------------------------+

Using CLI in Ansible 2.6
================================================================================

Example CLI ``group_vars/routeros.yml``
---------------------------------------

.. code-block:: yaml
ansible_connection: network_cli
ansible_network_os: routeros
ansible_user: myuser
ansible_ssh_pass: !vault...
ansible_become: yes
ansible_become_method: enable
ansible_become_pass: !vault...
ansible_ssh_common_args: '-o ProxyCommand="ssh -W %h:%p -q bastion01"'
- If you are using SSH keys (including an ssh-agent) you can remove the ``ansible_ssh_pass`` configuration.
- If you are accessing your host directly (not through a bastion/jump host) you can remove the ``ansible_ssh_common_args`` configuration.
- If you are accessing your host through a bastion/jump host, you cannot include your SSH password in the ``ProxyCommand`` directive. To prevent secrets from leaking out (for example in ``ps`` output), SSH does not support providing passwords via environment variables.

Example CLI Task
----------------

.. code-block:: yaml
- name: Display resource statistics (routeros)
routeros_command:
commands: /system resource print
register: routeros_resources
when: ansible_network_os == 'routeros'
.. include:: shared_snippets/SSH_warning.txt
Empty file.
156 changes: 156 additions & 0 deletions lib/ansible/module_utils/network/routeros/routeros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# (c) 2016 Red Hat Inc.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import json
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback, return_values
from ansible.module_utils.network.common.utils import to_list, ComplexList
from ansible.module_utils.connection import Connection

_DEVICE_CONFIGS = {}

routeros_provider_spec = {
'host': dict(),
'port': dict(type='int'),
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
'timeout': dict(type='int')
}
routeros_argument_spec = {}


def get_provider_argspec():
return routeros_provider_spec


def get_connection(module):
if hasattr(module, '_routeros_connection'):
return module._routeros_connection

capabilities = get_capabilities(module)
network_api = capabilities.get('network_api')
if network_api == 'cliconf':
module._routeros_connection = Connection(module._socket_path)
else:
module.fail_json(msg='Invalid connection type %s' % network_api)

return module._routeros_connection


def get_capabilities(module):
if hasattr(module, '_routeros_capabilities'):
return module._routeros_capabilities

capabilities = Connection(module._socket_path).get_capabilities()
module._routeros_capabilities = json.loads(capabilities)
return module._routeros_capabilities


def get_defaults_flag(module):
connection = get_connection(module)

try:
out = connection.get('/system default-configuration print')
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))

out = to_text(out, errors='surrogate_then_replace')

commands = set()
for line in out.splitlines():
if line.strip():
commands.add(line.strip().split()[0])

if 'all' in commands:
return ['all']
else:
return ['full']


def get_config(module, flags=None):
flag_str = ' '.join(to_list(flags))

try:
return _DEVICE_CONFIGS[flag_str]
except KeyError:
connection = get_connection(module)

try:
out = connection.get_config(flags=flags)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))

cfg = to_text(out, errors='surrogate_then_replace').strip()
_DEVICE_CONFIGS[flag_str] = cfg
return cfg


def to_commands(module, commands):
spec = {
'command': dict(key=True),
'prompt': dict(),
'answer': dict()
}
transform = ComplexList(spec, module)
return transform(commands)


def run_commands(module, commands, check_rc=True):
responses = list()
connection = get_connection(module)

for cmd in to_list(commands):
if isinstance(cmd, dict):
command = cmd['command']
prompt = cmd['prompt']
answer = cmd['answer']
else:
command = cmd
prompt = None
answer = None

try:
out = connection.get(command, prompt, answer)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))

try:
out = to_text(out, errors='surrogate_or_strict')
except UnicodeError:
module.fail_json(
msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out)))

responses.append(out)

return responses


def load_config(module, commands):
connection = get_connection(module)

out = connection.edit_config(commands)
Empty file.
Loading

0 comments on commit 249a6aa

Please sign in to comment.