blob: 23a124bd14a1157a26afba944fea6dfa1ef8a20a [file] [log] [blame]
Prashanth B489b91d2014-03-15 12:17:16 -07001# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
beepscc9fc702013-12-02 12:45:38 -08005"""Rdb server module.
beeps7d8273b2013-11-06 09:44:34 -08006"""
beepscc9fc702013-12-02 12:45:38 -08007
beeps7d8273b2013-11-06 09:44:34 -08008import logging
beepscc9fc702013-12-02 12:45:38 -08009
10import common
11
12from django.core import exceptions as django_exceptions
13from django.db.models import fields
14from django.db.models import Q
Gabe Black1e1c41b2015-02-04 23:55:15 -080015from autotest_lib.client.common_lib.cros.graphite import autotest_stats
beepscc9fc702013-12-02 12:45:38 -080016from autotest_lib.frontend.afe import models
Prashanth B2d8047e2014-04-27 18:54:47 -070017from autotest_lib.scheduler import rdb_cache_manager
Prashanth B489b91d2014-03-15 12:17:16 -070018from autotest_lib.scheduler import rdb_hosts
19from autotest_lib.scheduler import rdb_requests
beepscc9fc702013-12-02 12:45:38 -080020from autotest_lib.scheduler import rdb_utils
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -080021from autotest_lib.server import utils
beeps7d8273b2013-11-06 09:44:34 -080022
23
Gabe Black1e1c41b2015-02-04 23:55:15 -080024_timer = autotest_stats.Timer(rdb_utils.RDB_STATS_KEY)
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -080025_is_master = not utils.is_shard()
beeps7d8273b2013-11-06 09:44:34 -080026
beeps7d8273b2013-11-06 09:44:34 -080027
beepscc9fc702013-12-02 12:45:38 -080028# Qeury managers: Provide a layer of abstraction over the database by
29# encapsulating common query patterns used by the rdb.
30class BaseHostQueryManager(object):
31 """Base manager for host queries on all hosts.
beeps7d8273b2013-11-06 09:44:34 -080032 """
33
beepscc9fc702013-12-02 12:45:38 -080034 host_objects = models.Host.objects
beeps7d8273b2013-11-06 09:44:34 -080035
beepscc9fc702013-12-02 12:45:38 -080036
37 def update_hosts(self, host_ids, **kwargs):
38 """Update fields on a hosts.
39
40 @param host_ids: A list of ids of hosts to update.
41 @param kwargs: A key value dictionary corresponding to column, value
42 in the host database.
43 """
44 self.host_objects.filter(id__in=host_ids).update(**kwargs)
45
46
Prashanth B489b91d2014-03-15 12:17:16 -070047 @rdb_hosts.return_rdb_host
beepscc9fc702013-12-02 12:45:38 -080048 def get_hosts(self, ids):
49 """Get host objects for the given ids.
50
51 @param ids: The ids for which we need host objects.
52
53 @returns: A list of RDBServerHostWrapper objects, ordered by host_id.
54 """
55 return self.host_objects.filter(id__in=ids).order_by('id')
56
57
Prashanth B489b91d2014-03-15 12:17:16 -070058 @rdb_hosts.return_rdb_host
beepscc9fc702013-12-02 12:45:38 -080059 def find_hosts(self, deps, acls):
60 """Finds valid hosts matching deps, acls.
61
62 @param deps: A list of dependencies to match.
63 @param acls: A list of acls, at least one of which must coincide with
64 an acl group the chosen host is in.
65
Prashanth Bb474fdf2014-04-03 16:05:38 -070066 @return: A set of matching hosts available.
beepscc9fc702013-12-02 12:45:38 -080067 """
68 hosts_available = self.host_objects.filter(invalid=0)
69 queries = [Q(labels__id=dep) for dep in deps]
70 queries += [Q(aclgroup__id__in=acls)]
71 for query in queries:
72 hosts_available = hosts_available.filter(query)
Prashanth Bb474fdf2014-04-03 16:05:38 -070073 return set(hosts_available)
beepscc9fc702013-12-02 12:45:38 -080074
75
76class AvailableHostQueryManager(BaseHostQueryManager):
77 """Query manager for requests on un-leased, un-locked hosts.
78 """
79
80 host_objects = models.Host.leased_objects
81
82
83# Request Handlers: Used in conjunction with requests in rdb_utils, these
84# handlers acquire hosts for a request and record the acquisition in
85# an response_map dictionary keyed on the request itself, with the host/hosts
86# as values.
87class BaseHostRequestHandler(object):
88 """Handler for requests related to hosts, leased or unleased.
89
90 This class is only capable of blindly returning host information.
91 """
92
93 def __init__(self):
94 self.host_query_manager = BaseHostQueryManager()
95 self.response_map = {}
96
97
Fang Deng52a23932014-11-20 18:30:22 -080098 def update_response_map(self, request, response, append=False):
beepscc9fc702013-12-02 12:45:38 -080099 """Record a response for a request.
100
101 The response_map only contains requests that were either satisfied, or
102 that ran into an exception. Often this translates to reserving hosts
103 against a request. If the rdb hit an exception processing a request, the
104 exception gets recorded in the map for the client to reraise.
105
106 @param response: A response for the request.
107 @param request: The request that has reserved these hosts.
Fang Deng52a23932014-11-20 18:30:22 -0800108 @param append: Boolean, whether to append new hosts in
109 |response| for existing request.
110 Will not append if existing response is
111 a list of exceptions.
beepscc9fc702013-12-02 12:45:38 -0800112
113 @raises RDBException: If an empty values is added to the map.
114 """
115 if not response:
116 raise rdb_utils.RDBException('response_map dict can only contain '
117 'valid responses. Request %s, response %s is invalid.' %
118 (request, response))
Fang Deng52a23932014-11-20 18:30:22 -0800119 exist_response = self.response_map.setdefault(request, [])
120 if exist_response and not append:
beepscc9fc702013-12-02 12:45:38 -0800121 raise rdb_utils.RDBException('Request %s already has response %s '
122 'the rdb cannot return multiple '
123 'responses for the same request.' %
124 (request, response))
Fang Deng52a23932014-11-20 18:30:22 -0800125 if exist_response and append and not isinstance(
126 exist_response[0], rdb_hosts.RDBHost):
127 # Do not append if existing response contains exception.
128 return
129 exist_response.extend(response)
beepscc9fc702013-12-02 12:45:38 -0800130
131
Prashanth B2d8047e2014-04-27 18:54:47 -0700132 def _check_response_map(self):
133 """Verify that we never give the same host to different requests.
134
135 @raises RDBException: If the same host is assigned to multiple requests.
136 """
137 unique_hosts = set([])
138 for request, response in self.response_map.iteritems():
139 # Each value in the response map can only either be a list of
140 # RDBHosts or a list of RDBExceptions, not a mix of both.
141 if isinstance(response[0], rdb_hosts.RDBHost):
142 if any([host in unique_hosts for host in response]):
143 raise rdb_utils.RDBException(
144 'Assigning the same host to multiple requests. New '
145 'hosts %s, request %s, response_map: %s' %
146 (response, request, self.response_map))
147 else:
148 unique_hosts = unique_hosts.union(response)
149
150
beepscc9fc702013-12-02 12:45:38 -0800151 def _record_exceptions(self, request, exceptions):
152 """Record a list of exceptions for a request.
153
154 @param request: The request for which the exceptions were hit.
155 @param exceptions: The exceptions hit while processing the request.
156 """
157 rdb_exceptions = [rdb_utils.RDBException(ex) for ex in exceptions]
158 self.update_response_map(request, rdb_exceptions)
159
160
161 def get_response(self):
162 """Convert all RDBServerHostWrapper objects to host info dictionaries.
163
164 @return: A dictionary mapping requests to a list of matching host_infos.
Prashanth B2d8047e2014-04-27 18:54:47 -0700165
166 @raises RDBException: If the same host is assigned to multiple requests.
beepscc9fc702013-12-02 12:45:38 -0800167 """
Prashanth B2d8047e2014-04-27 18:54:47 -0700168 self._check_response_map()
beepscc9fc702013-12-02 12:45:38 -0800169 for request, response in self.response_map.iteritems():
170 self.response_map[request] = [reply.wire_format()
171 for reply in response]
172 return self.response_map
173
174
175 def update_hosts(self, update_requests):
176 """Updates host tables with a payload.
177
178 @param update_requests: A list of update requests, as defined in
Prashanth B489b91d2014-03-15 12:17:16 -0700179 rdb_requests.UpdateHostRequest.
beepscc9fc702013-12-02 12:45:38 -0800180 """
181 # Last payload for a host_id wins in the case of conflicting requests.
182 unique_host_requests = {}
183 for request in update_requests:
184 if unique_host_requests.get(request.host_id):
185 unique_host_requests[request.host_id].update(request.payload)
186 else:
187 unique_host_requests[request.host_id] = request.payload
188
189 # Batch similar payloads so we can do them in one table scan.
190 similar_requests = {}
191 for host_id, payload in unique_host_requests.iteritems():
192 similar_requests.setdefault(payload, []).append(host_id)
193
194 # If fields of the update don't match columns in the database,
195 # record the exception in the response map. This also means later
196 # updates will get applied even if previous updates fail.
197 for payload, hosts in similar_requests.iteritems():
198 try:
199 response = self.host_query_manager.update_hosts(hosts, **payload)
200 except (django_exceptions.FieldError,
Prashanth Bb474fdf2014-04-03 16:05:38 -0700201 fields.FieldDoesNotExist, ValueError) as e:
beepscc9fc702013-12-02 12:45:38 -0800202 for host in hosts:
203 # Since update requests have a consistent hash this will map
204 # to the same key as the original request.
Prashanth B489b91d2014-03-15 12:17:16 -0700205 request = rdb_requests.UpdateHostRequest(
beepscc9fc702013-12-02 12:45:38 -0800206 host_id=host, payload=payload).get_request()
207 self._record_exceptions(request, [e])
208
209
210 def batch_get_hosts(self, host_requests):
211 """Get hosts matching the requests.
212
213 This method does not acquire the hosts, i.e it reserves hosts against
214 requests leaving their leased state untouched.
215
216 @param host_requests: A list of requests, as defined in
217 rdb_utils.BaseHostRequest.
218 """
219 host_ids = set([request.host_id for request in host_requests])
220 host_map = {}
221
222 # This list will not contain available hosts if executed using
223 # an AvailableHostQueryManager.
224 for host in self.host_query_manager.get_hosts(host_ids):
225 host_map[host.id] = host
226 for request in host_requests:
227 if request.host_id in host_map:
228 self.update_response_map(request, [host_map[request.host_id]])
229 else:
230 logging.warning('rdb could not get host for request: %s, it '
231 'is already leased or locked', request)
232
233
234class AvailableHostRequestHandler(BaseHostRequestHandler):
235 """Handler for requests related to available (unleased and unlocked) hosts.
236
237 This class is capable of acquiring or validating hosts for requests.
238 """
239
240
241 def __init__(self):
242 self.host_query_manager = AvailableHostQueryManager()
Prashanth B2d8047e2014-04-27 18:54:47 -0700243 self.cache = rdb_cache_manager.RDBHostCacheManager()
beepscc9fc702013-12-02 12:45:38 -0800244 self.response_map = {}
Prashanth B2d8047e2014-04-27 18:54:47 -0700245 self.unsatisfied_requests = 0
246 self.leased_hosts_count = 0
Fang Deng52a23932014-11-20 18:30:22 -0800247 self.request_accountant = None
beepscc9fc702013-12-02 12:45:38 -0800248
249
Prashanth B2d8047e2014-04-27 18:54:47 -0700250 @_timer.decorate
beepscc9fc702013-12-02 12:45:38 -0800251 def lease_hosts(self, hosts):
Prashanth Bb474fdf2014-04-03 16:05:38 -0700252 """Leases a list of hosts.
beepscc9fc702013-12-02 12:45:38 -0800253
Prashanth Bb474fdf2014-04-03 16:05:38 -0700254 @param hosts: A list of RDBServerHostWrapper instances to lease.
255
256 @return: The list of RDBServerHostWrappers that were successfully
257 leased.
beepscc9fc702013-12-02 12:45:38 -0800258 """
Prashanth Bb474fdf2014-04-03 16:05:38 -0700259 #TODO(beeps): crbug.com/353183.
260 unleased_hosts = set(hosts)
261 leased_hosts = set([])
262 for host in unleased_hosts:
263 try:
264 host.lease()
265 except rdb_utils.RDBException as e:
266 logging.error('Unable to lease host %s: %s', host.hostname, e)
267 else:
268 leased_hosts.add(host)
269 return list(leased_hosts)
270
271
272 @classmethod
273 def valid_host_assignment(cls, request, host):
274 """Check if a host, request pairing is valid.
275
276 @param request: The request to match against the host.
277 @param host: An RDBServerHostWrapper instance.
278
279 @return: True if the host, request assignment is valid.
280
281 @raises RDBException: If the request already has another host_ids
282 associated with it.
283 """
284 if request.host_id and request.host_id != host.id:
285 raise rdb_utils.RDBException(
286 'Cannot assign a different host for request: %s, it '
287 'already has one: %s ' % (request, host.id))
288
289 # Getting all labels and acls might result in large queries, so
290 # bail early if the host is already leased.
291 if host.leased:
292 return False
293 # If a host is invalid it must be a one time host added to the
294 # afe specifically for this purpose, so it doesn't require acl checking.
295 acl_match = (request.acls.intersection(host.acls) or host.invalid)
296 label_match = (request.deps.intersection(host.labels) == request.deps)
297 return acl_match and label_match
beepscc9fc702013-12-02 12:45:38 -0800298
299
Fang Denga9bc9592015-01-27 17:09:57 -0800300 @classmethod
301 def _sort_hosts_by_preferred_deps(cls, hosts, preferred_deps):
302 """Sort hosts in the order of how many preferred deps it has.
303
304 This allows rdb always choose the hosts with the most preferred deps
305 for a request. One important use case is including cros-version as
306 a preferred dependence. By choosing a host with the same cros-version,
307 we can save the time on provisioning it. Note this is not guaranteed
308 if preferred_deps contains other labels as well.
309
310 @param hosts: A list of hosts to sort.
311 @param preferred_deps: A list of deps that are preferred.
312
313 @return: A list of sorted hosts.
314
315 """
316 hosts = sorted(
317 hosts,
318 key=lambda host: len(set(preferred_deps) & set(host.labels)),
319 reverse=True)
320 return hosts
321
322
Prashanth B2d8047e2014-04-27 18:54:47 -0700323 @rdb_cache_manager.memoize_hosts
Fang Deng52a23932014-11-20 18:30:22 -0800324 def _acquire_hosts(self, request, hosts_required, is_acquire_min_duts=False,
325 **kwargs):
Prashanth B2d8047e2014-04-27 18:54:47 -0700326 """Acquire hosts for a group of similar requests.
327
328 Find and acquire hosts that can satisfy a group of requests.
329 1. If the caching decorator doesn't pass in a list of matching hosts
330 via the MEMOIZE_KEY this method will directly check the database for
331 matching hosts.
332 2. If all matching hosts are not leased for this request, the remaining
333 hosts are returned to the caching decorator, to place in the cache.
334
335 @param hosts_required: Number of hosts required to satisfy request.
336 @param request: The request for hosts.
Fang Deng52a23932014-11-20 18:30:22 -0800337 @param is_acquire_min_duts: Boolean. Indicate whether this is to
338 acquire minimum required duts, only used
339 for stats purpose.
Prashanth B2d8047e2014-04-27 18:54:47 -0700340
341 @return: The list of excess matching hosts.
342 """
343 hosts = kwargs.get(rdb_cache_manager.MEMOIZE_KEY, [])
344 if not hosts:
345 hosts = self.host_query_manager.find_hosts(
346 request.deps, request.acls)
347
348 # <-----[:attempt_lease_hosts](evicted)--------> <-(returned, cached)->
349 # | -leased_hosts- | -stale cached hosts- | -unleased matching- |
350 # --used this request---used by earlier request----------unused--------
Fang Denga9bc9592015-01-27 17:09:57 -0800351 hosts = self._sort_hosts_by_preferred_deps(
352 hosts, request.preferred_deps)
Prashanth B2d8047e2014-04-27 18:54:47 -0700353 attempt_lease_hosts = min(len(hosts), hosts_required)
354 leased_host_count = 0
355 if attempt_lease_hosts:
356 leased_hosts = self.lease_hosts(hosts[:attempt_lease_hosts])
357 if leased_hosts:
Fang Deng52a23932014-11-20 18:30:22 -0800358 self.update_response_map(request, leased_hosts, append=True)
Prashanth B2d8047e2014-04-27 18:54:47 -0700359
360 # [:attempt_leased_hosts] - leased_hosts will include hosts that
361 # failed leasing, most likely because they're already leased, so
362 # don't cache them again.
363 leased_host_count = len(leased_hosts)
364 failed_leasing = attempt_lease_hosts - leased_host_count
365 if failed_leasing > 0:
366 # For the sake of simplicity this calculation assumes that
367 # leasing only fails if there's a stale cached host already
368 # leased by a previous request, ergo, we can only get here
369 # through a cache hit.
370 line_length = len(hosts)
371 self.cache.stale_entries.append(
Prashanth B86934c82014-05-09 11:17:20 -0700372 (float(failed_leasing)/line_length) * 100)
Prashanth B2d8047e2014-04-27 18:54:47 -0700373 self.leased_hosts_count += leased_host_count
Fang Deng52a23932014-11-20 18:30:22 -0800374 if is_acquire_min_duts:
375 self.request_accountant.record_acquire_min_duts(
376 request, hosts_required, leased_host_count)
Prashanth B2d8047e2014-04-27 18:54:47 -0700377 self.unsatisfied_requests += max(hosts_required - leased_host_count, 0)
378 # Cache the unleased matching hosts against the request.
379 return hosts[attempt_lease_hosts:]
380
381
beepscc9fc702013-12-02 12:45:38 -0800382 @_timer.decorate
383 def batch_acquire_hosts(self, host_requests):
384 """Acquire hosts for a list of requests.
385
Prashanth B2d8047e2014-04-27 18:54:47 -0700386 The act of acquisition involves finding and leasing a set of hosts
387 that match the parameters of a request. Each acquired host is added
388 to the response_map dictionary as an RDBServerHostWrapper.
beepscc9fc702013-12-02 12:45:38 -0800389
390 @param host_requests: A list of requests to acquire hosts.
391 """
Prashanth B2d8047e2014-04-27 18:54:47 -0700392 distinct_requests = 0
393
394 logging.debug('Processing %s host acquisition requests',
395 len(host_requests))
Fang Deng52a23932014-11-20 18:30:22 -0800396
397 self.request_accountant = rdb_utils.RequestAccountant(host_requests)
398 # First pass tries to satisfy min_duts for each suite.
399 for request in self.request_accountant.requests:
400 to_acquire = self.request_accountant.get_min_duts(request)
401 if to_acquire > 0:
402 self._acquire_hosts(request, to_acquire,
403 is_acquire_min_duts=True)
Prashanth B2d8047e2014-04-27 18:54:47 -0700404 distinct_requests += 1
405
Fang Deng52a23932014-11-20 18:30:22 -0800406 # Second pass tries to allocate duts to the rest unsatisfied requests.
407 for request in self.request_accountant.requests:
408 to_acquire = self.request_accountant.get_duts(request)
409 if to_acquire > 0:
410 self._acquire_hosts(request, to_acquire,
411 is_acquire_min_duts=False)
412
Prashanth B2d8047e2014-04-27 18:54:47 -0700413 self.cache.record_stats()
414 logging.debug('Host acquisition stats: distinct requests: %s, leased '
415 'hosts: %s, unsatisfied requests: %s', distinct_requests,
416 self.leased_hosts_count, self.unsatisfied_requests)
Gabe Black1e1c41b2015-02-04 23:55:15 -0800417 autotest_stats.Gauge(rdb_utils.RDB_STATS_KEY).send(
418 'leased_hosts', self.leased_hosts_count)
419 autotest_stats.Gauge(rdb_utils.RDB_STATS_KEY).send(
420 'unsatisfied_requests', self.unsatisfied_requests)
beepscc9fc702013-12-02 12:45:38 -0800421
422
423 @_timer.decorate
424 def batch_validate_hosts(self, requests):
425 """Validate requests with hosts.
426
427 Reserve all hosts, check each one for validity and discard invalid
428 request-host pairings. Lease the remaining hsots.
429
430 @param requests: A list of requests to validate.
Prashanth Bb474fdf2014-04-03 16:05:38 -0700431
432 @raises RDBException: If multiple hosts or the wrong host is returned
433 for a response.
beepscc9fc702013-12-02 12:45:38 -0800434 """
Prashanth Bb474fdf2014-04-03 16:05:38 -0700435 # The following cases are possible for frontend requests:
436 # 1. Multiple requests for 1 host, with different acls/deps/priority:
437 # These form distinct requests because they hash differently.
438 # The response map will contain entries like: {r1: h1, r2: h1}
439 # after the batch_get_hosts call. There are 2 sub-cases:
440 # a. Same deps/acls, different priority:
441 # Since we sort the requests based on priority, the
442 # higher priority request r1, will lease h1. The
443 # validation of r2, h1 will fail because of the r1 lease.
444 # b. Different deps/acls, only one of which matches the host:
445 # The matching request will lease h1. The other host
446 # pairing will get dropped from the response map.
447 # 2. Multiple requests with the same acls/deps/priority and 1 host:
448 # These all have the same request hash, so the response map will
449 # contain: {r: h}, regardless of the number of r's. If this is not
450 # a valid host assignment it will get dropped from the response.
beepscc9fc702013-12-02 12:45:38 -0800451 self.batch_get_hosts(set(requests))
Prashanth Bb474fdf2014-04-03 16:05:38 -0700452 for request in sorted(self.response_map.keys(),
453 key=lambda request: request.priority, reverse=True):
beepscc9fc702013-12-02 12:45:38 -0800454 hosts = self.response_map[request]
455 if len(hosts) > 1:
456 raise rdb_utils.RDBException('Got multiple hosts for a single '
457 'request. Hosts: %s, request %s.' % (hosts, request))
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800458 # Job-shard is 1:1 mapping. Because a job can only belongs
459 # to one shard, or belongs to master, we disallow frontend job
460 # that spans hosts on and off shards or across multiple shards,
461 # which would otherwise break the 1:1 mapping.
462 # As such, on master, if a request asks for multiple hosts and
463 # if any host is found on shard, we assume other requested hosts
464 # would also be on the same shard. We can safely drop this request.
465 ignore_request = _is_master and any(
466 [host.shard_id for host in hosts])
467 if (not ignore_request and
468 (self.valid_host_assignment(request, hosts[0]) and
469 self.lease_hosts(hosts))):
Prashanth Bb474fdf2014-04-03 16:05:38 -0700470 continue
471 del self.response_map[request]
472 logging.warning('Request %s was not able to lease host %s',
473 request, hosts[0])
beepscc9fc702013-12-02 12:45:38 -0800474
475
476# Request dispatchers: Create the appropriate request handler, send a list
477# of requests to one of its methods. The corresponding request handler in
478# rdb_lib must understand how to match each request with a response from a
479# dispatcher, the easiest way to achieve this is to returned the response_map
480# attribute of the request handler, after making the appropriate requests.
481def get_hosts(host_requests):
482 """Get host information about the requested hosts.
483
484 @param host_requests: A list of requests as defined in BaseHostRequest.
485 @return: A dictionary mapping each request to a list of hosts.
486 """
487 rdb_handler = BaseHostRequestHandler()
488 rdb_handler.batch_get_hosts(host_requests)
489 return rdb_handler.get_response()
490
491
492def update_hosts(update_requests):
493 """Update hosts.
494
495 @param update_requests: A list of updates to host tables
496 as defined in UpdateHostRequest.
497 """
498 rdb_handler = BaseHostRequestHandler()
499 rdb_handler.update_hosts(update_requests)
500 return rdb_handler.get_response()
501
502
503def rdb_host_request_dispatcher(host_requests):
504 """Dispatcher for all host acquisition queries.
505
506 @param host_requests: A list of requests for acquiring hosts, as defined in
507 AcquireHostRequest.
508 @return: A dictionary mapping each request to a list of hosts, or
509 an empty list if none could satisfy the request. Eg:
510 {AcquireHostRequest.template: [host_info_dictionaries]}
511 """
512 validation_requests = []
513 require_hosts_requests = []
514
515 # Validation requests are made by a job scheduled against a specific host
516 # specific host (eg: through the frontend) and only require the rdb to
517 # match the parameters of the host against the request. Acquisition
518 # requests are made by jobs that need hosts (eg: suites) and the rdb needs
519 # to find hosts matching the parameters of the request.
520 for request in host_requests:
521 if request.host_id:
522 validation_requests.append(request)
523 else:
524 require_hosts_requests.append(request)
525
526 rdb_handler = AvailableHostRequestHandler()
527 rdb_handler.batch_validate_hosts(validation_requests)
528 rdb_handler.batch_acquire_hosts(require_hosts_requests)
529 return rdb_handler.get_response()