Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:bewing/napalm-base into develop
Browse files Browse the repository at this point in the history
* 'develop' of github.com:bewing/napalm-base: (53 commits)
  Documentation: Fix example mac addresses to be proper form
  Fix mac address format
  Version 0.24.1
  Test when as number is int
  Fix the as_number helper
  Adding note about is_up and positive uptime
  Add reference example for get_bgp_neighbors
  Add numeric compare for validation (napalm-automation#243)
  Reword docstring as per David's suggestions
  Prepare release 0.24.0
  Defien more exceptions as per napalm-automation#218
  New exception: ConnectionClosedException
  Fixing unneeded import
  Removing some obsolete PY3 transition code
  Fixing test name error
  Remove unneeded print statement
  Updating unit tests for new helpers; updating PY3 support to include fortinet
  Working on helpers
  napalm-base release 0.23.3
  Adding more tests; fix pylama
  ...
  • Loading branch information
bewing committed Jun 22, 2017
2 parents 01e2044 + 96fd9d4 commit 7a6e3f6
Show file tree
Hide file tree
Showing 43 changed files with 1,207 additions and 225 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ language: python

python:
- 2.7
- 3.4
- 3.4
- 3.5

install:
- pip install .
- pip install -r requirements-dev.txt
- pip install -r requirements.txt
- if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then pip install -r test/unit/requirements.txt; fi
- if [[ $TRAVIS_PYTHON_VERSION == 3.4 ]]; then pip install -r test/unit/requirements_py3.txt; fi
- pip install -r test/unit/requirements.txt

deploy:
provider: pypi
Expand All @@ -24,5 +24,5 @@ script:
- nosetests ./test/unit/TestGetNetworkDriver.py
- nosetests ./test/unit/TestHelpers.py
- nosetests ./test/unit/TestNapalmTestFramework.py
- py.test test/unit/validate/test_validate.py
- py.test test/unit/validate
- pylama .
81 changes: 64 additions & 17 deletions napalm_base/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
def __enter__(self):
try:
self.open()
except:
except: # noqa
exc_info = sys.exc_info()
self.__raise_clean_exception(exc_info[0], exc_info[1], exc_info[2])
return self
Expand All @@ -59,6 +59,18 @@ def __exit__(self, exc_type, exc_value, exc_traceback):
if exc_type is not None:
self.__raise_clean_exception(exc_type, exc_value, exc_traceback)

def __del__(self):
"""
This method is used to cleanup when the program is terminated suddenly.
We need to make sure the connection is closed properly and the configuration DB
is released (unlocked).
"""
try:
if self.is_alive()["is_alive"]:
self.close()
except NotImplementedError:
pass

@staticmethod
def __raise_clean_exception(exc_type, exc_value, exc_traceback):
"""
Expand Down Expand Up @@ -231,37 +243,37 @@ def get_interfaces(self):
{
'is_up': False,
'is_enabled': False,
'description': u'',
'description': '',
'last_flapped': -1,
'speed': 1000,
'mac_address': u'dead:beef:dead',
'mac_address': 'FA:16:3E:57:33:61',
},
u'Ethernet1':
{
'is_up': True,
'is_enabled': True,
'description': u'foo',
'description': 'foo',
'last_flapped': 1429978575.1554043,
'speed': 1000,
'mac_address': u'beef:dead:beef',
'mac_address': 'FA:16:3E:57:33:62',
},
u'Ethernet2':
{
'is_up': True,
'is_enabled': True,
'description': u'bla',
'description': 'bla',
'last_flapped': 1429978575.1555667,
'speed': 1000,
'mac_address': u'beef:beef:beef',
'mac_address': 'FA:16:3E:57:33:63',
},
u'Ethernet3':
{
'is_up': False,
'is_enabled': True,
'description': u'bar',
'description': 'bar',
'last_flapped': -1,
'speed': 1000,
'mac_address': u'dead:dead:dead',
'mac_address': 'FA:16:3E:57:33:64',
}
}
"""
Expand Down Expand Up @@ -333,6 +345,40 @@ def get_bgp_neighbors(self):
* received_prefixes (int)
* accepted_prefixes (int)
* sent_prefixes (int)
Note, if is_up is False and uptime has a positive value then this indicates the
uptime of the last active BGP session.
Example response:
{
"global": {
"router_id": "10.0.1.1",
"peers": {
"10.0.0.2": {
"local_as": 65000,
"remote_as": 65000,
"remote_id": "10.0.1.2",
"is_up": True,
"is_enabled": True,
"description": "internal-2",
"uptime": 4838400,
"address_family": {
"ipv4": {
"sent_prefixes": 637213,
"accepted_prefixes": 3142,
"received_prefixes": 3142
},
"ipv6": {
"sent_prefixes": 36714,
"accepted_prefixes": 148,
"received_prefixes": 148
}
}
}
}
}
}
"""
raise NotImplementedError

Expand Down Expand Up @@ -471,7 +517,7 @@ def get_bgp_config(self, group='', neighbor=''):
:param neighbor: Returns the configuration of a specific BGP neighbor.
Main dictionary keys represent the group name and the values represent a dictionary having
the following keys:
the keys below. Neighbors which aren't members of a group will be stored in a key named "_":
* type (string)
* description (string)
* apply_groups (string list)
Expand Down Expand Up @@ -694,13 +740,13 @@ def get_arp_table(self):
[
{
'interface' : 'MgmtEth0/RSP0/CPU0/0',
'mac' : '5c:5e:ab:da:3c:f0',
'mac' : '5C:5E:AB:DA:3C:F0',
'ip' : '172.17.17.1',
'age' : 1454496274.84
},
{
'interface' : 'MgmtEth0/RSP0/CPU0/0',
'mac' : '66:0e:94:96:e0:ff',
'mac' : '5C:5E:AB:DA:3C:FF',
'ip' : '172.17.17.2',
'age' : 1435641582.49
}
Expand Down Expand Up @@ -859,7 +905,7 @@ def get_mac_address_table(self):
[
{
'mac' : '00:1c:58:29:4a:71',
'mac' : '00:1C:58:29:4A:71',
'interface' : 'Ethernet47',
'vlan' : 100,
'static' : False,
Expand All @@ -868,7 +914,7 @@ def get_mac_address_table(self):
'last_move' : 1454417742.58
},
{
'mac' : '8c:60:4f:58:e1:c1',
'mac' : '00:1C:58:29:4A:C1',
'interface' : 'xe-1/0/1',
'vlan' : 100,
'static' : False,
Expand All @@ -877,7 +923,7 @@ def get_mac_address_table(self):
'last_move' : 1453191948.11
},
{
'mac' : 'f4:b5:2f:56:72:01',
'mac' : '00:1C:58:29:4A:C2',
'interface' : 'ae7.900',
'vlan' : 900,
'static' : False,
Expand Down Expand Up @@ -1111,7 +1157,7 @@ def get_probes_results(self):
raise NotImplementedError

def ping(self, destination, source=c.PING_SOURCE, ttl=c.PING_TTL, timeout=c.PING_TIMEOUT,
size=c.PING_SIZE, count=c.PING_COUNT):
size=c.PING_SIZE, count=c.PING_COUNT, vrf=c.PING_VRF):
"""
Executes ping on the device and returns a dictionary with the result
Expand Down Expand Up @@ -1178,7 +1224,8 @@ def traceroute(self,
destination,
source=c.TRACEROUTE_SOURCE,
ttl=c.TRACEROUTE_TTL,
timeout=c.TRACEROUTE_TIMEOUT):
timeout=c.TRACEROUTE_TIMEOUT,
vrf=c.TRACEROUTE_VRF):
"""
Executes traceroute on the device and returns a dictionary with the result.
Expand Down
4 changes: 4 additions & 0 deletions napalm_base/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

INTERFACE_NULL_SPEED = -1

ACTION_TYPE_METHODS = ('ping', 'traceroute', )

BGP_NEIGHBOR_NULL_COUNTER = -1

SNMP_AUTHORIZATION_MODE_MAP = {
Expand Down Expand Up @@ -54,6 +56,7 @@
TRACEROUTE_TIMEOUT = 2
TRACEROUTE_NULL_HOST_NAME = '*'
TRACEROUTE_NULL_IP_ADDRESS = '*'
TRACEROUTE_VRF = ''

OPTICS_NULL_LEVEL = '-Inf'

Expand All @@ -62,3 +65,4 @@
PING_TIMEOUT = 2
PING_SIZE = 100
PING_COUNT = 5
PING_VRF = ''
56 changes: 56 additions & 0 deletions napalm_base/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,40 @@ class ModuleImportError(Exception):


class ConnectionException(Exception):
'''
Unable to connect to the network device.
'''
pass


class ConnectAuthError(ConnectionException):
'''
Unable to connect to the network device
due to invalid credentials.
'''
pass


class ConnectTimeoutError(ConnectionException):
'''
Exception raised when the connection to the
network device takes too long.
This may be avoided by adjusting the `timeout`
argument.
'''
pass


class ConnectionClosedException(ConnectionException):
'''
The network device closed the connection.
Raised whenever we try to execute a certain
function, but we detect that the connection
is not usable anymore. This can happen for
various reasons: the network device terminates the
session or it is dropped by a firewall or
the server.
'''
pass


Expand All @@ -33,6 +67,28 @@ class MergeConfigException(Exception):
pass


class CommitError(Exception):
'''
Raised when unable to commit the candidate config
into the running config.
'''
pass


class LockError(Exception):
'''
Unable to lock the candidate config.
'''
pass


class UnlockError(Exception):
'''
Unable to unlock the candidate config.
'''
pass


class SessionLockedException(Exception):
pass

Expand Down
24 changes: 20 additions & 4 deletions napalm_base/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def convert(to, who, default=u''):
return default
try:
return to(who)
except:
except: # noqa
return default


Expand Down Expand Up @@ -219,16 +219,19 @@ def mac(raw):
return py23_compat.text_type(EUI(raw, dialect=_MACFormat))


def ip(addr):
def ip(addr, version=None):
"""
Converts a raw string to a valid IP address.
Converts a raw string to a valid IP address. Optional version argument will detect that \
object matches specified version.
Motivation: the groups of the IP addreses may contain leading zeros. IPv6 addresses can \
contain sometimes uppercase characters. E.g.: 2001:0dB8:85a3:0000:0000:8A2e:0370:7334 has \
the same logical value as 2001:db8:85a3::8a2e:370:7334. However, their values as strings are \
not the same.
:param raw: the raw string containing the value of the IP Address
:param version: (optional) insist on a specific IP address version.
:type version: int.
:return: a string containing the IP Address in a standard format (no leading zeros, \
zeros-grouping, lowercase)
Expand All @@ -239,4 +242,17 @@ def ip(addr):
>>> ip('2001:0dB8:85a3:0000:0000:8A2e:0370:7334')
u'2001:db8:85a3::8a2e:370:7334'
"""
return py23_compat.text_type(IPAddress(addr))
addr_obj = IPAddress(addr)
if version and addr_obj.version != version:
raise ValueError("{} is not an ipv{} address".format(addr, version))
return py23_compat.text_type(addr_obj)


def as_number(as_number_val):
"""Convert AS Number to standardized asplain notation as an integer."""
as_number_str = py23_compat.text_type(as_number_val)
if '.' in as_number_str:
big, little = as_number_str.split('.')
return (int(big) << 16) + int(little)
else:
return int(as_number_str)
10 changes: 10 additions & 0 deletions napalm_base/test/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@

class TestConfigNetworkDriver(object):

@classmethod
def setup_class(cls):
"""Added for py.test/nosetests compatibility"""
cls.setUpClass()

@classmethod
def teardown_class(cls):
"""Added for py.test/nosetests compatibility"""
cls.tearDownClass()

@classmethod
def tearDownClass(cls):
cls.device.load_replace_candidate(filename='%s/initial.conf' % cls.vendor)
Expand Down
4 changes: 3 additions & 1 deletion napalm_base/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from __future__ import print_function
from __future__ import unicode_literals

import ast

import json
import os

NAPALM_TEST_MOCK = eval("{}".format(os.getenv('NAPALM_TEST_MOCK', default=True)))
NAPALM_TEST_MOCK = ast.literal_eval(os.getenv('NAPALM_TEST_MOCK', default="1"))
NAPALM_HOSTNAME = os.getenv('NAPALM_HOSTNAME', default='127.0.0.1')
NAPALM_USERNAME = os.getenv('NAPALM_USERNAME', default='vagrant')
NAPALM_PASSWORD = os.getenv('NAPALM_PASSWORD', default='vagrant')
Expand Down
Loading

0 comments on commit 7a6e3f6

Please sign in to comment.