Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commit_confirmed support to commit_config #543

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions napalm/base/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,19 @@ def compare_config(self):
"""
raise NotImplementedError

def commit_config(self):
def commit_config(self, confirmed=None):
"""
Commits the changes requested by the method load_replace_candidate or load_merge_candidate.

:param confirmed (optional): Number of minutes until rollback occurs. Default is None \
(no confirmation).
"""
raise NotImplementedError

def commit_confirm(self):
"""Confirm pending commit."""
raise NotImplementedError

def discard_config(self):
"""
Discards the configuration loaded into the candidate.
Expand Down Expand Up @@ -1587,6 +1594,6 @@ def compliance_report(self, validation_file=None, validation_source=None):
def _canonical_int(self, interface):
"""Expose the helper function within this class."""
if self.use_canonical_interface is True:
return napalm.base.helpers.canonical_interface_name(interface, update_os_mapping=None)
return napalm.base.helpers.canonical_interface_name(interface, addl_name_map=None)
else:
return interface
73 changes: 38 additions & 35 deletions napalm/base/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,52 +258,55 @@ def as_number(as_number_val):
return int(as_number_str)


def int_split_on_match(split_interface):
'''
simple fuction to split on first digit, slash, or space match
'''
head = split_interface.rstrip(r'/\0123456789 ')
tail = split_interface[len(head):].lstrip()
return head, tail


def canonical_interface_name(interface, update_os_mapping=None):
'''
Function to retun interface canonical name
def split_interface(intf_name):
"""Split an interface name based on first digit, slash, or space match."""
head = intf_name.rstrip(r'/\0123456789 ')
tail = intf_name[len(head):].lstrip()
return (head, tail)


def canonical_interface_name(interface, addl_name_map=None):
"""Function to return an interface's canonical name (fully expanded name).

This puposely does not use regex, or first X characters, to ensure
there is no chance for false positives. As an example, Po = PortChannel, and
PO = POS. With either character or regex, that would produce a false positive.
'''

interface_type, interface_number = int_split_on_match(interface)
"""
name_map = {}
name_map.update(base_interfaces)
interface_type, interface_number = split_interface(interface)

if isinstance(update_os_mapping, dict):
base_interfaces.update(update_os_mapping)
if isinstance(addl_name_map, dict):
name_map.update(addl_name_map)
# check in dict for mapping
if base_interfaces.get(interface_type):
long_int = base_interfaces.get(interface_type)
if name_map.get(interface_type):
long_int = name_map.get(interface_type)
return long_int + str(interface_number)
# if nothing matched, at least return the original
# if nothing matched, return the original name
else:
return interface


def abbreviated_interface_name(interface, update_os_mapping=None):
'''
Function to retun interface canonical name
This puposely does not use regex, or first X characters, to ensure
there is no chance for false positives. As an example, Po = PortChannel, and
PO = POS. With either character or regex, that would produce a false positive.
'''
def abbreviated_interface_name(interface, addl_name_map=None):
"""Function to return an abbreviated representation of the interface name."""
reverse_name_map = {}
reverse_name_map.update(reverse_mapping)
interface_type, interface_number = split_interface(interface)

interface_type, interface_number = int_split_on_match(interface)
if isinstance(addl_name_map, dict):
reverse_name_map.update(addl_name_map)

if isinstance(update_os_mapping, dict):
base_interfaces.update(update_os_mapping)
# check in dict for mapping
# Try to ensure canonical type.
if base_interfaces.get(interface_type):
long_int = base_interfaces.get(interface_type)
return reverse_mapping[long_int] + str(interface_number)
# if nothing matched, at least return the original
canonical_type = base_interfaces.get(interface_type)
else:
return interface
canonical_type = interface_type

try:
abbreviated_name = reverse_mapping[canonical_type] + str(interface_number)
return abbreviated_name
except KeyError:
pass

# If abbreviated name lookup fails, return original name
return interface
17 changes: 17 additions & 0 deletions napalm/base/test/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ def test_replacing_and_committing_config(self):

self.assertEqual(len(diff), 0)

def test_replacing_and_committing_config_with_confirm(self):
try:
self.device.load_replace_candidate(filename='%s/new_good.conf' % self.vendor)
self.device.commit_config(confirmed=5)
self.device.commit_confirm()
except NotImplementedError:
raise SkipTest()

# The diff should be empty as the configuration has been committed already
diff = self.device.compare_config()

# Reverting changes
self.device.load_replace_candidate(filename='%s/initial.conf' % self.vendor)
self.device.commit_config()

self.assertEqual(len(diff), 0)

def test_replacing_config_with_typo(self):
result = False
try:
Expand Down
4 changes: 3 additions & 1 deletion napalm/eos/eos.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ def compare_config(self):

return result.strip()

def commit_config(self):
def commit_config(self, confirmed=None):
"""Implementation of NAPALM method commit_config."""
if confirmed is not None:
raise NotImplementedError
commands = []
commands.append('copy startup-config flash:rollback-0')
commands.append('configure session {}'.format(self.config_session))
Expand Down
5 changes: 4 additions & 1 deletion napalm/ios/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,15 @@ def _commit_hostname_handler(self, cmd):
self.device.set_base_prompt()
return output

def commit_config(self):
def commit_config(self, confirmed=None):
"""
If replacement operation, perform 'configure replace' for the entire config.

If merge operation, perform copy <file> running-config.
"""
if confirmed is not None:
raise NotImplementedError

# Always generate a rollback config on commit
self._gen_rollback_cfg()

Expand Down
5 changes: 4 additions & 1 deletion napalm/iosxr/iosxr.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ def compare_config(self):
else:
return self.device.compare_config().strip()

def commit_config(self):
def commit_config(self, confirmed=None):
if confirmed is not None:
raise NotImplementedError

if self.replace:
self.device.commit_replace_config()
else:
Expand Down
4 changes: 3 additions & 1 deletion napalm/junos/junos.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,10 @@ def compare_config(self):
else:
return diff.strip()

def commit_config(self):
def commit_config(self, confirmed=None):
"""Commit configuration."""
if confirmed is not None:
raise NotImplementedError
self.device.cu.commit(ignore_warning=self.ignore_warning)
if not self.config_lock:
self._unlock()
Expand Down
5 changes: 4 additions & 1 deletion napalm/nxos/nxos.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,10 @@ def _load_config(self):
raise ReplaceConfigException(rollback_result['msg'])
return True

def commit_config(self):
def commit_config(self, confirmed=None):
if confirmed is not None:
raise NotImplementedError

if self.loaded:
self.backup_file = 'config_' + str(datetime.now()).replace(' ', '_')
self._save_config(self.backup_file)
Expand Down
5 changes: 4 additions & 1 deletion napalm/nxos_ssh/nxos_ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,10 @@ def _load_cfg_from_checkpoint(self):
return False
return True

def commit_config(self):
def commit_config(self, confirmed=None):
if confirmed is not None:
raise NotImplementedError

if self.loaded:
self.backup_file = 'config_' + str(datetime.now()).replace(' ', '_')
# Create Checkpoint from current running-config
Expand Down