This repository has been archived by the owner on Jan 18, 2022. It is now read-only.
forked from ansible/ansible
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New module: routeros — manage MikroTik RouterOS (ansible#41155)
* 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
Showing
15 changed files
with
941 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Oops, something went wrong.