[Python-modules-commits] [napalm-eos] 01/03: New upstream release.
Vincent Bernat
bernat at moszumanska.debian.org
Tue Nov 14 09:41:43 UTC 2017
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to annotated tag debian/0.6.1-1
in repository napalm-eos.
commit a046a284029b364c1de71533729fa9e95acbd39a
Author: Vincent Bernat <bernat at debian.org>
Date: Tue Nov 14 10:39:59 2017 +0100
New upstream release.
---
PKG-INFO | 2 +-
napalm_eos.egg-info/PKG-INFO | 2 +-
napalm_eos.egg-info/SOURCES.txt | 2 +
napalm_eos.egg-info/requires.txt | 2 +-
napalm_eos/eos.py | 578 +++++++++++++++++------------
napalm_eos/templates/delete_snmp_config.j2 | 6 +-
napalm_eos/templates/delete_users.j2 | 17 +
napalm_eos/templates/set_users.j2 | 15 +
napalm_eos/utils/textfsm_templates/vrf.tpl | 17 +-
requirements.txt | 2 +-
setup.cfg | 2 +-
setup.py | 2 +-
12 files changed, 396 insertions(+), 251 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index 2a12cec..4617347 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: napalm-eos
-Version: 0.5.1
+Version: 0.6.1
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm-eos
Author: David Barroso, Mircea Ulinic
diff --git a/napalm_eos.egg-info/PKG-INFO b/napalm_eos.egg-info/PKG-INFO
index 2a12cec..4617347 100644
--- a/napalm_eos.egg-info/PKG-INFO
+++ b/napalm_eos.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: napalm-eos
-Version: 0.5.1
+Version: 0.6.1
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm-eos
Author: David Barroso, Mircea Ulinic
diff --git a/napalm_eos.egg-info/SOURCES.txt b/napalm_eos.egg-info/SOURCES.txt
index 1d55617..24d3987 100644
--- a/napalm_eos.egg-info/SOURCES.txt
+++ b/napalm_eos.egg-info/SOURCES.txt
@@ -11,7 +11,9 @@ napalm_eos.egg-info/requires.txt
napalm_eos.egg-info/top_level.txt
napalm_eos/templates/delete_ntp_servers.j2
napalm_eos/templates/delete_snmp_config.j2
+napalm_eos/templates/delete_users.j2
napalm_eos/templates/set_ntp_servers.j2
+napalm_eos/templates/set_users.j2
napalm_eos/templates/snmp_config.j2
napalm_eos/utils/__init__.py
napalm_eos/utils/textfsm_templates/bgp_detail.tpl
diff --git a/napalm_eos.egg-info/requires.txt b/napalm_eos.egg-info/requires.txt
index 9b2ee5b..c3bcd0e 100644
--- a/napalm_eos.egg-info/requires.txt
+++ b/napalm_eos.egg-info/requires.txt
@@ -1,2 +1,2 @@
-napalm-base>=0.19.0
+napalm-base>=0.23.0
pyeapi
diff --git a/napalm_eos/eos.py b/napalm_eos/eos.py
index e7accf0..59fcd2e 100644
--- a/napalm_eos/eos.py
+++ b/napalm_eos/eos.py
@@ -59,6 +59,9 @@ class EOSDriver(NetworkDriver):
_RE_BGP_DESC = re.compile('\s+Description: (?P<description>.*?)')
_RE_BGP_LOCAL = re.compile('Local AS is (?P<as>.*?),.*')
_RE_BGP_PREFIX = re.compile('(\s*?)(?P<af>IPv[46]) Unicast:\s*(?P<sent>\d+)\s*(?P<received>\d+)') # noqa
+ _RE_SNMP_COMM = re.compile(r"""^snmp-server\s+community\s+(?P<community>\S+)
+ (\s+view\s+(?P<view>\S+))?(\s+(?P<access>ro|rw)?)
+ (\s+ipv6\s+(?P<v6_acl>\S+))?(\s+(?P<v4_acl>\S+))?$""", re.VERBOSE)
def __init__(self, hostname, username, password, timeout=60, optional_args=None):
"""Constructor."""
@@ -68,11 +71,13 @@ class EOSDriver(NetworkDriver):
self.password = password
self.timeout = timeout
self.config_session = None
+ self.locked = False
if optional_args is None:
optional_args = {}
- self.transport = optional_args.get('eos_transport', 'https')
+ # eos_transport is there for backwards compatibility, transport is the preferred method
+ self.transport = optional_args.get('transport', optional_args.get('eos_transport', 'https'))
if self.transport == 'https':
self.port = optional_args.get('port', 443)
@@ -81,6 +86,8 @@ class EOSDriver(NetworkDriver):
self.enablepwd = optional_args.get('enable_password', '')
+ self.profile = ["eos"]
+
def open(self):
"""Implementation of NAPALM method open."""
try:
@@ -119,15 +126,18 @@ class EOSDriver(NetworkDriver):
'is_alive': True # always true as eAPI is HTTP-based
}
- def _load_config(self, filename=None, config=None, replace=True):
- if self.config_session is not None:
- raise SessionLockedException('Session is already in use by napalm')
- else:
+ def _lock(self):
+ if self.config_session is None:
self.config_session = 'napalm_{}'.format(datetime.now().microsecond)
+ sess = self.device.run_commands(['show configuration sessions'])[0]['sessions']
+ if [k for k, v in sess.items() if v['state'] == 'pending' and k != self.config_session]:
+ raise SessionLockedException('Session is already in use')
- commands = list()
- commands.append('configure session {}'.format(self.config_session))
+ def _load_config(self, filename=None, config=None, replace=True):
+ commands = []
+ self._lock()
+ commands.append('configure session {}'.format(self.config_session))
if replace:
commands.append('rollback clean-config')
@@ -152,7 +162,6 @@ class EOSDriver(NetworkDriver):
self.device.run_commands(commands)
except pyeapi.eapilib.CommandError as e:
self.discard_config()
-
if replace:
raise ReplaceConfigException(e.message)
else:
@@ -180,7 +189,7 @@ class EOSDriver(NetworkDriver):
def commit_config(self):
"""Implementation of NAPALM method commit_config."""
- commands = list()
+ commands = []
commands.append('copy startup-config flash:rollback-0')
commands.append('configure session {}'.format(self.config_session))
commands.append('commit')
@@ -192,7 +201,7 @@ class EOSDriver(NetworkDriver):
def discard_config(self):
"""Implementation of NAPALM method discard_config."""
if self.config_session is not None:
- commands = list()
+ commands = []
commands.append('configure session {}'.format(self.config_session))
commands.append('abort')
self.device.run_commands(commands)
@@ -200,7 +209,7 @@ class EOSDriver(NetworkDriver):
def rollback(self):
"""Implementation of NAPALM method rollback."""
- commands = list()
+ commands = []
commands.append('configure replace flash:rollback-0')
commands.append('write memory')
self.device.run_commands(commands)
@@ -235,14 +244,14 @@ class EOSDriver(NetworkDriver):
}
def get_interfaces(self):
- commands = list()
+ commands = []
commands.append('show interfaces')
output = self.device.run_commands(commands)[0]
- interfaces = dict()
+ interfaces = {}
for interface, values in output['interfaces'].items():
- interfaces[interface] = dict()
+ interfaces[interface] = {}
if values['lineProtocolStatus'] == 'up':
interfaces[interface]['is_up'] = True
@@ -265,15 +274,15 @@ class EOSDriver(NetworkDriver):
return interfaces
def get_lldp_neighbors(self):
- commands = list()
+ commands = []
commands.append('show lldp neighbors')
output = self.device.run_commands(commands)[0]['lldpNeighbors']
- lldp = dict()
+ lldp = {}
for n in output:
if n['port'] not in lldp.keys():
- lldp[n['port']] = list()
+ lldp[n['port']] = []
lldp[n['port']].append(
{
@@ -326,7 +335,7 @@ class EOSDriver(NetworkDriver):
['show ip ' + NEIGHBOR_FILTER, 'show ipv6 ' + NEIGHBOR_FILTER],
encoding='text')
- bgp_counters = defaultdict(lambda: dict(peers=dict()))
+ bgp_counters = defaultdict(lambda: dict(peers={}))
for summary in output_summary_cmds:
"""
Json output looks as follows
@@ -494,13 +503,13 @@ class EOSDriver(NetworkDriver):
def get_lldp_neighbors_detail(self, interface=''):
- lldp_neighbors_out = dict()
+ lldp_neighbors_out = {}
- filters = list()
+ filters = []
if interface:
filters.append(interface)
- commands = list()
+ commands = []
commands.append(
'show lldp neighbors {filters} detail'.format(
filters=' '.join(filters)
@@ -519,20 +528,23 @@ class EOSDriver(NetworkDriver):
# it is provided a list of neighbors per interface
for neighbor in interface_neighbors:
if interface not in lldp_neighbors_out.keys():
- lldp_neighbors_out[interface] = list()
- capabilities = neighbor.get('systemCapabilities')
+ lldp_neighbors_out[interface] = []
+ capabilities = neighbor.get('systemCapabilities', {})
capabilities_list = list(capabilities.keys())
capabilities_list.sort()
+ remote_chassis_id = neighbor.get('chassisId', u'')
+ if neighbor.get("chassisIdType", u'') == "macAddress":
+ remote_chassis_id = napalm_base.helpers.mac(remote_chassis_id)
+ neighbor_interface_info = neighbor.get('neighborInterfaceInfo', {})
lldp_neighbors_out[interface].append(
{
'parent_interface': interface, # no parent interfaces
- 'remote_port':
- neighbor.get('neighborInterfaceInfo', {}).get('interfaceId', u''),
- 'remote_port_description': u'',
+ 'remote_port': neighbor_interface_info.get('interfaceId', u''),
+ 'remote_port_description':
+ neighbor_interface_info.get('interfaceDescription', u''),
'remote_system_name': neighbor.get('systemName', u''),
'remote_system_description': neighbor.get('systemDescription', u''),
- 'remote_chassis_id': napalm_base.helpers.mac(
- neighbor.get('chassisId', u'')),
+ 'remote_chassis_id': remote_chassis_id,
'remote_system_capab': py23_compat.text_type(', '.join(capabilities_list)),
'remote_system_enable_capab': py23_compat.text_type(', '.join(
[capability for capability in capabilities_list
@@ -542,7 +554,7 @@ class EOSDriver(NetworkDriver):
return lldp_neighbors_out
def cli(self, commands):
- cli_output = dict()
+ cli_output = {}
if type(commands) is not list:
raise TypeError('Please enter a valid list of commands!')
@@ -629,10 +641,36 @@ class EOSDriver(NetworkDriver):
list: []
}
+ def default_group_dict(local_as):
+ group_dict = {}
+ group_dict.update({
+ key: _DATATYPE_DEFAULT_.get(_PROPERTY_TYPE_MAP_.get(prop))
+ for prop, key in _GROUP_FIELD_MAP_.items()
+ })
+ group_dict.update({
+ 'prefix_limit': {},
+ 'neighbors': {},
+ 'local_as': local_as
+ }) # few more default values
+ return group_dict
+
+ def default_neighbor_dict(local_as):
+ neighbor_dict = {}
+ neighbor_dict.update({
+ key: _DATATYPE_DEFAULT_.get(_PROPERTY_TYPE_MAP_.get(prop))
+ for prop, key in _PEER_FIELD_MAP_.items()
+ }) # populating with default values
+ neighbor_dict.update({
+ 'prefix_limit': {},
+ 'local_as': local_as,
+ 'authentication_key': u''
+ }) # few more default values
+ return neighbor_dict
+
def parse_options(options, default_value=False):
if not options:
- return dict()
+ return {}
config_property = options[0]
field_name = _PROPERTY_FIELD_MAP_.get(config_property)
@@ -641,11 +679,13 @@ class EOSDriver(NetworkDriver):
if not field_type:
# no type specified at all => return empty dictionary
- return dict()
+ return {}
if not default_value:
if len(options) > 1:
- field_value = field_type(options[1])
+ field_value = napalm_base.helpers.convert(field_type,
+ options[1],
+ _DATATYPE_DEFAULT_.get(field_type))
else:
if field_type is bool:
field_value = True
@@ -670,22 +710,22 @@ class EOSDriver(NetworkDriver):
field_name = 'export_policy'
return {field_name: field_value}
- return dict()
+ return {}
- bgp_config = dict()
+ bgp_config = {}
- commands = list()
+ commands = []
commands.append('show running-config | section router bgp')
bgp_conf = self.device.run_commands(commands, encoding='text')[0].get('output', '\n\n')
- bgp_conf_lines = bgp_conf.splitlines()[2:]
+ bgp_conf_lines = bgp_conf.splitlines()
- bgp_neighbors = dict()
+ bgp_neighbors = {}
if not group:
- neighbor = ''
+ neighbor = '' # noqa
- last_peer_group = ''
local_as = 0
+ bgp_neighbors = {}
for bgp_conf_line in bgp_conf_lines:
default_value = False
bgp_conf_line = bgp_conf_line.strip()
@@ -708,18 +748,10 @@ class EOSDriver(NetworkDriver):
IPAddress(group_or_neighbor)
# if passes the test => it is an IP Address, thus a Neighbor!
peer_address = group_or_neighbor
-
+ if peer_address not in bgp_neighbors:
+ bgp_neighbors[peer_address] = default_neighbor_dict(local_as)
if options[0] == 'peer-group':
- last_peer_group = options[1]
-
- # if looking for a specific group
- if group and last_peer_group != group:
- continue
-
- # or even more. a specific neighbor within a group
- if neighbor and peer_address != neighbor:
- continue
- # skip all other except the target
+ bgp_neighbors[peer_address]['__group'] = options[1]
# in the config, neighbor details are lister after
# the group is specified for the neighbor:
@@ -733,20 +765,8 @@ class EOSDriver(NetworkDriver):
# that way we avoid one more loop to
# match the neighbors with the group they belong to
# directly will apend the neighbor in the neighbor list of the group at the end
- if last_peer_group not in bgp_neighbors.keys():
- bgp_neighbors[last_peer_group] = dict()
- if peer_address not in bgp_neighbors[last_peer_group]:
- bgp_neighbors[last_peer_group][peer_address] = dict()
- bgp_neighbors[last_peer_group][peer_address].update({
- key: _DATATYPE_DEFAULT_.get(_PROPERTY_TYPE_MAP_.get(prop))
- for prop, key in _PEER_FIELD_MAP_.items()
- }) # populating with default values
- bgp_neighbors[last_peer_group][peer_address].update({
- 'prefix_limit': {},
- 'local_as': local_as,
- 'authentication_key': u''
- }) # few more default values
- bgp_neighbors[last_peer_group][peer_address].update(
+
+ bgp_neighbors[peer_address].update(
parse_options(options, default_value)
)
except AddrFormatError:
@@ -756,33 +776,24 @@ class EOSDriver(NetworkDriver):
if group and group_name != group:
continue
if group_name not in bgp_config.keys():
- bgp_config[group_name] = dict()
- bgp_config[group_name].update({
- key: _DATATYPE_DEFAULT_.get(_PROPERTY_TYPE_MAP_.get(prop))
- for prop, key in _GROUP_FIELD_MAP_.items()
- })
- bgp_config[group_name].update({
- 'prefix_limit': {},
- 'neighbors': {},
- 'local_as': local_as
- }) # few more default values
+ bgp_config[group_name] = default_group_dict(local_as)
bgp_config[group_name].update(
parse_options(options, default_value)
)
- except Exception:
- # for other kind of exception pass to next line
- continue
- for group, peers in bgp_neighbors.items():
- if group not in bgp_config.keys():
- continue
- bgp_config[group]['neighbors'] = peers
+ for peer, peer_details in bgp_neighbors.items():
+ peer_group = peer_details.pop('__group', None)
+ if not peer_group:
+ peer_group = '_'
+ if peer_group not in bgp_config:
+ bgp_config[peer_group] = default_group_dict(local_as)
+ bgp_config[peer_group]['neighbors'][peer] = peer_details
return bgp_config
def get_arp_table(self):
- arp_table = list()
+ arp_table = []
commands = ['show arp']
@@ -819,7 +830,7 @@ class EOSDriver(NetworkDriver):
for ntp_peer in ntp_config if ntp_peer.get('ntppeer', '')}
def get_ntp_stats(self):
- ntp_stats = list()
+ ntp_stats = []
REGEX = (
'^\s?(\+|\*|x|-)?([a-zA-Z0-9\.+-:]+)'
@@ -829,7 +840,7 @@ class EOSDriver(NetworkDriver):
'\s+([0-9\.]+)\s?$'
)
- commands = list()
+ commands = []
commands.append('show ntp associations')
# output = self.device.run_commands(commands)
@@ -866,7 +877,7 @@ class EOSDriver(NetworkDriver):
def get_interfaces_ip(self):
- interfaces_ip = dict()
+ interfaces_ip = {}
interfaces_ipv4_out = self.device.run_commands(['show ip interface'])[0]['interfaces']
try:
@@ -878,14 +889,14 @@ class EOSDriver(NetworkDriver):
raise
for interface_name, interface_details in interfaces_ipv4_out.items():
- ipv4_list = list()
+ ipv4_list = []
if interface_name not in interfaces_ip.keys():
- interfaces_ip[interface_name] = dict()
+ interfaces_ip[interface_name] = {}
if u'ipv4' not in interfaces_ip.get(interface_name):
- interfaces_ip[interface_name][u'ipv4'] = dict()
+ interfaces_ip[interface_name][u'ipv4'] = {}
if u'ipv6' not in interfaces_ip.get(interface_name):
- interfaces_ip[interface_name][u'ipv6'] = dict()
+ interfaces_ip[interface_name][u'ipv6'] = {}
iface_details = interface_details.get('interfaceAddress', {})
if iface_details.get('primaryIp', {}).get('address') != '0.0.0.0':
@@ -913,14 +924,14 @@ class EOSDriver(NetworkDriver):
}
for interface_name, interface_details in interfaces_ipv6_out.items():
- ipv6_list = list()
+ ipv6_list = []
if interface_name not in interfaces_ip.keys():
- interfaces_ip[interface_name] = dict()
+ interfaces_ip[interface_name] = {}
if u'ipv4' not in interfaces_ip.get(interface_name):
- interfaces_ip[interface_name][u'ipv4'] = dict()
+ interfaces_ip[interface_name][u'ipv4'] = {}
if u'ipv6' not in interfaces_ip.get(interface_name):
- interfaces_ip[interface_name][u'ipv6'] = dict()
+ interfaces_ip[interface_name][u'ipv6'] = {}
ipv6_list.append(
{
@@ -951,16 +962,12 @@ class EOSDriver(NetworkDriver):
def get_mac_address_table(self):
- mac_table = list()
+ mac_table = []
commands = ['show mac address-table']
- mac_entries = []
- try:
- mac_entries = self.device.run_commands(commands)[0].get(
- 'unicastTable', {}).get('tableEntries', [])
- except Exception:
- return {}
+ mac_entries = self.device.run_commands(commands)[0].get(
+ 'unicastTable', {}).get('tableEntries', [])
for mac_entry in mac_entries:
vlan = mac_entry.get('vlanId')
@@ -984,7 +991,17 @@ class EOSDriver(NetworkDriver):
return mac_table
def get_route_to(self, destination='', protocol=''):
- routes = dict()
+ routes = {}
+
+ # Placeholder for vrf arg
+ vrf = ''
+
+ # Right not iterating through vrfs is necessary
+ # show ipv6 route doesn't support vrf 'all'
+ if vrf == '':
+ vrfs = sorted(self._get_vrfs())
+ else:
+ vrfs = [vrf]
if protocol.lower() == 'direct':
protocol = 'connected'
@@ -996,129 +1013,155 @@ class EOSDriver(NetworkDriver):
except AddrFormatError:
return 'Please specify a valid destination!'
- command = 'show ip{ipv} route {destination} {protocol} detail'.format(
- ipv=ipv,
- destination=destination,
- protocol=protocol,
- )
-
- command_output = self.device.run_commands([command])[0]
- if ipv == 'v6':
- routes_out = command_output.get('routes', {})
- else:
- # on a multi-VRF configured device need to go through a loop and get for each instance
- routes_out = command_output.get('vrfs', {}).get('default', {}).get('routes', {})
-
- for prefix, route_details in routes_out.items():
- if prefix not in routes.keys():
- routes[prefix] = list()
- route_protocol = route_details.get('routeType').upper()
- preference = route_details.get('preference', 0)
-
- route = {
- 'current_active': False,
- 'last_active': False,
- 'age': 0,
- 'next_hop': u'',
- 'protocol': route_protocol,
- 'outgoing_interface': u'',
- 'preference': preference,
- 'inactive_reason': u'',
- 'routing_table': u'default',
- 'selected_next_hop': False,
- 'protocol_attributes': {}
- }
- if protocol == 'bgp':
- metric = route_details.get('metric')
- command = 'show ip{ipv} bgp {destination} detail'.format(
- ipv=ipv,
- destination=prefix
- )
- default_vrf_details = self.device.run_commands([command])[0].get(
- 'vrfs', {}).get('default', {})
- local_as = default_vrf_details.get('asn')
- bgp_routes = default_vrf_details.get(
- 'bgpRouteEntries', {}).get(prefix, {}).get('bgpRoutePaths', [])
- for bgp_route_details in bgp_routes:
- bgp_route = route.copy()
- as_path = bgp_route_details.get('asPathEntry', {}).get('asPath', u'')
- remote_as = int(as_path.split()[-1])
- remote_address = napalm_base.helpers.ip(bgp_route_details.get(
- 'routeDetail', {}).get('peerEntry', {}).get('peerAddr', ''))
- local_preference = bgp_route_details.get('localPreference')
- next_hop = napalm_base.helpers.ip(bgp_route_details.get('nextHop'))
- active_route = bgp_route_details.get('routeType', {}).get('active', False)
- last_active = active_route # should find smth better
- communities = bgp_route_details.get('routeDetail', {}).get('communityList', [])
- preference2 = bgp_route_details.get('weight')
- bgp_route.update({
- 'current_active': active_route,
- 'last_active': last_active,
- 'next_hop': next_hop,
- 'selected_next_hop': active_route,
- 'protocol_attributes': {
- 'metric': metric,
- 'as_path': as_path,
- 'local_preference': local_preference,
- 'local_as': local_as,
- 'remote_as': remote_as,
- 'remote_address': remote_address,
- 'preference2': preference2,
- 'communities': communities
- }
- })
- routes[prefix].append(bgp_route)
+ commands = []
+ for _vrf in vrfs:
+ commands.append('show ip{ipv} route vrf {_vrf} {destination} {protocol} detail'.format(
+ ipv=ipv,
+ _vrf=_vrf,
+ destination=destination,
+ protocol=protocol,
+ ))
+
+ commands_output = self.device.run_commands(commands)
+
+ for _vrf, command_output in zip(vrfs, commands_output):
+ if ipv == 'v6':
+ routes_out = command_output.get('routes', {})
else:
- for next_hop in route_details.get('vias'):
- route_next_hop = route.copy()
- if next_hop.get('nexthopAddr') is None:
- route_next_hop.update(
- {
- 'next_hop': '',
- 'outgoing_interface': next_hop.get('interface')
- }
- )
- else:
- route_next_hop.update(
- {
- 'next_hop': napalm_base.helpers.ip(next_hop.get('nexthopAddr')),
- 'outgoing_interface': next_hop.get('interface')
+ routes_out = command_output.get('vrfs', {}).get(_vrf, {}).get('routes', {})
+
+ for prefix, route_details in routes_out.items():
+ if prefix not in routes.keys():
+ routes[prefix] = []
+ route_protocol = route_details.get('routeType')
+ preference = route_details.get('preference', 0)
+
+ route = {
+ 'current_active': True,
+ 'last_active': True,
+ 'age': 0,
+ 'next_hop': u'',
+ 'protocol': route_protocol,
+ 'outgoing_interface': u'',
+ 'preference': preference,
+ 'inactive_reason': u'',
+ 'routing_table': _vrf,
+ 'selected_next_hop': True,
+ 'protocol_attributes': {}
+ }
+ if protocol == 'bgp' or route_protocol.lower() in ('ebgp', 'ibgp'):
+ nexthop_interface_map = {}
+ for next_hop in route_details.get('vias'):
+ nexthop_ip = napalm_base.helpers.ip(next_hop.get('nexthopAddr'))
+ nexthop_interface_map[nexthop_ip] = next_hop.get('interface')
+ metric = route_details.get('metric')
+ command = 'show ip{ipv} bgp {destination} detail vrf {_vrf}'.format(
+ ipv=ipv,
+ destination=prefix,
+ _vrf=_vrf
+ )
+ vrf_details = self.device.run_commands([command])[0].get(
+ 'vrfs', {}).get(_vrf, {})
+ local_as = vrf_details.get('asn')
+ bgp_routes = vrf_details.get(
+ 'bgpRouteEntries', {}).get(prefix, {}).get('bgpRoutePaths', [])
+ for bgp_route_details in bgp_routes:
+ bgp_route = route.copy()
+ as_path = bgp_route_details.get('asPathEntry', {}).get('asPath', u'')
+ remote_as = int(as_path.split()[-1])
+ remote_address = napalm_base.helpers.ip(bgp_route_details.get(
+ 'routeDetail', {}).get('peerEntry', {}).get('peerAddr', ''))
+ local_preference = bgp_route_details.get('localPreference')
+ next_hop = napalm_base.helpers.ip(bgp_route_details.get('nextHop'))
+ active_route = bgp_route_details.get('routeType', {}).get('active', False)
+ last_active = active_route # should find smth better
+ communities = bgp_route_details.get('routeDetail', {}).get(
+ 'communityList', [])
+ preference2 = bgp_route_details.get('weight')
+ inactive_reason = bgp_route_details.get('reasonNotBestpath', '')
+ bgp_route.update({
+ 'current_active': active_route,
+ 'inactive_reason': inactive_reason,
+ 'last_active': last_active,
+ 'next_hop': next_hop,
+ 'outgoing_interface': nexthop_interface_map.get(next_hop),
+ 'selected_next_hop': active_route,
+ 'protocol_attributes': {
+ 'metric': metric,
+ 'as_path': as_path,
+ 'local_preference': local_preference,
+ 'local_as': local_as,
+ 'remote_as': remote_as,
+ 'remote_address': remote_address,
+ 'preference2': preference2,
+ 'communities': communities
}
- )
- routes[prefix].append(route_next_hop)
-
+ })
+ routes[prefix].append(bgp_route)
+ else:
+ if route_details.get('routeAction') in ('drop',):
+ route['next_hop'] = 'NULL'
+ if route_details.get('routingDisabled') is True:
+ route['last_active'] = False
+ route['current_active'] = False
+ for next_hop in route_details.get('vias'):
+ route_next_hop = route.copy()
+ if next_hop.get('nexthopAddr') is None:
+ route_next_hop.update(
+ {
+ 'next_hop': '',
+ 'outgoing_interface': next_hop.get('interface')
+ }
+ )
+ else:
+ route_next_hop.update(
+ {
+ 'next_hop': napalm_base.helpers.ip(next_hop.get('nexthopAddr')),
+ 'outgoing_interface': next_hop.get('interface')
+ }
+ )
+ routes[prefix].append(route_next_hop)
+ if route_details.get('vias') == []: # empty list
+ routes[prefix].append(route)
return routes
def get_snmp_information(self):
+ """get_snmp_information() for EOS. Re-written to not use TextFSM"""
- snmp_information = dict()
-
- commands = list()
- commands.append('show running-config | section snmp-server')
- raw_snmp_config = self.device.run_commands(commands, encoding='text')[0].get('output', '')
-
- snmp_config = napalm_base.helpers.textfsm_extractor(self, 'snmp_config', raw_snmp_config)
-
- if not snmp_config:
- return snmp_information
-
- snmp_information = {
- 'contact': py23_compat.text_type(snmp_config[0].get('contact', '')),
- 'location': py23_compat.text_type(snmp_config[0].get('location', '')),
- 'chassis_id': py23_compat.text_type(snmp_config[0].get('chassis_id', '')),
+ # Default values
+ snmp_dict = {
+ 'chassis_id': '',
+ 'location': '',
+ 'contact': '',
'community': {}
}
- for snmp_entry in snmp_config:
- community_name = py23_compat.text_type(snmp_entry.get('community', ''))
- if not community_name:
- continue
- snmp_information['community'][community_name] = {
- 'acl': py23_compat.text_type(snmp_entry.get('acl', '')),
- 'mode': py23_compat.text_type(snmp_entry.get('mode', 'ro').lower())
- }
+ commands = [
+ 'show snmp chassis',
+ 'show snmp location',
+ 'show snmp contact'
+ ]
+ snmp_config = self.device.run_commands(commands, encoding='json')
+ for line in snmp_config:
+ for k, v in line.items():
+ if k == 'chassisId':
+ snmp_dict['chassis_id'] = v
+ else:
+ # Some EOS versions add extra quotes
+ snmp_dict[k] = v.strip('"')
- return snmp_information
+ commands = ['show running-config | section snmp-server community']
+ raw_snmp_config = self.device.run_commands(commands, encoding='text')[0].get('output', '')
+ for line in raw_snmp_config.splitlines():
+ match = self._RE_SNMP_COMM.search(line)
+ if match:
+ matches = match.groupdict('')
+ snmp_dict['community'][match.group('community')] = {
+ 'acl': py23_compat.text_type(matches['v4_acl']),
+ 'mode': py23_compat.text_type(matches['access'])
+ }
+
+ return snmp_dict
def get_users(self):
@@ -1129,7 +1172,7 @@ class EOSDriver(NetworkDriver):
return u'ssh_dsa', py23_compat.text_type(sshkey)
return u'ssh_rsa', u''
- users = dict()
+ users = {}
commands = ['show user-account']
user_items = self.device.run_commands(commands)[0].get('users', {})
@@ -1138,10 +1181,14 @@ class EOSDriver(NetworkDriver):
user_details.pop('username', '')
sshkey_value = user_details.pop('sshAuthorizedKey', '')
sshkey_type, sshkey_value = _sshkey_type(sshkey_value)
+ if sshkey_value != '':
+ sshkey_list = [sshkey_value]
+ else:
+ sshkey_list = []
user_details.update({
'level': user_details.pop('privLevel', 0),
'password': py23_compat.text_type(user_details.pop('secret', '')),
- 'sshkeys': [sshkey_value]
+ 'sshkeys': sshkey_list
})
users[user] = user_details
@@ -1151,7 +1198,8 @@ class EOSDriver(NetworkDriver):
destination,
source=c.TRACEROUTE_SOURCE,
ttl=c.TRACEROUTE_TTL,
- timeout=c.TRACEROUTE_TIMEOUT):
+ timeout=c.TRACEROUTE_TIMEOUT,
+ vrf=c.TRACEROUTE_VRF):
_HOP_ENTRY_PROBE = [
'\s+',
@@ -1186,27 +1234,36 @@ class EOSDriver(NetworkDriver):
probes = 3
# in case will be added one further param to adjust the number of probes/hop
+ commands = []
+
+ if vrf:
+ commands.append('routing-context vrf {vrf}'.format(vrf=vrf))
+
if source:
source_opt = '-s {source}'.format(source=source)
if ttl:
ttl_opt = '-m {ttl}'.format(ttl=ttl)
if timeout:
timeout_opt = '-w {timeout}'.format(timeout=timeout)
- else:
- timeout = 5
-
- command = 'traceroute {destination} {source_opt} {ttl_opt} {timeout_opt}'.format(
- destination=destination,
- source_opt=source_opt,
- ttl_opt=ttl_opt,
- timeout_opt=timeout_opt
+ total_timeout = timeout * ttl
+ # `ttl`, `source` and `timeout` are not supported by default CLI
+ # so we need to go through the bash and set a specific timeout
+ commands.append(
+ ('bash timeout {total_timeout} traceroute {destination} '
+ '{source_opt} {ttl_opt} {timeout_opt}').format(
+ total_timeout=total_timeout,
+ destination=destination,
+ source_opt=source_opt,
+ ttl_opt=ttl_opt,
+ timeout_opt=timeout_opt
+ )
)
try:
traceroute_raw_output = self.device.run_commands(
- [command], encoding='text')[0].get('output')
+ commands, encoding='text')[-1].get('output')
except CommandErrorException:
- return {'error': 'Cannot execute traceroute on the device: {}'.format(command)}
+ return {'error': 'Cannot execute traceroute on the device: {}'.format(commands[0])}
hop_regex = ''.join(_HOP_ENTRY + _HOP_ENTRY_PROBE * probes)
@@ -1502,24 +1559,38 @@ class EOSDriver(NetworkDriver):
else:
raise Exception("Wrong retrieve filter: {}".format(retrieve))
- def get_network_instances(self, name=''):
- """get_network_instances implementation for EOS."""
-
+ def _show_vrf(self):
commands = ['show vrf']
# This command has no JSON yet
raw_output = self.device.run_commands(commands, encoding='text')[0].get('output', '')
output = napalm_base.helpers.textfsm_extractor(self, 'vrf', raw_output)
- vrfs = dict()
- all_vrf_interfaces = dict()
+
+ return output
+
+ def _get_vrfs(self):
+ output = self._show_vrf()
+
+ vrfs = [py23_compat.text_type(vrf['name']) for vrf in output]
+
+ vrfs.append(u'default')
+
+ return vrfs
+
+ def get_network_instances(self, name=''):
+ """get_network_instances implementation for EOS."""
+
+ output = self._show_vrf()
+ vrfs = {}
+ all_vrf_interfaces = {}
for vrf in output:
if (vrf.get('route_distinguisher', '') == "<not set>" or
vrf.get('route_distinguisher', '') == 'None'):
vrf['route_distinguisher'] = u''
else:
vrf['route_distinguisher'] = py23_compat.text_type(vrf['route_distinguisher'])
- interfaces = dict()
+ interfaces = {}
for interface_raw in vrf.get('interfaces', []):
interface = interface_raw.split(',')
for line in interface:
@@ -1559,7 +1630,7 @@ class EOSDriver(NetworkDriver):
return vrfs
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):
"""
Execute ping on the device and returns a dictionary with the result.
Output dictionary has one of following keys:
@@ -1578,13 +1649,21 @@ class EOSDriver(NetworkDriver):
* rtt (float)
"""
ping_dict = {}
+ commands = []
+
+ if vrf:
+ commands.append('routing-context vrf {vrf}'.format(vrf=vrf))
+
command = 'ping {}'.format(destination)
command += ' timeout {}'.format(timeout)
command += ' size {}'.format(size)
command += ' repeat {}'.format(count)
if source != '':
command += ' source {}'.format(source)
- output = self.device.run_commands([command], encoding='text')[0]['output']
+
+ commands.append(command)
+ output = self.device.run_commands(commands, encoding='text')[-1]['output']
+
if 'connect:' in output:
ping_dict['error'] = output
elif 'PING' in output:
@@ -1602,11 +1681,34 @@ class EOSDriver(NetworkDriver):
fields = line.split()
if 'icmp' in line:
if 'Unreachable' in line:
- results_array.append({'ip_address': py23_compat.text_type(fields[1]),
- 'rtt': 0.0})
+ if "(" in fields[2]:
+ results_array.append(
+ {
+ 'ip_address': py23_compat.text_type(fields[2][1:-1]),
+ 'rtt': 0.0,
+ }
+ )
+ else:
+ results_array.append({'ip_address': py23_compat.text_type(fields[1]),
+ 'rtt': 0.0})
+ elif 'truncated' in line:
+ if "(" in fields[4]:
+ results_array.append(
+ {
+ 'ip_address': py23_compat.text_type(fields[4][1:-2]),
+ 'rtt': 0.0,
+ }
+ )
+ else:
+ results_array.append(
+ {
+ 'ip_address': py23_compat.text_type(fields[3][:-1]),
+ 'rtt': 0.0,
... 132 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/napalm-eos.git
More information about the Python-modules-commits
mailing list