Repository: napalm-automation/napalm

Issue:
get_lldp_neighbors_detail() for junos EX3400-48T fails
Like issue #861 I bumped into the same issue with a `EX3400-48T`.

The piece of code handling this with or without information part is not maintainable like it is.
After some research I found out that all models we have than need a `with_information` RPC call are devices that have their `switch_style` fact set to `VLAN`.
So maybe it's better to look at the `switch_style` device fact (`self.device.facts.get('switch_style') == "VLAN"` and then try a with-call first. Else try a without-call.
If that call fails with a `bad_element` error, try the other type of call.

With the switch-style prediction, the first attempt will be right in 90% or so of all devices. But auto-retry with the other type of call makes that the list of devices doesn't need to be kept up to date.


Discussion:
**update**
It also fails in the same way for `SRX320-POE` models.

From all investigations I did, every single device with `switch_style` set to `VLAN` needs a `with_information`. Every other device needed a `without_information` call.



Resolving patch:
diff --git a/napalm/junos/junos.py b/napalm/junos/junos.py
--- a/napalm/junos/junos.py
+++ b/napalm/junos/junos.py
@@ -718,83 +718,32 @@ def get_lldp_neighbors_detail(self, interface=''):
             log.error(py23_compat.text_type(rpcerr))
             return {}
         interfaces = lldp_table.get().keys()
-        rpc_call_without_information = {
-            'get_rpc': 'get-lldp-interface-neighbors',
-            'interface_rpc': 'interface_device'
-        }
-        rpc_call_with_information = {
-            'get_rpc': 'get-lldp-interface-neighbors-information',
-            'interface_rpc': 'interface_name'
-        }
-        # 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
-        # Exceptions:
-        # EX9208    personality = SWITCH    RPC: <get-lldp-interface-neighbors><interface-device>
-        # QFX10008  personality = SWITCH    RPC: <get-lldp-interface-neighbors><interface-device>
-        # QFX5110-48S-4C personality = SWITCH RPC: <get-lldp-interface-neighbors><interface-device>
-        # EX3400    personality = SWITCH    RPC: <get-lldp-interface-neighbors><interface-device>
-        # SRX4100   personality = SRX_HIGHEND  RPC: <get-lldp-interface-neighbors><interface-device>
-        #
-        # This is very inconsistent and diverges from the documented behaviour.
-        # The following object permits a per personality (a junos-pyEZ library feature) and per
-        # model mapping to the correct rpc call
-        rpc_call_map = {
-            'default': rpc_call_with_information,
-            'MX': {
-                'default': rpc_call_without_information
-            },
-            'M': {
-                'default': rpc_call_without_information
-            },
-            'T': {
-                'default': rpc_call_without_information
-            },
-            'PTX': {
-                'default': rpc_call_without_information
-            },
-            'SWITCH': {
-                'default': rpc_call_with_information,
-                'EX9208': rpc_call_without_information,
-                'EX3400': rpc_call_without_information,
-                'EX4300-48P': rpc_call_without_information,
-                'EX4600-40F': rpc_call_without_information,
-                'QFX5100-48S-6Q': rpc_call_without_information,
-                'QFX5110-48S-4C': rpc_call_without_information,
-                'QFX10002-36Q': rpc_call_without_information,
-                'QFX10008': rpc_call_without_information,
-                'EX2300-24P': rpc_call_without_information,
-                'EX2300-C-12P': rpc_call_without_information
-            },
-            'SRX_BRANCH': {
-                'default': rpc_call_with_information,
-                'SRX300': rpc_call_without_information
-            },
-            'SRX_HIGHEND': {
-                'default': rpc_call_without_information
-            }
-        }
-
-        personality = self.device.facts.get('personality')
-        model = self.device.facts.get('model')
 
-        if rpc_call_map.get(personality) is not None:
-            if rpc_call_map.get(personality).get(model) is not None:
-                lldp_table.GET_RPC = rpc_call_map.get(personality).get(model).get('get_rpc')
-                interface_variable = rpc_call_map.get(personality).get(model).get('interface_rpc')
-            else:
-                lldp_table.GET_RPC = rpc_call_map.get(personality).get('default').get('get_rpc')
-                interface_variable = rpc_call_map.get(personality).get('default').get(
-                    'interface_rpc')
+        if self.device.facts.get('switch_style') == "VLAN":
+            lldp_table.GET_RPC = "get-lldp-interface-neighbors-information"
+            interface_variable = "interface_name"
+            alt_rpc = "get-lldp-interface-neighbors"
+            alt_interface_variable = "interface_device"
         else:
-            lldp_table.GET_RPC = rpc_call_map.get('default').get('get_rpc')
-            interface_variable = rpc_call_map.get('default').get('interface_rpc')
+            lldp_table.GET_RPC = "get-lldp-interface-neighbors"
+            interface_variable = "interface_device"
+            alt_rpc = "get-lldp-interface-neighbors-information"
+            alt_interface_variable = "interface_name"
 
         for interface in interfaces:
-            interface_args = {interface_variable: interface}
-            lldp_table.get(**interface_args)
+            try:
+                interface_args = {interface_variable: interface}
+                lldp_table.get(**interface_args)
+            except RpcError as e:
+                if "syntax error" in e.message:
+                    # Looks like we need to call a different RPC on this device
+                    # Switch to the alternate style
+                    lldp_table.GET_RPC = alt_rpc
+                    interface_variable = alt_interface_variable
+                    # Retry
+                    interface_args = {interface_variable: interface}
+                    lldp_table.get(**interface_args)
+
             for item in lldp_table:
                 if interface not in lldp_neighbors.keys():
                     lldp_neighbors[interface] = []


Test patch:
diff --git a/test/junos/mocked_data/test_get_lldp_neighbors_detail/ex9208_qfx10008/facts.yml b/test/junos/mocked_data/test_get_lldp_neighbors_detail/ex9208_qfx10008/facts.yml
--- a/test/junos/mocked_data/test_get_lldp_neighbors_detail/ex9208_qfx10008/facts.yml
+++ b/test/junos/mocked_data/test_get_lldp_neighbors_detail/ex9208_qfx10008/facts.yml
@@ -1,2 +1,3 @@
 model: EX9208
-personality: SWITCH
\ No newline at end of file
+personality: SWITCH
+switch_style: VLAN_L2NG
\ No newline at end of file
diff --git a/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_ex_fx_srx/facts.yml b/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_ex_fx_srx/facts.yml
--- a/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_ex_fx_srx/facts.yml
+++ b/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_ex_fx_srx/facts.yml
@@ -1 +1,2 @@
 personality: SWITCH
+switch_style: VLAN
\ No newline at end of file
diff --git a/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_mx_m_t/facts.yml b/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_mx_m_t/facts.yml
--- a/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_mx_m_t/facts.yml
+++ b/test/junos/mocked_data/test_get_lldp_neighbors_detail/normal_mx_m_t/facts.yml
@@ -1 +1,2 @@
 personality: MX
+switch_style: BRIDGE_DOMAIN
diff --git a/test/junos/mocked_data/test_get_lldp_neighbors_detail/qfx5110/facts.yml b/test/junos/mocked_data/test_get_lldp_neighbors_detail/qfx5110/facts.yml
--- a/test/junos/mocked_data/test_get_lldp_neighbors_detail/qfx5110/facts.yml
+++ b/test/junos/mocked_data/test_get_lldp_neighbors_detail/qfx5110/facts.yml
@@ -1,2 +1,3 @@
 model: QFX5110-48S-4C
-personality: SWITCH
\ No newline at end of file
+personality: SWITCH
+switch_style: VLAN_L2NG
\ No newline at end of file
diff --git a/test/junos/mocked_data/test_get_lldp_neighbors_detail/srx4100/facts.yml b/test/junos/mocked_data/test_get_lldp_neighbors_detail/srx4100/facts.yml
--- a/test/junos/mocked_data/test_get_lldp_neighbors_detail/srx4100/facts.yml
+++ b/test/junos/mocked_data/test_get_lldp_neighbors_detail/srx4100/facts.yml
@@ -1,2 +1,3 @@
 model: SRX4100
-personality: SRX_HIGHEND
\ No newline at end of file
+personality: SRX_HIGHEND
+switch_style: NONE
\ No newline at end of file