3636from napalm_base .base import NetworkDriver
3737from napalm_base .utils import string_parsers
3838from napalm_base .utils import py23_compat
39+ import napalm_junos .constants as C
3940from napalm_base .exceptions import ConnectionException
4041from napalm_base .exceptions import MergeConfigException
4142from napalm_base .exceptions import CommandErrorException
@@ -84,26 +85,33 @@ def open(self):
8485 del self .device .cu
8586 self .device .bind (cu = Config )
8687 if self .config_lock :
87- self .lock ()
88+ self ._lock ()
8889
8990 def close (self ):
9091 """Close the connection."""
9192 if self .config_lock :
92- self .unlock ()
93+ self ._unlock ()
9394 self .device .close ()
9495
95- def lock (self ):
96+ def _lock (self ):
9697 """Lock the config DB."""
9798 if not self .locked :
9899 self .device .cu .lock ()
99100 self .locked = True
100101
101- def unlock (self ):
102+ def _unlock (self ):
102103 """Unlock the config DB."""
103104 if self .locked :
104105 self .device .cu .unlock ()
105106 self .locked = False
106107
108+ def is_alive (self ):
109+ # evaluate the state of the underlying SSH connection
110+ # and also the NETCONF status from PyEZ
111+ return {
112+ 'is_alive' : self .device ._conn ._session .transport .is_active () and self .device .connected
113+ }
114+
107115 def _load_candidate (self , filename , config , overwrite ):
108116 if filename is None :
109117 configuration = config
@@ -114,7 +122,7 @@ def _load_candidate(self, filename, config, overwrite):
114122 if not self .config_lock :
115123 # if not locked during connection time
116124 # will try to lock it if not already aquired
117- self .lock ()
125+ self ._lock ()
118126 # and the device will be locked till first commit/rollback
119127
120128 try :
@@ -148,13 +156,13 @@ def commit_config(self):
148156 """Commit configuration."""
149157 self .device .cu .commit ()
150158 if not self .config_lock :
151- self .unlock ()
159+ self ._unlock ()
152160
153161 def discard_config (self ):
154162 """Discard changes (rollback 0)."""
155163 self .device .cu .rollback (rb_id = 0 )
156164 if not self .config_lock :
157- self .unlock ()
165+ self ._unlock ()
158166
159167 def rollback (self ):
160168 """Rollback to previous commit."""
@@ -468,7 +476,7 @@ def get_lldp_neighbors_detail(self, interface=''):
468476
469477 return lldp_neighbors
470478
471- def cli (self , commands = None ):
479+ def cli (self , commands ):
472480 """Execute raw CLI commands and returns their output."""
473481 cli_output = {}
474482
@@ -951,21 +959,24 @@ def get_mac_address_table(self):
951959
952960 return mac_address_table
953961
954- def get_route_to (self , destination = None , protocol = None ):
962+ def get_route_to (self , destination = '' , protocol = '' ):
955963 """Return route details to a specific destination, learned from a certain protocol."""
956964 routes = {}
957965
958966 if not isinstance (destination , py23_compat .string_types ):
959967 raise TypeError ('Please specify a valid destination!' )
960968
961- if not isinstance (protocol , py23_compat .string_types ) or \
962- protocol .lower () not in ('static' , 'bgp' , 'isis' ):
969+ if protocol and ( not isinstance (protocol , py23_compat .string_types ) or
970+ protocol .lower () not in ('static' , 'bgp' , 'isis' , 'connected' , 'direct' ) ):
963971 raise TypeError ("Protocol not supported: {protocol}." .format (
964972 protocol = protocol
965973 ))
966974
967975 protocol = protocol .lower ()
968976
977+ if protocol == 'connected' :
978+ protocol = 'direct' # this is how is called on JunOS
979+
969980 _COMMON_PROTOCOL_FIELDS_ = [
970981 'destination' ,
971982 'prefix_length' ,
@@ -1003,18 +1014,19 @@ def get_route_to(self, destination=None, protocol=None):
10031014 'level' ,
10041015 'metric' ,
10051016 'local_as'
1006- ],
1007- 'static' : [ # nothing specific to static routes
10081017 ]
10091018 }
10101019
10111020 routes_table = junos_views .junos_protocol_route_table (self .device )
10121021
1022+ rt_kargs = {
1023+ 'destination' : destination
1024+ }
1025+ if protocol :
1026+ rt_kargs ['protocol' ] = protocol
1027+
10131028 try :
1014- routes_table .get (
1015- destination = destination ,
1016- protocol = protocol
1017- )
1029+ routes_table .get (** rt_kargs )
10181030 except RpcTimeoutError :
10191031 # on devices with milions of routes
10201032 # in case the destination is too generic (e.g.: 10/8)
@@ -1030,7 +1042,7 @@ def get_route_to(self, destination=None, protocol=None):
10301042
10311043 for route in routes_items :
10321044 d = {}
1033- next_hop = route [0 ]
1045+ # next_hop = route[0]
10341046 d = {elem [0 ]: elem [1 ] for elem in route [1 ]}
10351047 destination = napalm_base .helpers .ip (d .pop ('destination' , '' ))
10361048 prefix_length = d .pop ('prefix_length' , 32 )
@@ -1048,10 +1060,13 @@ def get_route_to(self, destination=None, protocol=None):
10481060 # to be sure that contains only AS Numbers
10491061 if d .get ('inactive_reason' ) is None :
10501062 d ['inactive_reason' ] = u''
1063+ route_protocol = d .get ('protocol' ).lower ()
1064+ if protocol and protocol != route_protocol :
1065+ continue
10511066 communities = d .get ('communities' )
10521067 if communities is not None and type (communities ) is not list :
10531068 d ['communities' ] = [communities ]
1054- d ['next_hop' ] = unicode (next_hop )
1069+ # d['next_hop'] = unicode(next_hop)
10551070 d_keys = d .keys ()
10561071 # fields that are not in _COMMON_PROTOCOL_FIELDS_ are supposed to be protocol specific
10571072 all_protocol_attributes = {
@@ -1061,7 +1076,7 @@ def get_route_to(self, destination=None, protocol=None):
10611076 }
10621077 protocol_attributes = {
10631078 key : value for key , value in all_protocol_attributes .iteritems ()
1064- if key in _PROTOCOL_SPECIFIC_FIELDS_ .get (protocol )
1079+ if key in _PROTOCOL_SPECIFIC_FIELDS_ .get (route_protocol , [] )
10651080 }
10661081 d ['protocol_attributes' ] = protocol_attributes
10671082 if destination not in routes .keys ():
@@ -1170,7 +1185,11 @@ def get_probes_results(self):
11701185
11711186 return probes_results
11721187
1173- def traceroute (self , destination , source = '' , ttl = 0 , timeout = 0 ):
1188+ def traceroute (self ,
1189+ destination ,
1190+ source = C .TRACEROUTE_SOURCE ,
1191+ ttl = C .TRACEROUTE_TTL ,
1192+ timeout = C .TRACEROUTE_TIMEOUT ):
11741193 """Execute traceroute and return results."""
11751194 traceroute_result = {}
11761195
@@ -1285,9 +1304,10 @@ def get_optics(self):
12851304 # Formatting data into return data structure
12861305 optics_detail = {}
12871306 for intf_optic_item in optics_items :
1307+ interface_name = py23_compat .text_type (intf_optic_item [0 ])
12881308 optics = dict (intf_optic_item [1 ])
1289- if intf_optic_item [ 0 ] not in optics_detail :
1290- optics_detail [intf_optic_item [ 0 ] ] = {}
1309+ if interface_name not in optics_detail :
1310+ optics_detail [interface_name ] = {}
12911311
12921312 # Defaulting avg, min, max values to 0.0 since device does not
12931313 # return these values
@@ -1327,7 +1347,7 @@ def get_optics(self):
13271347 }]
13281348 }
13291349 }
1330- optics_detail [intf_optic_item [ 0 ] ] = intf_optics
1350+ optics_detail [interface_name ] = intf_optics
13311351
13321352 return optics_detail
13331353
@@ -1353,3 +1373,60 @@ def get_config(self, retrieve='all'):
13531373 rv ['running' ] = py23_compat .text_type (config .text .encode ('ascii' , 'replace' ))
13541374
13551375 return rv
1376+
1377+ def get_network_instances (self , name = '' ):
1378+
1379+ network_instances = {}
1380+
1381+ ri_table = junos_views .junos_nw_instances_table (self .device )
1382+ ri_table .get ()
1383+ ri_entries = ri_table .items ()
1384+
1385+ vrf_interfaces = []
1386+
1387+ for ri_entry in ri_entries :
1388+ ri_name = py23_compat .text_type (ri_entry [0 ])
1389+ ri_details = {
1390+ d [0 ]: d [1 ] for d in ri_entry [1 ]
1391+ }
1392+ ri_type = ri_details ['instance_type' ]
1393+ if ri_type is None :
1394+ ri_type = 'default'
1395+ ri_rd = ri_details ['route_distinguisher' ]
1396+ ri_interfaces = ri_details ['interfaces' ]
1397+ network_instances [ri_name ] = {
1398+ 'name' : ri_name ,
1399+ 'type' : C .OC_NETWORK_INSTANCE_TYPE_MAP .get (ri_type , ri_type ), # default: return raw
1400+ 'state' : {
1401+ 'route_distinguisher' : ri_rd if ri_rd else ''
1402+ },
1403+ 'interfaces' : {
1404+ 'interface' : {
1405+ intrf_name : {} for intrf_name in ri_interfaces if intrf_name
1406+ }
1407+ }
1408+ }
1409+ vrf_interfaces .extend (network_instances [ri_name ]['interfaces' ]['interface' ].keys ())
1410+
1411+ all_interfaces = self .get_interfaces ().keys ()
1412+ default_interfaces = list (set (all_interfaces ) - set (vrf_interfaces ))
1413+ if 'default' not in network_instances :
1414+ network_instances ['default' ] = {
1415+ 'name' : 'default' ,
1416+ 'type' : C .OC_NETWORK_INSTANCE_TYPE_MAP .get ('default' ),
1417+ 'state' : {
1418+ 'route_distinguisher' : ''
1419+ },
1420+ 'interfaces' : {
1421+ 'interface' : {
1422+ py23_compat .text_type (intrf_name ): {}
1423+ for intrf_name in default_interfaces
1424+ }
1425+ }
1426+ }
1427+
1428+ if not name :
1429+ return network_instances
1430+ if name not in network_instances :
1431+ return {}
1432+ return {name : network_instances [name ]}
0 commit comments