mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-02 14:40:19 -07:00
Add support for cliconf and netconf plugin (#25093)
* ansible-connection refactor and action plugin changes * Add cliconf plugin for eos, ios, iosxr, junos, nxos, vyos * Add netconf plugin for junos * Add jsonrpc support * Modify network_cli and netconf connection plugin * Fix py3 unit test failure * Fix review comment * Minor fixes * Fix ansible-connection review comments * Fix CI issue * platform_agnostic related changes
This commit is contained in:
parent
c20285782d
commit
6215922889
32 changed files with 1542 additions and 585 deletions
188
lib/ansible/plugins/cliconf/__init__.py
Normal file
188
lib/ansible/plugins/cliconf/__init__.py
Normal file
|
@ -0,0 +1,188 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import signal
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from functools import wraps
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleConnectionFailure
|
||||
from ansible.module_utils.six import with_metaclass
|
||||
|
||||
try:
|
||||
from __main__ import display
|
||||
except ImportError:
|
||||
from ansible.utils.display import Display
|
||||
display = Display()
|
||||
|
||||
|
||||
def enable_mode(func):
|
||||
@wraps(func)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
prompt = self.get_prompt()
|
||||
if not str(prompt).strip().endswith('#'):
|
||||
raise AnsibleError('operation requires privilege escalation')
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
class CliconfBase(with_metaclass(ABCMeta, object)):
|
||||
"""
|
||||
A base class for implementing cli connections
|
||||
|
||||
.. note:: Unlike most of Ansible, nearly all strings in
|
||||
:class:`CliconfBase` plugins are byte strings. This is because of
|
||||
how close to the underlying platform these plugins operate. Remember
|
||||
to mark literal strings as byte string (``b"string"``) and to use
|
||||
:func:`~ansible.module_utils._text.to_bytes` and
|
||||
:func:`~ansible.module_utils._text.to_text` to avoid unexpected
|
||||
problems.
|
||||
|
||||
List of supported rpc's:
|
||||
:get_config: Retrieves the specified configuration from the device
|
||||
:edit_config: Loads the specified commands into the remote device
|
||||
:get: Execute specified command on remote device
|
||||
:get_capabilities: Retrieves device information and supported rpc methods
|
||||
:commit: Load configuration from candidate to running
|
||||
:discard_changes: Discard changes to candidate datastore
|
||||
|
||||
Note: List of supported rpc's for remote device can be extracted from
|
||||
output of get_capabilities()
|
||||
|
||||
:returns: Returns output received from remote device as byte string
|
||||
|
||||
Usage:
|
||||
from ansible.module_utils.connection import Connection
|
||||
|
||||
conn = Connection()
|
||||
conn.get('show lldp neighbors detail'')
|
||||
conn.get_config('running')
|
||||
conn.edit_config(['hostname test', 'netconf ssh'])
|
||||
"""
|
||||
|
||||
def __init__(self, connection):
|
||||
self._connection = connection
|
||||
|
||||
def _alarm_handler(self, signum, frame):
|
||||
raise AnsibleConnectionFailure('timeout waiting for command to complete')
|
||||
|
||||
def send_command(self, command, prompt=None, answer=None, sendonly=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
|
||||
"""
|
||||
timeout = self._connection._play_context.timeout or 30
|
||||
signal.signal(signal.SIGALRM, self._alarm_handler)
|
||||
signal.alarm(timeout)
|
||||
display.display("command: %s" % command, log_only=True)
|
||||
resp = self._connection.send(command, prompt, answer, sendonly)
|
||||
signal.alarm(0)
|
||||
return resp
|
||||
|
||||
def get_prompt(self):
|
||||
"""Returns the current prompt from the device"""
|
||||
return self._connection._matched_prompt
|
||||
|
||||
def get_base_rpc(self):
|
||||
"""Returns list of base rpc method supported by remote device"""
|
||||
return ['get_config', 'edit_config', 'get_capabilities', 'get']
|
||||
|
||||
@abstractmethod
|
||||
def get_config(self, source='running', format='text'):
|
||||
"""Retrieves the specified configuration from the device
|
||||
This method will retrieve the configuration specified by source and
|
||||
return it to the caller as a string. Subsequent calls to this method
|
||||
will retrieve a new configuration from the device
|
||||
:args:
|
||||
arg[0] source: Datastore from which configuration should be retrieved eg: running/candidate/startup. (optional)
|
||||
default is running.
|
||||
arg[1] format: Output format in which configuration is retrieved
|
||||
Note: Specified datastore should be supported by remote device.
|
||||
:kwargs:
|
||||
Keywords supported
|
||||
:command: the command string to execute
|
||||
:source: Datastore from which configuration should be retrieved
|
||||
:format: Output format in which configuration is retrieved
|
||||
:returns: Returns output received from remote device as byte string
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def edit_config(self, commands):
|
||||
"""Loads the specified commands into the remote device
|
||||
This method will load the commands into the remote device. This
|
||||
method will make sure the device is in the proper context before
|
||||
send the commands (eg config mode)
|
||||
:args:
|
||||
arg[0] command: List of configuration commands
|
||||
:kwargs:
|
||||
Keywords supported
|
||||
:command: the command string to execute
|
||||
:returns: Returns output received from remote device as byte string
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get(self, *args, **kwargs):
|
||||
"""Execute specified command on remote device
|
||||
This method will retrieve the specified data and
|
||||
return it to the caller as a string.
|
||||
:args:
|
||||
arg[0] command: command in string format to be executed on remote device
|
||||
arg[1] prompt: the expected prompt generated by executing command.
|
||||
This can be a string or a list of strings (optional)
|
||||
arg[2] answer: the string to respond to the prompt with (optional)
|
||||
arg[3] sendonly: bool to disable waiting for response, default is false (optional)
|
||||
:kwargs:
|
||||
:command: the command string to execute
|
||||
:prompt: the expected prompt generated by executing command.
|
||||
This can be a string or a list of strings
|
||||
:answer: the string to respond to the prompt with
|
||||
:sendonly: bool to disable waiting for response
|
||||
:returns: Returns output received from remote device as byte string
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_capabilities(self):
|
||||
"""Retrieves device information and supported
|
||||
rpc methods by device platform and return result
|
||||
as a string
|
||||
:returns: Returns output received from remote device as byte string
|
||||
"""
|
||||
pass
|
||||
|
||||
def commit(self, comment=None):
|
||||
"""Commit configuration changes"""
|
||||
return self._connection.method_not_found("commit is not supported by network_os %s" % self._play_context.network_os)
|
||||
|
||||
def discard_changes(self):
|
||||
"Discard changes in candidate datastore"
|
||||
return self._connection.method_not_found("discard_changes is not supported by network_os %s" % self._play_context.network_os)
|
||||
|
||||
def put_file(self, source, destination):
|
||||
"""Copies file over scp to remote device"""
|
||||
pass
|
||||
|
||||
def fetch_file(self, source, destination):
|
||||
"""Fetch file over scp from remote device"""
|
||||
pass
|
73
lib/ansible/plugins/cliconf/eos.py
Normal file
73
lib/ansible/plugins/cliconf/eos.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils.network_common import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
device_info['network_os'] = 'eos'
|
||||
reply = self.get(b'show version | json')
|
||||
data = json.loads(reply)
|
||||
|
||||
device_info['network_os_version'] = data['version']
|
||||
device_info['network_os_model'] = data['modelName']
|
||||
|
||||
reply = self.get(b'show hostname | json')
|
||||
data = json.loads(reply)
|
||||
|
||||
device_info['network_os_hostname'] = data['hostname']
|
||||
|
||||
return device_info
|
||||
|
||||
@enable_mode
|
||||
def get_config(self, source='running', format='text'):
|
||||
lookup = {'running': 'running-config', 'startup': 'startup-config'}
|
||||
if source not in lookup:
|
||||
return self.invalid_params("fetching configuration from %s is not supported" % source)
|
||||
if format == 'text':
|
||||
cmd = b'show %s' % lookup[source]
|
||||
else:
|
||||
cmd = b'show %s | %s' % (lookup[source], format)
|
||||
return self.send_command(cmd)
|
||||
|
||||
@enable_mode
|
||||
def edit_config(self, command):
|
||||
for cmd in chain([b'configure'], to_list(command), [b'end']):
|
||||
self.send_command(cmd)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.send_command(*args, **kwargs)
|
||||
|
||||
def get_capabilities(self):
|
||||
result = {}
|
||||
result['rpc'] = self.get_base_rpc()
|
||||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
78
lib/ansible/plugins/cliconf/ios.py
Normal file
78
lib/ansible/plugins/cliconf/ios.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.network_common import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
device_info['network_os'] = 'ios'
|
||||
reply = self.get(b'show version')
|
||||
data = to_text(reply, errors='surrogate_or_strict').strip()
|
||||
|
||||
match = re.search(r'Version (\S+),', data)
|
||||
if match:
|
||||
device_info['network_os_version'] = match.group(1)
|
||||
|
||||
match = re.search(r'^Cisco (.+) \(revision', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_model'] = match.group(1)
|
||||
|
||||
match = re.search(r'^(.+) uptime', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_hostname'] = match.group(1)
|
||||
|
||||
return device_info
|
||||
|
||||
@enable_mode
|
||||
def get_config(self, source='running'):
|
||||
if source not in ('running', 'startup'):
|
||||
return self.invalid_params("fetching configuration from %s is not supported" % source)
|
||||
if source == 'running':
|
||||
cmd = b'show running-config all'
|
||||
else:
|
||||
cmd = b'show startup-config'
|
||||
return self.send_command(cmd)
|
||||
|
||||
@enable_mode
|
||||
def edit_config(self, command):
|
||||
for cmd in chain([b'configure terminal'], to_list(command), [b'end']):
|
||||
self.send_command(cmd)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.send_command(*args, **kwargs)
|
||||
|
||||
def get_capabilities(self):
|
||||
result = {}
|
||||
result['rpc'] = self.get_base_rpc()
|
||||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
87
lib/ansible/plugins/cliconf/iosxr.py
Normal file
87
lib/ansible/plugins/cliconf/iosxr.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.network_common import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
device_info['network_os'] = 'iosxr'
|
||||
reply = self.get(b'show version brief')
|
||||
data = to_text(reply, errors='surrogate_or_strict').strip()
|
||||
|
||||
match = re.search(r'Version (\S+)$', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_version'] = match.group(1)
|
||||
|
||||
match = re.search(r'image file is "(.+)"', data)
|
||||
if match:
|
||||
device_info['network_os_image'] = match.group(1)
|
||||
|
||||
match = re.search(r'^Cisco (.+) \(revision', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_model'] = match.group(1)
|
||||
|
||||
match = re.search(r'^(.+) uptime', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_hostname'] = match.group(1)
|
||||
|
||||
return device_info
|
||||
|
||||
def get_config(self, source='running'):
|
||||
lookup = {'running': 'running-config'}
|
||||
if source not in lookup:
|
||||
return self.invalid_params("fetching configuration from %s is not supported" % source)
|
||||
return self.send_command(to_bytes(b'show %s' % lookup[source], errors='surrogate_or_strict'))
|
||||
|
||||
def edit_config(self, command):
|
||||
for cmd in chain([b'configure'], to_list(command), [b'end']):
|
||||
self.send_command(cmd)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.send_command(*args, **kwargs)
|
||||
|
||||
def commit(self, comment=None):
|
||||
if comment:
|
||||
command = b'commit comment {0}'.format(comment)
|
||||
else:
|
||||
command = b'commit'
|
||||
self.send_command(command)
|
||||
|
||||
def discard_changes(self):
|
||||
self.send_command(b'abort')
|
||||
|
||||
def get_capabilities(self):
|
||||
result = {}
|
||||
result['rpc'] = self.get_base_rpc() + ['commit', 'discard_changes']
|
||||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
87
lib/ansible/plugins/cliconf/junos.py
Normal file
87
lib/ansible/plugins/cliconf/junos.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
from xml.etree.ElementTree import fromstring
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.network_common import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
device_info['network_os'] = 'junos'
|
||||
reply = self.get(b'show version | display xml')
|
||||
data = fromstring(to_text(reply, errors='surrogate_then_replace').strip())
|
||||
|
||||
sw_info = data.find('.//software-information')
|
||||
|
||||
device_info['network_os_version'] = self.get_text(sw_info, 'junos-version')
|
||||
device_info['network_os_hostname'] = self.get_text(sw_info, 'host-name')
|
||||
device_info['network_os_model'] = self.get_text(sw_info, 'product-model')
|
||||
|
||||
return device_info
|
||||
|
||||
def get_config(self, source='running', format='text'):
|
||||
if source != 'running':
|
||||
return self.invalid_params("fetching configuration from %s is not supported" % source)
|
||||
if format == 'text':
|
||||
cmd = b'show configuration'
|
||||
else:
|
||||
cmd = b'show configuration | display %s' % format
|
||||
return self.send_command(to_bytes(cmd), errors='surrogate_or_strict')
|
||||
|
||||
def edit_config(self, command):
|
||||
for cmd in chain([b'configure'], to_list(command)):
|
||||
self.send_command(cmd)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.send_command(*args, **kwargs)
|
||||
|
||||
def commit(self, comment=None):
|
||||
if comment:
|
||||
command = b'commit comment {0}'.format(comment)
|
||||
else:
|
||||
command = b'commit'
|
||||
self.send_command(command)
|
||||
|
||||
def discard_changes(self):
|
||||
self.send_command(b'rollback')
|
||||
|
||||
def get_capabilities(self):
|
||||
result = {}
|
||||
result['rpc'] = self.get_base_rpc() + ['commit', 'discard_changes']
|
||||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
62
lib/ansible/plugins/cliconf/nxos.py
Normal file
62
lib/ansible/plugins/cliconf/nxos.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils.network_common import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
device_info['network_os'] = 'nxos'
|
||||
reply = self.get(b'show version | json')
|
||||
data = json.loads(reply)
|
||||
|
||||
device_info['network_os_version'] = data['sys_ver_str']
|
||||
device_info['network_os_model'] = data['chassis_id']
|
||||
device_info['network_os_hostname'] = data['host_name']
|
||||
device_info['network_os_image'] = data['isan_file_name']
|
||||
|
||||
return device_info
|
||||
|
||||
def get_config(self, source='running'):
|
||||
lookup = {'running': 'running-config', 'startup': 'startup-config'}
|
||||
return self.send_command(b'show %s' % lookup[source])
|
||||
|
||||
def edit_config(self, command):
|
||||
for cmd in chain([b'configure'], to_list(command), [b'end']):
|
||||
self.send_command(cmd)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.send_command(*args, **kwargs)
|
||||
|
||||
def get_capabilities(self):
|
||||
result = {}
|
||||
result['rpc'] = self.get_base_rpc()
|
||||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
79
lib/ansible/plugins/cliconf/vyos.py
Normal file
79
lib/ansible/plugins/cliconf/vyos.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.network_common import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase, enable_mode
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
||||
device_info['network_os'] = 'vyos'
|
||||
reply = self.get(b'show version')
|
||||
data = to_text(reply, errors='surrogate_or_strict').strip()
|
||||
|
||||
match = re.search(r'Version:\s*(\S+)', data)
|
||||
if match:
|
||||
device_info['network_os_version'] = match.group(1)
|
||||
|
||||
match = re.search(r'HW model:\s*(\S+)', data)
|
||||
if match:
|
||||
device_info['network_os_model'] = match.group(1)
|
||||
|
||||
reply = self.get(b'show host name')
|
||||
device_info['network_os_hostname'] = to_text(reply, errors='surrogate_or_strict').strip()
|
||||
|
||||
return device_info
|
||||
|
||||
def get_config(self):
|
||||
return self.send_command(b'show configuration all')
|
||||
|
||||
def edit_config(self, command):
|
||||
for cmd in chain([b'configure'], to_list(command)):
|
||||
self.send_command(cmd)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.send_command(*args, **kwargs)
|
||||
|
||||
def commit(self, comment=None):
|
||||
if comment:
|
||||
command = b'commit comment {0}'.format(comment)
|
||||
else:
|
||||
command = b'commit'
|
||||
self.send_command(command)
|
||||
|
||||
def discard_changes(self, *args, **kwargs):
|
||||
self.send_command(b'discard')
|
||||
|
||||
def get_capabilities(self):
|
||||
result = {}
|
||||
result['rpc'] = self.get_base_rpc() + ['commit', 'discard_changes']
|
||||
result['network_api'] = 'cliconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
return json.dumps(result)
|
Loading…
Add table
Add a link
Reference in a new issue