[autotest] Hosts locked manually by users should not count as usable.
We need to ignore them in Reimager._count_usable_hosts(), just like we
do 'Repairing' and 'Repair Failed' hosts.
The trick is _not_ painting hosts correctly locked by the test infrastructure
with the same brush. These hosts will become available in due time.
BUG=chromium-os:33623
TEST=unit
TEST=run_suite while all usable devices are locked manually. Should fail.
TEST=run_suite while all usable devices are locked for reimaging. Should run.
Change-Id: I7bf001d9503cdd027741760493cce53947f95e45
Reviewed-on: https://gerrit.chromium.org/gerrit/30768
Commit-Ready: Chris Masone <cmasone@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
diff --git a/global_config.ini b/global_config.ini
index dcfc703..b6da726 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -137,6 +137,7 @@
dev_server: http://172.22.50.2:8082,http://172.22.50.205:8082
crash_server: http://172.22.50.2:8082
sharding_factor: 1
+infrastructure_users: chromeos-test
image_url_pattern: %s/update/%s
package_url_pattern: %s/static/archive/%s/autotest/packages
diff --git a/server/cros/dynamic_suite/fakes.py b/server/cros/dynamic_suite/fakes.py
index 87df4e2..f0a5db3 100644
--- a/server/cros/dynamic_suite/fakes.py
+++ b/server/cros/dynamic_suite/fakes.py
@@ -32,9 +32,11 @@
class FakeHost(object):
"""Faked out RPC-client-side Host object."""
- def __init__(self, hostname='', status='Ready'):
+ def __init__(self, hostname='', status='Ready', locked=False, locked_by=''):
self.hostname = hostname
self.status = status
+ self.locked = locked
+ self.locked_by = locked_by
class FakeLabel(object):
diff --git a/server/cros/dynamic_suite/reimager.py b/server/cros/dynamic_suite/reimager.py
index 9d135c0..b50ca27 100644
--- a/server/cros/dynamic_suite/reimager.py
+++ b/server/cros/dynamic_suite/reimager.py
@@ -162,6 +162,47 @@
'Too few hosts with %r' % labels)
+ def _count_usable_hosts(self, host_spec):
+ """
+ Given a set of host labels, count the live hosts that have them all.
+
+ @param host_spec: list of labels specifying a set of hosts.
+ @return the number of live hosts that satisfy |host_spec|.
+ """
+ count = 0
+ for host in self._afe.get_hosts(multiple_labels=host_spec):
+ if self._alive(host) and not self._incorrectly_locked(host):
+ count += 1
+ return count
+
+
+ def _alive(self, host):
+ """
+ Given a host, determine if the host is alive.
+
+ @param host: Host instance (as in server/frontend.py)
+ @return True if host is not under, or in need of, repair. Else, False.
+ """
+ return host.status not in ['Repair Failed', 'Repairing']
+
+
+ def _incorrectly_locked(self, host):
+ """
+ Given a host, determine if the host is locked by some user.
+
+ If the host is unlocked, or locked by the test infrastructure,
+ this will return False. Usernames defined as 'part of the test
+ infrastructure' are listed in global_config.ini under the [CROS]
+ section in the 'infrastructure_users' field.
+
+ @param host: Host instance (as in server/frontend.py)
+ @return False if the host is not locked, or locked by the infra.
+ True if the host is locked by someone we haven't blessed.
+ """
+ return (host.locked and
+ host.locked_by not in tools.infrastructure_user_list())
+
+
def clear_reimaged_host_state(self, build):
"""
Clear per-host state created in the autotest DB for this job.
@@ -204,20 +245,6 @@
{hashlib.md5(test_name).hexdigest(): job_id_owner})
- def _count_usable_hosts(self, host_spec):
- """
- Given a set of host labels, count the live hosts that have them all.
-
- @param host_spec: list of labels specifying a set of hosts.
- @return the number of live hosts that satisfy |host_spec|.
- """
- count = 0
- for h in self._afe.get_hosts(multiple_labels=host_spec):
- if h.status not in ['Repair Failed', 'Repairing']:
- count += 1
- return count
-
-
def _ensure_version_label(self, name):
"""
Ensure that a label called |name| exists in the autotest DB.
diff --git a/server/cros/dynamic_suite/reimager_unittest.py b/server/cros/dynamic_suite/reimager_unittest.py
index 91afcfb..3d887ca 100644
--- a/server/cros/dynamic_suite/reimager_unittest.py
+++ b/server/cros/dynamic_suite/reimager_unittest.py
@@ -74,6 +74,23 @@
self.reimager._ensure_version_label(name)
+ def testIncorrectlyLocked(self):
+ """Should detect hosts locked by random users."""
+ host = FakeHost(locked=True)
+ host.locked_by = 'some guy'
+ self.assertTrue(self.reimager._incorrectly_locked(host))
+
+
+ def testNotIncorrectlyLocked(self):
+ """Should accept hosts locked by the infrastructure."""
+ infra_user = 'an infra user'
+ self.mox.StubOutWithMock(tools, 'infrastructure_user_list')
+ tools.infrastructure_user_list().AndReturn([infra_user])
+ host = FakeHost(locked=True, locked_by=infra_user)
+ self.mox.ReplayAll()
+ self.assertFalse(self.reimager._incorrectly_locked(host))
+
+
def testCountHostsByBoardAndPool(self):
"""Should count available hosts by board and pool."""
spec = [self._BOARD, 'pool:bvt']
@@ -98,6 +115,27 @@
self.assertEquals(self.reimager._count_usable_hosts(spec), 0)
+ def testCountAllHostsIncorrectlyLockedByBoard(self):
+ """Should count the available hosts, by board, getting a locked host."""
+ spec = [self._BOARD]
+ badly_locked_host = FakeHost(locked=True, locked_by = 'some guy')
+ self.afe.get_hosts(multiple_labels=spec).AndReturn([badly_locked_host])
+ self.mox.ReplayAll()
+ self.assertEquals(self.reimager._count_usable_hosts(spec), 0)
+
+
+ def testCountAllHostsInfraLockedByBoard(self):
+ """Should count the available hosts, get a host locked by infra."""
+ infra_user = 'an infra user'
+ self.mox.StubOutWithMock(tools, 'infrastructure_user_list')
+ spec = [self._BOARD]
+ self.afe.get_hosts(multiple_labels=spec).AndReturn(
+ [FakeHost(locked=True, locked_by=infra_user)])
+ tools.infrastructure_user_list().AndReturn([infra_user])
+ self.mox.ReplayAll()
+ self.assertEquals(self.reimager._count_usable_hosts(spec), 1)
+
+
def testScheduleJob(self):
"""Should be able to create a job with the AFE."""
# Fake out getting the autoupdate control file contents.
diff --git a/server/cros/dynamic_suite/tools.py b/server/cros/dynamic_suite/tools.py
index 08205ad..8ab674c 100644
--- a/server/cros/dynamic_suite/tools.py
+++ b/server/cros/dynamic_suite/tools.py
@@ -18,6 +18,11 @@
return _CONFIG.get_config_value('CROS', 'sharding_factor', type=int)
+def infrastructure_user_list():
+ return _CONFIG.get_config_value('CROS', 'infrastructure_users', type=list,
+ default=[])
+
+
def package_url_pattern():
return _CONFIG.get_config_value('CROS', 'package_url_pattern', type=str)