Add caching to improve performance of port_get_mode() calls

We're calling this a lot when validating parameters, so cache the
results for better performance. No need to ask the switch on every
call.
Rename the existing port_get_mode() methods to _port_get_mode() for
each driver, and add a common driver method to try the cache first then
fall through to the switch-specific code only if needed.
Obviously, also update port_set_mode to update the cache as well!

Change-Id: I2f938e8d7d0cf3223104c59609e833473346b8c1
diff --git a/drivers/CiscoCatalyst.py b/drivers/CiscoCatalyst.py
index 0ea392a..1e9f019 100644
--- a/drivers/CiscoCatalyst.py
+++ b/drivers/CiscoCatalyst.py
@@ -221,41 +221,19 @@
             self._end_configure()
 
             # Validate it happened
-            read_mode = self.port_get_mode(port)
+            read_mode = self._port_get_mode(port)
 
             if read_mode != mode:
                 raise IOError("Failed to set mode for port %s" % port)
 
+            # And cache the result
+            self._port_modes[port] = mode
+
         except PExpectError:
             # recurse on error
             self._switch_connect()
             self.port_set_mode(port, mode)
 
-    # Get the mode of a port: access or trunk
-    def port_get_mode(self, port):
-        logging.debug("Getting mode of port %s", port)
-        mode = ''
-        if not self._is_port_name_valid(port):
-            raise InputError("Port name %s not recognised" % port)
-        regex = re.compile('Administrative Mode: (.*)')
-
-        try:
-            self._cli("show interfaces %s switchport" % port)
-            for line in self._read_long_output("show interfaces switchport"):
-                match = regex.match(line)
-                if match:
-                    mode = match.group(1)
-                    if mode == 'static access':
-                        return 'access'
-                    if mode == 'dynamic auto':
-                        return 'trunk'
-            return mode
-
-        except PExpectError:
-            # recurse on error
-            self._switch_connect()
-            return self.port_get_mode(port)
-
     # Set an access port to be in a specified VLAN (tag)
     def port_set_access_vlan(self, port, tag):
         logging.debug("Setting access port %s to VLAN %d", port, tag)
@@ -530,6 +508,31 @@
             self._switch_connect()
             return self._get_port_names()
 
+    # Get the mode of a port: access or trunk
+    def _port_get_mode(self, port):
+        logging.debug("Getting mode of port %s", port)
+        mode = ''
+        if not self._is_port_name_valid(port):
+            raise InputError("Port name %s not recognised" % port)
+        regex = re.compile('Administrative Mode: (.*)')
+
+        try:
+            self._cli("show interfaces %s switchport" % port)
+            for line in self._read_long_output("show interfaces switchport"):
+                match = regex.match(line)
+                if match:
+                    mode = match.group(1)
+                    if mode == 'static access':
+                        return 'access'
+                    if mode == 'dynamic auto':
+                        return 'trunk'
+            return mode
+
+        except PExpectError:
+            # recurse on error
+            self._switch_connect()
+            return self.port_get_mode(port)
+
     def _show_config(self):
         logging.debug("Grabbing config")
         try: