Repository: napalm-automation/napalm

Issue:
Usage of PyEZ tables in junos is not optimized
We're using celery and gevent (greenlets / green threads) as a pool implementation and we found out that the `get_interfaces()` getter is blocking a greenlet run too long when running a lot of concurrent greenlets and eventually starving the others. 

Digging into this deeper is appears that the Juniper PyEZ tables are used in a suboptimal way in that function. When acting on a PyEZ table as if it is a dictionary, performance starts crippeling.

Left: current code
Right: wrapping the table in a `dict()` call, which is 6x faster.

The example is for a device with 219 interfaces (so 438 entries in total, counting the logic interfaces)

![image](https://user-images.githubusercontent.com/16192496/50363582-86f01a80-056c-11e9-92b5-8e6a0db31e6a.png)


Discussion:

Resolving patch:
diff --git a/napalm/junos/junos.py b/napalm/junos/junos.py
--- a/napalm/junos/junos.py
+++ b/napalm/junos/junos.py
@@ -310,30 +310,33 @@ def get_interfaces(self):
 
         # convert all the tuples to our pre-defined dict structure
         def _convert_to_dict(interfaces):
-            for iface in interfaces.keys():
+            # calling .items() here wont work.
+            # The dictionary values will end up being tuples instead of dictionaries
+            interfaces = dict(interfaces)
+            for iface, iface_data in interfaces.items():
                 result[iface] = {
-                    "is_up": interfaces[iface]["is_up"],
+                    "is_up": iface_data["is_up"],
                     # For physical interfaces <admin-status> will always be there, so just
                     # return the value interfaces[iface]['is_enabled']
                     # For logical interfaces if <iff-down> is present interface is disabled,
                     # otherwise interface is enabled
                     "is_enabled": (
                         True
-                        if interfaces[iface]["is_enabled"] is None
-                        else interfaces[iface]["is_enabled"]
+                        if iface_data["is_enabled"] is None
+                        else iface_data["is_enabled"]
                     ),
-                    "description": (interfaces[iface]["description"] or ""),
-                    "last_flapped": float((interfaces[iface]["last_flapped"] or -1)),
+                    "description": (iface_data["description"] or ""),
+                    "last_flapped": float((iface_data["last_flapped"] or -1)),
                     "mac_address": napalm.base.helpers.convert(
                         napalm.base.helpers.mac,
-                        interfaces[iface]["mac_address"],
-                        py23_compat.text_type(interfaces[iface]["mac_address"]),
+                        iface_data["mac_address"],
+                        py23_compat.text_type(iface_data["mac_address"]),
                     ),
                     "speed": -1,
                 }
                 # result[iface]['last_flapped'] = float(result[iface]['last_flapped'])
 
-                match = re.search(r"(\d+)(\w*)", interfaces[iface]["speed"] or "")
+                match = re.search(r"(\d+)(\w*)", iface_data["speed"] or "")
                 if match is None:
                     continue
                 speed_value = napalm.base.helpers.convert(int, match.group(1), -1)


Test patch: