[autotest] rpm dispatcher/controller get power info from frontend server

This is the 3rd CL for making the rpm infra read rpm/outlet/hydra
info from AFE.

Previously, rpm dispatcher/controler uses regular expression pattern
to determine the hydra hostname. And outlet(ports) must be labeled
with hostname beforehand.

This cl makes rpm dispatcher/controler receive a PowerUnitInfo instance
from frontend server, which includes the rpm(poe)/outlet(port)/hydra
information for the device.

CQ-DEPEND=CL:212346
BUG=chromium:392548
TEST=unittest; Integration tests with other cls in this series, set
up a local rpm server and power cycle devices.

Change-Id: I492531c8dc3f134d32f73d0a0560bf54f8a28b70
Reviewed-on: https://chromium-review.googlesource.com/212357
Tested-by: Fang Deng <fdeng@chromium.org>
Reviewed-by: Simran Basi <sbasi@chromium.org>
Commit-Queue: Fang Deng <fdeng@chromium.org>
diff --git a/site_utils/rpm_control_system/rpm_dispatcher.py b/site_utils/rpm_control_system/rpm_dispatcher.py
index f050a3f..25fca6d 100755
--- a/site_utils/rpm_control_system/rpm_dispatcher.py
+++ b/site_utils/rpm_control_system/rpm_dispatcher.py
@@ -6,7 +6,6 @@
 import atexit
 import errno
 import logging
-import os
 import re
 import sys
 import socket
@@ -25,11 +24,6 @@
 
 LOG_FILENAME_FORMAT = rpm_config.get('GENERAL','dispatcher_logname_format')
 
-# Servo-interface mapping file
-MAPPING_FILE = os.path.join(
-        os.path.dirname(__file__),
-        rpm_config.get('CiscoPOE', 'servo_interface_mapping_file'))
-
 
 class RPMDispatcher(object):
     """
@@ -72,8 +66,6 @@
         self._worker_dict = {}
         self._frontend_server = rpm_config.get('RPM_INFRASTRUCTURE',
                                                'frontend_uri')
-        self._mapping_last_modified = os.path.getmtime(MAPPING_FILE)
-        self._servo_interface = utils.load_servo_interface_mapping()
         logging.info('Registering this rpm dispatcher with the frontend '
                      'server at %s.', self._frontend_server)
         client = xmlrpclib.ServerProxy(self._frontend_server)
@@ -121,67 +113,31 @@
         return True
 
 
-    def queue_request(self, dut_hostname, new_state):
+    def queue_request(self, powerunit_info_dict, new_state):
         """
-        Looks up the appropriate RPMController instance for this DUT and queues
+        Looks up the appropriate RPMController instance for the device and queues
         up the request.
 
-        @param dut_hostname: hostname of the DUT whose outlet we are trying to
-                             change.
+        @param powerunit_info_dict: A dictionary, containing the attribute/values
+                                    of an unmarshalled PowerUnitInfo instance.
         @param new_state: [ON, OFF, CYCLE] state we want to the change the
                           outlet to.
         @return: True if the attempt to change power state was successful,
                  False otherwise.
         """
-        logging.info('Received request to set DUT: %s to state: %s',
-                     dut_hostname, new_state)
-        rpm_hostname = self._get_rpm_hostname_for_dut(dut_hostname)
+        powerunit_info = utils.PowerUnitInfo(**powerunit_info_dict)
+        logging.info('Received request to set device: %s to state: %s',
+                     powerunit_info.device_hostname, new_state)
         result = False
-        while not result and rpm_hostname:
-            rpm_controller = self._get_rpm_controller(rpm_hostname)
-            result = rpm_controller.queue_request(dut_hostname, new_state)
-            if not result:
-                # If the request failed, check to see if there is another RPM
-                # at this location.
-                rpm_hostname = rpm_controller.get_next_rpm_hostname()
+        while not result:
+            rpm_controller = self._get_rpm_controller(
+                    powerunit_info.powerunit_hostname,
+                    powerunit_info.hydra_hostname)
+            result = rpm_controller.queue_request(powerunit_info, new_state)
         return result
 
 
-    def _get_rpm_hostname_for_dut(self, dut_hostname):
-        """
-        Private method that retreives the appropriate RPMController instance
-        for this DUT.
-
-        @param dut_hostname: hostname of the DUT whose RPMController we want.
-
-        @return: RPM Hostname responsible for this DUT.
-                 Return None on failure.
-        """
-        if dut_hostname.endswith('servo'):
-            # Servos are managed by Cisco POE switches.
-            reload_info = utils.reload_servo_interface_mapping_if_necessary(
-                    self._mapping_last_modified)
-            if reload_info:
-                self._mapping_last_modified, self._servo_interface = reload_info
-            switch_if_tuple = self._servo_interface.get(dut_hostname)
-            if not switch_if_tuple:
-                logging.error('Could not determine POE hostname for %s. '
-                              'Please check the servo-interface mapping file.',
-                              dut_hostname)
-                return None
-            else:
-                rpm_hostname = switch_if_tuple[0]
-            logging.info('POE hostname for DUT %s is %s', dut_hostname,
-                         rpm_hostname)
-        else:
-            # Regular DUTs are managed by RPMs.
-            rpm_hostname = re.sub('host[^.]*', 'rpm1', dut_hostname, count=1)
-            logging.info('RPM hostname for DUT %s is %s',  dut_hostname,
-                         rpm_hostname)
-        return rpm_hostname
-
-
-    def _get_rpm_controller(self, rpm_hostname):
+    def _get_rpm_controller(self, rpm_hostname, hydra_hostname=None):
         """
         Private method that retreives the appropriate RPMController instance
         for this RPM Hostname or calls _create_rpm_controller it if it does not
@@ -195,12 +151,13 @@
             return None
         rpm_controller = self._worker_dict_get(rpm_hostname)
         if not rpm_controller:
-            rpm_controller = self._create_rpm_controller(rpm_hostname)
+            rpm_controller = self._create_rpm_controller(
+                    rpm_hostname, hydra_hostname)
             self._worker_dict_put(rpm_hostname, rpm_controller)
         return rpm_controller
 
 
-    def _create_rpm_controller(self, rpm_hostname):
+    def _create_rpm_controller(self, rpm_hostname, hydra_hostname):
         """
         Determines the type of RPMController required and initializes it.
 
@@ -212,8 +169,7 @@
         if hostname_elements[-2] == 'poe':
             # POE switch hostname looks like 'chromeos2-poe-switch1'.
             logging.info('The controller is a Cisco POE switch.')
-            return rpm_controller.CiscoPOEController(
-                    rpm_hostname, self._servo_interface)
+            return rpm_controller.CiscoPOEController(rpm_hostname)
         else:
             # The device is an RPM.
             rack_id = hostname_elements[-2]
@@ -223,7 +179,9 @@
                 return rpm_controller.WebPoweredRPMController(rpm_hostname)
             else:
                 logging.info('RPM is a Sentry CDU device.')
-                return rpm_controller.SentryRPMController(rpm_hostname)
+                return rpm_controller.SentryRPMController(
+                        hostname=rpm_hostname,
+                        hydra_hostname=hydra_hostname)
 
 
     def _get_serveruri(self):