blob: 0312a05c750d804eef31f7351374e1e9230ab391 [file] [log] [blame]
Prashanth B489b91d2014-03-15 12:17:16 -07001#!/usr/bin/python
2#pylint: disable-msg=C0111
3
4# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
Prashanth B489b91d2014-03-15 12:17:16 -07007import collections
Justin Giorgi67ad67d2016-06-29 14:41:04 -07008import unittest
Prashanth B489b91d2014-03-15 12:17:16 -07009
10import common
Prashanth Bb474fdf2014-04-03 16:05:38 -070011from autotest_lib.client.common_lib import host_queue_entry_states
Prashanth Bb474fdf2014-04-03 16:05:38 -070012from autotest_lib.frontend import setup_django_environment
13from autotest_lib.frontend.afe import frontend_test_utils
Prashanth B489b91d2014-03-15 12:17:16 -070014from autotest_lib.frontend.afe import models
Prashanth Bb474fdf2014-04-03 16:05:38 -070015from autotest_lib.frontend.afe import rdb_model_extensions
Prashanth Bb474fdf2014-04-03 16:05:38 -070016from autotest_lib.scheduler import rdb
17from autotest_lib.scheduler import rdb_hosts
18from autotest_lib.scheduler import rdb_lib
19from autotest_lib.scheduler import rdb_requests
Prashanth B2d8047e2014-04-27 18:54:47 -070020from autotest_lib.scheduler import rdb_testing_utils
Prashanth B2c1a22a2014-04-02 17:30:51 -070021from autotest_lib.server.cros import provision
Prashanth Bb474fdf2014-04-03 16:05:38 -070022
Prashanth B489b91d2014-03-15 12:17:16 -070023
Prashanth Bb474fdf2014-04-03 16:05:38 -070024class AssignmentValidator(object):
Prashanth B489b91d2014-03-15 12:17:16 -070025 """Utility class to check that priority inversion doesn't happen. """
26
27
28 @staticmethod
29 def check_acls_deps(host, request):
30 """Check if a host and request match by comparing acls and deps.
31
32 @param host: A dictionary representing attributes of the host.
33 @param request: A request, as defined in rdb_requests.
34
35 @return True if the deps/acls of the request match the host.
36 """
37 # Unfortunately the hosts labels are labelnames, not ids.
38 request_deps = set([l.name for l in
39 models.Label.objects.filter(id__in=request.deps)])
40 return (set(host['labels']).intersection(request_deps) == request_deps
41 and set(host['acls']).intersection(request.acls))
42
43
44 @staticmethod
45 def find_matching_host_for_request(hosts, request):
46 """Find a host from the given list of hosts, matching the request.
47
48 @param hosts: A list of dictionaries representing host attributes.
49 @param requetst: The unsatisfied request.
50
51 @return: A host, if a matching host is found from the input list.
52 """
53 if not hosts or not request:
54 return None
55 for host in hosts:
Prashanth Bb474fdf2014-04-03 16:05:38 -070056 if AssignmentValidator.check_acls_deps(host, request):
Prashanth B489b91d2014-03-15 12:17:16 -070057 return host
58
59
60 @staticmethod
61 def sort_requests(requests):
62 """Sort the requests by priority.
63
64 @param requests: Unordered requests.
65
66 @return: A list of requests ordered by priority.
67 """
68 return sorted(collections.Counter(requests).items(),
69 key=lambda request: request[0].priority, reverse=True)
70
71
72 @staticmethod
Prashanth Bb474fdf2014-04-03 16:05:38 -070073 def verify_priority(request_queue, result):
74 requests = AssignmentValidator.sort_requests(request_queue)
75 for request, count in requests:
76 hosts = result.get(request)
77 # The request was completely satisfied.
78 if hosts and len(hosts) == count:
79 continue
80 # Go through all hosts given to lower priority requests and
81 # make sure we couldn't have allocated one of them for this
82 # unsatisfied higher priority request.
83 lower_requests = requests[requests.index((request,count))+1:]
84 for lower_request, count in lower_requests:
85 if (lower_request.priority < request.priority and
86 AssignmentValidator.find_matching_host_for_request(
87 result.get(lower_request), request)):
88 raise ValueError('Priority inversion occured between '
89 'priorities %s and %s' %
90 (request.priority, lower_request.priority))
91
92
93 @staticmethod
Prashanth B489b91d2014-03-15 12:17:16 -070094 def priority_checking_response_handler(request_manager):
95 """Fake response handler wrapper for any request_manager.
96
97 Check that higher priority requests get a response over lower priority
98 requests, by re-validating all the hosts assigned to a lower priority
99 request against the unsatisfied higher priority ones.
100
101 @param request_manager: A request_manager as defined in rdb_lib.
102
103 @raises ValueError: If priority inversion is detected.
104 """
105 # Fist call the rdb to make its decisions, then sort the requests
106 # by priority and make sure unsatisfied requests higher up in the list
107 # could not have been satisfied by hosts assigned to requests lower
108 # down in the list.
109 result = request_manager.api_call(request_manager.request_queue)
Prashanth Bb474fdf2014-04-03 16:05:38 -0700110 if not result:
111 raise ValueError('Expected results but got none.')
112 AssignmentValidator.verify_priority(
113 request_manager.request_queue, result)
114 for hosts in result.values():
115 for host in hosts:
116 yield host
Prashanth B489b91d2014-03-15 12:17:16 -0700117
118
Prashanth B2d8047e2014-04-27 18:54:47 -0700119class BaseRDBTest(rdb_testing_utils.AbstractBaseRDBTester, unittest.TestCase):
Prashanth B489b91d2014-03-15 12:17:16 -0700120 _config_section = 'AUTOTEST_WEB'
121
122
Prashanth Bb474fdf2014-04-03 16:05:38 -0700123 def testAcquireLeasedHostBasic(self):
124 """Test that acquisition of a leased host doesn't happen.
125
126 @raises AssertionError: If the one host that satisfies the request
127 is acquired.
128 """
129 job = self.create_job(deps=set(['a']))
130 host = self.db_helper.create_host('h1', deps=set(['a']))
131 host.leased = 1
132 host.save()
133 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700134 hosts = list(rdb_lib.acquire_hosts(queue_entries))
Prashanth Bb474fdf2014-04-03 16:05:38 -0700135 self.assertTrue(len(hosts) == 1 and hosts[0] is None)
136
137
138 def testAcquireLeasedHostRace(self):
139 """Test behaviour when hosts are leased just before acquisition.
140
141 If a fraction of the hosts somehow get leased between finding and
142 acquisition, the rdb should just return the remaining hosts for the
143 request to use.
144
145 @raises AssertionError: If both the requests get a host successfully,
146 since one host gets leased before the final attempt to lease both.
147 """
148 j1 = self.create_job(deps=set(['a']))
149 j2 = self.create_job(deps=set(['a']))
150 hosts = [self.db_helper.create_host('h1', deps=set(['a'])),
151 self.db_helper.create_host('h2', deps=set(['a']))]
152
153 @rdb_hosts.return_rdb_host
Prashanth Bf66d51b2014-05-06 12:42:25 -0700154 def local_find_hosts(host_query_manger, deps, acls):
Prashanth Bb474fdf2014-04-03 16:05:38 -0700155 """Return a predetermined list of hosts, one of which is leased."""
156 h1 = models.Host.objects.get(hostname='h1')
157 h1.leased = 1
158 h1.save()
159 h2 = models.Host.objects.get(hostname='h2')
160 return [h1, h2]
161
162 self.god.stub_with(rdb.AvailableHostQueryManager, 'find_hosts',
163 local_find_hosts)
164 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700165 hosts = list(rdb_lib.acquire_hosts(queue_entries))
Prashanth Bb474fdf2014-04-03 16:05:38 -0700166 self.assertTrue(len(hosts) == 2 and None in hosts)
167 self.check_hosts(iter(hosts))
168
169
170 def testHostReleaseStates(self):
171 """Test that we will only release an unused host if it is in Ready.
172
173 @raises AssertionError: If the host gets released in any other state.
174 """
175 host = self.db_helper.create_host('h1', deps=set(['x']))
176 for state in rdb_model_extensions.AbstractHostModel.Status.names:
177 host.status = state
178 host.leased = 1
179 host.save()
180 self._release_unused_hosts()
181 host = models.Host.objects.get(hostname='h1')
182 self.assertTrue(host.leased == (state != 'Ready'))
183
184
185 def testHostReleseHQE(self):
186 """Test that we will not release a ready host if it's being used.
187
188 @raises AssertionError: If the host is released even though it has
189 been assigned to an active hqe.
190 """
191 # Create a host and lease it out in Ready.
192 host = self.db_helper.create_host('h1', deps=set(['x']))
193 host.status = 'Ready'
194 host.leased = 1
195 host.save()
196
197 # Create a job and give its hqe the leased host.
198 job = self.create_job(deps=set(['x']))
199 self.db_helper.add_host_to_job(host, job.id)
200 hqe = models.HostQueueEntry.objects.get(job_id=job.id)
201
202 # Activate the hqe by setting its state.
203 hqe.status = host_queue_entry_states.ACTIVE_STATUSES[0]
204 hqe.save()
205
206 # Make sure the hqes host isn't released, even if its in ready.
207 self._release_unused_hosts()
208 host = models.Host.objects.get(hostname='h1')
209 self.assertTrue(host.leased == 1)
210
211
Prashanth B489b91d2014-03-15 12:17:16 -0700212 def testBasicDepsAcls(self):
213 """Test a basic deps/acls request.
214
215 Make sure that a basic request with deps and acls, finds a host from
216 the ready pool that has matching labels and is in a matching aclgroups.
217
218 @raises AssertionError: If the request doesn't find a host, since the
219 we insert a matching host in the ready pool.
220 """
221 deps = set(['a', 'b'])
222 acls = set(['a', 'b'])
Prashanth Bb474fdf2014-04-03 16:05:38 -0700223 self.db_helper.create_host('h1', deps=deps, acls=acls)
Prashanth B489b91d2014-03-15 12:17:16 -0700224 job = self.create_job(user='autotest_system', deps=deps, acls=acls)
225 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700226 matching_host = rdb_lib.acquire_hosts(queue_entries).next()
Prashanth B489b91d2014-03-15 12:17:16 -0700227 self.check_host_assignment(job.id, matching_host.id)
Prashanth Bb474fdf2014-04-03 16:05:38 -0700228 self.assertTrue(matching_host.leased == 1)
Prashanth B489b91d2014-03-15 12:17:16 -0700229
230
Fang Denga9bc9592015-01-27 17:09:57 -0800231 def testPreferredDeps(self):
232 """Test that perferred deps is respected.
233
234 If multiple hosts satisfied a job's deps, the one with preferred
235 label will be assigned to the job.
236
237 @raises AssertionError: If a host without a preferred label is
238 assigned to the job instead of one with
239 a preferred label.
240 """
241 lumpy_deps = set(['board:lumpy'])
242 stumpy_deps = set(['board:stumpy'])
243 stumpy_deps_with_crosversion = set(
244 ['board:stumpy', 'cros-version:lumpy-release/R41-6323.0.0'])
245
246 acls = set(['a', 'b'])
247 # Hosts lumpy1 and lumpy2 are created as a control group,
248 # which ensures that if no preferred label is used, the host
249 # with a smaller id will be chosen first. We need to make sure
250 # stumpy2 was chosen because it has a cros-version label, but not
251 # because of other randomness.
252 self.db_helper.create_host('lumpy1', deps=lumpy_deps, acls=acls)
253 self.db_helper.create_host('lumpy2', deps=lumpy_deps, acls=acls)
254 self.db_helper.create_host('stumpy1', deps=stumpy_deps, acls=acls)
255 self.db_helper.create_host(
256 'stumpy2', deps=stumpy_deps_with_crosversion , acls=acls)
257 job_1 = self.create_job(user='autotest_system',
258 deps=lumpy_deps, acls=acls)
259 job_2 = self.create_job(user='autotest_system',
260 deps=stumpy_deps_with_crosversion, acls=acls)
261 queue_entries = self._dispatcher._refresh_pending_queue_entries()
262 matching_hosts = list(rdb_lib.acquire_hosts(queue_entries))
263 assignment = {}
264 import logging
265 for job, host in zip(queue_entries, matching_hosts):
266 self.check_host_assignment(job.id, host.id)
267 assignment[job.id] = host.hostname
268 self.assertEqual(assignment[job_1.id], 'lumpy1')
269 self.assertEqual(assignment[job_2.id], 'stumpy2')
270
271
Prashanth B489b91d2014-03-15 12:17:16 -0700272 def testBadDeps(self):
273 """Test that we find no hosts when only acls match.
274
275 @raises AssertionError: If the request finds a host, since the only
276 host in the ready pool will not have matching deps.
277 """
278 host_labels = set(['a'])
279 job_deps = set(['b'])
280 acls = set(['a', 'b'])
Prashanth Bb474fdf2014-04-03 16:05:38 -0700281 self.db_helper.create_host('h1', deps=host_labels, acls=acls)
Prashanth B489b91d2014-03-15 12:17:16 -0700282 job = self.create_job(user='autotest_system', deps=job_deps, acls=acls)
283 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700284 matching_host = rdb_lib.acquire_hosts(queue_entries).next()
Prashanth B489b91d2014-03-15 12:17:16 -0700285 self.assert_(not matching_host)
286
287
288 def testBadAcls(self):
289 """Test that we find no hosts when only deps match.
290
291 @raises AssertionError: If the request finds a host, since the only
292 host in the ready pool will not have matching acls.
293 """
294 deps = set(['a'])
295 host_acls = set(['a'])
296 job_acls = set(['b'])
Prashanth Bb474fdf2014-04-03 16:05:38 -0700297 self.db_helper.create_host('h1', deps=deps, acls=host_acls)
Prashanth B489b91d2014-03-15 12:17:16 -0700298
299 # Create the job as a new user who is only in the 'b' and 'Everyone'
300 # aclgroups. Though there are several hosts in the Everyone group, the
301 # 1 host that has the 'a' dep isn't.
302 job = self.create_job(user='new_user', deps=deps, acls=job_acls)
303 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700304 matching_host = rdb_lib.acquire_hosts(queue_entries).next()
Prashanth B489b91d2014-03-15 12:17:16 -0700305 self.assert_(not matching_host)
306
307
308 def testBasicPriority(self):
309 """Test that priority inversion doesn't happen.
310
311 Schedule 2 jobs with the same deps, acls and user, but different
312 priorities, and confirm that the higher priority request gets the host.
Prashanth Bb474fdf2014-04-03 16:05:38 -0700313 This confirmation happens through the AssignmentValidator.
Prashanth B489b91d2014-03-15 12:17:16 -0700314
315 @raises AssertionError: If the un important request gets host h1 instead
316 of the important request.
317 """
318 deps = set(['a', 'b'])
319 acls = set(['a', 'b'])
Prashanth Bb474fdf2014-04-03 16:05:38 -0700320 self.db_helper.create_host('h1', deps=deps, acls=acls)
Prashanth B489b91d2014-03-15 12:17:16 -0700321 important_job = self.create_job(user='autotest_system',
322 deps=deps, acls=acls, priority=2)
323 un_important_job = self.create_job(user='autotest_system',
324 deps=deps, acls=acls, priority=0)
325 queue_entries = self._dispatcher._refresh_pending_queue_entries()
326
327 self.god.stub_with(rdb_requests.BaseHostRequestManager, 'response',
Prashanth Bb474fdf2014-04-03 16:05:38 -0700328 AssignmentValidator.priority_checking_response_handler)
Prashanth Bf66d51b2014-05-06 12:42:25 -0700329 self.check_hosts(rdb_lib.acquire_hosts(queue_entries))
Prashanth B489b91d2014-03-15 12:17:16 -0700330
331
332 def testPriorityLevels(self):
333 """Test that priority inversion doesn't happen.
334
335 Increases a job's priority and makes several requests for hosts,
336 checking that priority inversion doesn't happen.
337
338 @raises AssertionError: If the unimportant job gets h1 while it is
339 still unimportant, or doesn't get h1 while after it becomes the
340 most important job.
341 """
342 deps = set(['a', 'b'])
343 acls = set(['a', 'b'])
Prashanth Bb474fdf2014-04-03 16:05:38 -0700344 self.db_helper.create_host('h1', deps=deps, acls=acls)
Prashanth B489b91d2014-03-15 12:17:16 -0700345
346 # Create jobs that will bucket differently and confirm that jobs in an
347 # earlier bucket get a host.
348 first_job = self.create_job(user='autotest_system', deps=deps, acls=acls)
349 important_job = self.create_job(user='autotest_system', deps=deps,
350 acls=acls, priority=2)
351 deps.pop()
352 unimportant_job = self.create_job(user='someother_system', deps=deps,
353 acls=acls, priority=1)
354 queue_entries = self._dispatcher._refresh_pending_queue_entries()
355
356 self.god.stub_with(rdb_requests.BaseHostRequestManager, 'response',
Prashanth Bb474fdf2014-04-03 16:05:38 -0700357 AssignmentValidator.priority_checking_response_handler)
Prashanth Bf66d51b2014-05-06 12:42:25 -0700358 self.check_hosts(rdb_lib.acquire_hosts(queue_entries))
Prashanth B489b91d2014-03-15 12:17:16 -0700359
360 # Elevate the priority of the unimportant job, so we now have
361 # 2 jobs at the same priority.
362 self.db_helper.increment_priority(job_id=unimportant_job.id)
363 queue_entries = self._dispatcher._refresh_pending_queue_entries()
364 self._release_unused_hosts()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700365 self.check_hosts(rdb_lib.acquire_hosts(queue_entries))
Prashanth B489b91d2014-03-15 12:17:16 -0700366
367 # Prioritize the first job, and confirm that it gets the host over the
368 # jobs that got it the last time.
369 self.db_helper.increment_priority(job_id=unimportant_job.id)
370 queue_entries = self._dispatcher._refresh_pending_queue_entries()
371 self._release_unused_hosts()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700372 self.check_hosts(rdb_lib.acquire_hosts(queue_entries))
Prashanth Bb474fdf2014-04-03 16:05:38 -0700373
374
375 def testFrontendJobScheduling(self):
376 """Test that basic frontend job scheduling.
377
378 @raises AssertionError: If the received and requested host don't match,
379 or the mis-matching host is returned instead.
380 """
381 deps = set(['x', 'y'])
382 acls = set(['a', 'b'])
383
384 # Create 2 frontend jobs and only one matching host.
385 matching_job = self.create_job(acls=acls, deps=deps)
386 matching_host = self.db_helper.create_host('h1', acls=acls, deps=deps)
387 mis_matching_job = self.create_job(acls=acls, deps=deps)
388 mis_matching_host = self.db_helper.create_host(
389 'h2', acls=acls, deps=deps.pop())
390 self.db_helper.add_host_to_job(matching_host, matching_job.id)
391 self.db_helper.add_host_to_job(mis_matching_host, mis_matching_job.id)
392
393 # Check that only the matching host is returned, and that we get 'None'
394 # for the second request.
395 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700396 hosts = list(rdb_lib.acquire_hosts(queue_entries))
Prashanth Bb474fdf2014-04-03 16:05:38 -0700397 self.assertTrue(len(hosts) == 2 and None in hosts)
398 returned_host = [host for host in hosts if host].pop()
399 self.assertTrue(matching_host.id == returned_host.id)
400
401
402 def testFrontendJobPriority(self):
403 """Test that frontend job scheduling doesn't ignore priorities.
404
405 @raises ValueError: If the priorities of frontend jobs are ignored.
406 """
407 board = 'x'
408 high_priority = self.create_job(priority=2, deps=set([board]))
409 low_priority = self.create_job(priority=1, deps=set([board]))
410 host = self.db_helper.create_host('h1', deps=set([board]))
411 self.db_helper.add_host_to_job(host, low_priority.id)
412 self.db_helper.add_host_to_job(host, high_priority.id)
413
414 queue_entries = self._dispatcher._refresh_pending_queue_entries()
415
416 def local_response_handler(request_manager):
417 """Confirms that a higher priority frontend job gets a host.
418
419 @raises ValueError: If priority inversion happens and the job
420 with priority 1 gets the host instead.
421 """
422 result = request_manager.api_call(request_manager.request_queue)
423 if not result:
424 raise ValueError('Excepted the high priority request to '
425 'get a host, but the result is empty.')
426 for request, hosts in result.iteritems():
427 if request.priority == 1:
428 raise ValueError('Priority of frontend job ignored.')
429 if len(hosts) > 1:
430 raise ValueError('Multiple hosts returned against one '
431 'frontend job scheduling request.')
432 yield hosts[0]
433
434 self.god.stub_with(rdb_requests.BaseHostRequestManager, 'response',
435 local_response_handler)
Prashanth Bf66d51b2014-05-06 12:42:25 -0700436 self.check_hosts(rdb_lib.acquire_hosts(queue_entries))
Prashanth B489b91d2014-03-15 12:17:16 -0700437
Prashanth B2c1a22a2014-04-02 17:30:51 -0700438
439 def testSuiteOrderedHostAcquisition(self):
440 """Test that older suite jobs acquire hosts first.
441
442 Make sure older suite jobs get hosts first, but not at the expense of
443 higher priority jobs.
444
445 @raises ValueError: If unexpected acquisitions occur, eg:
446 suite_job_2 acquires the last 2 hosts instead of suite_job_1.
447 isolated_important_job doesn't get any hosts.
448 Any job acquires more hosts than necessary.
449 """
450 board = 'x'
Prashanth B2d8047e2014-04-27 18:54:47 -0700451
452 # Create 2 suites such that the later suite has an ordering of deps
453 # that places it ahead of the earlier suite, if parent_job_id is
454 # ignored.
Prashanth B2c1a22a2014-04-02 17:30:51 -0700455 suite_without_dep = self.create_suite(num=2, priority=0, board=board)
Prashanth B2d8047e2014-04-27 18:54:47 -0700456
Prashanth B2c1a22a2014-04-02 17:30:51 -0700457 suite_with_dep = self.create_suite(num=1, priority=0, board=board)
Prashanth B2d8047e2014-04-27 18:54:47 -0700458 self.db_helper.add_deps_to_job(suite_with_dep[0], dep_names=list('y'))
459
460 # Create an important job that should be ahead of the first suite,
461 # because priority trumps parent_job_id and time of creation.
Prashanth B2c1a22a2014-04-02 17:30:51 -0700462 isolated_important_job = self.create_job(priority=3, deps=set([board]))
463
Prashanth B2d8047e2014-04-27 18:54:47 -0700464 # Create 3 hosts, all with the deps to satisfy the last suite.
Prashanth B2c1a22a2014-04-02 17:30:51 -0700465 for i in range(0, 3):
Prashanth B2d8047e2014-04-27 18:54:47 -0700466 self.db_helper.create_host('h%s' % i, deps=set([board, 'y']))
Prashanth B2c1a22a2014-04-02 17:30:51 -0700467
468 queue_entries = self._dispatcher._refresh_pending_queue_entries()
469
470 def local_response_handler(request_manager):
471 """Reorder requests and check host acquisition.
472
473 @raises ValueError: If unexpected/no acquisitions occur.
474 """
475 if any([request for request in request_manager.request_queue
476 if request.parent_job_id is None]):
477 raise ValueError('Parent_job_id can never be None.')
478
479 # This will result in the ordering:
480 # [suite_2_1, suite_1_*, suite_1_*, isolated_important_job]
481 # The priority scheduling order should be:
482 # [isolated_important_job, suite_1_*, suite_1_*, suite_2_1]
483 # Since:
484 # a. the isolated_important_job is the most important.
485 # b. suite_1 was created before suite_2, regardless of deps
486 disorderly_queue = sorted(request_manager.request_queue,
487 key=lambda r: -r.parent_job_id)
488 request_manager.request_queue = disorderly_queue
489 result = request_manager.api_call(request_manager.request_queue)
490 if not result:
491 raise ValueError('Expected results but got none.')
492
493 # Verify that the isolated_important_job got a host, and that the
494 # first suite got both remaining free hosts.
495 for request, hosts in result.iteritems():
496 if request.parent_job_id == 0:
497 if len(hosts) > 1:
498 raise ValueError('First job acquired more hosts than '
499 'necessary. Response map: %s' % result)
500 continue
501 if request.parent_job_id == 1:
502 if len(hosts) < 2:
503 raise ValueError('First suite job requests were not '
504 'satisfied. Response_map: %s' % result)
505 continue
506 # The second suite job got hosts instead of one of
507 # the others. Eitherway this is a failure.
508 raise ValueError('Unexpected host acquisition '
509 'Response map: %s' % result)
510 yield None
511
512 self.god.stub_with(rdb_requests.BaseHostRequestManager, 'response',
513 local_response_handler)
Prashanth Bf66d51b2014-05-06 12:42:25 -0700514 list(rdb_lib.acquire_hosts(queue_entries))
Prashanth B2c1a22a2014-04-02 17:30:51 -0700515
Alex Milleraa772002014-04-10 17:51:21 -0700516
517 def testConfigurations(self):
518 """Test that configurations don't matter.
Prashanth B2d8047e2014-04-27 18:54:47 -0700519 @raises AssertionError: If the request doesn't find a host,
Alex Milleraa772002014-04-10 17:51:21 -0700520 this will happen if configurations are not stripped out.
521 """
522 self.god.stub_with(provision.Cleanup,
523 '_actions',
524 {'action': 'fakeTest'})
525 job_labels = set(['action', 'a'])
526 host_deps = set(['a'])
527 db_host = self.db_helper.create_host('h1', deps=host_deps)
528 self.create_job(user='autotest_system', deps=job_labels)
529 queue_entries = self._dispatcher._refresh_pending_queue_entries()
Prashanth Bf66d51b2014-05-06 12:42:25 -0700530 matching_host = rdb_lib.acquire_hosts(queue_entries).next()
Alex Milleraa772002014-04-10 17:51:21 -0700531 self.assert_(matching_host.id == db_host.id)
Fang Deng52a23932014-11-20 18:30:22 -0800532
533
534class RDBMinDutTest(
535 rdb_testing_utils.AbstractBaseRDBTester, unittest.TestCase):
536 """Test AvailableHostRequestHandler"""
537
538 _config_section = 'AUTOTEST_WEB'
539
540
541 def min_dut_test_helper(self, num_hosts, suite_settings):
542 """A helper function to test min_dut logic.
543
544 @param num_hosts: Total number of hosts to create.
545 @param suite_settings: A dictionary specify how suites would be created
546 and verified.
547 E.g. {'priority': 10, 'num_jobs': 3,
548 'min_duts':2, 'expected_aquired': 1}
549 With this setting, will create a suite that has 3
550 child jobs, with priority 10 and min_duts 2.
551 The suite is expected to get 1 dut.
552 """
553 acls = set(['fake_acl'])
554 hosts = []
555 for i in range (0, num_hosts):
556 hosts.append(self.db_helper.create_host(
557 'h%d' % i, deps=set(['board:lumpy']), acls=acls))
558 suites = {}
559 suite_min_duts = {}
560 for setting in suite_settings:
561 s = self.create_suite(num=setting['num_jobs'],
562 priority=setting['priority'],
563 board='board:lumpy', acls=acls)
564 # Empty list will be used to store acquired hosts.
565 suites[s['parent_job'].id] = (setting, [])
566 suite_min_duts[s['parent_job'].id] = setting['min_duts']
567 queue_entries = self._dispatcher._refresh_pending_queue_entries()
568 matching_hosts = rdb_lib.acquire_hosts(queue_entries, suite_min_duts)
569 for host, queue_entry in zip(matching_hosts, queue_entries):
570 if host:
571 suites[queue_entry.job.parent_job_id][1].append(host)
572
573 for setting, hosts in suites.itervalues():
574 self.assertEqual(len(hosts),setting['expected_aquired'])
575
576
577 def testHighPriorityTakeAll(self):
578 """Min duts not satisfied."""
579 num_hosts = 1
580 suite1 = {'priority':20, 'num_jobs': 3, 'min_duts': 2,
581 'expected_aquired': 1}
582 suite2 = {'priority':10, 'num_jobs': 7, 'min_duts': 5,
583 'expected_aquired': 0}
584 self.min_dut_test_helper(num_hosts, [suite1, suite2])
585
586
587 def testHighPriorityMinSatisfied(self):
588 """High priority min duts satisfied."""
589 num_hosts = 4
590 suite1 = {'priority':20, 'num_jobs': 4, 'min_duts': 2,
591 'expected_aquired': 2}
592 suite2 = {'priority':10, 'num_jobs': 7, 'min_duts': 5,
593 'expected_aquired': 2}
594 self.min_dut_test_helper(num_hosts, [suite1, suite2])
595
596
597 def testAllPrioritiesMinSatisfied(self):
598 """Min duts satisfied."""
599 num_hosts = 7
600 suite1 = {'priority':20, 'num_jobs': 4, 'min_duts': 2,
601 'expected_aquired': 2}
602 suite2 = {'priority':10, 'num_jobs': 7, 'min_duts': 5,
603 'expected_aquired': 5}
604 self.min_dut_test_helper(num_hosts, [suite1, suite2])
605
606
607 def testHighPrioritySatisfied(self):
608 """Min duts satisfied, high priority suite satisfied."""
609 num_hosts = 10
610 suite1 = {'priority':20, 'num_jobs': 4, 'min_duts': 2,
611 'expected_aquired': 4}
612 suite2 = {'priority':10, 'num_jobs': 7, 'min_duts': 5,
613 'expected_aquired': 6}
614 self.min_dut_test_helper(num_hosts, [suite1, suite2])
615
616
617 def testEqualPriorityFirstSuiteMinSatisfied(self):
618 """Equal priority, earlier suite got min duts."""
619 num_hosts = 4
620 suite1 = {'priority':20, 'num_jobs': 4, 'min_duts': 2,
621 'expected_aquired': 2}
622 suite2 = {'priority':20, 'num_jobs': 7, 'min_duts': 5,
623 'expected_aquired': 2}
624 self.min_dut_test_helper(num_hosts, [suite1, suite2])
625
626
627 def testEqualPriorityAllSuitesMinSatisfied(self):
628 """Equal priority, all suites got min duts."""
629 num_hosts = 7
630 suite1 = {'priority':20, 'num_jobs': 4, 'min_duts': 2,
631 'expected_aquired': 2}
632 suite2 = {'priority':20, 'num_jobs': 7, 'min_duts': 5,
633 'expected_aquired': 5}
634 self.min_dut_test_helper(num_hosts, [suite1, suite2])
635
636
637if __name__ == '__main__':
638 unittest.main()