[Python-modules-commits] [napalm-junos] 01/03: New upstream release.
Vincent Bernat
bernat at moszumanska.debian.org
Tue Nov 14 09:54:40 UTC 2017
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to annotated tag debian/0.12.1-1
in repository napalm-junos.
commit 1d5ed8e892164de27549ab32093b5d8999212a25
Author: Vincent Bernat <bernat at debian.org>
Date: Tue Nov 14 10:52:40 2017 +0100
New upstream release.
---
PKG-INFO | 2 +-
napalm_junos.egg-info/PKG-INFO | 2 +-
napalm_junos.egg-info/SOURCES.txt | 2 +
napalm_junos.egg-info/requires.txt | 4 +-
napalm_junos/junos.py | 717 +++++++++++++++++++++++++++------
napalm_junos/templates/delete_users.j2 | 26 ++
napalm_junos/templates/set_users.j2 | 34 ++
napalm_junos/utils/junos_views.yml | 70 +++-
requirements.txt | 4 +-
setup.cfg | 1 -
setup.py | 2 +-
11 files changed, 724 insertions(+), 140 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index 2f79b12..454f55e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: napalm-junos
-Version: 0.6.0
+Version: 0.12.1
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm-junos
Author: David Barroso, Mircea Ulinic
diff --git a/napalm_junos.egg-info/PKG-INFO b/napalm_junos.egg-info/PKG-INFO
index 2f79b12..454f55e 100644
--- a/napalm_junos.egg-info/PKG-INFO
+++ b/napalm_junos.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: napalm-junos
-Version: 0.6.0
+Version: 0.12.1
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm-junos
Author: David Barroso, Mircea Ulinic
diff --git a/napalm_junos.egg-info/SOURCES.txt b/napalm_junos.egg-info/SOURCES.txt
index affcf58..33e9330 100644
--- a/napalm_junos.egg-info/SOURCES.txt
+++ b/napalm_junos.egg-info/SOURCES.txt
@@ -14,11 +14,13 @@ napalm_junos/templates/delete_ntp_peers.j2
napalm_junos/templates/delete_ntp_servers.j2
napalm_junos/templates/delete_probes.j2
napalm_junos/templates/delete_snmp_config.j2
+napalm_junos/templates/delete_users.j2
napalm_junos/templates/schedule_probes.j2
napalm_junos/templates/set_hostname.j2
napalm_junos/templates/set_ntp_peers.j2
napalm_junos/templates/set_ntp_servers.j2
napalm_junos/templates/set_probes.j2
+napalm_junos/templates/set_users.j2
napalm_junos/templates/snmp_config.j2
napalm_junos/utils/__init__.py
napalm_junos/utils/junos_views.py
diff --git a/napalm_junos.egg-info/requires.txt b/napalm_junos.egg-info/requires.txt
index 724db5e..cc42db9 100644
--- a/napalm_junos.egg-info/requires.txt
+++ b/napalm_junos.egg-info/requires.txt
@@ -1,2 +1,2 @@
-napalm-base>=0.20.3
-junos-eznc
+napalm-base>=0.24.1
+junos-eznc>=2.1.5
diff --git a/napalm_junos/junos.py b/napalm_junos/junos.py
index 0fadf31..363192d 100644
--- a/napalm_junos/junos.py
+++ b/napalm_junos/junos.py
@@ -19,11 +19,15 @@ from __future__ import unicode_literals
# import stdlib
import re
+import json
+import logging
import collections
from copy import deepcopy
+from collections import OrderedDict
# import third party lib
from lxml.builder import E
+from lxml import etree
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
@@ -35,7 +39,6 @@ from jnpr.junos.exception import ConnectTimeoutError
# import NAPALM Base
import napalm_base.helpers
from napalm_base.base import NetworkDriver
-from napalm_base.utils import string_parsers
from napalm_base.utils import py23_compat
import napalm_junos.constants as C
from napalm_base.exceptions import ConnectionException
@@ -47,6 +50,8 @@ from napalm_base.exceptions import CommandTimeoutException
# import local modules
from napalm_junos.utils import junos_views
+log = logging.getLogger(__file__)
+
class JunOSDriver(NetworkDriver):
"""JunOSDriver class - inherits NetworkDriver from napalm_base."""
@@ -56,8 +61,11 @@ class JunOSDriver(NetworkDriver):
Initialise JunOS driver.
Optional args:
- * port (int): custom port
* config_lock (True/False): lock configuration DB after the connection is established.
+ * port (int): custom port
+ * key_file (string): SSH key file path
+ * keepalive (int): Keepalive interval
+ * ignore_warning (boolean): not generate warning exceptions
"""
self.hostname = hostname
self.username = username
@@ -66,12 +74,32 @@ class JunOSDriver(NetworkDriver):
self.config_replace = False
self.locked = False
+ # Get optional arguments
if optional_args is None:
optional_args = {}
+
+ self.config_lock = optional_args.get('config_lock', False)
self.port = optional_args.get('port', 22)
- self.config_lock = optional_args.get('config_lock', True)
+ self.key_file = optional_args.get('key_file', None)
+ self.keepalive = optional_args.get('keepalive', 30)
+ self.ssh_config_file = optional_args.get('ssh_config_file', None)
+ self.ignore_warning = optional_args.get('ignore_warning', False)
+
+ if self.key_file:
+ self.device = Device(hostname,
+ user=username,
+ password=password,
+ ssh_private_key_file=self.key_file,
+ ssh_config=self.ssh_config_file,
+ port=self.port)
+ else:
+ self.device = Device(hostname,
+ user=username,
+ password=password,
+ port=self.port,
+ ssh_config=self.ssh_config_file)
- self.device = Device(hostname, user=username, password=password, port=self.port)
+ self.profile = ["junos"]
def open(self):
"""Open the connection wit the device."""
@@ -80,6 +108,7 @@ class JunOSDriver(NetworkDriver):
except ConnectTimeoutError as cte:
raise ConnectionException(cte.message)
self.device.timeout = self.timeout
+ self.device._conn._session.transport.set_keepalive(self.keepalive)
if hasattr(self.device, "cu"):
# make sure to remove the cu attr from previous session
# ValueError: requested attribute name cu already exists
@@ -106,6 +135,23 @@ class JunOSDriver(NetworkDriver):
self.device.cu.unlock()
self.locked = False
+ def _rpc(self, get, child=None, **kwargs):
+ """
+ This allows you to construct an arbitrary RPC call to retreive common stuff. For example:
+ Configuration: get: "<get-configuration/>"
+ Interface information: get: "<get-interface-information/>"
+ A particular interfacece information:
+ get: "<get-interface-information/>"
+ child: "<interface-name>ge-0/0/0</interface-name>"
+ """
+ rpc = etree.fromstring(get)
+
+ if child:
+ rpc.append(etree.fromstring(child))
+
+ response = self.device.execute(rpc)
+ return etree.tostring(response)
+
def is_alive(self):
# evaluate the state of the underlying SSH connection
# and also the NETCONF status from PyEZ
@@ -113,6 +159,36 @@ class JunOSDriver(NetworkDriver):
'is_alive': self.device._conn._session.transport.is_active() and self.device.connected
}
+ @staticmethod
+ def _is_json_format(config):
+ try:
+ _ = json.loads(config) # noqa
+ except (TypeError, ValueError):
+ return False
+ return True
+
+ def _detect_config_format(self, config):
+ fmt = 'text'
+ set_action_matches = [
+ 'set',
+ 'activate',
+ 'deactivate',
+ 'annotate',
+ 'copy',
+ 'delete',
+ 'insert',
+ 'protect',
+ 'rename',
+ 'unprotect',
+ ]
+ if config.strip().startswith('<'):
+ return 'xml'
+ elif config.strip().split(' ')[0] in set_action_matches:
+ return 'set'
+ elif self._is_json_format(config):
+ return 'json'
+ return fmt
+
def _load_candidate(self, filename, config, overwrite):
if filename is None:
configuration = config
@@ -127,12 +203,18 @@ class JunOSDriver(NetworkDriver):
# and the device will be locked till first commit/rollback
try:
- self.device.cu.load(configuration, format='text', overwrite=overwrite)
+ fmt = self._detect_config_format(configuration)
+
+ if fmt == "xml":
+ configuration = etree.XML(configuration)
+
+ self.device.cu.load(configuration, format=fmt, overwrite=overwrite,
+ ignore_warning=self.ignore_warning)
except ConfigLoadError as e:
if self.config_replace:
- raise ReplaceConfigException(e.message)
+ raise ReplaceConfigException(e.errs)
else:
- raise MergeConfigException(e.message)
+ raise MergeConfigException(e.errs)
def load_replace_candidate(self, filename=None, config=None):
"""Open the candidate config and merge."""
@@ -155,7 +237,7 @@ class JunOSDriver(NetworkDriver):
def commit_config(self):
"""Commit configuration."""
- self.device.cu.commit()
+ self.device.cu.commit(ignore_warning=self.ignore_warning)
if not self.config_lock:
self._unlock()
@@ -174,9 +256,7 @@ class JunOSDriver(NetworkDriver):
"""Return facts of the device."""
output = self.device.facts
- uptime = '0'
- if 'RE0' in output:
- uptime = output['RE0']['up_time']
+ uptime = self.device.uptime or -1
interfaces = junos_views.junos_iface_table(self.device)
interfaces.get()
@@ -189,7 +269,7 @@ class JunOSDriver(NetworkDriver):
'os_version': py23_compat.text_type(output['version']),
'hostname': py23_compat.text_type(output['hostname']),
'fqdn': py23_compat.text_type(output['fqdn']),
- 'uptime': string_parsers.convert_uptime_string_seconds(uptime),
+ 'uptime': uptime,
'interface_list': interface_list
}
@@ -242,14 +322,25 @@ class JunOSDriver(NetworkDriver):
environment = junos_views.junos_enviroment_table(self.device)
routing_engine = junos_views.junos_routing_engine_table(self.device)
temperature_thresholds = junos_views.junos_temperature_thresholds(self.device)
+ power_supplies = junos_views.junos_pem_table(self.device)
environment.get()
routing_engine.get()
temperature_thresholds.get()
environment_data = {}
+ current_class = None
for sensor_object, object_data in environment.items():
structured_object_data = {k: v for k, v in object_data}
+ if structured_object_data['class']:
+ # If current object has a 'class' defined, store it for use
+ # on subsequent unlabeled lines.
+ current_class = structured_object_data['class']
+ else:
+ # Juniper doesn't label the 2nd+ lines of a given class with a
+ # class name. In that case, we use the most recent class seen.
+ structured_object_data['class'] = current_class
+
if structured_object_data['class'] == 'Power':
# Create a dict for the 'power' key
try:
@@ -258,7 +349,6 @@ class JunOSDriver(NetworkDriver):
environment_data['power'] = {}
environment_data['power'][sensor_object] = {}
- # Set these values to -1, because Junos does not provide them
environment_data['power'][sensor_object]['capacity'] = -1.0
environment_data['power'][sensor_object]['output'] = -1.0
@@ -310,6 +400,21 @@ class JunOSDriver(NetworkDriver):
elif structured_temperature_data['yellow-alarm'] <= temp:
environment_data['temperature'][sensor_object]['is_alert'] = True
+ # Try to correct Power Supply information
+ pem_table = dict()
+ try:
+ power_supplies.get()
+ except RpcError:
+ # Not all platforms have support for this
+ pass
+ else:
+ # Format PEM information and correct capacity and output values
+ for pem in power_supplies.items():
+ pem_name = pem[0].replace("PEM", "Power Supply")
+ pem_table[pem_name] = dict(pem[1])
+ environment_data['power'][pem_name]['capacity'] = pem_table[pem_name]['capacity']
+ environment_data['power'][pem_name]['output'] = pem_table[pem_name]['output']
+
for routing_engine_object, routing_engine_data in routing_engine.items():
structured_routing_engine_data = {k: v for k, v in routing_engine_data}
# Create dicts for 'cpu' and 'memory'.
@@ -363,16 +468,34 @@ class JunOSDriver(NetworkDriver):
return address_family
def _parse_route_stats(self, neighbor):
- data = {}
+ data = {
+ 'ipv4': {
+ 'received_prefixes': -1,
+ 'accepted_prefixes': -1,
+ 'sent_prefixes': -1
+ },
+ 'ipv6': {
+ 'received_prefixes': -1,
+ 'accepted_prefixes': -1,
+ 'sent_prefixes': -1
+ }
+ }
if not neighbor['is_up']:
- pass
+ return data
elif isinstance(neighbor['tables'], list):
+ if isinstance(neighbor['sent_prefixes'], int):
+ # We expect sent_prefixes to be a list, but sometimes it
+ # is of type int. Therefore convert attribute to list
+ neighbor['sent_prefixes'] = [neighbor['sent_prefixes']]
for idx, table in enumerate(neighbor['tables']):
family = self._get_address_family(table)
data[family] = {}
data[family]['received_prefixes'] = neighbor['received_prefixes'][idx]
data[family]['accepted_prefixes'] = neighbor['accepted_prefixes'][idx]
- data[family]['sent_prefixes'] = neighbor['sent_prefixes'][idx]
+ if 'in sync' in neighbor['send-state'][idx]:
+ data[family]['sent_prefixes'] = neighbor['sent_prefixes'].pop(0)
+ else:
+ data[family]['sent_prefixes'] = 0
else:
family = self._get_address_family(neighbor['tables'])
data[family] = {}
@@ -392,33 +515,112 @@ class JunOSDriver(NetworkDriver):
def get_bgp_neighbors(self):
"""Return BGP neighbors details."""
- instances = junos_views.junos_route_instance_table(self.device)
- uptime_table = junos_views.junos_bgp_uptime_table(self.device)
- bgp_neighbors = junos_views.junos_bgp_table(self.device)
- keys = ['local_as', 'remote_as', 'is_up', 'is_enabled', 'description', 'remote_id']
bgp_neighbor_data = {}
- for instance, instance_data in instances.get().items():
- if instance.startswith('__'):
- # junos internal instances
- continue
- instance_name = "global" if instance == 'master' else instance
- bgp_neighbor_data[instance_name] = {'peers': {}}
- for neighbor, data in bgp_neighbors.get(instance=instance).items():
- neighbor_data = {k: v for k, v in data}
- peer_ip = napalm_base.helpers.ip(neighbor.split('+')[0])
+ default_neighbor_details = {
+ 'local_as': 0,
+ 'remote_as': 0,
+ 'remote_id': '',
+ 'is_up': False,
+ 'is_enabled': False,
+ 'description': '',
+ 'uptime': 0,
+ 'address_family': {}
+ }
+ keys = default_neighbor_details.keys()
+
+ uptime_table = junos_views.junos_bgp_uptime_table(self.device)
+ bgp_neighbors_table = junos_views.junos_bgp_table(self.device)
+
+ uptime_table_lookup = {}
+
+ def _get_uptime_table(instance):
+ if instance not in uptime_table_lookup:
+ uptime_table_lookup[instance] = uptime_table.get(instance=instance).items()
+ return uptime_table_lookup[instance]
+
+ def _get_bgp_neighbors_core(neighbor_data, instance=None, uptime_table_items=None):
+ '''
+ Make sure to execute a simple request whenever using
+ junos > 13. This is a helper used to avoid code redundancy
+ and reuse the function also when iterating through the list
+ BGP neighbors under a specific routing instance,
+ also when the device is capable to return the routing
+ instance name at the BGP neighbor level.
+ '''
+ for bgp_neighbor in neighbor_data:
+ peer_ip = napalm_base.helpers.ip(bgp_neighbor[0].split('+')[0])
+ neighbor_details = deepcopy(default_neighbor_details)
+ neighbor_details.update(
+ {elem[0]: elem[1] for elem in bgp_neighbor[1] if elem[1] is not None}
+ )
+ if not instance:
+ # not instance, means newer Junos version,
+ # as we request everything in a single request
+ peer_fwd_rti = neighbor_details.pop('peer_fwd_rti')
+ instance = peer_fwd_rti
+ else:
+ # instance is explicitly requests,
+ # thus it's an old Junos, so we retrieve the BGP neighbors
+ # under a certain routing instance
+ peer_fwd_rti = neighbor_details.pop('peer_fwd_rti', '')
+ instance_name = 'global' if instance == 'master' else instance
+ if instance_name not in bgp_neighbor_data:
+ bgp_neighbor_data[instance_name] = {}
if 'router_id' not in bgp_neighbor_data[instance_name]:
# we only need to set this once
bgp_neighbor_data[instance_name]['router_id'] = \
- py23_compat.text_type(neighbor_data['local_id'])
+ py23_compat.text_type(neighbor_details.get('local_id', ''))
peer = {
key: self._parse_value(value)
- for key, value in neighbor_data.items()
+ for key, value in neighbor_details.items()
if key in keys
}
- peer['address_family'] = self._parse_route_stats(neighbor_data)
+ peer['local_as'] = napalm_base.helpers.as_number(peer['local_as'])
+ peer['remote_as'] = napalm_base.helpers.as_number(peer['remote_as'])
+ peer['address_family'] = self._parse_route_stats(neighbor_details)
+ if 'peers' not in bgp_neighbor_data[instance_name]:
+ bgp_neighbor_data[instance_name]['peers'] = {}
bgp_neighbor_data[instance_name]['peers'][peer_ip] = peer
- for neighbor, uptime in uptime_table.get(instance=instance).items():
- bgp_neighbor_data[instance_name]['peers'][neighbor]['uptime'] = uptime[0][1]
+ if not uptime_table_items:
+ uptime_table_items = _get_uptime_table(instance)
+ for neighbor, uptime in uptime_table_items:
+ if neighbor not in bgp_neighbor_data[instance_name]['peers']:
+ bgp_neighbor_data[instance_name]['peers'][neighbor] = {}
+ bgp_neighbor_data[instance_name]['peers'][neighbor]['uptime'] = uptime[0][1]
+
+ # Commenting out the following sections, till Junos
+ # will provide a way to identify the routing instance name
+ # from the details of the BGP neighbor
+ # currently, there are Junos 15 version having a field called `peer_fwd_rti`
+ # but unfortunately, this is not consistent.
+ # Junos 17 might have this fixed, but this needs to be revisited later.
+ # In the definition below, `old_junos` means a version that does not provide
+ # the forwarding RTI information.
+ #
+ # old_junos = napalm_base.helpers.convert(
+ # int, self.device.facts.get('version', '0.0').split('.')[0], 0) < 15
+
+ # if old_junos:
+ instances = junos_views.junos_route_instance_table(self.device).get()
+ for instance, instance_data in instances.items():
+ if instance.startswith('__'):
+ # junos internal instances
+ continue
+ bgp_neighbor_data[instance] = {'peers': {}}
+ instance_neighbors = bgp_neighbors_table.get(instance=instance).items()
+ uptime_table_items = uptime_table.get(instance=instance).items()
+ _get_bgp_neighbors_core(instance_neighbors,
+ instance=instance,
+ uptime_table_items=uptime_table_items)
+ # If the OS provides the `peer_fwd_rti` or any way to identify the
+ # rotuing instance name (see above), the performances of this getter
+ # can be significantly improved, as we won't execute one request
+ # for each an every RT.
+ # However, this improvement would only be beneficial for multi-VRF envs.
+ #
+ # else:
+ # instance_neighbors = bgp_neighbors_table.get().items()
+ # _get_bgp_neighbors_core(instance_neighbors)
bgp_tmp_dict = {}
for k, v in bgp_neighbor_data.items():
if bgp_neighbor_data[k]['peers']:
@@ -428,8 +630,15 @@ class JunOSDriver(NetworkDriver):
def get_lldp_neighbors(self):
"""Return LLDP neighbors details."""
lldp = junos_views.junos_lldp_table(self.device)
- lldp.get()
-
+ try:
+ lldp.get()
+ except RpcError as rpcerr:
+ # this assumes the library runs in an environment
+ # able to handle logs
+ # otherwise, the user just won't see this happening
+ log.error('Unable to retrieve the LLDP neighbors information:')
+ log.error(rpcerr.message)
+ return {}
result = lldp.items()
neighbors = {}
@@ -445,18 +654,28 @@ class JunOSDriver(NetworkDriver):
lldp_neighbors = {}
lldp_table = junos_views.junos_lldp_neighbors_detail_table(self.device)
- lldp_table.get()
+ try:
+ lldp_table.get()
+ except RpcError as rpcerr:
+ # this assumes the library runs in an environment
+ # able to handle logs
+ # otherwise, the user just won't see this happening
+ log.error('Unable to retrieve the LLDP neighbors information:')
+ log.error(rpcerr.message)
+ return {}
interfaces = lldp_table.get().keys()
- old_junos = napalm_base.helpers.convert(
- int, self.device.facts.get('version', '0.0').split('.')[0], '0') < 13
-
+ # get lldp neighbor by interface rpc for EX Series, QFX Series, J Series
+ # and SRX Series is get-lldp-interface-neighbors-information,
+ # and rpc for M, MX, and T Series is get-lldp-interface-neighbors
+ # ref1: https://apps.juniper.net/xmlapi/operTags.jsp (Junos 13.1 and later)
+ # ref2: https://www.juniper.net/documentation/en_US/junos12.3/information-products/topic-collections/junos-xml-ref-oper/index.html (Junos 12.3) # noqa
lldp_table.GET_RPC = 'get-lldp-interface-neighbors'
- if old_junos:
+ if self.device.facts.get('personality') not in ('MX', 'M', 'T'):
lldp_table.GET_RPC = 'get-lldp-interface-neighbors-information'
for interface in interfaces:
- if old_junos:
+ if self.device.facts.get('personality') not in ('MX', 'M', 'T'):
lldp_table.get(interface_name=interface)
else:
lldp_table.get(interface_device=interface)
@@ -482,13 +701,124 @@ class JunOSDriver(NetworkDriver):
"""Execute raw CLI commands and returns their output."""
cli_output = {}
+ def _count(txt, none): # Second arg for consistency only. noqa
+ '''
+ Return the exact output, as Junos displays
+ e.g.:
+ > show system processes extensive | match root | count
+ Count: 113 lines
+ '''
+ count = len(txt.splitlines())
+ return 'Count: {count} lines'.format(count=count)
+
+ def _trim(txt, length):
+ '''
+ Trim specified number of columns from start of line.
+ '''
+ try:
+ newlines = []
+ for line in txt.splitlines():
+ newlines.append(line[int(length):])
+ return '\n'.join(newlines)
+ except ValueError:
+ return txt
+
+ def _except(txt, pattern):
+ '''
+ Show only text that does not match a pattern.
+ '''
+ rgx = '^.*({pattern}).*$'.format(pattern=pattern)
+ unmatched = [
+ line for line in txt.splitlines()
+ if not re.search(rgx, line, re.I)
+ ]
+ return '\n'.join(unmatched)
+
+ def _last(txt, length):
+ '''
+ Display end of output only.
+ '''
+ try:
+ return '\n'.join(
+ txt.splitlines()[(-1)*int(length):]
+ )
+ except ValueError:
+ return txt
+
+ def _match(txt, pattern):
+ '''
+ Show only text that matches a pattern.
+ '''
+ rgx = '^.*({pattern}).*$'.format(pattern=pattern)
+ matched = [
+ line for line in txt.splitlines()
+ if re.search(rgx, line, re.I)
+ ]
+ return '\n'.join(matched)
+
+ def _find(txt, pattern):
+ '''
+ Search for first occurrence of pattern.
+ '''
+ rgx = '^.*({pattern})(.*)$'.format(pattern=pattern)
+ match = re.search(rgx, txt, re.I | re.M | re.DOTALL)
+ if match:
+ return '{pattern}{rest}'.format(pattern=pattern, rest=match.group(2))
+ else:
+ return '\nPattern not found'
+
+ def _process_pipe(cmd, txt):
+ '''
+ Process CLI output from Juniper device that
+ doesn't allow piping the output.
+ '''
+ if txt is not None:
+ return txt
+ _OF_MAP = OrderedDict()
+ _OF_MAP['except'] = _except
+ _OF_MAP['match'] = _match
+ _OF_MAP['last'] = _last
+ _OF_MAP['trim'] = _trim
+ _OF_MAP['count'] = _count
+ _OF_MAP['find'] = _find
+ # the operations order matter in this case!
+ exploded_cmd = cmd.split('|')
+ pipe_oper_args = {}
+ for pipe in exploded_cmd[1:]:
+ exploded_pipe = pipe.split()
+ pipe_oper = exploded_pipe[0] # always there
+ pipe_args = ''.join(exploded_pipe[1:2])
+ # will not throw error when there's no arg
+ pipe_oper_args[pipe_oper] = pipe_args
+ for oper in _OF_MAP.keys():
+ # to make sure the operation sequence is correct
+ if oper not in pipe_oper_args.keys():
+ continue
+ txt = _OF_MAP[oper](txt, pipe_oper_args[oper])
+ return txt
+
if not isinstance(commands, list):
raise TypeError('Please enter a valid list of commands!')
-
+ _PIPE_BLACKLIST = ['save']
+ # Preprocessing to avoid forbidden commands
for command in commands:
+ exploded_cmd = command.split('|')
+ command_safe_parts = []
+ for pipe in exploded_cmd[1:]:
+ exploded_pipe = pipe.split()
+ pipe_oper = exploded_pipe[0] # always there
+ if pipe_oper in _PIPE_BLACKLIST:
+ continue
+ pipe_args = ''.join(exploded_pipe[1:2])
+ safe_pipe = pipe_oper if not pipe_args else '{fun} {args}'.format(fun=pipe_oper,
+ args=pipe_args)
+ command_safe_parts.append(safe_pipe)
+ safe_command = exploded_cmd[0] if not command_safe_parts else\
+ '{base} | {pipes}'.format(base=exploded_cmd[0],
+ pipes=' | '.join(command_safe_parts))
+ raw_txt = self.device.cli(safe_command, warning=False)
cli_output[py23_compat.text_type(command)] = py23_compat.text_type(
- self.device.cli(command))
-
+ _process_pipe(command, raw_txt))
return cli_output
def get_bgp_config(self, group='', neighbor=''):
@@ -648,6 +978,12 @@ class JunOSDriver(NetworkDriver):
napalm_base.helpers.convert(datatype, elem[1], default)
})
bgp_config[bgp_group_name]['prefix_limit'] = build_prefix_limit(**prefix_limit_fields)
+ if 'multihop' in bgp_config[bgp_group_name].keys():
+ # Delete 'multihop' key from the output
+ del bgp_config[bgp_group_name]['multihop']
+ if bgp_config[bgp_group_name]['multihop_ttl'] == 0:
+ # Set ttl to default value 64
+ bgp_config[bgp_group_name]['multihop_ttl'] = 64
bgp_config[bgp_group_name]['neighbors'] = {}
for bgp_group_neighbor in bgp_group_peers.items():
@@ -676,6 +1012,17 @@ class JunOSDriver(NetworkDriver):
bgp_peer_details.update({
key: napalm_base.helpers.convert(datatype, value, default)
})
+ bgp_peer_details['local_as'] = napalm_base.helpers.as_number(
+ bgp_peer_details['local_as'])
+ bgp_peer_details['remote_as'] = napalm_base.helpers.as_number(
+ bgp_peer_details['remote_as'])
+ if key == 'cluster':
+ bgp_peer_details['route_reflector_client'] = True
+ # we do not want cluster in the output
+ del bgp_peer_details['cluster']
+
+ if 'cluster' in bgp_config[bgp_group_name].keys():
+ bgp_peer_details['route_reflector_client'] = True
prefix_limit_fields = {}
for elem in bgp_group_details:
if '_prefix_limit' in elem[0] and elem[1] is not None:
@@ -690,19 +1037,15 @@ class JunOSDriver(NetworkDriver):
if neighbor and bgp_peer_address == neighbor_ip:
break # found the desired neighbor
+ if 'cluster' in bgp_config[bgp_group_name].keys():
+ # we do not want cluster in the output
+ del bgp_config[bgp_group_name]['cluster']
+
return bgp_config
def get_bgp_neighbors_detail(self, neighbor_address=''):
"""Detailed view of the BGP neighbors operational data."""
bgp_neighbors = {}
-
- bgp_neighbors_table = junos_views.junos_bgp_neighbors_table(self.device)
-
- bgp_neighbors_table.get(
- neighbor_address=neighbor_address
- )
- bgp_neighbors_items = bgp_neighbors_table.items()
-
default_neighbor_details = {
'up': False,
'local_as': 0,
@@ -740,7 +1083,6 @@ class JunOSDriver(NetworkDriver):
'advertised_prefix_count': -1,
'flap_count': 0
}
-
OPTION_KEY_MAP = {
'RemovePrivateAS': 'remove_private_as',
'Multipath': 'multipath',
@@ -752,52 +1094,105 @@ class JunOSDriver(NetworkDriver):
# Preference, HoldTime, Ttl, LogUpDown, Refresh
}
- for bgp_neighbor in bgp_neighbors_items:
- remote_as = int(bgp_neighbor[0])
- neighbor_details = deepcopy(default_neighbor_details)
- neighbor_details.update(
- {elem[0]: elem[1] for elem in bgp_neighbor[1] if elem[1] is not None}
- )
- options = neighbor_details.pop('options', '')
- if isinstance(options, str):
- options_list = options.split()
- for option in options_list:
- key = OPTION_KEY_MAP.get(option)
- if key is not None:
- neighbor_details[key] = True
- four_byte_as = neighbor_details.pop('4byte_as', 0)
- local_address = neighbor_details.pop('local_address', '')
- local_details = local_address.split('+')
- neighbor_details['local_address'] = napalm_base.helpers.convert(
- napalm_base.helpers.ip, local_details[0], local_details[0])
- if len(local_details) == 2:
- neighbor_details['local_port'] = int(local_details[1])
- else:
- neighbor_details['local_port'] = 179
- neighbor_details['suppress_4byte_as'] = (remote_as != four_byte_as)
- peer_address = neighbor_details.pop('peer_address', '')
- remote_details = peer_address.split('+')
- neighbor_details['remote_address'] = napalm_base.helpers.convert(
- napalm_base.helpers.ip, remote_details[0], remote_details[0])
- if len(remote_details) == 2:
- neighbor_details['remote_port'] = int(remote_details[1])
- else:
- neighbor_details['remote_port'] = 179
- neighbors_rib = neighbor_details.pop('rib')
- neighbors_rib_items = neighbors_rib.items()
- for rib_entry in neighbors_rib_items:
- _table = py23_compat.text_type(rib_entry[0])
- if _table not in bgp_neighbors.keys():
- bgp_neighbors[_table] = {}
- if remote_as not in bgp_neighbors[_table].keys():
- bgp_neighbors[_table][remote_as] = []
- neighbor_rib_details = deepcopy(neighbor_details)
- neighbor_rib_details.update({
- elem[0]: elem[1] for elem in rib_entry[1]
- })
- neighbor_rib_details['routing_table'] = py23_compat.text_type(_table)
- bgp_neighbors[_table][remote_as].append(neighbor_rib_details)
+ def _bgp_iter_core(neighbor_data, instance=None):
+ '''
+ Iterate over a list of neighbors.
+ For older junos, the routing instance is not specified inside the
+ BGP neighbors XML, therefore we need to use a super sub-optimal structure
+ as in get_bgp_neighbors: iterate through the list of network instances
+ then execute one request for each and every routing instance.
+ For newer junos, this is not necessary as the routing instance is available
+ and we can get everything solve in a single request.
+ '''
+ for bgp_neighbor in neighbor_data:
+ remote_as = int(bgp_neighbor[0])
+ neighbor_details = deepcopy(default_neighbor_details)
+ neighbor_details.update(
+ {elem[0]: elem[1] for elem in bgp_neighbor[1] if elem[1] is not None}
+ )
+ if not instance:
+ peer_fwd_rti = neighbor_details.pop('peer_fwd_rti')
+ instance = peer_fwd_rti
+ else:
+ peer_fwd_rti = neighbor_details.pop('peer_fwd_rti', '')
+ instance_name = 'global' if instance == 'master' else instance
+ options = neighbor_details.pop('options', '')
+ if isinstance(options, str):
+ options_list = options.split()
+ for option in options_list:
+ key = OPTION_KEY_MAP.get(option)
+ if key is not None:
+ neighbor_details[key] = True
+ four_byte_as = neighbor_details.pop('4byte_as', 0)
+ local_address = neighbor_details.pop('local_address', '')
+ local_details = local_address.split('+')
+ neighbor_details['local_address'] = napalm_base.helpers.convert(
+ napalm_base.helpers.ip, local_details[0], local_details[0])
+ if len(local_details) == 2:
+ neighbor_details['local_port'] = int(local_details[1])
+ else:
+ neighbor_details['local_port'] = 179
+ neighbor_details['suppress_4byte_as'] = (remote_as != four_byte_as)
+ peer_address = neighbor_details.pop('peer_address', '')
+ remote_details = peer_address.split('+')
+ neighbor_details['remote_address'] = napalm_base.helpers.convert(
+ napalm_base.helpers.ip, remote_details[0], remote_details[0])
+ if len(remote_details) == 2:
+ neighbor_details['remote_port'] = int(remote_details[1])
+ else:
+ neighbor_details['remote_port'] = 179
+ neighbor_details['routing_table'] = instance_name
+ neighbor_details['local_as'] = napalm_base.helpers.as_number(
+ neighbor_details['local_as'])
+ neighbor_details['remote_as'] = napalm_base.helpers.as_number(
+ neighbor_details['remote_as'])
+ neighbors_rib = neighbor_details.pop('rib')
+ neighbors_queue = neighbor_details.pop('queue')
+ messages_queued_out = 0
+ for queue_entry in neighbors_queue.items():
+ messages_queued_out += queue_entry[1][0][1]
+ neighbor_details['messages_queued_out'] = messages_queued_out
+ if instance_name not in bgp_neighbors.keys():
+ bgp_neighbors[instance_name] = {}
+ if remote_as not in bgp_neighbors[instance_name].keys():
+ bgp_neighbors[instance_name][remote_as] = []
+ neighbor_rib_stats = neighbors_rib.items()
+ if not neighbor_rib_stats:
+ bgp_neighbors[instance_name][remote_as].append(neighbor_details)
+ continue # no RIBs available, pass default details
+ neighbor_rib_details = {
+ 'active_prefix_count': 0,
+ 'received_prefix_count': 0,
+ 'accepted_prefix_count': 0,
+ 'suppressed_prefix_count': 0,
+ 'advertised_prefix_count': 0
+ }
+ for rib_entry in neighbor_rib_stats:
+ for elem in rib_entry[1]:
+ if elem[1] is None:
+ neighbor_rib_details[elem[0]] += 0
+ else:
+ neighbor_rib_details[elem[0]] += elem[1]
+ neighbor_details.update(neighbor_rib_details)
+ bgp_neighbors[instance_name][remote_as].append(neighbor_details)
+
+ # old_junos = napalm_base.helpers.convert(
+ # int, self.device.facts.get('version', '0.0').split('.')[0], 0) < 15
+ bgp_neighbors_table = junos_views.junos_bgp_neighbors_table(self.device)
+ # if old_junos:
+ instances = junos_views.junos_route_instance_table(self.device)
+ for instance, instance_data in instances.get().items():
+ if instance.startswith('__'):
+ # junos internal instances
+ continue
+ neighbor_data = bgp_neighbors_table.get(instance=instance,
+ neighbor_address=str(neighbor_address)).items()
+ _bgp_iter_core(neighbor_data, instance=instance)
+ # else:
+ # bgp_neighbors_table = junos_views.junos_bgp_neighbors_table(self.device)
+ # neighbor_data = bgp_neighbors_table.get(neighbor_address=neighbor_address).items()
+ # _bgp_iter_core(neighbor_data)
return bgp_neighbors
def get_arp_table(self):
@@ -907,19 +1302,25 @@ class JunOSDriver(NetworkDriver):
'inet6': u'ipv6'
# can add more mappings
}
+ _FAMILY_MAX_PREFIXLEN = {
+ 'inet': 32,
+ 'inet6': 128
+ }
for interface_details in interface_table_items:
ip_network = interface_details[0]
ip_address = ip_network.split('/')[0]
address = napalm_base.helpers.convert(
napalm_base.helpers.ip, ip_address, ip_address)
- prefix = napalm_base.helpers.convert(int, ip_network.split('/')[-1], 0)
try:
interface_details_dict = dict(interface_details[1])
family_raw = interface_details_dict.get('family')
interface = py23_compat.text_type(interface_details_dict.get('interface'))
except ValueError:
continue
+ prefix = napalm_base.helpers.convert(int,
+ ip_network.split('/')[-1],
+ _FAMILY_MAX_PREFIXLEN.get(family_raw))
family = _FAMILY_VMAP_.get(family_raw)
if not family or not interface:
continue
@@ -938,7 +1339,10 @@ class JunOSDriver(NetworkDriver):
mac_address_table = []
if self.device.facts.get('personality', '') in ['SWITCH']: # for EX & QFX devices
- mac_table = junos_views.junos_mac_address_table_switch(self.device)
+ if self.device.facts.get('switch_style', '') in ['VLAN_L2NG']: # for L2NG devices
+ mac_table = junos_views.junos_mac_address_table_switch_l2ng(self.device)
+ else:
+ mac_table = junos_views.junos_mac_address_table_switch(self.device)
else:
mac_table = junos_views.junos_mac_address_table(self.device)
@@ -961,6 +1365,11 @@ class JunOSDriver(NetworkDriver):
{elem[0]: elem[1] for elem in mac_table_entry[1]}
)
mac = mac_entry.get('mac')
+
+ # JUNOS returns '*' for Type = Flood
+ if mac == '*':
+ continue
+
mac_entry['mac'] = napalm_base.helpers.mac(mac)
mac_address_table.append(mac_entry)
@@ -1195,7 +1604,8 @@ class JunOSDriver(NetworkDriver):
destination,
source=C.TRACEROUTE_SOURCE,
ttl=C.TRACEROUTE_TTL,
- timeout=C.TRACEROUTE_TIMEOUT):
+ timeout=C.TRACEROUTE_TIMEOUT,
+ vrf=C.TRACEROUTE_VRF):
"""Execute traceroute and return results."""
traceroute_result = {}
@@ -1205,19 +1615,23 @@ class JunOSDriver(NetworkDriver):
source_str = ''
maxttl_str = ''
wait_str = ''
+ vrf_str = ''
if source:
- source_str = 'source {source}'.format(source=source)
+ source_str = ' source {source}'.format(source=source)
if ttl:
- maxttl_str = 'ttl {ttl}'.format(ttl=ttl)
+ maxttl_str = ' ttl {ttl}'.format(ttl=ttl)
if timeout:
- wait_str = 'wait {timeout}'.format(timeout=timeout)
+ wait_str = ' wait {timeout}'.format(timeout=timeout)
+ if vrf:
+ vrf_str = ' routing-instance {vrf}'.format(vrf=vrf)
- traceroute_command = 'traceroute {destination} {source} {maxttl} {wait}'.format(
+ traceroute_command = 'traceroute {destination}{source}{maxttl}{wait}{vrf}'.format(
destination=destination,
source=source_str,
maxttl=maxttl_str,
- wait=wait_str
+ wait=wait_str,
+ vrf=vrf_str
)
traceroute_rpc = E('command', traceroute_command)
@@ -1257,7 +1671,7 @@ class JunOSDriver(NetworkDriver):
return traceroute_result
def ping(self, destination, source=C.PING_SOURCE, ttl=C.PING_TTL,
- timeout=C.PING_TIMEOUT, size=C.PING_SIZE, count=C.PING_COUNT):
+ timeout=C.PING_TIMEOUT, size=C.PING_SIZE, count=C.PING_COUNT, vrf=C.PING_VRF):
ping_dict = {}
@@ -1266,25 +1680,29 @@ class JunOSDriver(NetworkDriver):
timeout_str = ''
size_str = ''
count_str = ''
+ vrf_str = ''
... 419 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/napalm-junos.git
More information about the Python-modules-commits
mailing list