autotest: Add Servo-topology verifier

Verify topology again saved version and servo expectations.

BUG=b:166680257
TEST=run local

servo_v4 type-c
./server/autoserv -s --host-info-subdir host_info_store -m chromeos1-row4-rack4-host3 --lab True --local-only-host-info True -R -r /tr/

servo_v3 (no topology)
./server/autoserv -s --host-info-subdir host_info_store -m chromeos1-row4-rack1-host4 --lab True --local-only-host-info True -R -r /tr/

servo_v4 type-a
./server/autoserv -s --host-info-subdir host_info_store -m chromeos1-row4-rack8-host2 --lab True --local-only-host-info True -R -r /tr/

servo_v4 dual
./server/autoserv -s --host-info-subdir host_info_store -m chromeos1-row2-rack11-host6 --lab True --local-only-host-info True -R -r /tr/

Topology verified
test_that --args="servo_host=chromeos1-row4-rack4-labstation servo_port=9997 servo_serial=C1903145591" --board=eve --autotest_dir=.  chromeos1-row4-rack4-host3 servo_USBInstall

Topology skipped due missed servo serial
test_that --args="servo_host=chromeos1-row4-rack4-labstation servo_port=9997" --board=eve --autotest_dir=.  chromeos1-row4-rack4-host3 servo_USBInstall


Change-Id: Ia1cc947d059c7caa0d148c224f9af4189a702b64
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2473059
Tested-by: Otabek Kasimov <otabek@google.com>
Reviewed-by: Garry Wang <xianuowang@chromium.org>
Commit-Queue: Otabek Kasimov <otabek@google.com>
diff --git a/server/hosts/servo_constants.py b/server/hosts/servo_constants.py
index e2cf6ad..fbd4ba0 100644
--- a/server/hosts/servo_constants.py
+++ b/server/hosts/servo_constants.py
@@ -50,6 +50,7 @@
 SERVO_STATE_SERVOD_ISSUE = 'SERVOD_ISSUE'
 SERVO_STATE_LID_OPEN_FAILED = 'LID_OPEN_FAILED'
 SERVO_STATE_BAD_RIBBON_CABLE = 'BAD_RIBBON_CABLE'
+SERVO_STATE_TOPOLOGY_ISSUE = 'TOPOLOGY_ISSUE'
 SERVO_STATE_DUT_NOT_CONNECTED = 'DUT_NOT_CONNECTED'
 SERVO_STATE_EC_BROKEN = 'EC_BROKEN'
 SERVO_STATE_BROKEN = 'BROKEN'
diff --git a/server/hosts/servo_repair.py b/server/hosts/servo_repair.py
index 8eb19bf..3004c8e 100644
--- a/server/hosts/servo_repair.py
+++ b/server/hosts/servo_repair.py
@@ -22,6 +22,7 @@
 from autotest_lib.server.hosts import cros_constants
 from autotest_lib.server.hosts import repair_utils
 from autotest_lib.server.hosts import servo_constants
+from autotest_lib.server.cros.servo.topology import servo_topology
 import six
 
 try:
@@ -550,6 +551,37 @@
         return 'Ensure the Servo connected to the DUT.'
 
 
+class _TopologyVerifier(hosts.Verifier):
+    """Verifier that all servo component is presented."""
+
+    @ignore_exception_for_non_cros_host
+    @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC)
+    def verify(self, host):
+        topology = servo_topology.ServoTopology(host)
+        topology.read(host.get_dut_host_info())
+        try:
+            topology.validate(raise_error=True,
+                              dual_set=host.is_dual_setup(),
+                              compare=True)
+        except servo_topology.ServoTopologyError as e:
+            six.reraise(hosts.AutoservVerifyError, str(e), sys.exc_info()[2])
+
+    def _is_applicable(self, host):
+        if host.is_localhost():
+            logging.info('Target servo is not in a lab,'
+                         ' action is not applicable.')
+            return False
+        if not host.is_servo_topology_supported():
+            logging.info('Target servo-topology is not supported,'
+                         ' action is not applicable.')
+            return False
+        return True
+
+    @property
+    def description(self):
+        return 'Ensure all Servo component present.'
+
+
 class _PowerButtonVerifier(hosts.Verifier):
     """
     Verifier to check sanity of the `pwr_button` signal.
@@ -890,6 +922,7 @@
             (_BoardConfigVerifier, 'brd_config', ['servo_ssh']),
             (_SerialConfigVerifier, 'ser_config', ['servo_ssh']),
             (_ServodJobVerifier, 'servod_job', config + ['disk_space']),
+            (_TopologyVerifier, 'servo_topology', ['servod_job']),
             (_ServodConnectionVerifier, 'servod_connection', ['servod_job']),
             (_ServodControlVerifier, 'servod_control', ['servod_connection']),
             (_DUTConnectionVerifier, 'dut_connected', ['servod_connection']),
@@ -903,8 +936,8 @@
     ]
 
     servod_deps = [
-            'servod_job', 'servod_connection', 'servod_control',
-            'dut_connected', 'pwr_button'
+            'servod_job', 'servo_topology', 'servod_connection',
+            'servod_control', 'dut_connected', 'pwr_button'
     ]
     repair_actions = [
             (_DiskCleanupRepair, 'disk_cleanup', ['servo_ssh'], ['disk_space'