nxos cliconf plugin refactor (#43203)

* nxos cliconf plugin refactor

Fixes #39056

*  Refactor nxos cliconf plugin as per new api definition
*  Minor changes in ios, eos, vyos cliconf plugin
*  Change nxos httpapi plugin edit_config method to be in sync with
   nxos cliconf edit_config

* Fix CI failure

* Fix unit test failure and review comment
This commit is contained in:
Ganesh Nalawade 2018-07-27 11:05:40 +05:30 committed by GitHub
parent e215f842ba
commit af3f510316
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 426 additions and 245 deletions

View file

@ -189,7 +189,7 @@ class CliconfBase(AnsiblePlugin):
pass
@abstractmethod
def edit_config(self, candidate=None, commit=True, replace=False, diff=False, comment=None):
def edit_config(self, candidate=None, commit=True, replace=None, diff=False, comment=None):
"""Loads the candidate configuration into the network device
This method will load the specified candidate config into the device
@ -203,8 +203,10 @@ class CliconfBase(AnsiblePlugin):
:param commit: Boolean value that indicates if the device candidate
configuration should be pushed in the running configuration or discarded.
:param replace: Boolean flag to indicate if running configuration should be completely
replace by candidate configuration.
:param replace: If the value is True/False it indicates if running configuration should be completely
replace by candidate configuration. If can also take configuration file path as value,
the file in this case should be present on the remote host in the mentioned path as a
prerequisite.
:param comment: Commit comment provided it is supported by remote host
:return: Returns a json string with contains configuration applied on remote host, the returned
response on executing configuration commands and platform relevant data.
@ -341,7 +343,7 @@ class CliconfBase(AnsiblePlugin):
with ssh.open_sftp() as sftp:
sftp.get(source, destination)
def get_diff(self, candidate=None, running=None, match=None, diff_ignore_lines=None, path=None, replace=None):
def get_diff(self, candidate=None, running=None, diff_match=None, diff_ignore_lines=None, path=None, diff_replace=None):
"""
Generate diff between candidate and running configuration. If the
remote host supports onbox diff capabilities ie. supports_onbox_diff in that case
@ -350,7 +352,7 @@ class CliconfBase(AnsiblePlugin):
and running argument is optional.
:param candidate: The configuration which is expected to be present on remote host.
:param running: The base configuration which is used to generate diff.
:param match: Instructs how to match the candidate configuration with current device configuration
:param diff_match: Instructs how to match the candidate configuration with current device configuration
Valid values are 'line', 'strict', 'exact', 'none'.
'line' - commands are matched line by line
'strict' - command lines are matched with respect to position
@ -364,7 +366,7 @@ class CliconfBase(AnsiblePlugin):
the commands should be checked against. If the parents argument
is omitted, the commands are checked against the set of top
level or global commands.
:param replace: Instructs on the way to perform the configuration on the device.
:param diff_replace: Instructs on the way to perform the configuration on the device.
If the replace argument is set to I(line) then the modified lines are
pushed to the device in configuration mode. If the replace argument is
set to I(block) then the entire command block is pushed to the device in
@ -396,3 +398,20 @@ class CliconfBase(AnsiblePlugin):
:return: List of returned response
"""
pass
def check_edit_config_capabiltiy(self, operations, candidate=None, commit=True, replace=None, comment=None):
if not candidate and not replace:
raise ValueError("must provide a candidate or replace to load configuration")
if commit not in (True, False):
raise ValueError("'commit' must be a bool, got %s" % commit)
if replace and not operations['supports_replace']:
raise ValueError("configuration replace is not supported")
if comment and not operations.get('supports_commit_comment', False):
raise ValueError("commit comment is not supported")
if replace and not operations.get('supports_replace', False):
raise ValueError("configuration replace is not supported")

View file

@ -59,23 +59,6 @@ class Cliconf(CliconfBase):
def __init__(self, *args, **kwargs):
super(Cliconf, self).__init__(*args, **kwargs)
self._session_support = None
if isinstance(self._connection, NetworkCli):
self.network_api = 'network_cli'
elif isinstance(self._connection, HttpApi):
self.network_api = 'eapi'
else:
raise ValueError("Invalid connection type")
def _get_command_with_output(self, command, output):
options_values = self.get_option_values()
if output not in options_values['output']:
raise ValueError("'output' value %s is invalid. Valid values are %s" % (output, ','.join(options_values['output'])))
if output == 'json' and not command.endswith('| json'):
cmd = '%s | json' % command
else:
cmd = command
return cmd
def send_command(self, command, **kwargs):
"""Executes a cli command and returns the results
@ -83,10 +66,12 @@ class Cliconf(CliconfBase):
the results to the caller. The command output will be returned as a
string
"""
if self.network_api == 'network_cli':
if isinstance(self._connection, NetworkCli):
resp = super(Cliconf, self).send_command(command, **kwargs)
else:
elif isinstance(self._connection, HttpApi):
resp = self._connection.send_request(command, **kwargs)
else:
raise ValueError("Invalid connection type")
return resp
@enable_mode
@ -108,32 +93,19 @@ class Cliconf(CliconfBase):
return self.send_command(cmd)
@enable_mode
def edit_config(self, candidate=None, commit=True, replace=False, comment=None):
if not candidate:
raise ValueError("must provide a candidate config to load")
if commit not in (True, False):
raise ValueError("'commit' must be a bool, got %s" % commit)
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
operations = self.get_device_operations()
if replace not in (True, False):
raise ValueError("'replace' must be a bool, got %s" % replace)
if replace and not operations['supports_replace']:
raise ValueError("configuration replace is supported only with configuration session")
if comment and not operations['supports_commit_comment']:
raise ValueError("commit comment is not supported")
self.check_edit_config_capabiltiy(operations, candidate, commit, replace, comment)
if (commit is False) and (not self.supports_sessions):
raise ValueError('check mode is not supported without configuration session')
response = {}
resp = {}
session = None
if self.supports_sessions:
session = 'ansible_%s' % int(time.time())
response.update({'session': session})
resp.update({'session': session})
self.send_command('configure session %s' % session)
if replace:
self.send_command('rollback clean-config')
@ -141,6 +113,7 @@ class Cliconf(CliconfBase):
self.send_command('configure')
results = []
requests = []
multiline = False
for line in to_list(candidate):
if not isinstance(line, collections.Mapping):
@ -160,15 +133,17 @@ class Cliconf(CliconfBase):
if cmd != 'end' and cmd[0] != '!':
try:
results.append(self.send_command(**line))
requests.append(cmd)
except AnsibleConnectionFailure as e:
self.discard_changes(session)
raise AnsibleConnectionFailure(e.message)
response['response'] = results
resp['request'] = requests
resp['response'] = results
if self.supports_sessions:
out = self.send_command('show session-config diffs')
if out:
response['diff'] = out.strip()
resp['diff'] = out.strip()
if commit:
self.commit()
@ -176,7 +151,7 @@ class Cliconf(CliconfBase):
self.discard_changes(session)
else:
self.send_command('end')
return response
return resp
def get(self, command, prompt=None, answer=None, sendonly=False, output=None):
if output:
@ -224,7 +199,7 @@ class Cliconf(CliconfBase):
responses.append(out)
return responses
def get_diff(self, candidate=None, running=None, match='line', diff_ignore_lines=None, path=None, replace='line'):
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'):
diff = {}
device_operations = self.get_device_operations()
option_values = self.get_option_values()
@ -232,26 +207,25 @@ class Cliconf(CliconfBase):
if candidate is None and device_operations['supports_generate_diff']:
raise ValueError("candidate configuration is required to generate diff")
if match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (match, ', '.join(option_values['diff_match'])))
if diff_match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match'])))
if replace not in option_values['diff_replace']:
raise ValueError("'replace' value %s in invalid, valid values are %s" % (replace, ', '.join(option_values['diff_replace'])))
if diff_replace not in option_values['diff_replace']:
raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace'])))
# prepare candidate configuration
candidate_obj = NetworkConfig(indent=3)
candidate_obj.load(candidate)
if running and match != 'none' and replace != 'config':
if running and diff_match != 'none' and diff_replace != 'config':
# running configuration
running_obj = NetworkConfig(indent=3, contents=running, ignore_lines=diff_ignore_lines)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=match, replace=replace)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace)
else:
configdiffobjs = candidate_obj.items
configdiff = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
diff['config_diff'] = configdiff if configdiffobjs else {}
diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
return diff
@property
@ -317,8 +291,25 @@ class Cliconf(CliconfBase):
result = {}
result['rpc'] = self.get_base_rpc()
result['device_info'] = self.get_device_info()
result['network_api'] = self.network_api
result['device_info'] = self.get_device_info()
result['device_operations'] = self.get_device_operations()
result.update(self.get_option_values())
if isinstance(self._connection, NetworkCli):
result['network_api'] = 'cliconf'
elif isinstance(self._connection, HttpApi):
result['network_api'] = 'eapi'
else:
raise ValueError("Invalid connection type")
return json.dumps(result)
def _get_command_with_output(self, command, output):
options_values = self.get_option_values()
if output not in options_values['output']:
raise ValueError("'output' value %s is invalid. Valid values are %s" % (output, ','.join(options_values['output'])))
if output == 'json' and not command.endswith('| json'):
cmd = '%s | json' % command
else:
cmd = command
return cmd

View file

@ -56,7 +56,7 @@ class Cliconf(CliconfBase):
return self.send_command(cmd)
def get_diff(self, candidate=None, running=None, match='line', diff_ignore_lines=None, path=None, replace='line'):
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'):
"""
Generate diff between candidate and running configuration. If the
remote host supports onbox diff capabilities ie. supports_onbox_diff in that case
@ -65,7 +65,7 @@ class Cliconf(CliconfBase):
and running argument is optional.
:param candidate: The configuration which is expected to be present on remote host.
:param running: The base configuration which is used to generate diff.
:param match: Instructs how to match the candidate configuration with current device configuration
:param diff_match: Instructs how to match the candidate configuration with current device configuration
Valid values are 'line', 'strict', 'exact', 'none'.
'line' - commands are matched line by line
'strict' - command lines are matched with respect to position
@ -79,7 +79,7 @@ class Cliconf(CliconfBase):
the commands should be checked against. If the parents argument
is omitted, the commands are checked against the set of top
level or global commands.
:param replace: Instructs on the way to perform the configuration on the device.
:param diff_replace: Instructs on the way to perform the configuration on the device.
If the replace argument is set to I(line) then the modified lines are
pushed to the device in configuration mode. If the replace argument is
set to I(block) then the entire command block is pushed to the device in
@ -87,7 +87,7 @@ class Cliconf(CliconfBase):
:return: Configuration diff in json format.
{
'config_diff': '',
'banner_diff': ''
'banner_diff': {}
}
"""
@ -98,71 +98,57 @@ class Cliconf(CliconfBase):
if candidate is None and device_operations['supports_generate_diff']:
raise ValueError("candidate configuration is required to generate diff")
if match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (match, ', '.join(option_values['diff_match'])))
if diff_match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match'])))
if replace not in option_values['diff_replace']:
raise ValueError("'replace' value %s in invalid, valid values are %s" % (replace, ', '.join(option_values['diff_replace'])))
if diff_replace not in option_values['diff_replace']:
raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace'])))
# prepare candidate configuration
candidate_obj = NetworkConfig(indent=1)
want_src, want_banners = self._extract_banners(candidate)
candidate_obj.load(want_src)
if running and match != 'none':
if running and diff_match != 'none':
# running configuration
have_src, have_banners = self._extract_banners(running)
running_obj = NetworkConfig(indent=1, contents=have_src, ignore_lines=diff_ignore_lines)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=match, replace=replace)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace)
else:
configdiffobjs = candidate_obj.items
have_banners = {}
configdiff = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
diff['config_diff'] = configdiff if configdiffobjs else {}
diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
banners = self._diff_banners(want_banners, have_banners)
diff['banner_diff'] = banners if banners else {}
return diff
@enable_mode
def edit_config(self, candidate=None, commit=True, replace=False, comment=None):
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
resp = {}
operations = self.get_device_operations()
if not candidate:
raise ValueError("must provide a candidate config to load")
if commit not in (True, False):
raise ValueError("'commit' must be a bool, got %s" % commit)
if replace not in (True, False):
raise ValueError("'replace' must be a bool, got %s" % replace)
if comment and not operations['supports_commit_comment']:
raise ValueError("commit comment is not supported")
operations = self.get_device_operations()
if replace and not operations['supports_replace']:
raise ValueError("configuration replace is not supported")
self.check_edit_config_capabiltiy(operations, candidate, commit, replace, comment)
results = []
requests = []
if commit:
for line in chain(['configure terminal'], to_list(candidate)):
self.send_command('configure terminal')
for line in to_list(candidate):
if not isinstance(line, collections.Mapping):
line = {'command': line}
cmd = line['command']
if cmd != 'end' and cmd[0] != '!':
results.append(self.send_command(**line))
requests.append(cmd)
results.append(self.send_command('end'))
self.send_command('end')
else:
raise ValueError('check mode is not supported')
resp['response'] = results[1:-1]
resp['request'] = requests
resp['response'] = results
return resp
def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None):
@ -241,17 +227,23 @@ class Cliconf(CliconfBase):
resp = {}
banners_obj = json.loads(candidate)
results = []
requests = []
if commit:
for key, value in iteritems(banners_obj):
key += ' %s' % multiline_delimiter
for cmd in ['config terminal', key, value, multiline_delimiter, 'end']:
self.send_commad('config terminal', sendonly=True)
for cmd in [key, value, multiline_delimiter]:
obj = {'command': cmd, 'sendonly': True}
results.append(self.send_command(**obj))
requests.append(cmd)
self.send_commad('end', sendonly=True)
time.sleep(0.1)
results.append(self.send_command('\n'))
requests.append('\n')
resp['response'] = results[1:-1]
resp['request'] = requests
resp['response'] = results
return resp

View file

@ -19,6 +19,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import collections
import json
import re
@ -27,30 +28,30 @@ from itertools import chain
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_bytes, to_text
from ansible.module_utils.connection import ConnectionError
from ansible.module_utils.network.common.config import NetworkConfig, dumps
from ansible.module_utils.network.common.utils import to_list
from ansible.plugins.cliconf import CliconfBase
from ansible.plugins.cliconf import CliconfBase, enable_mode
from ansible.plugins.connection.network_cli import Connection as NetworkCli
from ansible.plugins.connection.httpapi import Connection as HttpApi
class Cliconf(CliconfBase):
def send_command(self, command, prompt=None, answer=None, sendonly=False, newline=True, prompt_retry_check=False):
def __init__(self, *args, **kwargs):
super(Cliconf, self).__init__(*args, **kwargs)
def send_command(self, command, **kwargs):
"""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 = super(Cliconf, self).send_command(command, **kwargs)
elif isinstance(self._connection, HttpApi):
resp = self._connection.send_request(command, **kwargs)
else:
raise ValueError("Invalid connection type")
return resp
def get_device_info(self):
@ -101,66 +102,169 @@ class Cliconf(CliconfBase):
return device_info
def get_config(self, source='running', format='text', flags=None):
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'):
diff = {}
device_operations = self.get_device_operations()
option_values = self.get_option_values()
if candidate is None and device_operations['supports_generate_diff']:
raise ValueError("candidate configuration is required to generate diff")
if diff_match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match'])))
if diff_replace not in option_values['diff_replace']:
raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace'])))
# prepare candidate configuration
candidate_obj = NetworkConfig(indent=2)
candidate_obj.load(candidate)
if running and diff_match != 'none' and diff_replace != 'config':
# running configuration
running_obj = NetworkConfig(indent=2, contents=running, ignore_lines=diff_ignore_lines)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace)
else:
configdiffobjs = candidate_obj.items
diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
return diff
def get_config(self, source='running', format='text', filter=None):
options_values = self.get_option_values()
if format not in options_values['format']:
raise ValueError("'format' value %s is invalid. Valid values are %s" % (format, ','.join(options_values['format'])))
lookup = {'running': 'running-config', 'startup': 'startup-config'}
if source not in lookup:
return self.invalid_params("fetching configuration from %s is not supported" % source)
cmd = 'show {0} '.format(lookup[source])
if flags:
cmd += ' '.join(flags)
if format and format is not 'text':
cmd += '| %s ' % format
if filter:
cmd += ' '.join(to_list(filter))
cmd = cmd.strip()
return self.send_command(cmd)
def edit_config(self, command):
responses = []
for cmd in chain(['configure'], to_list(command), ['end']):
responses.append(self.send_command(cmd))
resp = responses[1:-1]
return json.dumps(resp)
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
resp = {}
operations = self.get_device_operations()
self.check_edit_config_capabiltiy(operations, candidate, commit, replace, comment)
results = []
requests = []
def get(self, command, prompt=None, answer=None, sendonly=False):
if replace:
candidate = 'config replace {0}'.format(replace)
if commit:
self.send_command('configure terminal')
for line in to_list(candidate):
if not isinstance(line, collections.Mapping):
line = {'command': line}
cmd = line['command']
if cmd != 'end':
results.append(self.send_command(**line))
requests.append(cmd)
self.send_command('end')
else:
raise ValueError('check mode is not supported')
resp['request'] = requests
resp['response'] = results
return resp
def get(self, command, prompt=None, answer=None, sendonly=False, output=None):
if output:
command = self._get_command_with_output(command, output)
return self.send_command(command, prompt=prompt, answer=answer, sendonly=sendonly)
def get_capabilities(self):
result = {}
result['rpc'] = self.get_base_rpc()
result['device_info'] = self.get_device_info()
if isinstance(self._connection, NetworkCli):
result['network_api'] = 'cliconf'
else:
result['network_api'] = 'nxapi'
return json.dumps(result)
def run_commands(self, commands=None, check_rc=True):
if commands is None:
raise ValueError("'commands' value is required")
# 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 cmd in to_list(commands):
if not isinstance(cmd, collections.Mapping):
cmd = {'command': cmd}
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']
output = cmd.pop('output', None)
if output:
cmd['command'] = self._get_command_with_output(cmd['command'], output)
try:
out = self.get(cmd)
out = self.send_command(**cmd)
except AnsibleConnectionFailure as e:
if check_rc:
raise
out = getattr(e, 'err', 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)))
if out is not None:
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
try:
out = json.loads(out)
except ValueError:
out = to_text(out, errors='surrogate_or_strict').strip()
responses.append(out)
responses.append(out)
return responses
def get_device_operations(self):
return {
'supports_diff_replace': True,
'supports_commit': False,
'supports_rollback': False,
'supports_defaults': True,
'supports_onbox_diff': False,
'supports_commit_comment': False,
'supports_multiline_delimiter': False,
'supports_diff_match': True,
'supports_diff_ignore_lines': True,
'supports_generate_diff': True,
'supports_replace': True
}
def get_option_values(self):
return {
'format': ['text', 'json'],
'diff_match': ['line', 'strict', 'exact', 'none'],
'diff_replace': ['line', 'block', 'config'],
'output': ['text', 'json']
}
def get_capabilities(self):
result = {}
result['rpc'] = self.get_base_rpc()
result['device_info'] = self.get_device_info()
result.update(self.get_option_values())
if isinstance(self._connection, NetworkCli):
result['network_api'] = 'cliconf'
elif isinstance(self._connection, HttpApi):
result['network_api'] = 'nxapi'
else:
raise ValueError("Invalid connection type")
return json.dumps(result)
def _get_command_with_output(self, command, output):
options_values = self.get_option_values()
if output not in options_values['output']:
raise ValueError("'output' value %s is invalid. Valid values are %s" % (output, ','.join(options_values['output'])))
if output == 'json' and not command.endswith('| json'):
cmd = '%s | json' % command
elif output == 'text' and command.endswith('| json'):
cmd = command.rsplit('|', 1)[0]
else:
cmd = command
return cmd

View file

@ -66,29 +66,20 @@ class Cliconf(CliconfBase):
out = self.send_command('show configuration commands')
return out
def edit_config(self, candidate=None, commit=True, replace=False, comment=None):
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
resp = {}
if not candidate:
raise ValueError('must provide a candidate config to load')
if commit not in (True, False):
raise ValueError("'commit' must be a bool, got %s" % commit)
if replace not in (True, False):
raise ValueError("'replace' must be a bool, got %s" % replace)
operations = self.get_device_operations()
if replace and not operations['supports_replace']:
raise ValueError("configuration replace is not supported")
self.check_edit_config_capabiltiy(operations, candidate, commit, replace, comment)
results = []
for cmd in chain(['configure'], to_list(candidate)):
requests = []
self.send_command('configure')
for cmd in to_list(candidate):
if not isinstance(cmd, collections.Mapping):
cmd = {'command': cmd}
results.append(self.send_command(**cmd))
requests.append(cmd['command'])
out = self.get('compare')
out = to_text(out, errors='surrogate_or_strict')
diff_config = out if not out.startswith('No changes') else None
@ -109,7 +100,8 @@ class Cliconf(CliconfBase):
self.send_command('exit')
resp['diff'] = diff_config
resp['response'] = results[1:-1]
resp['response'] = results
resp['request'] = requests
return resp
def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None):
@ -131,7 +123,7 @@ class Cliconf(CliconfBase):
def discard_changes(self):
self.send_command('exit discard')
def get_diff(self, candidate=None, running=None, match='line', diff_ignore_lines=None, path=None, replace=None):
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace=None):
diff = {}
device_operations = self.get_device_operations()
option_values = self.get_option_values()
@ -139,10 +131,10 @@ class Cliconf(CliconfBase):
if candidate is None and device_operations['supports_generate_diff']:
raise ValueError("candidate configuration is required to generate diff")
if match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (match, ', '.join(option_values['diff_match'])))
if diff_match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match'])))
if replace:
if diff_replace:
raise ValueError("'replace' in diff is not supported")
if diff_ignore_lines:
@ -169,7 +161,7 @@ class Cliconf(CliconfBase):
else:
candidate_commands = str(candidate).strip().split('\n')
if match == 'none':
if diff_match == 'none':
diff['config_diff'] = list(candidate_commands)
return diff