Skip to content
This repository has been archived by the owner on Sep 17, 2019. It is now read-only.

Commit

Permalink
Merge pull request #151 from napalm-automation/develop
Browse files Browse the repository at this point in the history
napalm-nxos release 0.7.1
  • Loading branch information
ktbyers authored Oct 26, 2017
2 parents 7550149 + 936d641 commit c3c454e
Show file tree
Hide file tree
Showing 18 changed files with 397 additions and 96 deletions.
33 changes: 4 additions & 29 deletions .github/ISSUE_TEMPLATE
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
### Description of Issue/Question
### The NAPALM Project has reunified!

Please submit all NAPALM issues to:
https://github.com/napalm-automation/napalm/issues

### Did you follow the steps from https://github.com/napalm-automation/napalm#faq
[ ] Yes
[ ] No


### Setup

### napalm-nxos version
(Paste verbatim output from `pip freeze | grep napalm-nxos` between quotes below)

```

```

### NX-OS version
(Paste verbatim output from `show version | json` between quotes below)

```

```

### Steps to Reproduce the Issue

### Error Traceback
(Paste the complete traceback of the exception between quotes below)

```

```
### DO NOT submit any issues to this repository.
8 changes: 7 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
<!-- Make sure you have read http://napalm.readthedocs.io/en/latest/contributing/index.html --!>
### The NAPALM Project has reunified!

Please submit all NAPALM pull requests to:
https://github.com/napalm-automation/napalm/pulls


### DO NOT submit any pull requests to this repository.
22 changes: 17 additions & 5 deletions napalm_nxos/nxos.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,12 +606,24 @@ def get_arp_table(self):
raw_mac = arp_table_entry.get('mac')
age = arp_table_entry.get('time-stamp')
if age == '-':
age_sec = float(-1)
age_sec = -1.0
elif ':' not in age:
# Cisco sometimes returns a sub second arp time 0.411797
try:
age_sec = float(age)
except ValueError:
age_sec = -1.0
else:
age_time = ''.join(age.split(':'))
age_sec = float(
3600 * int(age_time[:2]) + 60 * int(age_time[2:4]) + int(age_time[4:])
)
fields = age.split(':')
if len(fields) == 3:
try:
fields = [float(x) for x in fields]
hours, minutes, seconds = fields
age_sec = 3600 * hours + 60 * minutes + seconds
except ValueError:
age_sec = -1.0
age_sec = round(age_sec, 1)

interface = py23_compat.text_type(arp_table_entry.get('intf-out'))
arp_table.append({
'interface': interface,
Expand Down
105 changes: 45 additions & 60 deletions napalm_nxos_ssh/nxos_ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,16 +456,6 @@ def parse_uptime(uptime_str):
(hours * 3600) + (minutes * 60) + seconds
return uptime_sec

@staticmethod
def fix_checkpoint_string(string, filename):
# used to generate checkpoint-like files
pattern = '''!Command: Checkpoint cmd vdc 1'''

if '!Command' in string:
return re.sub('!Command.*', pattern.format(filename), string)
else:
return "{0}\n{1}".format(pattern.format(filename), string)

def is_alive(self):
"""Returns a flag with the state of the SSH connection."""
null = chr(0)
Expand Down Expand Up @@ -512,18 +502,12 @@ def _verify_remote_file_exists(self, dst, file_system='bootflash:'):

def _replace_candidate(self, filename, config):
if not filename:
file_content = self.fix_checkpoint_string(config, self.replace_file)
filename = self._create_tmp_file(config)
else:
if not os.path.isfile(filename):
raise ReplaceConfigException("File {} not found".format(filename))

self.replace_file = filename
with open(self.replace_file, 'r+') as f:
file_content = f.read()
file_content = self.fix_checkpoint_string(file_content, self.replace_file)
f.write(file_content)

if not self._enough_space(self.replace_file):
msg = 'Could not transfer file. Not enough space on device.'
raise ReplaceConfigException(msg)
Expand Down Expand Up @@ -668,43 +652,41 @@ def _commit_merge(self):
if 'Invalid command' in output:
raise MergeConfigException('Error while applying config!')
if not self._save():
raise CommandErrorException('Unable to commit config!')
raise CommandErrorException('Unable to save running-config to startup!')

def _save_config(self, filename):
def _save_to_checkpoint(self, filename):
"""Save the current running config to the given file."""
command = 'checkpoint file {}'.format(filename)
self.device.send_command(command)

def _disable_confirmation(self):
self._send_config_commands(['terminal dont-ask'])

def _load_config(self):
def _load_cfg_from_checkpoint(self):
command = 'rollback running file {0}'.format(self.replace_file.split('/')[-1])
self._disable_confirmation()
try:
rollback_result = self.device.send_command(command)
except Exception:
return False
if 'Rollback failed.' in rollback_result['msg'] or 'ERROR' in rollback_result:
raise ReplaceConfigException(rollback_result['msg'])
rollback_result = self.device.send_command(command)
if 'Rollback failed.' in rollback_result or 'ERROR' in rollback_result:
raise ReplaceConfigException(rollback_result)
elif rollback_result == []:
return False
return True

def commit_config(self):
if self.loaded:
self.backup_file = 'config_' + str(datetime.now()).replace(' ', '_')
self._save_config(self.backup_file)
# Create Checkpoint from current running-config
self._save_to_checkpoint(self.backup_file)
if self.replace:
if self._load_config() is False:
cfg_replace_status = self._load_cfg_from_checkpoint()
if not cfg_replace_status:
raise ReplaceConfigException
else:
try:
self._commit_merge()
self.merge_candidate = '' # clear the merge buffer
except Exception as e:
raise MergeConfigException(str(e))

self.changed = True
self.loaded = False
else:
Expand All @@ -726,16 +708,14 @@ def discard_config(self):
self._delete_file(self.replace_file)
self.loaded = False

def _rollback_ssh(self, backup_file):
command = 'rollback running-config file %s' % backup_file
result = self.device.send_command(command)
if 'completed' not in result.lower():
raise ReplaceConfigException(result)
self._save_ssh()

def rollback(self):
if self.changed:
self._rollback_ssh(self.backup_file)
command = 'rollback running-config file {}'.format(self.backup_file)
result = self.device.send_command(command)
if 'completed' not in result.lower():
raise ReplaceConfigException(result)
if not self._save():
raise CommandErrorException('Unable to save running-config to startup!')
self.changed = False

def _apply_key_map(self, key_map, table):
Expand All @@ -758,7 +738,7 @@ def get_facts(self):
# default values.
vendor = u'Cisco'
uptime = -1
serial_number, fqdn, os_version, hostname = (u'Unknown', u'Unknown', u'Unknown', u'Unknown')
serial_number, fqdn, os_version, hostname, domain_name = ('',) * 5

# obtain output from device
show_ver = self.device.send_command('show version')
Expand Down Expand Up @@ -793,11 +773,10 @@ def get_facts(self):
_, domain_name = re.split(r".*Default domain.*is ", line)
domain_name = domain_name.strip()
break
if domain_name != 'Unknown' and hostname != 'Unknown':
if hostname.count(".") >= 2:
fqdn = hostname
else:
fqdn = '{}.{}'.format(hostname, domain_name)
if hostname.count(".") >= 2:
fqdn = hostname
elif domain_name:
fqdn = '{}.{}'.format(hostname, domain_name)

# interface_list filter
interface_list = []
Expand Down Expand Up @@ -1098,14 +1077,18 @@ def get_arp_table(self):
else:
raise ValueError("Unexpected output from: {}".format(line.split()))

try:
if age == '-':
age = 0
if age == '-':
age = -1.0
elif ':' not in age:
# Cisco sometimes returns a sub second arp time 0.411797
try:
age = float(age)
except ValueError:
age = -1.0
else:
age = convert_hhmmss(age)
age = float(age)
age = round(age, 1)
except ValueError:
raise ValueError("Unable to convert age value to float: {}".format(age))
age = round(age, 1)

# Validate we matched correctly
if not re.search(RE_IPADDR, address):
Expand Down Expand Up @@ -1263,9 +1246,10 @@ def get_mac_address_table(self):
"""

# The '*' is stripped out later
RE_MACTABLE_FORMAT1 = r"^\s+{}\s+{}\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+".format(
VLAN_REGEX,
MAC_REGEX) # 7 fields
RE_MACTABLE_FORMAT1 = r"^\s+{}\s+{}\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+".format(VLAN_REGEX,
MAC_REGEX)
RE_MACTABLE_FORMAT2 = r"^\s+{}\s+{}\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+".format('-',
MAC_REGEX)

mac_address_table = []
command = 'show mac address-table'
Expand All @@ -1280,6 +1264,8 @@ def process_mac_fields(vlan, mac, mac_type, interface):
static = True
if vlan.lower() == 'all':
vlan = 0
elif vlan == '-':
vlan = 0
if interface.lower() == 'cpu' or re.search(r'router', interface.lower()) or \
re.search(r'switch', interface.lower()):
interface = ''
Expand All @@ -1302,8 +1288,8 @@ def process_mac_fields(vlan, mac, mac_type, interface):
# Skip the header lines
output = re.split(r'^----.*', output, flags=re.M)[1:]
output = "\n".join(output).strip()
# Strip any leading astericks
output = re.sub(r"^\*", "", output, flags=re.M)
# Strip any leading astericks or G character
output = re.sub(r"^[\*G]", "", output, flags=re.M)

for line in output.splitlines():

Expand All @@ -1321,14 +1307,13 @@ def process_mac_fields(vlan, mac, mac_type, interface):
continue
elif re.search('^\s*$', line):
continue
# Format1
elif re.search(RE_MACTABLE_FORMAT1, line):
if len(line.split()) == 7:
vlan, mac, mac_type, _, _, _, interface = line.split()
mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface))
else:
raise ValueError("Unexpected output from: {}".format(line.split()))

for pattern in [RE_MACTABLE_FORMAT1, RE_MACTABLE_FORMAT2]:
if re.search(pattern, line):
if len(line.split()) == 7:
vlan, mac, mac_type, _, _, _, interface = line.split()
mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface))
break
else:
raise ValueError("Unexpected output from: {}".format(repr(line)))

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

setup(
name="napalm-nxos",
version="0.7.0",
version="0.7.1",
packages=find_packages(exclude=["test", "test.*"]),
author="David Barroso",
author_email="[email protected]",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"interface": "Vlan100",
"ip": "10.122.100.3",
"mac": "18:8B:9D:0B:B3:3F",
"age": 583.0
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"TABLE_vrf": {
"ROW_vrf": {
"vrf-name-out": "default",
"TABLE_adj": {
"ROW_adj": [
{
"ip-addr-out": "10.122.100.3",
"mac": "188b.9d0b.b33f",
"time-stamp": "00:09:43",
"intf-out": "Vlan100"
}
]
},
"cnt-total": "2438"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"interface": "Vlan100",
"ip": "10.122.100.3",
"mac": "18:8B:9D:0B:B3:3F",
"age": 0.4
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"TABLE_vrf": {
"ROW_vrf": {
"vrf-name-out": "default",
"TABLE_adj": {
"ROW_adj": [
{
"ip-addr-out": "10.122.100.3",
"mac": "188b.9d0b.b33f",
"time-stamp": "0.411797",
"intf-out": "Vlan100"
}
]
},
"cnt-total": "2438"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[
{
"interface": "mgmt0",
"ip": "10.0.0.2",
"mac": "2C:C2:60:FF:00:21",
"age": 4.0
},
{
"interface": "mgmt0",
"ip": "10.0.0.72",
"mac": "2C:C2:60:36:32:21",
"age": 140.0
},
{
"interface": "Vlan357",
"ip": "10.5.159.59",
"mac": "00:50:56:14:44:BE",
"age": 590.0
},
{
"interface": "Vlan357",
"ip": "10.5.159.78",
"mac": "00:50:56:14:2F:2A",
"age": 0.3
},
{
"interface": "Vlan357",
"ip": "10.5.159.79",
"mac": "00:50:56:14:2F:2A",
"age": 851.0
}
]
Loading

0 comments on commit c3c454e

Please sign in to comment.