blob: 5130e93013a147aee58d08211cce903cfffcff31 [file] [log] [blame]
showardce38e0c2008-05-29 19:36:16 +00001#!/usr/bin/python
2
showard8fe93b52008-11-18 17:53:22 +00003import unittest, time, subprocess, os, StringIO, tempfile, datetime, shutil
showard6157c632009-07-06 20:19:31 +00004import logging
showardce38e0c2008-05-29 19:36:16 +00005import common
mbligh8bcd23a2009-02-03 19:14:06 +00006import MySQLdb
showard364fe862008-10-17 02:01:16 +00007from autotest_lib.frontend import setup_django_environment
showardb6d16622009-05-26 19:35:29 +00008from autotest_lib.frontend.afe import frontend_test_utils
jadmanskifb7cfb12008-07-09 14:13:21 +00009from autotest_lib.client.common_lib import global_config, host_protections
jadmanski3d161b02008-06-06 15:43:36 +000010from autotest_lib.client.common_lib.test_utils import mock
showard442e71e2008-10-06 10:05:20 +000011from autotest_lib.database import database_connection, migrate
showard21baa452008-10-21 00:08:39 +000012from autotest_lib.frontend import thread_local
showardb1e51872008-10-07 11:08:18 +000013from autotest_lib.frontend.afe import models
showard170873e2009-01-07 00:22:26 +000014from autotest_lib.scheduler import monitor_db, drone_manager, email_manager
showardd1ee1dd2009-01-07 21:33:08 +000015from autotest_lib.scheduler import scheduler_config
showardce38e0c2008-05-29 19:36:16 +000016
17_DEBUG = False
18
showarda3c58572009-03-12 20:36:59 +000019
showard170873e2009-01-07 00:22:26 +000020class DummyAgent(object):
showard8cc058f2009-09-08 16:26:33 +000021 started = False
showard170873e2009-01-07 00:22:26 +000022 _is_done = False
23 num_processes = 1
24 host_ids = []
25 queue_entry_ids = []
26
showard170873e2009-01-07 00:22:26 +000027
28 def tick(self):
showard8cc058f2009-09-08 16:26:33 +000029 self.started = True
showard170873e2009-01-07 00:22:26 +000030
31
32 def is_done(self):
33 return self._is_done
34
35
36 def set_done(self, done):
37 self._is_done = done
showard04c82c52008-05-29 19:38:12 +000038
showard56193bb2008-08-13 20:07:41 +000039
40class IsRow(mock.argument_comparator):
41 def __init__(self, row_id):
42 self.row_id = row_id
showardce38e0c2008-05-29 19:36:16 +000043
44
showard56193bb2008-08-13 20:07:41 +000045 def is_satisfied_by(self, parameter):
46 return list(parameter)[0] == self.row_id
47
48
49 def __str__(self):
50 return 'row with id %s' % self.row_id
51
52
showardd3dc1992009-04-22 21:01:40 +000053class IsAgentWithTask(mock.argument_comparator):
mbligh1ef218d2009-08-03 16:57:56 +000054 def __init__(self, task):
55 self._task = task
showardd3dc1992009-04-22 21:01:40 +000056
57
mbligh1ef218d2009-08-03 16:57:56 +000058 def is_satisfied_by(self, parameter):
59 if not isinstance(parameter, monitor_db.Agent):
60 return False
61 tasks = list(parameter.queue.queue)
62 if len(tasks) != 1:
63 return False
64 return tasks[0] == self._task
showardd3dc1992009-04-22 21:01:40 +000065
66
showard6b733412009-04-27 20:09:18 +000067def _set_host_and_qe_ids(agent_or_task, id_list=None):
68 if id_list is None:
69 id_list = []
70 agent_or_task.host_ids = agent_or_task.queue_entry_ids = id_list
71
72
showardb6d16622009-05-26 19:35:29 +000073class BaseSchedulerTest(unittest.TestCase,
74 frontend_test_utils.FrontendTestMixin):
showard50c0e712008-09-22 16:20:37 +000075 _config_section = 'AUTOTEST_WEB'
showardce38e0c2008-05-29 19:36:16 +000076
jadmanski0afbb632008-06-06 21:10:57 +000077 def _do_query(self, sql):
showardb1e51872008-10-07 11:08:18 +000078 self._database.execute(sql)
showardce38e0c2008-05-29 19:36:16 +000079
80
showardb6d16622009-05-26 19:35:29 +000081 def _set_monitor_stubs(self):
82 # Clear the instance cache as this is a brand new database.
83 monitor_db.DBObject._clear_instance_cache()
showardce38e0c2008-05-29 19:36:16 +000084
showardb1e51872008-10-07 11:08:18 +000085 self._database = (
86 database_connection.DatabaseConnection.get_test_database(
87 self._test_db_file))
88 self._database.connect()
89 self._database.debug = _DEBUG
showardce38e0c2008-05-29 19:36:16 +000090
showardb1e51872008-10-07 11:08:18 +000091 monitor_db._db = self._database
showard170873e2009-01-07 00:22:26 +000092 monitor_db._drone_manager._results_dir = '/test/path'
93 monitor_db._drone_manager._temporary_directory = '/test/path/tmp'
showard56193bb2008-08-13 20:07:41 +000094
95
showard56193bb2008-08-13 20:07:41 +000096 def setUp(self):
showardb6d16622009-05-26 19:35:29 +000097 self._frontend_common_setup()
showard56193bb2008-08-13 20:07:41 +000098 self._set_monitor_stubs()
99 self._dispatcher = monitor_db.Dispatcher()
showardce38e0c2008-05-29 19:36:16 +0000100
101
showard56193bb2008-08-13 20:07:41 +0000102 def tearDown(self):
showardb6d16622009-05-26 19:35:29 +0000103 self._database.disconnect()
104 self._frontend_common_teardown()
showardce38e0c2008-05-29 19:36:16 +0000105
106
showard56193bb2008-08-13 20:07:41 +0000107 def _update_hqe(self, set, where=''):
108 query = 'UPDATE host_queue_entries SET ' + set
109 if where:
110 query += ' WHERE ' + where
111 self._do_query(query)
112
113
showarda3c58572009-03-12 20:36:59 +0000114class DBObjectTest(BaseSchedulerTest):
115 # It may seem odd to subclass BaseSchedulerTest for this but it saves us
116 # duplicating some setup work for what we want to test.
117
118
119 def test_compare_fields_in_row(self):
120 host = monitor_db.Host(id=1)
121 fields = list(host._fields)
122 row_data = [getattr(host, fieldname) for fieldname in fields]
123 self.assertEqual({}, host._compare_fields_in_row(row_data))
124 row_data[fields.index('hostname')] = 'spam'
125 self.assertEqual({'hostname': ('host1', 'spam')},
126 host._compare_fields_in_row(row_data))
127 row_data[fields.index('id')] = 23
128 self.assertEqual({'hostname': ('host1', 'spam'), 'id': (1, 23)},
129 host._compare_fields_in_row(row_data))
130
131
showarddae680a2009-10-12 20:26:43 +0000132 def test_compare_fields_in_row_datetime_ignores_microseconds(self):
133 datetime_with_us = datetime.datetime(2009, 10, 07, 12, 34, 56, 7890)
134 datetime_without_us = datetime.datetime(2009, 10, 07, 12, 34, 56, 0)
135 class TestTable(monitor_db.DBObject):
136 _table_name = 'test_table'
137 _fields = ('id', 'test_datetime')
138 tt = TestTable(row=[1, datetime_without_us])
139 self.assertEqual({}, tt._compare_fields_in_row([1, datetime_with_us]))
140
141
showarda3c58572009-03-12 20:36:59 +0000142 def test_always_query(self):
143 host_a = monitor_db.Host(id=2)
144 self.assertEqual(host_a.hostname, 'host2')
145 self._do_query('UPDATE hosts SET hostname="host2-updated" WHERE id=2')
146 host_b = monitor_db.Host(id=2, always_query=True)
147 self.assert_(host_a is host_b, 'Cached instance not returned.')
148 self.assertEqual(host_a.hostname, 'host2-updated',
149 'Database was not re-queried')
150
151 # If either of these are called, a query was made when it shouldn't be.
152 host_a._compare_fields_in_row = lambda _: self.fail('eek! a query!')
showard12f3e322009-05-13 21:27:42 +0000153 host_a._update_fields_from_row = host_a._compare_fields_in_row
showarda3c58572009-03-12 20:36:59 +0000154 host_c = monitor_db.Host(id=2, always_query=False)
155 self.assert_(host_a is host_c, 'Cached instance not returned')
156
157
158 def test_delete(self):
159 host = monitor_db.Host(id=3)
160 host.delete()
161 host = self.assertRaises(monitor_db.DBError, monitor_db.Host, id=3,
162 always_query=False)
163 host = self.assertRaises(monitor_db.DBError, monitor_db.Host, id=3,
164 always_query=True)
165
showard76e29d12009-04-15 21:53:10 +0000166 def test_save(self):
167 # Dummy Job to avoid creating a one in the HostQueueEntry __init__.
168 class MockJob(object):
169 def __init__(self, id):
170 pass
171 def tag(self):
172 return 'MockJob'
173 self.god.stub_with(monitor_db, 'Job', MockJob)
174 hqe = monitor_db.HostQueueEntry(
175 new_record=True,
showard12f3e322009-05-13 21:27:42 +0000176 row=[0, 1, 2, 'Queued', None, 0, 0, 0, '.', None, False, None])
showard76e29d12009-04-15 21:53:10 +0000177 hqe.save()
178 new_id = hqe.id
179 # Force a re-query and verify that the correct data was stored.
180 monitor_db.DBObject._clear_instance_cache()
181 hqe = monitor_db.HostQueueEntry(id=new_id)
182 self.assertEqual(hqe.id, new_id)
183 self.assertEqual(hqe.job_id, 1)
184 self.assertEqual(hqe.host_id, 2)
185 self.assertEqual(hqe.status, 'Queued')
186 self.assertEqual(hqe.meta_host, None)
187 self.assertEqual(hqe.active, False)
188 self.assertEqual(hqe.complete, False)
189 self.assertEqual(hqe.deleted, False)
190 self.assertEqual(hqe.execution_subdir, '.')
191 self.assertEqual(hqe.atomic_group_id, None)
showard12f3e322009-05-13 21:27:42 +0000192 self.assertEqual(hqe.started_on, None)
showarda3c58572009-03-12 20:36:59 +0000193
194
showardb2e2c322008-10-14 17:33:55 +0000195class DispatcherSchedulingTest(BaseSchedulerTest):
showard56193bb2008-08-13 20:07:41 +0000196 _jobs_scheduled = []
197
showard89f84db2009-03-12 20:39:13 +0000198
199 def tearDown(self):
200 super(DispatcherSchedulingTest, self).tearDown()
201
202
showard56193bb2008-08-13 20:07:41 +0000203 def _set_monitor_stubs(self):
204 super(DispatcherSchedulingTest, self)._set_monitor_stubs()
showard89f84db2009-03-12 20:39:13 +0000205
showard8cc058f2009-09-08 16:26:33 +0000206 def hqe__do_schedule_pre_job_tasks_stub(queue_entry):
207 """Called by HostQueueEntry.run()."""
showard77182562009-06-10 00:16:05 +0000208 self._record_job_scheduled(queue_entry.job.id, queue_entry.host.id)
showard89f84db2009-03-12 20:39:13 +0000209 queue_entry.set_status('Starting')
showard89f84db2009-03-12 20:39:13 +0000210
showard8cc058f2009-09-08 16:26:33 +0000211 self.god.stub_with(monitor_db.HostQueueEntry,
212 '_do_schedule_pre_job_tasks',
213 hqe__do_schedule_pre_job_tasks_stub)
showard89f84db2009-03-12 20:39:13 +0000214
215 def hqe_queue_log_record_stub(self, log_line):
216 """No-Op to avoid calls down to the _drone_manager during tests."""
217
218 self.god.stub_with(monitor_db.HostQueueEntry, 'queue_log_record',
219 hqe_queue_log_record_stub)
showard56193bb2008-08-13 20:07:41 +0000220
221
222 def _record_job_scheduled(self, job_id, host_id):
223 record = (job_id, host_id)
224 self.assert_(record not in self._jobs_scheduled,
225 'Job %d scheduled on host %d twice' %
226 (job_id, host_id))
227 self._jobs_scheduled.append(record)
228
229
230 def _assert_job_scheduled_on(self, job_id, host_id):
231 record = (job_id, host_id)
232 self.assert_(record in self._jobs_scheduled,
233 'Job %d not scheduled on host %d as expected\n'
234 'Jobs scheduled: %s' %
235 (job_id, host_id, self._jobs_scheduled))
236 self._jobs_scheduled.remove(record)
237
238
showard89f84db2009-03-12 20:39:13 +0000239 def _assert_job_scheduled_on_number_of(self, job_id, host_ids, number):
240 """Assert job was scheduled on exactly number hosts out of a set."""
241 found = []
242 for host_id in host_ids:
243 record = (job_id, host_id)
244 if record in self._jobs_scheduled:
245 found.append(record)
246 self._jobs_scheduled.remove(record)
247 if len(found) < number:
248 self.fail('Job %d scheduled on fewer than %d hosts in %s.\n'
249 'Jobs scheduled: %s' % (job_id, number, host_ids, found))
250 elif len(found) > number:
251 self.fail('Job %d scheduled on more than %d hosts in %s.\n'
252 'Jobs scheduled: %s' % (job_id, number, host_ids, found))
253
254
showard56193bb2008-08-13 20:07:41 +0000255 def _check_for_extra_schedulings(self):
256 if len(self._jobs_scheduled) != 0:
257 self.fail('Extra jobs scheduled: ' +
258 str(self._jobs_scheduled))
259
260
jadmanski0afbb632008-06-06 21:10:57 +0000261 def _convert_jobs_to_metahosts(self, *job_ids):
262 sql_tuple = '(' + ','.join(str(i) for i in job_ids) + ')'
263 self._do_query('UPDATE host_queue_entries SET '
264 'meta_host=host_id, host_id=NULL '
265 'WHERE job_id IN ' + sql_tuple)
showardce38e0c2008-05-29 19:36:16 +0000266
267
jadmanski0afbb632008-06-06 21:10:57 +0000268 def _lock_host(self, host_id):
269 self._do_query('UPDATE hosts SET locked=1 WHERE id=' +
270 str(host_id))
showardce38e0c2008-05-29 19:36:16 +0000271
272
jadmanski0afbb632008-06-06 21:10:57 +0000273 def setUp(self):
showard56193bb2008-08-13 20:07:41 +0000274 super(DispatcherSchedulingTest, self).setUp()
jadmanski0afbb632008-06-06 21:10:57 +0000275 self._jobs_scheduled = []
showardce38e0c2008-05-29 19:36:16 +0000276
277
jadmanski0afbb632008-06-06 21:10:57 +0000278 def _test_basic_scheduling_helper(self, use_metahosts):
279 'Basic nonmetahost scheduling'
280 self._create_job_simple([1], use_metahosts)
281 self._create_job_simple([2], use_metahosts)
282 self._dispatcher._schedule_new_jobs()
283 self._assert_job_scheduled_on(1, 1)
284 self._assert_job_scheduled_on(2, 2)
285 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000286
287
jadmanski0afbb632008-06-06 21:10:57 +0000288 def _test_priorities_helper(self, use_metahosts):
289 'Test prioritization ordering'
290 self._create_job_simple([1], use_metahosts)
291 self._create_job_simple([2], use_metahosts)
292 self._create_job_simple([1,2], use_metahosts)
293 self._create_job_simple([1], use_metahosts, priority=1)
294 self._dispatcher._schedule_new_jobs()
295 self._assert_job_scheduled_on(4, 1) # higher priority
296 self._assert_job_scheduled_on(2, 2) # earlier job over later
297 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000298
299
jadmanski0afbb632008-06-06 21:10:57 +0000300 def _test_hosts_ready_helper(self, use_metahosts):
301 """
302 Only hosts that are status=Ready, unlocked and not invalid get
303 scheduled.
304 """
305 self._create_job_simple([1], use_metahosts)
306 self._do_query('UPDATE hosts SET status="Running" WHERE id=1')
307 self._dispatcher._schedule_new_jobs()
308 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000309
jadmanski0afbb632008-06-06 21:10:57 +0000310 self._do_query('UPDATE hosts SET status="Ready", locked=1 '
311 'WHERE id=1')
312 self._dispatcher._schedule_new_jobs()
313 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000314
jadmanski0afbb632008-06-06 21:10:57 +0000315 self._do_query('UPDATE hosts SET locked=0, invalid=1 '
316 'WHERE id=1')
317 self._dispatcher._schedule_new_jobs()
showard5df2b192008-07-03 19:51:57 +0000318 if not use_metahosts:
319 self._assert_job_scheduled_on(1, 1)
jadmanski0afbb632008-06-06 21:10:57 +0000320 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000321
322
jadmanski0afbb632008-06-06 21:10:57 +0000323 def _test_hosts_idle_helper(self, use_metahosts):
324 'Only idle hosts get scheduled'
showard2bab8f42008-11-12 18:15:22 +0000325 self._create_job(hosts=[1], active=True)
jadmanski0afbb632008-06-06 21:10:57 +0000326 self._create_job_simple([1], use_metahosts)
327 self._dispatcher._schedule_new_jobs()
328 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000329
330
showard63a34772008-08-18 19:32:50 +0000331 def _test_obey_ACLs_helper(self, use_metahosts):
332 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id=1')
333 self._create_job_simple([1], use_metahosts)
334 self._dispatcher._schedule_new_jobs()
335 self._check_for_extra_schedulings()
336
337
jadmanski0afbb632008-06-06 21:10:57 +0000338 def test_basic_scheduling(self):
339 self._test_basic_scheduling_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000340
341
jadmanski0afbb632008-06-06 21:10:57 +0000342 def test_priorities(self):
343 self._test_priorities_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000344
345
jadmanski0afbb632008-06-06 21:10:57 +0000346 def test_hosts_ready(self):
347 self._test_hosts_ready_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000348
349
jadmanski0afbb632008-06-06 21:10:57 +0000350 def test_hosts_idle(self):
351 self._test_hosts_idle_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000352
353
showard63a34772008-08-18 19:32:50 +0000354 def test_obey_ACLs(self):
355 self._test_obey_ACLs_helper(False)
356
357
showard2924b0a2009-06-18 23:16:15 +0000358 def test_one_time_hosts_ignore_ACLs(self):
359 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id=1')
360 self._do_query('UPDATE hosts SET invalid=1 WHERE id=1')
361 self._create_job_simple([1])
362 self._dispatcher._schedule_new_jobs()
363 self._assert_job_scheduled_on(1, 1)
364 self._check_for_extra_schedulings()
365
366
showard63a34772008-08-18 19:32:50 +0000367 def test_non_metahost_on_invalid_host(self):
368 """
369 Non-metahost entries can get scheduled on invalid hosts (this is how
370 one-time hosts work).
371 """
372 self._do_query('UPDATE hosts SET invalid=1')
373 self._test_basic_scheduling_helper(False)
374
375
jadmanski0afbb632008-06-06 21:10:57 +0000376 def test_metahost_scheduling(self):
showard63a34772008-08-18 19:32:50 +0000377 """
378 Basic metahost scheduling
379 """
jadmanski0afbb632008-06-06 21:10:57 +0000380 self._test_basic_scheduling_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000381
382
jadmanski0afbb632008-06-06 21:10:57 +0000383 def test_metahost_priorities(self):
384 self._test_priorities_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000385
386
jadmanski0afbb632008-06-06 21:10:57 +0000387 def test_metahost_hosts_ready(self):
388 self._test_hosts_ready_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000389
390
jadmanski0afbb632008-06-06 21:10:57 +0000391 def test_metahost_hosts_idle(self):
392 self._test_hosts_idle_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000393
394
showard63a34772008-08-18 19:32:50 +0000395 def test_metahost_obey_ACLs(self):
396 self._test_obey_ACLs_helper(True)
397
398
showard89f84db2009-03-12 20:39:13 +0000399 def _setup_test_only_if_needed_labels(self):
showardade14e22009-01-26 22:38:32 +0000400 # apply only_if_needed label3 to host1
showard89f84db2009-03-12 20:39:13 +0000401 models.Host.smart_get('host1').labels.add(self.label3)
402 return self._create_job_simple([1], use_metahost=True)
showardade14e22009-01-26 22:38:32 +0000403
showard89f84db2009-03-12 20:39:13 +0000404
405 def test_only_if_needed_labels_avoids_host(self):
406 job = self._setup_test_only_if_needed_labels()
showardade14e22009-01-26 22:38:32 +0000407 # if the job doesn't depend on label3, there should be no scheduling
408 self._dispatcher._schedule_new_jobs()
409 self._check_for_extra_schedulings()
410
showard89f84db2009-03-12 20:39:13 +0000411
412 def test_only_if_needed_labels_schedules(self):
413 job = self._setup_test_only_if_needed_labels()
414 job.dependency_labels.add(self.label3)
showardade14e22009-01-26 22:38:32 +0000415 self._dispatcher._schedule_new_jobs()
416 self._assert_job_scheduled_on(1, 1)
417 self._check_for_extra_schedulings()
418
showard89f84db2009-03-12 20:39:13 +0000419
420 def test_only_if_needed_labels_via_metahost(self):
421 job = self._setup_test_only_if_needed_labels()
422 job.dependency_labels.add(self.label3)
showardade14e22009-01-26 22:38:32 +0000423 # should also work if the metahost is the only_if_needed label
424 self._do_query('DELETE FROM jobs_dependency_labels')
425 self._create_job(metahosts=[3])
426 self._dispatcher._schedule_new_jobs()
427 self._assert_job_scheduled_on(2, 1)
428 self._check_for_extra_schedulings()
showard989f25d2008-10-01 11:38:11 +0000429
430
jadmanski0afbb632008-06-06 21:10:57 +0000431 def test_nonmetahost_over_metahost(self):
432 """
433 Non-metahost entries should take priority over metahost entries
434 for the same host
435 """
436 self._create_job(metahosts=[1])
437 self._create_job(hosts=[1])
438 self._dispatcher._schedule_new_jobs()
439 self._assert_job_scheduled_on(2, 1)
440 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000441
442
jadmanski0afbb632008-06-06 21:10:57 +0000443 def test_metahosts_obey_blocks(self):
444 """
445 Metahosts can't get scheduled on hosts already scheduled for
446 that job.
447 """
448 self._create_job(metahosts=[1], hosts=[1])
449 # make the nonmetahost entry complete, so the metahost can try
450 # to get scheduled
showard56193bb2008-08-13 20:07:41 +0000451 self._update_hqe(set='complete = 1', where='host_id=1')
jadmanski0afbb632008-06-06 21:10:57 +0000452 self._dispatcher._schedule_new_jobs()
453 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000454
455
showard89f84db2009-03-12 20:39:13 +0000456 # TODO(gps): These should probably live in their own TestCase class
457 # specific to testing HostScheduler methods directly. It was convenient
458 # to put it here for now to share existing test environment setup code.
459 def test_HostScheduler_check_atomic_group_labels(self):
460 normal_job = self._create_job(metahosts=[0])
461 atomic_job = self._create_job(atomic_group=1)
462 # Indirectly initialize the internal state of the host scheduler.
463 self._dispatcher._refresh_pending_queue_entries()
464
showard6157c632009-07-06 20:19:31 +0000465 atomic_hqe = monitor_db.HostQueueEntry.fetch(where='job_id=%d' %
showard8cc058f2009-09-08 16:26:33 +0000466 atomic_job.id)[0]
showard6157c632009-07-06 20:19:31 +0000467 normal_hqe = monitor_db.HostQueueEntry.fetch(where='job_id=%d' %
showard8cc058f2009-09-08 16:26:33 +0000468 normal_job.id)[0]
showard89f84db2009-03-12 20:39:13 +0000469
470 host_scheduler = self._dispatcher._host_scheduler
471 self.assertTrue(host_scheduler._check_atomic_group_labels(
472 [self.label4.id], atomic_hqe))
473 self.assertFalse(host_scheduler._check_atomic_group_labels(
474 [self.label4.id], normal_hqe))
475 self.assertFalse(host_scheduler._check_atomic_group_labels(
476 [self.label5.id, self.label6.id, self.label7.id], normal_hqe))
477 self.assertTrue(host_scheduler._check_atomic_group_labels(
478 [self.label4.id, self.label6.id], atomic_hqe))
showard6157c632009-07-06 20:19:31 +0000479 self.assertTrue(host_scheduler._check_atomic_group_labels(
480 [self.label4.id, self.label5.id],
481 atomic_hqe))
showard89f84db2009-03-12 20:39:13 +0000482
483
484 def test_HostScheduler_get_host_atomic_group_id(self):
showard6157c632009-07-06 20:19:31 +0000485 job = self._create_job(metahosts=[self.label6.id])
486 queue_entry = monitor_db.HostQueueEntry.fetch(
showard8cc058f2009-09-08 16:26:33 +0000487 where='job_id=%d' % job.id)[0]
showard89f84db2009-03-12 20:39:13 +0000488 # Indirectly initialize the internal state of the host scheduler.
489 self._dispatcher._refresh_pending_queue_entries()
490
491 # Test the host scheduler
492 host_scheduler = self._dispatcher._host_scheduler
showard6157c632009-07-06 20:19:31 +0000493
494 # Two labels each in a different atomic group. This should log an
495 # error and continue.
496 orig_logging_error = logging.error
497 def mock_logging_error(message, *args):
498 mock_logging_error._num_calls += 1
499 # Test the logging call itself, we just wrapped it to count it.
500 orig_logging_error(message, *args)
501 mock_logging_error._num_calls = 0
502 self.god.stub_with(logging, 'error', mock_logging_error)
503 self.assertNotEquals(None, host_scheduler._get_host_atomic_group_id(
504 [self.label4.id, self.label8.id], queue_entry))
505 self.assertTrue(mock_logging_error._num_calls > 0)
506 self.god.unstub(logging, 'error')
507
508 # Two labels both in the same atomic group, this should not raise an
509 # error, it will merely cause the job to schedule on the intersection.
510 self.assertEquals(1, host_scheduler._get_host_atomic_group_id(
511 [self.label4.id, self.label5.id]))
512
513 self.assertEquals(None, host_scheduler._get_host_atomic_group_id([]))
514 self.assertEquals(None, host_scheduler._get_host_atomic_group_id(
showard89f84db2009-03-12 20:39:13 +0000515 [self.label3.id, self.label7.id, self.label6.id]))
showard6157c632009-07-06 20:19:31 +0000516 self.assertEquals(1, host_scheduler._get_host_atomic_group_id(
showard89f84db2009-03-12 20:39:13 +0000517 [self.label4.id, self.label7.id, self.label6.id]))
showard6157c632009-07-06 20:19:31 +0000518 self.assertEquals(1, host_scheduler._get_host_atomic_group_id(
showard89f84db2009-03-12 20:39:13 +0000519 [self.label7.id, self.label5.id]))
520
521
522 def test_atomic_group_hosts_blocked_from_non_atomic_jobs(self):
523 # Create a job scheduled to run on label6.
524 self._create_job(metahosts=[self.label6.id])
525 self._dispatcher._schedule_new_jobs()
526 # label6 only has hosts that are in atomic groups associated with it,
527 # there should be no scheduling.
528 self._check_for_extra_schedulings()
529
530
531 def test_atomic_group_hosts_blocked_from_non_atomic_jobs_explicit(self):
532 # Create a job scheduled to run on label5. This is an atomic group
533 # label but this job does not request atomic group scheduling.
534 self._create_job(metahosts=[self.label5.id])
535 self._dispatcher._schedule_new_jobs()
536 # label6 only has hosts that are in atomic groups associated with it,
537 # there should be no scheduling.
538 self._check_for_extra_schedulings()
539
540
541 def test_atomic_group_scheduling_basics(self):
542 # Create jobs scheduled to run on an atomic group.
543 job_a = self._create_job(synchronous=True, metahosts=[self.label4.id],
544 atomic_group=1)
545 job_b = self._create_job(synchronous=True, metahosts=[self.label5.id],
546 atomic_group=1)
547 self._dispatcher._schedule_new_jobs()
548 # atomic_group.max_number_of_machines was 2 so we should run on 2.
549 self._assert_job_scheduled_on_number_of(job_a.id, (5, 6, 7), 2)
550 self._assert_job_scheduled_on(job_b.id, 8) # label5
551 self._assert_job_scheduled_on(job_b.id, 9) # label5
552 self._check_for_extra_schedulings()
553
554 # The three host label4 atomic group still has one host available.
555 # That means a job with a synch_count of 1 asking to be scheduled on
556 # the atomic group can still use the final machine.
557 #
558 # This may seem like a somewhat odd use case. It allows the use of an
559 # atomic group as a set of machines to run smaller jobs within (a set
560 # of hosts configured for use in network tests with eachother perhaps?)
561 onehost_job = self._create_job(atomic_group=1)
562 self._dispatcher._schedule_new_jobs()
563 self._assert_job_scheduled_on_number_of(onehost_job.id, (5, 6, 7), 1)
564 self._check_for_extra_schedulings()
565
566 # No more atomic groups have hosts available, no more jobs should
567 # be scheduled.
568 self._create_job(atomic_group=1)
569 self._dispatcher._schedule_new_jobs()
570 self._check_for_extra_schedulings()
571
572
573 def test_atomic_group_scheduling_obeys_acls(self):
574 # Request scheduling on a specific atomic label but be denied by ACLs.
575 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id in (8,9)')
576 job = self._create_job(metahosts=[self.label5.id], atomic_group=1)
577 self._dispatcher._schedule_new_jobs()
578 self._check_for_extra_schedulings()
579
580
581 def test_atomic_group_scheduling_dependency_label_exclude(self):
582 # A dependency label that matches no hosts in the atomic group.
583 job_a = self._create_job(atomic_group=1)
584 job_a.dependency_labels.add(self.label3)
585 self._dispatcher._schedule_new_jobs()
586 self._check_for_extra_schedulings()
587
588
589 def test_atomic_group_scheduling_metahost_dependency_label_exclude(self):
590 # A metahost and dependency label that excludes too many hosts.
591 job_b = self._create_job(synchronous=True, metahosts=[self.label4.id],
592 atomic_group=1)
593 job_b.dependency_labels.add(self.label7)
594 self._dispatcher._schedule_new_jobs()
595 self._check_for_extra_schedulings()
596
597
598 def test_atomic_group_scheduling_dependency_label_match(self):
599 # A dependency label that exists on enough atomic group hosts in only
600 # one of the two atomic group labels.
601 job_c = self._create_job(synchronous=True, atomic_group=1)
602 job_c.dependency_labels.add(self.label7)
603 self._dispatcher._schedule_new_jobs()
604 self._assert_job_scheduled_on_number_of(job_c.id, (8, 9), 2)
605 self._check_for_extra_schedulings()
606
607
608 def test_atomic_group_scheduling_no_metahost(self):
609 # Force it to schedule on the other group for a reliable test.
610 self._do_query('UPDATE hosts SET invalid=1 WHERE id=9')
611 # An atomic job without a metahost.
612 job = self._create_job(synchronous=True, atomic_group=1)
613 self._dispatcher._schedule_new_jobs()
614 self._assert_job_scheduled_on_number_of(job.id, (5, 6, 7), 2)
615 self._check_for_extra_schedulings()
616
617
618 def test_atomic_group_scheduling_partial_group(self):
619 # Make one host in labels[3] unavailable so that there are only two
620 # hosts left in the group.
621 self._do_query('UPDATE hosts SET status="Repair Failed" WHERE id=5')
622 job = self._create_job(synchronous=True, metahosts=[self.label4.id],
623 atomic_group=1)
624 self._dispatcher._schedule_new_jobs()
625 # Verify that it was scheduled on the 2 ready hosts in that group.
626 self._assert_job_scheduled_on(job.id, 6)
627 self._assert_job_scheduled_on(job.id, 7)
628 self._check_for_extra_schedulings()
629
630
631 def test_atomic_group_scheduling_not_enough_available(self):
632 # Mark some hosts in each atomic group label as not usable.
633 # One host running, another invalid in the first group label.
634 self._do_query('UPDATE hosts SET status="Running" WHERE id=5')
635 self._do_query('UPDATE hosts SET invalid=1 WHERE id=6')
636 # One host invalid in the second group label.
637 self._do_query('UPDATE hosts SET invalid=1 WHERE id=9')
638 # Nothing to schedule when no group label has enough (2) good hosts..
639 self._create_job(atomic_group=1, synchronous=True)
640 self._dispatcher._schedule_new_jobs()
641 # There are not enough hosts in either atomic group,
642 # No more scheduling should occur.
643 self._check_for_extra_schedulings()
644
645 # Now create an atomic job that has a synch count of 1. It should
646 # schedule on exactly one of the hosts.
647 onehost_job = self._create_job(atomic_group=1)
648 self._dispatcher._schedule_new_jobs()
649 self._assert_job_scheduled_on_number_of(onehost_job.id, (7, 8), 1)
650
651
652 def test_atomic_group_scheduling_no_valid_hosts(self):
653 self._do_query('UPDATE hosts SET invalid=1 WHERE id in (8,9)')
654 self._create_job(synchronous=True, metahosts=[self.label5.id],
655 atomic_group=1)
656 self._dispatcher._schedule_new_jobs()
657 # no hosts in the selected group and label are valid. no schedulings.
658 self._check_for_extra_schedulings()
659
660
661 def test_atomic_group_scheduling_metahost_works(self):
662 # Test that atomic group scheduling also obeys metahosts.
663 self._create_job(metahosts=[0], atomic_group=1)
664 self._dispatcher._schedule_new_jobs()
665 # There are no atomic group hosts that also have that metahost.
666 self._check_for_extra_schedulings()
667
668 job_b = self._create_job(metahosts=[self.label5.id], atomic_group=1)
669 self._dispatcher._schedule_new_jobs()
670 self._assert_job_scheduled_on(job_b.id, 8)
671 self._assert_job_scheduled_on(job_b.id, 9)
672 self._check_for_extra_schedulings()
673
674
675 def test_atomic_group_skips_ineligible_hosts(self):
676 # Test hosts marked ineligible for this job are not eligible.
677 # How would this ever happen anyways?
678 job = self._create_job(metahosts=[self.label4.id], atomic_group=1)
679 models.IneligibleHostQueue.objects.create(job=job, host_id=5)
680 models.IneligibleHostQueue.objects.create(job=job, host_id=6)
681 models.IneligibleHostQueue.objects.create(job=job, host_id=7)
682 self._dispatcher._schedule_new_jobs()
683 # No scheduling should occur as all desired hosts were ineligible.
684 self._check_for_extra_schedulings()
685
686
687 def test_atomic_group_scheduling_fail(self):
688 # If synch_count is > the atomic group number of machines, the job
689 # should be aborted immediately.
690 model_job = self._create_job(synchronous=True, atomic_group=1)
691 model_job.synch_count = 4
692 model_job.save()
693 job = monitor_db.Job(id=model_job.id)
694 self._dispatcher._schedule_new_jobs()
695 self._check_for_extra_schedulings()
696 queue_entries = job.get_host_queue_entries()
697 self.assertEqual(1, len(queue_entries))
698 self.assertEqual(queue_entries[0].status,
699 models.HostQueueEntry.Status.ABORTED)
700
701
showard205fd602009-03-21 00:17:35 +0000702 def test_atomic_group_no_labels_no_scheduling(self):
703 # Never schedule on atomic groups marked invalid.
704 job = self._create_job(metahosts=[self.label5.id], synchronous=True,
705 atomic_group=1)
706 # Deleting an atomic group via the frontend marks it invalid and
707 # removes all label references to the group. The job now references
708 # an invalid atomic group with no labels associated with it.
709 self.label5.atomic_group.invalid = True
710 self.label5.atomic_group.save()
711 self.label5.atomic_group = None
712 self.label5.save()
713
714 self._dispatcher._schedule_new_jobs()
715 self._check_for_extra_schedulings()
716
717
showard89f84db2009-03-12 20:39:13 +0000718 def test_schedule_directly_on_atomic_group_host_fail(self):
719 # Scheduling a job directly on hosts in an atomic group must
720 # fail to avoid users inadvertently holding up the use of an
721 # entire atomic group by using the machines individually.
722 job = self._create_job(hosts=[5])
723 self._dispatcher._schedule_new_jobs()
724 self._check_for_extra_schedulings()
725
726
727 def test_schedule_directly_on_atomic_group_host(self):
728 # Scheduling a job directly on one host in an atomic group will
729 # work when the atomic group is listed on the HQE in addition
730 # to the host (assuming the sync count is 1).
731 job = self._create_job(hosts=[5], atomic_group=1)
732 self._dispatcher._schedule_new_jobs()
733 self._assert_job_scheduled_on(job.id, 5)
734 self._check_for_extra_schedulings()
735
736
737 def test_schedule_directly_on_atomic_group_hosts_sync2(self):
738 job = self._create_job(hosts=[5,8], atomic_group=1, synchronous=True)
739 self._dispatcher._schedule_new_jobs()
740 self._assert_job_scheduled_on(job.id, 5)
741 self._assert_job_scheduled_on(job.id, 8)
742 self._check_for_extra_schedulings()
743
744
745 def test_schedule_directly_on_atomic_group_hosts_wrong_group(self):
746 job = self._create_job(hosts=[5,8], atomic_group=2, synchronous=True)
747 self._dispatcher._schedule_new_jobs()
748 self._check_for_extra_schedulings()
749
750
showard56193bb2008-08-13 20:07:41 +0000751 def test_only_schedule_queued_entries(self):
752 self._create_job(metahosts=[1])
753 self._update_hqe(set='active=1, host_id=2')
754 self._dispatcher._schedule_new_jobs()
755 self._check_for_extra_schedulings()
756
757
showardfa8629c2008-11-04 16:51:23 +0000758 def test_no_ready_hosts(self):
759 self._create_job(hosts=[1])
760 self._do_query('UPDATE hosts SET status="Repair Failed"')
761 self._dispatcher._schedule_new_jobs()
762 self._check_for_extra_schedulings()
763
764
showardb2e2c322008-10-14 17:33:55 +0000765class DispatcherThrottlingTest(BaseSchedulerTest):
showard4c5374f2008-09-04 17:02:56 +0000766 """
767 Test that the dispatcher throttles:
768 * total number of running processes
769 * number of processes started per cycle
770 """
771 _MAX_RUNNING = 3
772 _MAX_STARTED = 2
773
774 def setUp(self):
775 super(DispatcherThrottlingTest, self).setUp()
showard324bf812009-01-20 23:23:38 +0000776 scheduler_config.config.max_processes_per_drone = self._MAX_RUNNING
showardd1ee1dd2009-01-07 21:33:08 +0000777 scheduler_config.config.max_processes_started_per_cycle = (
778 self._MAX_STARTED)
showard4c5374f2008-09-04 17:02:56 +0000779
showard324bf812009-01-20 23:23:38 +0000780 def fake_max_runnable_processes(fake_self):
781 running = sum(agent.num_processes
782 for agent in self._agents
showard8cc058f2009-09-08 16:26:33 +0000783 if agent.started and not agent.is_done())
showard324bf812009-01-20 23:23:38 +0000784 return self._MAX_RUNNING - running
785 self.god.stub_with(drone_manager.DroneManager, 'max_runnable_processes',
786 fake_max_runnable_processes)
showard2fa51692009-01-13 23:48:08 +0000787
showard4c5374f2008-09-04 17:02:56 +0000788
showard4c5374f2008-09-04 17:02:56 +0000789 def _setup_some_agents(self, num_agents):
showard170873e2009-01-07 00:22:26 +0000790 self._agents = [DummyAgent() for i in xrange(num_agents)]
showard4c5374f2008-09-04 17:02:56 +0000791 self._dispatcher._agents = list(self._agents)
792
793
794 def _run_a_few_cycles(self):
795 for i in xrange(4):
796 self._dispatcher._handle_agents()
797
798
799 def _assert_agents_started(self, indexes, is_started=True):
800 for i in indexes:
showard8cc058f2009-09-08 16:26:33 +0000801 self.assert_(self._agents[i].started == is_started,
showard4c5374f2008-09-04 17:02:56 +0000802 'Agent %d %sstarted' %
803 (i, is_started and 'not ' or ''))
804
805
806 def _assert_agents_not_started(self, indexes):
807 self._assert_agents_started(indexes, False)
808
809
810 def test_throttle_total(self):
811 self._setup_some_agents(4)
812 self._run_a_few_cycles()
813 self._assert_agents_started([0, 1, 2])
814 self._assert_agents_not_started([3])
815
816
817 def test_throttle_per_cycle(self):
818 self._setup_some_agents(3)
819 self._dispatcher._handle_agents()
820 self._assert_agents_started([0, 1])
821 self._assert_agents_not_started([2])
822
823
824 def test_throttle_with_synchronous(self):
825 self._setup_some_agents(2)
826 self._agents[0].num_processes = 3
827 self._run_a_few_cycles()
828 self._assert_agents_started([0])
829 self._assert_agents_not_started([1])
830
831
832 def test_large_agent_starvation(self):
833 """
834 Ensure large agents don't get starved by lower-priority agents.
835 """
836 self._setup_some_agents(3)
837 self._agents[1].num_processes = 3
838 self._run_a_few_cycles()
839 self._assert_agents_started([0])
840 self._assert_agents_not_started([1, 2])
841
842 self._agents[0].set_done(True)
843 self._run_a_few_cycles()
844 self._assert_agents_started([1])
845 self._assert_agents_not_started([2])
846
847
848 def test_zero_process_agent(self):
849 self._setup_some_agents(5)
850 self._agents[4].num_processes = 0
851 self._run_a_few_cycles()
852 self._assert_agents_started([0, 1, 2, 4])
853 self._assert_agents_not_started([3])
854
855
showard1be97432008-10-17 15:30:45 +0000856class FindAbortTest(BaseSchedulerTest):
showard56193bb2008-08-13 20:07:41 +0000857 """
showard1be97432008-10-17 15:30:45 +0000858 Test the dispatcher abort functionality.
showard56193bb2008-08-13 20:07:41 +0000859 """
showard170873e2009-01-07 00:22:26 +0000860 def _check_host_agent(self, agent, host_id):
861 self.assert_(isinstance(agent, monitor_db.Agent))
showard8cc058f2009-09-08 16:26:33 +0000862 self.assert_(agent.task)
863 cleanup = agent.task
showard9d9ffd52008-11-09 23:14:35 +0000864
showard45ae8192008-11-05 19:32:53 +0000865 self.assert_(isinstance(cleanup, monitor_db.CleanupTask))
showard170873e2009-01-07 00:22:26 +0000866 self.assertEquals(cleanup.host.id, host_id)
showard1be97432008-10-17 15:30:45 +0000867
showard56193bb2008-08-13 20:07:41 +0000868
showardd3dc1992009-04-22 21:01:40 +0000869 def _check_agents(self, agents):
showard170873e2009-01-07 00:22:26 +0000870 agents = list(agents)
showard8cc058f2009-09-08 16:26:33 +0000871 self.assertEquals(len(agents), 2)
872 self._check_host_agent(agents[0], 1)
873 self._check_host_agent(agents[1], 2)
showard56193bb2008-08-13 20:07:41 +0000874
875
showardd3dc1992009-04-22 21:01:40 +0000876 def _common_setup(self):
showard56193bb2008-08-13 20:07:41 +0000877 self._create_job(hosts=[1, 2])
showardd3dc1992009-04-22 21:01:40 +0000878 self._update_hqe(set='aborted=1')
879 self._agent = self.god.create_mock_class(monitor_db.Agent, 'old_agent')
showard8cc058f2009-09-08 16:26:33 +0000880 self._agent.started = True
showard6b733412009-04-27 20:09:18 +0000881 _set_host_and_qe_ids(self._agent, [1, 2])
showardd3dc1992009-04-22 21:01:40 +0000882 self._agent.abort.expect_call()
883 self._agent.abort.expect_call() # gets called once for each HQE
884 self._dispatcher.add_agent(self._agent)
showard56193bb2008-08-13 20:07:41 +0000885
showardd3dc1992009-04-22 21:01:40 +0000886
887 def test_find_aborting(self):
888 self._common_setup()
showard56193bb2008-08-13 20:07:41 +0000889 self._dispatcher._find_aborting()
showard56193bb2008-08-13 20:07:41 +0000890 self.god.check_playback()
891
892
showardd3dc1992009-04-22 21:01:40 +0000893 def test_find_aborting_verifying(self):
894 self._common_setup()
895 self._update_hqe(set='active=1, status="Verifying"')
showard56193bb2008-08-13 20:07:41 +0000896
showard8cc058f2009-09-08 16:26:33 +0000897 self._agent.tick.expect_call()
898 self._agent.is_done.expect_call().and_return(True)
showard56193bb2008-08-13 20:07:41 +0000899 self._dispatcher._find_aborting()
showard8cc058f2009-09-08 16:26:33 +0000900 self._dispatcher._handle_agents()
901 self._dispatcher._schedule_special_tasks()
showardd3dc1992009-04-22 21:01:40 +0000902 self._check_agents(self._dispatcher._agents)
showard56193bb2008-08-13 20:07:41 +0000903 self.god.check_playback()
904
905
jadmanski3d161b02008-06-06 15:43:36 +0000906class PidfileRunMonitorTest(unittest.TestCase):
showard170873e2009-01-07 00:22:26 +0000907 execution_tag = 'test_tag'
jadmanski0afbb632008-06-06 21:10:57 +0000908 pid = 12345
showard170873e2009-01-07 00:22:26 +0000909 process = drone_manager.Process('myhost', pid)
showard21baa452008-10-21 00:08:39 +0000910 num_tests_failed = 1
jadmanski3d161b02008-06-06 15:43:36 +0000911
jadmanski0afbb632008-06-06 21:10:57 +0000912 def setUp(self):
913 self.god = mock.mock_god()
showard170873e2009-01-07 00:22:26 +0000914 self.mock_drone_manager = self.god.create_mock_class(
915 drone_manager.DroneManager, 'drone_manager')
916 self.god.stub_with(monitor_db, '_drone_manager',
917 self.mock_drone_manager)
918 self.god.stub_function(email_manager.manager, 'enqueue_notify_email')
showardec6a3b92009-09-25 20:29:13 +0000919 self.god.stub_with(monitor_db, '_get_pidfile_timeout_secs',
920 self._mock_get_pidfile_timeout_secs)
showard170873e2009-01-07 00:22:26 +0000921
922 self.pidfile_id = object()
923
showardd3dc1992009-04-22 21:01:40 +0000924 (self.mock_drone_manager.get_pidfile_id_from
925 .expect_call(self.execution_tag,
926 pidfile_name=monitor_db._AUTOSERV_PID_FILE)
927 .and_return(self.pidfile_id))
showard170873e2009-01-07 00:22:26 +0000928 self.mock_drone_manager.register_pidfile.expect_call(self.pidfile_id)
929
930 self.monitor = monitor_db.PidfileRunMonitor()
931 self.monitor.attach_to_existing_process(self.execution_tag)
jadmanski3d161b02008-06-06 15:43:36 +0000932
933
jadmanski0afbb632008-06-06 21:10:57 +0000934 def tearDown(self):
935 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +0000936
937
showardec6a3b92009-09-25 20:29:13 +0000938 def _mock_get_pidfile_timeout_secs(self):
939 return 300
940
941
showard170873e2009-01-07 00:22:26 +0000942 def setup_pidfile(self, pid=None, exit_code=None, tests_failed=None,
943 use_second_read=False):
944 contents = drone_manager.PidfileContents()
945 if pid is not None:
946 contents.process = drone_manager.Process('myhost', pid)
947 contents.exit_status = exit_code
948 contents.num_tests_failed = tests_failed
949 self.mock_drone_manager.get_pidfile_contents.expect_call(
950 self.pidfile_id, use_second_read=use_second_read).and_return(
951 contents)
952
953
jadmanski0afbb632008-06-06 21:10:57 +0000954 def set_not_yet_run(self):
showard170873e2009-01-07 00:22:26 +0000955 self.setup_pidfile()
jadmanski3d161b02008-06-06 15:43:36 +0000956
957
showard3dd6b882008-10-27 19:21:39 +0000958 def set_empty_pidfile(self):
showard170873e2009-01-07 00:22:26 +0000959 self.setup_pidfile()
showard3dd6b882008-10-27 19:21:39 +0000960
961
showard170873e2009-01-07 00:22:26 +0000962 def set_running(self, use_second_read=False):
963 self.setup_pidfile(self.pid, use_second_read=use_second_read)
jadmanski3d161b02008-06-06 15:43:36 +0000964
965
showard170873e2009-01-07 00:22:26 +0000966 def set_complete(self, error_code, use_second_read=False):
967 self.setup_pidfile(self.pid, error_code, self.num_tests_failed,
968 use_second_read=use_second_read)
969
970
971 def _check_monitor(self, expected_pid, expected_exit_status,
972 expected_num_tests_failed):
973 if expected_pid is None:
974 self.assertEquals(self.monitor._state.process, None)
975 else:
976 self.assertEquals(self.monitor._state.process.pid, expected_pid)
977 self.assertEquals(self.monitor._state.exit_status, expected_exit_status)
978 self.assertEquals(self.monitor._state.num_tests_failed,
979 expected_num_tests_failed)
980
981
982 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +0000983
984
showard21baa452008-10-21 00:08:39 +0000985 def _test_read_pidfile_helper(self, expected_pid, expected_exit_status,
986 expected_num_tests_failed):
987 self.monitor._read_pidfile()
showard170873e2009-01-07 00:22:26 +0000988 self._check_monitor(expected_pid, expected_exit_status,
989 expected_num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +0000990
991
showard21baa452008-10-21 00:08:39 +0000992 def _get_expected_tests_failed(self, expected_exit_status):
993 if expected_exit_status is None:
994 expected_tests_failed = None
995 else:
996 expected_tests_failed = self.num_tests_failed
997 return expected_tests_failed
998
999
jadmanski0afbb632008-06-06 21:10:57 +00001000 def test_read_pidfile(self):
1001 self.set_not_yet_run()
showard21baa452008-10-21 00:08:39 +00001002 self._test_read_pidfile_helper(None, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001003
showard3dd6b882008-10-27 19:21:39 +00001004 self.set_empty_pidfile()
1005 self._test_read_pidfile_helper(None, None, None)
1006
jadmanski0afbb632008-06-06 21:10:57 +00001007 self.set_running()
showard21baa452008-10-21 00:08:39 +00001008 self._test_read_pidfile_helper(self.pid, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001009
jadmanski0afbb632008-06-06 21:10:57 +00001010 self.set_complete(123)
showard21baa452008-10-21 00:08:39 +00001011 self._test_read_pidfile_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001012
1013
jadmanski0afbb632008-06-06 21:10:57 +00001014 def test_read_pidfile_error(self):
showard170873e2009-01-07 00:22:26 +00001015 self.mock_drone_manager.get_pidfile_contents.expect_call(
1016 self.pidfile_id, use_second_read=False).and_return(
1017 drone_manager.InvalidPidfile('error'))
1018 self.assertRaises(monitor_db.PidfileRunMonitor._PidfileException,
showard21baa452008-10-21 00:08:39 +00001019 self.monitor._read_pidfile)
jadmanski0afbb632008-06-06 21:10:57 +00001020 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001021
1022
showard170873e2009-01-07 00:22:26 +00001023 def setup_is_running(self, is_running):
1024 self.mock_drone_manager.is_process_running.expect_call(
1025 self.process).and_return(is_running)
jadmanski3d161b02008-06-06 15:43:36 +00001026
1027
showard21baa452008-10-21 00:08:39 +00001028 def _test_get_pidfile_info_helper(self, expected_pid, expected_exit_status,
1029 expected_num_tests_failed):
1030 self.monitor._get_pidfile_info()
showard170873e2009-01-07 00:22:26 +00001031 self._check_monitor(expected_pid, expected_exit_status,
1032 expected_num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001033
1034
jadmanski0afbb632008-06-06 21:10:57 +00001035 def test_get_pidfile_info(self):
showard21baa452008-10-21 00:08:39 +00001036 """
1037 normal cases for get_pidfile_info
1038 """
jadmanski0afbb632008-06-06 21:10:57 +00001039 # running
1040 self.set_running()
showard170873e2009-01-07 00:22:26 +00001041 self.setup_is_running(True)
showard21baa452008-10-21 00:08:39 +00001042 self._test_get_pidfile_info_helper(self.pid, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001043
jadmanski0afbb632008-06-06 21:10:57 +00001044 # exited during check
1045 self.set_running()
showard170873e2009-01-07 00:22:26 +00001046 self.setup_is_running(False)
1047 self.set_complete(123, use_second_read=True) # pidfile gets read again
showard21baa452008-10-21 00:08:39 +00001048 self._test_get_pidfile_info_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001049
jadmanski0afbb632008-06-06 21:10:57 +00001050 # completed
1051 self.set_complete(123)
showard21baa452008-10-21 00:08:39 +00001052 self._test_get_pidfile_info_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001053
1054
jadmanski0afbb632008-06-06 21:10:57 +00001055 def test_get_pidfile_info_running_no_proc(self):
showard21baa452008-10-21 00:08:39 +00001056 """
1057 pidfile shows process running, but no proc exists
1058 """
jadmanski0afbb632008-06-06 21:10:57 +00001059 # running but no proc
1060 self.set_running()
showard170873e2009-01-07 00:22:26 +00001061 self.setup_is_running(False)
1062 self.set_running(use_second_read=True)
1063 email_manager.manager.enqueue_notify_email.expect_call(
jadmanski0afbb632008-06-06 21:10:57 +00001064 mock.is_string_comparator(), mock.is_string_comparator())
showard21baa452008-10-21 00:08:39 +00001065 self._test_get_pidfile_info_helper(self.pid, 1, 0)
jadmanski0afbb632008-06-06 21:10:57 +00001066 self.assertTrue(self.monitor.lost_process)
jadmanski3d161b02008-06-06 15:43:36 +00001067
1068
jadmanski0afbb632008-06-06 21:10:57 +00001069 def test_get_pidfile_info_not_yet_run(self):
showard21baa452008-10-21 00:08:39 +00001070 """
1071 pidfile hasn't been written yet
1072 """
jadmanski0afbb632008-06-06 21:10:57 +00001073 self.set_not_yet_run()
showard21baa452008-10-21 00:08:39 +00001074 self._test_get_pidfile_info_helper(None, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001075
jadmanski3d161b02008-06-06 15:43:36 +00001076
showard170873e2009-01-07 00:22:26 +00001077 def test_process_failed_to_write_pidfile(self):
jadmanski0afbb632008-06-06 21:10:57 +00001078 self.set_not_yet_run()
showard170873e2009-01-07 00:22:26 +00001079 email_manager.manager.enqueue_notify_email.expect_call(
1080 mock.is_string_comparator(), mock.is_string_comparator())
showardec6a3b92009-09-25 20:29:13 +00001081 self.monitor._start_time = (time.time() -
1082 monitor_db._get_pidfile_timeout_secs() - 1)
showard35162b02009-03-03 02:17:30 +00001083 self._test_get_pidfile_info_helper(None, 1, 0)
1084 self.assertTrue(self.monitor.lost_process)
jadmanski3d161b02008-06-06 15:43:36 +00001085
1086
1087class AgentTest(unittest.TestCase):
jadmanski0afbb632008-06-06 21:10:57 +00001088 def setUp(self):
1089 self.god = mock.mock_god()
showard6b733412009-04-27 20:09:18 +00001090 self._dispatcher = self.god.create_mock_class(monitor_db.Dispatcher,
1091 'dispatcher')
jadmanski3d161b02008-06-06 15:43:36 +00001092
1093
jadmanski0afbb632008-06-06 21:10:57 +00001094 def tearDown(self):
1095 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001096
1097
showard170873e2009-01-07 00:22:26 +00001098 def _create_mock_task(self, name):
1099 task = self.god.create_mock_class(monitor_db.AgentTask, name)
showard6b733412009-04-27 20:09:18 +00001100 _set_host_and_qe_ids(task)
showard170873e2009-01-07 00:22:26 +00001101 return task
1102
showard8cc058f2009-09-08 16:26:33 +00001103 def _create_agent(self, task):
1104 agent = monitor_db.Agent(task)
showard6b733412009-04-27 20:09:18 +00001105 agent.dispatcher = self._dispatcher
1106 return agent
1107
1108
1109 def _finish_agent(self, agent):
1110 while not agent.is_done():
1111 agent.tick()
1112
showard170873e2009-01-07 00:22:26 +00001113
showard8cc058f2009-09-08 16:26:33 +00001114 def test_agent_abort(self):
1115 task = self._create_mock_task('task')
1116 task.poll.expect_call()
1117 task.is_done.expect_call().and_return(False)
1118 task.abort.expect_call()
1119 task.aborted = True
jadmanski3d161b02008-06-06 15:43:36 +00001120
showard8cc058f2009-09-08 16:26:33 +00001121 agent = self._create_agent(task)
showard6b733412009-04-27 20:09:18 +00001122 agent.tick()
1123 agent.abort()
1124 self._finish_agent(agent)
1125 self.god.check_playback()
1126
1127
showard08a36412009-05-05 01:01:13 +00001128 def _test_agent_abort_before_started_helper(self, ignore_abort=False):
showard20f9bdd2009-04-29 19:48:33 +00001129 task = self._create_mock_task('task')
showard08a36412009-05-05 01:01:13 +00001130 task.abort.expect_call()
1131 if ignore_abort:
1132 task.aborted = False
1133 task.poll.expect_call()
1134 task.is_done.expect_call().and_return(True)
showard08a36412009-05-05 01:01:13 +00001135 task.success = True
1136 else:
1137 task.aborted = True
1138
showard8cc058f2009-09-08 16:26:33 +00001139 agent = self._create_agent(task)
showard20f9bdd2009-04-29 19:48:33 +00001140 agent.abort()
showard20f9bdd2009-04-29 19:48:33 +00001141 self._finish_agent(agent)
1142 self.god.check_playback()
1143
1144
showard08a36412009-05-05 01:01:13 +00001145 def test_agent_abort_before_started(self):
1146 self._test_agent_abort_before_started_helper()
1147 self._test_agent_abort_before_started_helper(True)
1148
1149
showard77182562009-06-10 00:16:05 +00001150class DelayedCallTaskTest(unittest.TestCase):
1151 def setUp(self):
1152 self.god = mock.mock_god()
1153
1154
1155 def tearDown(self):
1156 self.god.unstub_all()
1157
1158
1159 def test_delayed_call(self):
mbligh1ef218d2009-08-03 16:57:56 +00001160 test_time = self.god.create_mock_function('time')
showard77182562009-06-10 00:16:05 +00001161 test_time.expect_call().and_return(33)
1162 test_time.expect_call().and_return(34.01)
1163 test_time.expect_call().and_return(34.99)
1164 test_time.expect_call().and_return(35.01)
1165 def test_callback():
1166 test_callback.calls += 1
1167 test_callback.calls = 0
1168 delay_task = monitor_db.DelayedCallTask(
1169 delay_seconds=2, callback=test_callback,
1170 now_func=test_time) # time 33
1171 self.assertEqual(35, delay_task.end_time)
showard8cc058f2009-09-08 16:26:33 +00001172 agent = monitor_db.Agent(delay_task, num_processes=0)
1173 self.assert_(not agent.started)
showard77182562009-06-10 00:16:05 +00001174 agent.tick() # activates the task and polls it once, time 34.01
1175 self.assertEqual(0, test_callback.calls, "callback called early")
1176 agent.tick() # time 34.99
1177 self.assertEqual(0, test_callback.calls, "callback called early")
1178 agent.tick() # time 35.01
1179 self.assertEqual(1, test_callback.calls)
1180 self.assert_(agent.is_done())
1181 self.assert_(delay_task.is_done())
1182 self.assert_(delay_task.success)
1183 self.assert_(not delay_task.aborted)
1184 self.god.check_playback()
1185
1186
1187 def test_delayed_call_abort(self):
1188 delay_task = monitor_db.DelayedCallTask(
1189 delay_seconds=987654, callback=lambda : None)
showard8cc058f2009-09-08 16:26:33 +00001190 agent = monitor_db.Agent(delay_task, num_processes=0)
showard77182562009-06-10 00:16:05 +00001191 agent.abort()
1192 agent.tick()
1193 self.assert_(agent.is_done())
1194 self.assert_(delay_task.aborted)
1195 self.assert_(delay_task.is_done())
1196 self.assert_(not delay_task.success)
1197 self.god.check_playback()
1198
1199
1200
showard184a5e82009-05-29 18:42:20 +00001201class AgentTasksTest(BaseSchedulerTest):
showarded2afea2009-07-07 20:54:07 +00001202 ABSPATH_BASE = '/abspath/'
jadmanski0afbb632008-06-06 21:10:57 +00001203 HOSTNAME = 'myhost'
showard381341a2009-07-15 14:28:56 +00001204 BASE_TASK_DIR = 'hosts/%s/' % HOSTNAME
1205 RESULTS_DIR = '/results/dir'
showard170873e2009-01-07 00:22:26 +00001206 DUMMY_PROCESS = object()
jadmanskifb7cfb12008-07-09 14:13:21 +00001207 HOST_PROTECTION = host_protections.default
showard170873e2009-01-07 00:22:26 +00001208 PIDFILE_ID = object()
showard87ba02a2009-04-20 19:37:32 +00001209 JOB_OWNER = 'test_owner'
1210 JOB_NAME = 'test_job_name'
1211 JOB_AUTOSERV_PARAMS = set(['-u', JOB_OWNER, '-l', JOB_NAME])
showarddb502762009-09-09 15:31:20 +00001212 PLATFORM = 'test_platform'
jadmanski3d161b02008-06-06 15:43:36 +00001213
jadmanski0afbb632008-06-06 21:10:57 +00001214 def setUp(self):
showard184a5e82009-05-29 18:42:20 +00001215 super(AgentTasksTest, self).setUp()
jadmanski0afbb632008-06-06 21:10:57 +00001216 self.god = mock.mock_god()
showard170873e2009-01-07 00:22:26 +00001217 self.god.stub_with(drone_manager.DroneManager, 'get_temporary_path',
1218 mock.mock_function('get_temporary_path',
1219 default_return_val='tempdir'))
1220 self.god.stub_function(drone_manager.DroneManager,
showard678df4f2009-02-04 21:36:39 +00001221 'copy_results_on_drone')
1222 self.god.stub_function(drone_manager.DroneManager,
showard170873e2009-01-07 00:22:26 +00001223 'copy_to_results_repository')
1224 self.god.stub_function(drone_manager.DroneManager,
1225 'get_pidfile_id_from')
showarddb502762009-09-09 15:31:20 +00001226 self.god.stub_function(drone_manager.DroneManager,
1227 'attach_file_to_execution')
showard170873e2009-01-07 00:22:26 +00001228
showarded2afea2009-07-07 20:54:07 +00001229 def dummy_absolute_path(drone_manager_self, path):
1230 return self.ABSPATH_BASE + path
showard170873e2009-01-07 00:22:26 +00001231 self.god.stub_with(drone_manager.DroneManager, 'absolute_path',
1232 dummy_absolute_path)
1233
showarddb502762009-09-09 15:31:20 +00001234 def dummy_abspath(path):
1235 return self.ABSPATH_BASE + path
1236 self.god.stub_with(os.path, 'abspath', dummy_abspath)
1237
showard170873e2009-01-07 00:22:26 +00001238 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'run')
1239 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'exit_code')
showardb6681aa2009-07-08 21:15:00 +00001240 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'kill')
showard170873e2009-01-07 00:22:26 +00001241 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'get_process')
showardcdaeae82009-08-31 18:32:48 +00001242 self.god.stub_class_method(monitor_db.PidfileRunMonitor,
1243 'try_copy_to_results_repository')
1244 self.god.stub_class_method(monitor_db.PidfileRunMonitor,
1245 'try_copy_results_on_drone')
showard6b733412009-04-27 20:09:18 +00001246 def mock_has_process(unused):
1247 return True
1248 self.god.stub_with(monitor_db.PidfileRunMonitor, 'has_process',
1249 mock_has_process)
showard381341a2009-07-15 14:28:56 +00001250
jadmanski0afbb632008-06-06 21:10:57 +00001251 self.host = self.god.create_mock_class(monitor_db.Host, 'host')
showard170873e2009-01-07 00:22:26 +00001252 self.host.id = 1
jadmanski0afbb632008-06-06 21:10:57 +00001253 self.host.hostname = self.HOSTNAME
jadmanskifb7cfb12008-07-09 14:13:21 +00001254 self.host.protection = self.HOST_PROTECTION
showarda5288b42009-07-28 20:06:08 +00001255
1256 # For this (and other model creations), we must create the entry this
1257 # way; otherwise, if an entry matching the id already exists, Django 1.0
1258 # will raise an exception complaining about a duplicate primary key.
1259 # This way, Django performs an UPDATE query if an entry matching the id
1260 # already exists.
1261 host = models.Host(id=self.host.id, hostname=self.host.hostname,
1262 protection=self.host.protection)
1263 host.save()
showard381341a2009-07-15 14:28:56 +00001264
showard97aed502008-11-04 02:01:24 +00001265 self.job = self.god.create_mock_class(monitor_db.Job, 'job')
showard87ba02a2009-04-20 19:37:32 +00001266 self.job.owner = self.JOB_OWNER
1267 self.job.name = self.JOB_NAME
mblighe7d9c602009-07-02 19:02:33 +00001268 self.job.id = 1337
1269 self.job.tag = lambda: 'fake-job-tag'
showarda5288b42009-07-28 20:06:08 +00001270 job = models.Job(id=self.job.id, owner=self.job.owner,
1271 name=self.job.name, created_on=datetime.datetime.now())
1272 job.save()
showard381341a2009-07-15 14:28:56 +00001273
1274 self.queue_entry = self.god.create_mock_class(
1275 monitor_db.HostQueueEntry, 'queue_entry')
showard170873e2009-01-07 00:22:26 +00001276 self.queue_entry.id = 1
showard97aed502008-11-04 02:01:24 +00001277 self.queue_entry.job = self.job
jadmanski0afbb632008-06-06 21:10:57 +00001278 self.queue_entry.host = self.host
1279 self.queue_entry.meta_host = None
showarda5288b42009-07-28 20:06:08 +00001280 queue_entry = models.HostQueueEntry(id=self.queue_entry.id, job=job,
1281 host=host, meta_host=None)
1282 queue_entry.save()
showard381341a2009-07-15 14:28:56 +00001283
showard8cc058f2009-09-08 16:26:33 +00001284 self.task = models.SpecialTask(id=1, host=host,
1285 task=models.SpecialTask.Task.CLEANUP,
1286 is_active=False, is_complete=False,
1287 time_requested=datetime.datetime.now(),
1288 time_started=None,
1289 queue_entry=queue_entry)
1290 self.task.save()
1291 self.god.stub_function(self.task, 'activate')
1292 self.god.stub_function(self.task, 'finish')
1293 self.god.stub_function(models.SpecialTask.objects, 'create')
1294
showardd3dc1992009-04-22 21:01:40 +00001295 self._dispatcher = self.god.create_mock_class(monitor_db.Dispatcher,
1296 'dispatcher')
1297
jadmanski3d161b02008-06-06 15:43:36 +00001298
jadmanski0afbb632008-06-06 21:10:57 +00001299 def tearDown(self):
showard184a5e82009-05-29 18:42:20 +00001300 super(AgentTasksTest, self).tearDown()
jadmanski0afbb632008-06-06 21:10:57 +00001301 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001302
1303
jadmanski0afbb632008-06-06 21:10:57 +00001304 def run_task(self, task, success):
1305 """
1306 Do essentially what an Agent would do, but protect againt
1307 infinite looping from test errors.
1308 """
1309 if not getattr(task, 'agent', None):
1310 task.agent = object()
jadmanski0afbb632008-06-06 21:10:57 +00001311 count = 0
1312 while not task.is_done():
1313 count += 1
1314 if count > 10:
1315 print 'Task failed to finish'
1316 # in case the playback has clues to why it
1317 # failed
1318 self.god.check_playback()
1319 self.fail()
1320 task.poll()
1321 self.assertEquals(task.success, success)
jadmanski3d161b02008-06-06 15:43:36 +00001322
1323
showardb6681aa2009-07-08 21:15:00 +00001324 def setup_run_monitor(self, exit_status, task_tag, copy_log_file=True,
1325 aborted=False):
showard170873e2009-01-07 00:22:26 +00001326 monitor_db.PidfileRunMonitor.run.expect_call(
1327 mock.is_instance_comparator(list),
showarded2afea2009-07-07 20:54:07 +00001328 self.BASE_TASK_DIR + task_tag,
showard170873e2009-01-07 00:22:26 +00001329 nice_level=monitor_db.AUTOSERV_NICE_LEVEL,
showardd3dc1992009-04-22 21:01:40 +00001330 log_file=mock.anything_comparator(),
1331 pidfile_name=monitor_db._AUTOSERV_PID_FILE,
1332 paired_with_pidfile=None)
showard170873e2009-01-07 00:22:26 +00001333 monitor_db.PidfileRunMonitor.exit_code.expect_call()
showardb6681aa2009-07-08 21:15:00 +00001334 if aborted:
1335 monitor_db.PidfileRunMonitor.kill.expect_call()
1336 else:
1337 monitor_db.PidfileRunMonitor.exit_code.expect_call().and_return(
1338 exit_status)
jadmanski3d161b02008-06-06 15:43:36 +00001339
showard8cc058f2009-09-08 16:26:33 +00001340 self.task.finish.expect_call()
1341
showard170873e2009-01-07 00:22:26 +00001342 if copy_log_file:
1343 self._setup_move_logfile()
1344
1345
showard678df4f2009-02-04 21:36:39 +00001346 def _setup_move_logfile(self, copy_on_drone=False,
1347 include_destination=False):
showardcdaeae82009-08-31 18:32:48 +00001348 monitor = monitor_db.PidfileRunMonitor
showard678df4f2009-02-04 21:36:39 +00001349 if copy_on_drone:
showarded2afea2009-07-07 20:54:07 +00001350 self.queue_entry.execution_path.expect_call().and_return('tag')
showardcdaeae82009-08-31 18:32:48 +00001351 monitor.try_copy_results_on_drone.expect_call(
1352 source_path=mock.is_string_comparator(),
showard678df4f2009-02-04 21:36:39 +00001353 destination_path=mock.is_string_comparator())
1354 elif include_destination:
showardcdaeae82009-08-31 18:32:48 +00001355 monitor.try_copy_to_results_repository.expect_call(
1356 mock.is_string_comparator(),
showard170873e2009-01-07 00:22:26 +00001357 destination_path=mock.is_string_comparator())
1358 else:
showardcdaeae82009-08-31 18:32:48 +00001359 monitor.try_copy_to_results_repository.expect_call(
1360 mock.is_string_comparator())
showard170873e2009-01-07 00:22:26 +00001361
jadmanski3d161b02008-06-06 15:43:36 +00001362
showard8cc058f2009-09-08 16:26:33 +00001363 def _setup_special_task(self, task_id, task_type, use_queue_entry):
1364 self.task.id = task_id
1365 self.task.task = task_type
1366 if use_queue_entry:
1367 queue_entry = models.HostQueueEntry(id=self.queue_entry.id)
1368 else:
1369 queue_entry = None
1370 self.task.queue_entry = queue_entry
1371 self.task.save()
1372
1373
showarddb502762009-09-09 15:31:20 +00001374 def _setup_write_host_keyvals_expects(self, task_tag):
1375 self.host.platform_and_labels.expect_call().and_return(
1376 (self.PLATFORM, (self.PLATFORM,)))
1377
1378 execution_path = os.path.join(self.BASE_TASK_DIR, task_tag)
1379 file_contents = ('platform=%(platform)s\nlabels=%(platform)s\n'
1380 % dict(platform=self.PLATFORM))
1381 file_path = os.path.join(execution_path, 'host_keyvals', self.HOSTNAME)
1382 drone_manager.DroneManager.attach_file_to_execution.expect_call(
1383 execution_path, file_contents, file_path=file_path)
1384
1385
showard8cc058f2009-09-08 16:26:33 +00001386 def _test_repair_task_helper(self, success, task_id, use_queue_entry=False):
1387 self._setup_special_task(task_id, models.SpecialTask.Task.REPAIR,
1388 use_queue_entry)
1389 task_tag = '%d-repair' % task_id
1390
1391 self.task.activate.expect_call()
showarddb502762009-09-09 15:31:20 +00001392 self._setup_write_host_keyvals_expects(task_tag)
showard8cc058f2009-09-08 16:26:33 +00001393
jadmanski0afbb632008-06-06 21:10:57 +00001394 self.host.set_status.expect_call('Repairing')
1395 if success:
showarded2afea2009-07-07 20:54:07 +00001396 self.setup_run_monitor(0, task_tag)
jadmanski0afbb632008-06-06 21:10:57 +00001397 self.host.set_status.expect_call('Ready')
1398 else:
showarded2afea2009-07-07 20:54:07 +00001399 self.setup_run_monitor(1, task_tag)
jadmanski0afbb632008-06-06 21:10:57 +00001400 self.host.set_status.expect_call('Repair Failed')
jadmanski3d161b02008-06-06 15:43:36 +00001401
showard8cc058f2009-09-08 16:26:33 +00001402 task = monitor_db.RepairTask(task=self.task)
1403 task.host = self.host
1404 if use_queue_entry:
1405 task.queue_entry = self.queue_entry
1406
jadmanski0afbb632008-06-06 21:10:57 +00001407 self.run_task(task, success)
jadmanskifb7cfb12008-07-09 14:13:21 +00001408
1409 expected_protection = host_protections.Protection.get_string(
1410 host_protections.default)
mbligh3e0f7e02008-07-28 19:42:01 +00001411 expected_protection = host_protections.Protection.get_attr_name(
1412 expected_protection)
1413
showard170873e2009-01-07 00:22:26 +00001414 self.assertTrue(set(task.cmd) >=
1415 set([monitor_db._autoserv_path, '-p', '-R', '-m',
showarded2afea2009-07-07 20:54:07 +00001416 self.HOSTNAME, '-r',
1417 drone_manager.WORKING_DIRECTORY,
showard170873e2009-01-07 00:22:26 +00001418 '--host-protection', expected_protection]))
jadmanski0afbb632008-06-06 21:10:57 +00001419 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001420
1421
jadmanski0afbb632008-06-06 21:10:57 +00001422 def test_repair_task(self):
showard8cc058f2009-09-08 16:26:33 +00001423 self._test_repair_task_helper(True, 1)
1424 self._test_repair_task_helper(False, 2)
jadmanski3d161b02008-06-06 15:43:36 +00001425
1426
showardcfd4a7e2009-07-11 01:47:33 +00001427 def test_repair_task_with_hqe_already_requeued(self):
1428 # during recovery, a RepairTask can be passed a queue entry that has
1429 # already been requeued. ensure it leaves the HQE alone in that case.
1430 self.queue_entry.meta_host = 1
1431 self.queue_entry.host = None
showard8cc058f2009-09-08 16:26:33 +00001432 self._test_repair_task_helper(False, 1, True)
showardcfd4a7e2009-07-11 01:47:33 +00001433
1434
showard8ac6f2a2009-07-16 14:50:32 +00001435 def test_recovery_repair_task_working_directory(self):
1436 # ensure that a RepairTask recovering an existing SpecialTask picks up
1437 # the working directory immediately
1438 class MockSpecialTask(object):
1439 def execution_path(self):
1440 return '/my/path'
showard8cc058f2009-09-08 16:26:33 +00001441 host = models.Host(id=self.host.id)
1442 queue_entry = None
showard8ac6f2a2009-07-16 14:50:32 +00001443
showard8cc058f2009-09-08 16:26:33 +00001444 task = monitor_db.RepairTask(task=MockSpecialTask())
showard8ac6f2a2009-07-16 14:50:32 +00001445
1446 self.assertEquals(task._working_directory, '/my/path')
1447
1448
showardb6681aa2009-07-08 21:15:00 +00001449 def test_repair_task_aborted(self):
showard8cc058f2009-09-08 16:26:33 +00001450 self._setup_special_task(1, models.SpecialTask.Task.REPAIR, False)
showarddb502762009-09-09 15:31:20 +00001451 task_tag = '1-repair'
showard8cc058f2009-09-08 16:26:33 +00001452
1453 self.task.activate.expect_call()
showarddb502762009-09-09 15:31:20 +00001454 self._setup_write_host_keyvals_expects(task_tag)
1455
showardb6681aa2009-07-08 21:15:00 +00001456 self.host.set_status.expect_call('Repairing')
showarddb502762009-09-09 15:31:20 +00001457 self.setup_run_monitor(0, task_tag, aborted=True)
showardb6681aa2009-07-08 21:15:00 +00001458
showard8cc058f2009-09-08 16:26:33 +00001459 task = monitor_db.RepairTask(task=self.task)
1460 task.host = self.host
showardb6681aa2009-07-08 21:15:00 +00001461 task.agent = object()
showard8cc058f2009-09-08 16:26:33 +00001462
showardb6681aa2009-07-08 21:15:00 +00001463 task.poll()
1464 task.abort()
1465
1466 self.assertTrue(task.done)
1467 self.assertTrue(task.aborted)
showardb6681aa2009-07-08 21:15:00 +00001468 self.god.check_playback()
1469
1470
showarded2afea2009-07-07 20:54:07 +00001471 def _test_repair_task_with_queue_entry_helper(self, parse_failed_repair,
showard8cc058f2009-09-08 16:26:33 +00001472 task_id):
1473 self._setup_special_task(task_id, models.SpecialTask.Task.REPAIR, True)
1474 task_tag = '%d-repair' % task_id
1475
showardde634ee2009-01-30 01:44:24 +00001476 self.god.stub_class(monitor_db, 'FinalReparseTask')
1477 self.god.stub_class(monitor_db, 'Agent')
showardd9205182009-04-27 20:09:55 +00001478 self.god.stub_class_method(monitor_db.TaskWithJobKeyvals,
1479 '_write_keyval_after_job')
showardde634ee2009-01-30 01:44:24 +00001480 agent = DummyAgent()
showardd3dc1992009-04-22 21:01:40 +00001481 agent.dispatcher = self._dispatcher
showardde634ee2009-01-30 01:44:24 +00001482
showard8cc058f2009-09-08 16:26:33 +00001483 self.task.activate.expect_call()
showarddb502762009-09-09 15:31:20 +00001484 self._setup_write_host_keyvals_expects(task_tag)
showard8cc058f2009-09-08 16:26:33 +00001485
jadmanski0afbb632008-06-06 21:10:57 +00001486 self.host.set_status.expect_call('Repairing')
showarded2afea2009-07-07 20:54:07 +00001487 self.setup_run_monitor(1, task_tag)
jadmanski0afbb632008-06-06 21:10:57 +00001488 self.host.set_status.expect_call('Repair Failed')
showardccbd6c52009-03-21 00:10:21 +00001489 self.queue_entry.update_from_database.expect_call()
showard8cc058f2009-09-08 16:26:33 +00001490 self.queue_entry.status = 'Queued'
showardde634ee2009-01-30 01:44:24 +00001491 self.queue_entry.set_execution_subdir.expect_call()
showardd9205182009-04-27 20:09:55 +00001492 monitor_db.TaskWithJobKeyvals._write_keyval_after_job.expect_call(
1493 'job_queued', mock.is_instance_comparator(int))
1494 monitor_db.TaskWithJobKeyvals._write_keyval_after_job.expect_call(
1495 'job_finished', mock.is_instance_comparator(int))
showard678df4f2009-02-04 21:36:39 +00001496 self._setup_move_logfile(copy_on_drone=True)
showarded2afea2009-07-07 20:54:07 +00001497 self.queue_entry.execution_path.expect_call().and_return('tag')
showard678df4f2009-02-04 21:36:39 +00001498 self._setup_move_logfile()
showarda1e74b32009-05-12 17:32:04 +00001499 self.job.parse_failed_repair = parse_failed_repair
showarde788ea62008-11-17 21:02:47 +00001500 self.queue_entry.handle_host_failure.expect_call()
showard8cc058f2009-09-08 16:26:33 +00001501 if parse_failed_repair:
1502 self.queue_entry.set_status.expect_call('Parsing')
1503 self.queue_entry.execution_path.expect_call().and_return('tag')
1504 drone_manager.DroneManager.get_pidfile_id_from.expect_call(
1505 'tag', pidfile_name=monitor_db._AUTOSERV_PID_FILE).and_return(
1506 self.PIDFILE_ID)
jadmanski3d161b02008-06-06 15:43:36 +00001507
showard8cc058f2009-09-08 16:26:33 +00001508 task = monitor_db.RepairTask(task=self.task)
showardde634ee2009-01-30 01:44:24 +00001509 task.agent = agent
showard8cc058f2009-09-08 16:26:33 +00001510 task.host = self.host
1511 task.queue_entry = self.queue_entry
showardccbd6c52009-03-21 00:10:21 +00001512 self.queue_entry.status = 'Queued'
showardd9205182009-04-27 20:09:55 +00001513 self.job.created_on = datetime.datetime(2009, 1, 1)
showard8cc058f2009-09-08 16:26:33 +00001514
jadmanski0afbb632008-06-06 21:10:57 +00001515 self.run_task(task, False)
showard87ba02a2009-04-20 19:37:32 +00001516 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
jadmanski0afbb632008-06-06 21:10:57 +00001517 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001518
1519
showarda1e74b32009-05-12 17:32:04 +00001520 def test_repair_task_with_queue_entry(self):
showard8cc058f2009-09-08 16:26:33 +00001521 self._test_repair_task_with_queue_entry_helper(True, 1)
1522 self._test_repair_task_with_queue_entry_helper(False, 2)
showarda1e74b32009-05-12 17:32:04 +00001523
1524
showard58721a82009-08-20 23:32:40 +00001525 def _setup_prejob_task_failure(self, task_tag, use_queue_entry):
1526 self.setup_run_monitor(1, task_tag)
1527 if use_queue_entry:
1528 if not self.queue_entry.meta_host:
1529 self.queue_entry.set_execution_subdir.expect_call()
1530 self.queue_entry.execution_path.expect_call().and_return('tag')
1531 self._setup_move_logfile(include_destination=True)
1532 self.queue_entry.requeue.expect_call()
showard8cc058f2009-09-08 16:26:33 +00001533 queue_entry = models.HostQueueEntry(id=self.queue_entry.id)
1534 else:
1535 queue_entry = None
1536 models.SpecialTask.objects.create.expect_call(
1537 host=models.Host(id=self.host.id),
1538 task=models.SpecialTask.Task.REPAIR,
1539 queue_entry=queue_entry)
showard58721a82009-08-20 23:32:40 +00001540
1541
showarded2afea2009-07-07 20:54:07 +00001542 def setup_verify_expects(self, success, use_queue_entry, task_tag):
showard8cc058f2009-09-08 16:26:33 +00001543 self.task.activate.expect_call()
showarddb502762009-09-09 15:31:20 +00001544 self._setup_write_host_keyvals_expects(task_tag)
showard8cc058f2009-09-08 16:26:33 +00001545
jadmanski0afbb632008-06-06 21:10:57 +00001546 if use_queue_entry:
showard8fe93b52008-11-18 17:53:22 +00001547 self.queue_entry.set_status.expect_call('Verifying')
jadmanski0afbb632008-06-06 21:10:57 +00001548 self.host.set_status.expect_call('Verifying')
1549 if success:
showarded2afea2009-07-07 20:54:07 +00001550 self.setup_run_monitor(0, task_tag)
showard8cc058f2009-09-08 16:26:33 +00001551 if use_queue_entry:
1552 self.queue_entry.on_pending.expect_call()
1553 else:
1554 self.host.set_status.expect_call('Ready')
jadmanski0afbb632008-06-06 21:10:57 +00001555 else:
showard58721a82009-08-20 23:32:40 +00001556 self._setup_prejob_task_failure(task_tag, use_queue_entry)
jadmanski3d161b02008-06-06 15:43:36 +00001557
1558
showard8cc058f2009-09-08 16:26:33 +00001559 def _test_verify_task_helper(self, success, task_id, use_queue_entry=False,
showard56193bb2008-08-13 20:07:41 +00001560 use_meta_host=False):
showard8cc058f2009-09-08 16:26:33 +00001561 self._setup_special_task(task_id, models.SpecialTask.Task.VERIFY,
1562 use_queue_entry)
1563 task_tag = '%d-verify' % task_id
showarded2afea2009-07-07 20:54:07 +00001564 self.setup_verify_expects(success, use_queue_entry, task_tag)
jadmanski3d161b02008-06-06 15:43:36 +00001565
showard8cc058f2009-09-08 16:26:33 +00001566 task = monitor_db.VerifyTask(task=self.task)
1567 task.host = self.host
jadmanski0afbb632008-06-06 21:10:57 +00001568 if use_queue_entry:
showard8cc058f2009-09-08 16:26:33 +00001569 task.queue_entry = self.queue_entry
1570
jadmanski0afbb632008-06-06 21:10:57 +00001571 self.run_task(task, success)
showard170873e2009-01-07 00:22:26 +00001572 self.assertTrue(set(task.cmd) >=
1573 set([monitor_db._autoserv_path, '-p', '-v', '-m',
showarded2afea2009-07-07 20:54:07 +00001574 self.HOSTNAME, '-r',
1575 drone_manager.WORKING_DIRECTORY]))
showard87ba02a2009-04-20 19:37:32 +00001576 if use_queue_entry:
1577 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
jadmanski0afbb632008-06-06 21:10:57 +00001578 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001579
1580
jadmanski0afbb632008-06-06 21:10:57 +00001581 def test_verify_task_with_host(self):
showard8cc058f2009-09-08 16:26:33 +00001582 self._test_verify_task_helper(True, 1)
1583 self._test_verify_task_helper(False, 2)
jadmanski3d161b02008-06-06 15:43:36 +00001584
1585
jadmanski0afbb632008-06-06 21:10:57 +00001586 def test_verify_task_with_queue_entry(self):
showard8cc058f2009-09-08 16:26:33 +00001587 self._test_verify_task_helper(True, 1, use_queue_entry=True)
1588 self._test_verify_task_helper(False, 2, use_queue_entry=True)
showard56193bb2008-08-13 20:07:41 +00001589
1590
1591 def test_verify_task_with_metahost(self):
showard8fe93b52008-11-18 17:53:22 +00001592 self.queue_entry.meta_host = 1
1593 self.test_verify_task_with_queue_entry()
jadmanski3d161b02008-06-06 15:43:36 +00001594
1595
showarda640b2d2009-07-20 22:37:06 +00001596 def test_specialtask_abort_before_prolog(self):
showard8cc058f2009-09-08 16:26:33 +00001597 self._setup_special_task(1, models.SpecialTask.Task.REPAIR, False)
1598 task = monitor_db.RepairTask(task=self.task)
1599 self.task.finish.expect_call()
showarda640b2d2009-07-20 22:37:06 +00001600 task.abort()
1601 self.assertTrue(task.aborted)
1602
1603
showard5add1c82009-05-26 19:27:46 +00001604 def _setup_post_job_task_expects(self, autoserv_success, hqe_status=None,
showard8cc058f2009-09-08 16:26:33 +00001605 hqe_aborted=False, host_status=None):
showarded2afea2009-07-07 20:54:07 +00001606 self.queue_entry.execution_path.expect_call().and_return('tag')
showard170873e2009-01-07 00:22:26 +00001607 self.pidfile_monitor = monitor_db.PidfileRunMonitor.expect_new()
1608 self.pidfile_monitor.pidfile_id = self.PIDFILE_ID
1609 self.pidfile_monitor.attach_to_existing_process.expect_call('tag')
1610 if autoserv_success:
1611 code = 0
1612 else:
1613 code = 1
showardd3dc1992009-04-22 21:01:40 +00001614 self.queue_entry.update_from_database.expect_call()
showard6b733412009-04-27 20:09:18 +00001615 self.queue_entry.aborted = hqe_aborted
1616 if not hqe_aborted:
1617 self.pidfile_monitor.exit_code.expect_call().and_return(code)
showard170873e2009-01-07 00:22:26 +00001618
showard5add1c82009-05-26 19:27:46 +00001619 if hqe_status:
showard8cc058f2009-09-08 16:26:33 +00001620 self.queue_entry.status = hqe_status
1621 if host_status:
1622 self.host.status = host_status
showardd3dc1992009-04-22 21:01:40 +00001623
1624
1625 def _setup_pre_parse_expects(self, autoserv_success):
1626 self._setup_post_job_task_expects(autoserv_success, 'Parsing')
showard97aed502008-11-04 02:01:24 +00001627
1628
1629 def _setup_post_parse_expects(self, autoserv_success):
showard97aed502008-11-04 02:01:24 +00001630 if autoserv_success:
showard170873e2009-01-07 00:22:26 +00001631 status = 'Completed'
showard97aed502008-11-04 02:01:24 +00001632 else:
showard170873e2009-01-07 00:22:26 +00001633 status = 'Failed'
showard97aed502008-11-04 02:01:24 +00001634 self.queue_entry.set_status.expect_call(status)
1635
1636
showard5add1c82009-05-26 19:27:46 +00001637 def _expect_execute_run_monitor(self):
1638 self.monitor.exit_code.expect_call()
1639 self.monitor.exit_code.expect_call().and_return(0)
1640 self._expect_copy_results()
1641
1642
showardd3dc1992009-04-22 21:01:40 +00001643 def _setup_post_job_run_monitor(self, pidfile_name):
showard678df4f2009-02-04 21:36:39 +00001644 self.pidfile_monitor.has_process.expect_call().and_return(True)
showard170873e2009-01-07 00:22:26 +00001645 autoserv_pidfile_id = object()
showardd3dc1992009-04-22 21:01:40 +00001646 self.monitor = monitor_db.PidfileRunMonitor.expect_new()
1647 self.monitor.run.expect_call(
showard170873e2009-01-07 00:22:26 +00001648 mock.is_instance_comparator(list),
1649 'tag',
showardd3dc1992009-04-22 21:01:40 +00001650 nice_level=monitor_db.AUTOSERV_NICE_LEVEL,
showard170873e2009-01-07 00:22:26 +00001651 log_file=mock.anything_comparator(),
showardd3dc1992009-04-22 21:01:40 +00001652 pidfile_name=pidfile_name,
showard170873e2009-01-07 00:22:26 +00001653 paired_with_pidfile=self.PIDFILE_ID)
showard5add1c82009-05-26 19:27:46 +00001654 self._expect_execute_run_monitor()
showardd3dc1992009-04-22 21:01:40 +00001655
1656
showard6b733412009-04-27 20:09:18 +00001657 def _expect_copy_results(self, monitor=None, queue_entry=None):
1658 if monitor is None:
1659 monitor = self.monitor
showard6b733412009-04-27 20:09:18 +00001660 if queue_entry:
showarded2afea2009-07-07 20:54:07 +00001661 queue_entry.execution_path.expect_call().and_return('tag')
showardcdaeae82009-08-31 18:32:48 +00001662 monitor.try_copy_to_results_repository.expect_call(
1663 mock.is_string_comparator())
showard97aed502008-11-04 02:01:24 +00001664
showard170873e2009-01-07 00:22:26 +00001665
1666 def _test_final_reparse_task_helper(self, autoserv_success=True):
1667 self._setup_pre_parse_expects(autoserv_success)
showardd3dc1992009-04-22 21:01:40 +00001668 self._setup_post_job_run_monitor(monitor_db._PARSER_PID_FILE)
showard97aed502008-11-04 02:01:24 +00001669 self._setup_post_parse_expects(autoserv_success)
1670
1671 task = monitor_db.FinalReparseTask([self.queue_entry])
1672 self.run_task(task, True)
1673
1674 self.god.check_playback()
showard170873e2009-01-07 00:22:26 +00001675 cmd = [monitor_db._parser_path, '--write-pidfile', '-l', '2', '-r',
mbligh2d7c8bd2009-05-13 20:42:50 +00001676 '-o', '-P', '/abspath/tag']
showard97aed502008-11-04 02:01:24 +00001677 self.assertEquals(task.cmd, cmd)
1678
1679
1680 def test_final_reparse_task(self):
1681 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1682 self._test_final_reparse_task_helper()
showard97aed502008-11-04 02:01:24 +00001683 self._test_final_reparse_task_helper(autoserv_success=False)
1684
1685
1686 def test_final_reparse_throttling(self):
1687 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1688 self.god.stub_function(monitor_db.FinalReparseTask,
1689 '_can_run_new_parse')
1690
showard170873e2009-01-07 00:22:26 +00001691 self._setup_pre_parse_expects(True)
showard97aed502008-11-04 02:01:24 +00001692 monitor_db.FinalReparseTask._can_run_new_parse.expect_call().and_return(
1693 False)
1694 monitor_db.FinalReparseTask._can_run_new_parse.expect_call().and_return(
1695 True)
showardd3dc1992009-04-22 21:01:40 +00001696 self._setup_post_job_run_monitor(monitor_db._PARSER_PID_FILE)
showard97aed502008-11-04 02:01:24 +00001697 self._setup_post_parse_expects(True)
1698
1699 task = monitor_db.FinalReparseTask([self.queue_entry])
1700 self.run_task(task, True)
1701 self.god.check_playback()
showard1be97432008-10-17 15:30:45 +00001702
1703
showard5add1c82009-05-26 19:27:46 +00001704 def test_final_reparse_recovery(self):
1705 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1706 self.monitor = self.god.create_mock_class(monitor_db.PidfileRunMonitor,
1707 'run_monitor')
1708 self._setup_post_job_task_expects(True)
1709 self._expect_execute_run_monitor()
1710 self._setup_post_parse_expects(True)
1711
1712 task = monitor_db.FinalReparseTask([self.queue_entry],
showarded2afea2009-07-07 20:54:07 +00001713 recover_run_monitor=self.monitor)
showard5add1c82009-05-26 19:27:46 +00001714 self.run_task(task, True)
1715 self.god.check_playback()
1716
1717
showard597bfd32009-05-08 18:22:50 +00001718 def _setup_gather_logs_expects(self, autoserv_killed=True,
showard6d1c1432009-08-20 23:30:39 +00001719 hqe_aborted=False, has_process=True):
showardd3dc1992009-04-22 21:01:40 +00001720 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1721 self.god.stub_class(monitor_db, 'FinalReparseTask')
showard597bfd32009-05-08 18:22:50 +00001722 self._setup_post_job_task_expects(not autoserv_killed, 'Gathering',
showard8cc058f2009-09-08 16:26:33 +00001723 hqe_aborted, host_status='Running')
showard6d1c1432009-08-20 23:30:39 +00001724 if hqe_aborted or not has_process:
showard597bfd32009-05-08 18:22:50 +00001725 exit_code = None
1726 elif autoserv_killed:
1727 exit_code = 271
1728 else:
1729 exit_code = 0
1730 self.pidfile_monitor.exit_code.expect_call().and_return(exit_code)
showard6d1c1432009-08-20 23:30:39 +00001731
1732 if has_process:
1733 if exit_code != 0:
1734 self._setup_post_job_run_monitor('.collect_crashinfo_execute')
1735 self.pidfile_monitor.has_process.expect_call().and_return(True)
showardcdaeae82009-08-31 18:32:48 +00001736 self.pidfile_monitor.has_process.expect_call().and_return(True)
showard6d1c1432009-08-20 23:30:39 +00001737 self._expect_copy_results(monitor=self.pidfile_monitor,
1738 queue_entry=self.queue_entry)
1739 else:
1740 # The first has_process() is in GatherLogsTask.run(), and the second
1741 # is in AgentTask._copy_results()
1742 self.pidfile_monitor.has_process.expect_call().and_return(False)
1743 self.pidfile_monitor.has_process.expect_call().and_return(False)
1744
showard8cc058f2009-09-08 16:26:33 +00001745 self.queue_entry.set_status.expect_call('Parsing')
showard6d1c1432009-08-20 23:30:39 +00001746 self.pidfile_monitor.has_process.expect_call().and_return(has_process)
1747
1748 if has_process:
1749 self.pidfile_monitor.num_tests_failed.expect_call().and_return(0)
showardb5626452009-06-30 01:57:28 +00001750
showardd3dc1992009-04-22 21:01:40 +00001751
showard6d1c1432009-08-20 23:30:39 +00001752 def _run_gather_logs_task(self, success=True):
showardd3dc1992009-04-22 21:01:40 +00001753 task = monitor_db.GatherLogsTask(self.job, [self.queue_entry])
1754 task.agent = DummyAgent()
1755 task.agent.dispatcher = self._dispatcher
showard6d1c1432009-08-20 23:30:39 +00001756 self.run_task(task, success)
showardd3dc1992009-04-22 21:01:40 +00001757 self.god.check_playback()
1758
1759
showard6b733412009-04-27 20:09:18 +00001760 def test_gather_logs_task(self):
1761 self._setup_gather_logs_expects()
1762 # no rebooting for this basic test
1763 self.job.reboot_after = models.RebootAfter.NEVER
1764 self.host.set_status.expect_call('Ready')
1765
1766 self._run_gather_logs_task()
1767
1768
showard0bbfc212009-04-29 21:06:13 +00001769 def test_gather_logs_task_successful_autoserv(self):
showard597bfd32009-05-08 18:22:50 +00001770 # When Autoserv exits successfully, no collect_crashinfo stage runs
1771 self._setup_gather_logs_expects(autoserv_killed=False)
showard0bbfc212009-04-29 21:06:13 +00001772 self.job.reboot_after = models.RebootAfter.NEVER
1773 self.host.set_status.expect_call('Ready')
1774
1775 self._run_gather_logs_task()
1776
1777
showard6b733412009-04-27 20:09:18 +00001778 def _setup_gather_task_cleanup_expects(self):
showard8cc058f2009-09-08 16:26:33 +00001779 models.SpecialTask.objects.create.expect_call(
1780 host=models.Host(id=self.host.id),
1781 task=models.SpecialTask.Task.CLEANUP)
showard6b733412009-04-27 20:09:18 +00001782
1783
1784 def test_gather_logs_reboot_hosts(self):
1785 self._setup_gather_logs_expects()
1786 self.job.reboot_after = models.RebootAfter.ALWAYS
1787 self._setup_gather_task_cleanup_expects()
1788
1789 self._run_gather_logs_task()
1790
1791
1792 def test_gather_logs_reboot_on_abort(self):
1793 self._setup_gather_logs_expects(hqe_aborted=True)
1794 self.job.reboot_after = models.RebootAfter.NEVER
1795 self._setup_gather_task_cleanup_expects()
1796
1797 self._run_gather_logs_task()
1798
1799
showard6d1c1432009-08-20 23:30:39 +00001800 def test_gather_logs_no_process(self):
1801 self._setup_gather_logs_expects(has_process=False)
1802 self.job.reboot_after = models.RebootAfter.NEVER
1803 self.host.set_status.expect_call('Ready')
1804
1805 self._run_gather_logs_task(success=False)
1806
1807
showard8cc058f2009-09-08 16:26:33 +00001808 def _test_cleanup_task_helper(self, success, task_id,
showarded2afea2009-07-07 20:54:07 +00001809 use_queue_entry=False):
showard8cc058f2009-09-08 16:26:33 +00001810 self._setup_special_task(task_id, models.SpecialTask.Task.CLEANUP,
1811 use_queue_entry)
1812 task_tag = '%d-cleanup' % task_id
1813
1814 self.task.activate.expect_call()
showarddb502762009-09-09 15:31:20 +00001815 self._setup_write_host_keyvals_expects(task_tag)
1816
showard45ae8192008-11-05 19:32:53 +00001817 self.host.set_status.expect_call('Cleaning')
showard8cc058f2009-09-08 16:26:33 +00001818
1819 if use_queue_entry:
1820 self.queue_entry.set_status.expect_call('Verifying')
1821
showardfa8629c2008-11-04 16:51:23 +00001822 if success:
showarded2afea2009-07-07 20:54:07 +00001823 self.setup_run_monitor(0, task_tag)
showardfa8629c2008-11-04 16:51:23 +00001824 self.host.update_field.expect_call('dirty', 0)
showard8cc058f2009-09-08 16:26:33 +00001825 if use_queue_entry:
1826 queue_entry = models.HostQueueEntry(id=self.queue_entry.id)
1827 models.SpecialTask.objects.create.expect_call(
1828 host=models.Host(id=self.host.id),
1829 task=models.SpecialTask.Task.VERIFY,
1830 queue_entry=queue_entry)
1831 else:
1832 self.host.set_status.expect_call('Ready')
showardfa8629c2008-11-04 16:51:23 +00001833 else:
showard58721a82009-08-20 23:32:40 +00001834 self._setup_prejob_task_failure(task_tag, use_queue_entry)
showardfa8629c2008-11-04 16:51:23 +00001835
showard8cc058f2009-09-08 16:26:33 +00001836 task = monitor_db.CleanupTask(task=self.task)
1837 task.host = self.host
showardfa8629c2008-11-04 16:51:23 +00001838 if use_queue_entry:
showard8cc058f2009-09-08 16:26:33 +00001839 task.queue_entry = self.queue_entry
showardfa8629c2008-11-04 16:51:23 +00001840
1841 self.run_task(task, success)
1842
1843 self.god.check_playback()
showard170873e2009-01-07 00:22:26 +00001844 self.assert_(set(task.cmd) >=
showarded2afea2009-07-07 20:54:07 +00001845 set([monitor_db._autoserv_path, '-p', '--cleanup', '-m',
1846 self.HOSTNAME, '-r',
1847 drone_manager.WORKING_DIRECTORY]))
showard87ba02a2009-04-20 19:37:32 +00001848 if use_queue_entry:
1849 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
showardfa8629c2008-11-04 16:51:23 +00001850
showard45ae8192008-11-05 19:32:53 +00001851 def test_cleanup_task(self):
showard8cc058f2009-09-08 16:26:33 +00001852 self._test_cleanup_task_helper(True, 1)
1853 self._test_cleanup_task_helper(False, 2)
showardfa8629c2008-11-04 16:51:23 +00001854
1855
showard45ae8192008-11-05 19:32:53 +00001856 def test_cleanup_task_with_queue_entry(self):
showard8cc058f2009-09-08 16:26:33 +00001857 self._test_cleanup_task_helper(False, 1, use_queue_entry=True)
showardfa8629c2008-11-04 16:51:23 +00001858
1859
showard5add1c82009-05-26 19:27:46 +00001860 def test_recovery_queue_task_aborted_early(self):
showarded2afea2009-07-07 20:54:07 +00001861 # abort a recovery QueueTask right after it's created
showard5add1c82009-05-26 19:27:46 +00001862 self.god.stub_class_method(monitor_db.QueueTask, '_log_abort')
1863 self.god.stub_class_method(monitor_db.QueueTask, '_finish_task')
1864 run_monitor = self.god.create_mock_class(monitor_db.PidfileRunMonitor,
1865 'run_monitor')
1866
showard8cc058f2009-09-08 16:26:33 +00001867 self.queue_entry.get_group_name.expect_call().and_return('')
showarded2afea2009-07-07 20:54:07 +00001868 self.queue_entry.execution_path.expect_call().and_return('tag')
showard5add1c82009-05-26 19:27:46 +00001869 run_monitor.kill.expect_call()
showard5add1c82009-05-26 19:27:46 +00001870 monitor_db.QueueTask._log_abort.expect_call()
1871 monitor_db.QueueTask._finish_task.expect_call()
1872
showarded2afea2009-07-07 20:54:07 +00001873 task = monitor_db.QueueTask(self.job, [self.queue_entry],
1874 recover_run_monitor=run_monitor)
showard5add1c82009-05-26 19:27:46 +00001875 task.abort()
1876 self.assert_(task.aborted)
1877 self.god.check_playback()
1878
1879
showard54c1ea92009-05-20 00:32:58 +00001880class HostTest(BaseSchedulerTest):
1881 def test_cmp_for_sort(self):
1882 expected_order = [
1883 'alice', 'Host1', 'host2', 'host3', 'host09', 'HOST010',
1884 'host10', 'host11', 'yolkfolk']
1885 hostname_idx = list(monitor_db.Host._fields).index('hostname')
1886 row = [None] * len(monitor_db.Host._fields)
1887 hosts = []
1888 for hostname in expected_order:
1889 row[hostname_idx] = hostname
1890 hosts.append(monitor_db.Host(row=row, new_record=True))
1891
1892 host1 = hosts[expected_order.index('Host1')]
1893 host010 = hosts[expected_order.index('HOST010')]
1894 host10 = hosts[expected_order.index('host10')]
1895 host3 = hosts[expected_order.index('host3')]
1896 alice = hosts[expected_order.index('alice')]
1897 self.assertEqual(0, monitor_db.Host.cmp_for_sort(host10, host10))
1898 self.assertEqual(1, monitor_db.Host.cmp_for_sort(host10, host010))
1899 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host010, host10))
1900 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host1, host10))
1901 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host1, host010))
1902 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host3, host10))
1903 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host3, host010))
1904 self.assertEqual(1, monitor_db.Host.cmp_for_sort(host3, host1))
1905 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host1, host3))
1906 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(alice, host3))
1907 self.assertEqual(1, monitor_db.Host.cmp_for_sort(host3, alice))
1908 self.assertEqual(0, monitor_db.Host.cmp_for_sort(alice, alice))
1909
1910 hosts.sort(cmp=monitor_db.Host.cmp_for_sort)
1911 self.assertEqual(expected_order, [h.hostname for h in hosts])
1912
1913 hosts.reverse()
1914 hosts.sort(cmp=monitor_db.Host.cmp_for_sort)
1915 self.assertEqual(expected_order, [h.hostname for h in hosts])
1916
1917
showardf1ae3542009-05-11 19:26:02 +00001918class HostQueueEntryTest(BaseSchedulerTest):
1919 def _create_hqe(self, dependency_labels=(), **create_job_kwargs):
1920 job = self._create_job(**create_job_kwargs)
1921 for label in dependency_labels:
1922 job.dependency_labels.add(label)
1923 hqes = list(monitor_db.HostQueueEntry.fetch(where='job_id=%d' % job.id))
1924 self.assertEqual(1, len(hqes))
1925 return hqes[0]
1926
showard77182562009-06-10 00:16:05 +00001927
showardf1ae3542009-05-11 19:26:02 +00001928 def _check_hqe_labels(self, hqe, expected_labels):
1929 expected_labels = set(expected_labels)
1930 label_names = set(label.name for label in hqe.get_labels())
1931 self.assertEqual(expected_labels, label_names)
1932
showard77182562009-06-10 00:16:05 +00001933
showardf1ae3542009-05-11 19:26:02 +00001934 def test_get_labels_empty(self):
1935 hqe = self._create_hqe(hosts=[1])
1936 labels = list(hqe.get_labels())
1937 self.assertEqual([], labels)
1938
showard77182562009-06-10 00:16:05 +00001939
showardf1ae3542009-05-11 19:26:02 +00001940 def test_get_labels_metahost(self):
1941 hqe = self._create_hqe(metahosts=[2])
1942 self._check_hqe_labels(hqe, ['label2'])
1943
showard77182562009-06-10 00:16:05 +00001944
showardf1ae3542009-05-11 19:26:02 +00001945 def test_get_labels_dependancies(self):
1946 hqe = self._create_hqe(dependency_labels=(self.label3, self.label4),
1947 metahosts=[1])
1948 self._check_hqe_labels(hqe, ['label1', 'label3', 'label4'])
1949
1950
showardb2e2c322008-10-14 17:33:55 +00001951class JobTest(BaseSchedulerTest):
showard2bab8f42008-11-12 18:15:22 +00001952 def setUp(self):
1953 super(JobTest, self).setUp()
showard170873e2009-01-07 00:22:26 +00001954 self.god.stub_with(
1955 drone_manager.DroneManager, 'attach_file_to_execution',
1956 mock.mock_function('attach_file_to_execution',
1957 default_return_val='/test/path/tmp/foo'))
showard2bab8f42008-11-12 18:15:22 +00001958
showard8cc058f2009-09-08 16:26:33 +00001959 def _mock_create(**kwargs):
1960 task = models.SpecialTask(**kwargs)
1961 task.save()
1962 self._task = task
1963 self.god.stub_with(models.SpecialTask.objects, 'create', _mock_create)
1964
showard2bab8f42008-11-12 18:15:22 +00001965
showard77182562009-06-10 00:16:05 +00001966 def _test_pre_job_tasks_helper(self):
1967 """
showard8cc058f2009-09-08 16:26:33 +00001968 Calls HQE._do_schedule_pre_job_tasks() and returns the created special
1969 task
showard77182562009-06-10 00:16:05 +00001970 """
showard8cc058f2009-09-08 16:26:33 +00001971 self._task = None
1972 queue_entry = monitor_db.HostQueueEntry.fetch('id = 1')[0]
1973 queue_entry._do_schedule_pre_job_tasks()
1974 return self._task
showard2bab8f42008-11-12 18:15:22 +00001975
1976
showarde58e3f82008-11-20 19:04:59 +00001977 def _test_run_helper(self, expect_agent=True, expect_starting=False,
1978 expect_pending=False):
1979 if expect_starting:
1980 expected_status = models.HostQueueEntry.Status.STARTING
1981 elif expect_pending:
1982 expected_status = models.HostQueueEntry.Status.PENDING
1983 else:
1984 expected_status = models.HostQueueEntry.Status.VERIFYING
showard8cc058f2009-09-08 16:26:33 +00001985 job = monitor_db.Job.fetch('id = 1')[0]
1986 queue_entry = monitor_db.HostQueueEntry.fetch('id = 1')[0]
showard77182562009-06-10 00:16:05 +00001987 assert queue_entry.job is job
showard8cc058f2009-09-08 16:26:33 +00001988 job.run_if_ready(queue_entry)
showardb2e2c322008-10-14 17:33:55 +00001989
showard2bab8f42008-11-12 18:15:22 +00001990 self.god.check_playback()
showard8cc058f2009-09-08 16:26:33 +00001991
1992 self._dispatcher._schedule_delay_tasks()
1993 self._dispatcher._schedule_running_host_queue_entries()
1994 agent = self._dispatcher._agents[0]
1995
showard77182562009-06-10 00:16:05 +00001996 actual_status = models.HostQueueEntry.smart_get(1).status
1997 self.assertEquals(expected_status, actual_status)
showard2bab8f42008-11-12 18:15:22 +00001998
showard9976ce92008-10-15 20:28:13 +00001999 if not expect_agent:
2000 self.assertEquals(agent, None)
2001 return
2002
showardb2e2c322008-10-14 17:33:55 +00002003 self.assert_(isinstance(agent, monitor_db.Agent))
showard8cc058f2009-09-08 16:26:33 +00002004 self.assert_(agent.task)
2005 return agent.task
showardc9ae1782009-01-30 01:42:37 +00002006
2007
showard8375ce02009-10-12 20:35:13 +00002008 def test_schedule_running_host_queue_entries_fail(self):
2009 self._create_job(hosts=[2])
2010 self._update_hqe("status='%s', execution_subdir=''" %
2011 models.HostQueueEntry.Status.PENDING)
2012 job = monitor_db.Job.fetch('id = 1')[0]
2013 queue_entry = monitor_db.HostQueueEntry.fetch('id = 1')[0]
2014 assert queue_entry.job is job
2015 job.run_if_ready(queue_entry)
2016 self.assertEqual(queue_entry.status,
2017 models.HostQueueEntry.Status.STARTING)
2018 self.assert_(queue_entry.execution_subdir)
2019 self.god.check_playback()
2020
2021 class dummy_test_agent(object):
2022 task = 'dummy_test_agent'
2023 self._dispatcher._register_agent_for_ids(
2024 self._dispatcher._host_agents, [queue_entry.host.id],
2025 dummy_test_agent)
2026
2027 # Attempted to schedule on a host that already has an agent.
2028 self.assertRaises(monitor_db.SchedulerError,
2029 self._dispatcher._schedule_running_host_queue_entries)
2030
2031
showard77182562009-06-10 00:16:05 +00002032 def test_run_if_ready_delays(self):
2033 # Also tests Job.run_with_ready_delay() on atomic group jobs.
2034 django_job = self._create_job(hosts=[5, 6], atomic_group=1)
2035 job = monitor_db.Job(django_job.id)
2036 self.assertEqual(1, job.synch_count)
2037 django_hqes = list(models.HostQueueEntry.objects.filter(job=job.id))
2038 self.assertEqual(2, len(django_hqes))
2039 self.assertEqual(2, django_hqes[0].atomic_group.max_number_of_machines)
2040
2041 def set_hqe_status(django_hqe, status):
2042 django_hqe.status = status
2043 django_hqe.save()
2044 monitor_db.HostQueueEntry(django_hqe.id).host.set_status(status)
2045
2046 # An initial state, our synch_count is 1
2047 set_hqe_status(django_hqes[0], models.HostQueueEntry.Status.VERIFYING)
2048 set_hqe_status(django_hqes[1], models.HostQueueEntry.Status.PENDING)
2049
2050 # So that we don't depend on the config file value during the test.
2051 self.assert_(scheduler_config.config
2052 .secs_to_wait_for_atomic_group_hosts is not None)
2053 self.god.stub_with(scheduler_config.config,
2054 'secs_to_wait_for_atomic_group_hosts', 123456)
2055
2056 # Get the pending one as a monitor_db.HostQueueEntry object.
showard8cc058f2009-09-08 16:26:33 +00002057 hqe = monitor_db.HostQueueEntry(django_hqes[1].id)
showard77182562009-06-10 00:16:05 +00002058 self.assert_(not job._delay_ready_task)
2059 self.assertTrue(job.is_ready())
2060
2061 # Ready with one pending, one verifying and an atomic group should
2062 # result in a DelayCallTask to re-check if we're ready a while later.
showard8cc058f2009-09-08 16:26:33 +00002063 job.run_if_ready(hqe)
2064 self.assertEquals('Waiting', hqe.status)
2065 self._dispatcher._schedule_delay_tasks()
2066 self.assertEquals('Pending', hqe.status)
2067 agent = self._dispatcher._agents[0]
showard77182562009-06-10 00:16:05 +00002068 self.assert_(job._delay_ready_task)
2069 self.assert_(isinstance(agent, monitor_db.Agent))
showard8cc058f2009-09-08 16:26:33 +00002070 self.assert_(agent.task)
2071 delay_task = agent.task
2072 self.assert_(isinstance(delay_task, monitor_db.DelayedCallTask))
showard77182562009-06-10 00:16:05 +00002073 self.assert_(not delay_task.is_done())
2074
showard8cc058f2009-09-08 16:26:33 +00002075 self.god.stub_function(delay_task, 'abort')
2076
showard77182562009-06-10 00:16:05 +00002077 self.god.stub_function(job, 'run')
2078
showardd2014822009-10-12 20:26:58 +00002079 self.god.stub_function(job, '_pending_count')
2080 self.god.stub_function(job, '_pending_threshold')
2081
showard77182562009-06-10 00:16:05 +00002082 # Test that the DelayedCallTask's callback queued up above does the
showardd2014822009-10-12 20:26:58 +00002083 # correct thing and does not call run if there are not enough hosts
2084 # in pending after the delay.
2085 job._pending_threshold.expect_call(hqe.atomic_group).and_return(9)
2086 job._pending_count.expect_call().and_return(0)
2087 delay_task._callback()
2088 self.god.check_playback()
2089
2090 # Test that the DelayedCallTask's callback queued up above does the
2091 # correct thing and returns the Agent returned by job.run() if
2092 # there are still enough hosts pending after the delay.
2093 job._pending_threshold.expect_call(hqe.atomic_group).and_return(4)
2094 job._pending_count.expect_call().and_return(4)
showard8cc058f2009-09-08 16:26:33 +00002095 job.run.expect_call(hqe)
2096 delay_task._callback()
2097 self.god.check_playback()
showard77182562009-06-10 00:16:05 +00002098
showardd2014822009-10-12 20:26:58 +00002099 job._pending_threshold.expect_call(hqe.atomic_group).and_return(4)
2100 job._pending_count.expect_call().and_return(4)
2101
showard77182562009-06-10 00:16:05 +00002102 # Adjust the delay deadline so that enough time has passed.
2103 job._delay_ready_task.end_time = time.time() - 111111
showard8cc058f2009-09-08 16:26:33 +00002104 job.run.expect_call(hqe)
showard77182562009-06-10 00:16:05 +00002105 # ...the delay_expired condition should cause us to call run()
showard8cc058f2009-09-08 16:26:33 +00002106 self._dispatcher._handle_agents()
2107 self.god.check_playback()
2108 delay_task.success = False
showard77182562009-06-10 00:16:05 +00002109
2110 # Adjust the delay deadline back so that enough time has not passed.
2111 job._delay_ready_task.end_time = time.time() + 111111
showard8cc058f2009-09-08 16:26:33 +00002112 self._dispatcher._handle_agents()
2113 self.god.check_playback()
showard77182562009-06-10 00:16:05 +00002114
showard77182562009-06-10 00:16:05 +00002115 # Now max_number_of_machines HQEs are in pending state. Remaining
2116 # delay will now be ignored.
showard8cc058f2009-09-08 16:26:33 +00002117 other_hqe = monitor_db.HostQueueEntry(django_hqes[0].id)
2118 self.god.unstub(job, 'run')
showardd2014822009-10-12 20:26:58 +00002119 self.god.unstub(job, '_pending_count')
2120 self.god.unstub(job, '_pending_threshold')
showard77182562009-06-10 00:16:05 +00002121 # ...the over_max_threshold test should cause us to call run()
showard8cc058f2009-09-08 16:26:33 +00002122 delay_task.abort.expect_call()
2123 other_hqe.on_pending()
2124 self.assertEquals('Starting', other_hqe.status)
2125 self.assertEquals('Starting', hqe.status)
2126 self.god.stub_function(job, 'run')
2127 self.god.unstub(delay_task, 'abort')
showard77182562009-06-10 00:16:05 +00002128
showard8cc058f2009-09-08 16:26:33 +00002129 hqe.set_status('Pending')
2130 other_hqe.set_status('Pending')
showard708b3522009-08-20 23:26:15 +00002131 # Now we're not over the max for the atomic group. But all assigned
2132 # hosts are in pending state. over_max_threshold should make us run().
showard8cc058f2009-09-08 16:26:33 +00002133 hqe.atomic_group.max_number_of_machines += 1
2134 hqe.atomic_group.save()
2135 job.run.expect_call(hqe)
2136 hqe.on_pending()
2137 self.god.check_playback()
2138 hqe.atomic_group.max_number_of_machines -= 1
2139 hqe.atomic_group.save()
showard708b3522009-08-20 23:26:15 +00002140
showard77182562009-06-10 00:16:05 +00002141 other_hqe = monitor_db.HostQueueEntry(django_hqes[0].id)
showard8cc058f2009-09-08 16:26:33 +00002142 self.assertTrue(hqe.job is other_hqe.job)
showard77182562009-06-10 00:16:05 +00002143 # DBObject classes should reuse instances so these should be the same.
2144 self.assertEqual(job, other_hqe.job)
showard8cc058f2009-09-08 16:26:33 +00002145 self.assertEqual(other_hqe.job, hqe.job)
showard77182562009-06-10 00:16:05 +00002146 # Be sure our delay was not lost during the other_hqe construction.
showard8cc058f2009-09-08 16:26:33 +00002147 self.assertEqual(job._delay_ready_task, delay_task)
showard77182562009-06-10 00:16:05 +00002148 self.assert_(job._delay_ready_task)
2149 self.assertFalse(job._delay_ready_task.is_done())
2150 self.assertFalse(job._delay_ready_task.aborted)
2151
2152 # We want the real run() to be called below.
2153 self.god.unstub(job, 'run')
2154
2155 # We pass in the other HQE this time the same way it would happen
2156 # for real when one host finishes verifying and enters pending.
showard8cc058f2009-09-08 16:26:33 +00002157 job.run_if_ready(other_hqe)
showard77182562009-06-10 00:16:05 +00002158
2159 # The delayed task must be aborted by the actual run() call above.
2160 self.assertTrue(job._delay_ready_task.aborted)
2161 self.assertFalse(job._delay_ready_task.success)
2162 self.assertTrue(job._delay_ready_task.is_done())
2163
2164 # Check that job run() and _finish_run() were called by the above:
showard8cc058f2009-09-08 16:26:33 +00002165 self._dispatcher._schedule_running_host_queue_entries()
2166 agent = self._dispatcher._agents[0]
2167 self.assert_(agent.task)
2168 task = agent.task
2169 self.assert_(isinstance(task, monitor_db.QueueTask))
showard77182562009-06-10 00:16:05 +00002170 # Requery these hqes in order to verify the status from the DB.
2171 django_hqes = list(models.HostQueueEntry.objects.filter(job=job.id))
2172 for entry in django_hqes:
2173 self.assertEqual(models.HostQueueEntry.Status.STARTING,
2174 entry.status)
2175
2176 # We're already running, but more calls to run_with_ready_delay can
2177 # continue to come in due to straggler hosts enter Pending. Make
2178 # sure we don't do anything.
showard8cc058f2009-09-08 16:26:33 +00002179 self.god.stub_function(job, 'run')
2180 job.run_with_ready_delay(hqe)
2181 self.god.check_playback()
2182 self.god.unstub(job, 'run')
showard77182562009-06-10 00:16:05 +00002183
2184
2185 def test__atomic_and_has_started__on_atomic(self):
2186 self._create_job(hosts=[5, 6], atomic_group=1)
showard8cc058f2009-09-08 16:26:33 +00002187 job = monitor_db.Job.fetch('id = 1')[0]
showard77182562009-06-10 00:16:05 +00002188 self.assertFalse(job._atomic_and_has_started())
showardaf8b4ca2009-06-16 18:47:26 +00002189
showard77182562009-06-10 00:16:05 +00002190 self._update_hqe("status='Pending'")
2191 self.assertFalse(job._atomic_and_has_started())
2192 self._update_hqe("status='Verifying'")
2193 self.assertFalse(job._atomic_and_has_started())
showardaf8b4ca2009-06-16 18:47:26 +00002194 self.assertFalse(job._atomic_and_has_started())
2195 self._update_hqe("status='Failed'")
2196 self.assertFalse(job._atomic_and_has_started())
2197 self._update_hqe("status='Stopped'")
2198 self.assertFalse(job._atomic_and_has_started())
2199
showard77182562009-06-10 00:16:05 +00002200 self._update_hqe("status='Starting'")
2201 self.assertTrue(job._atomic_and_has_started())
2202 self._update_hqe("status='Completed'")
2203 self.assertTrue(job._atomic_and_has_started())
2204 self._update_hqe("status='Aborted'")
showard77182562009-06-10 00:16:05 +00002205
2206
2207 def test__atomic_and_has_started__not_atomic(self):
2208 self._create_job(hosts=[1, 2])
showard8cc058f2009-09-08 16:26:33 +00002209 job = monitor_db.Job.fetch('id = 1')[0]
showard77182562009-06-10 00:16:05 +00002210 self.assertFalse(job._atomic_and_has_started())
2211 self._update_hqe("status='Starting'")
2212 self.assertFalse(job._atomic_and_has_started())
2213
2214
showard8cc058f2009-09-08 16:26:33 +00002215 def _check_special_task(self, task, task_type, queue_entry_id=None):
2216 self.assertEquals(task.task, task_type)
2217 self.assertEquals(task.host.id, 1)
2218 if queue_entry_id:
2219 self.assertEquals(task.queue_entry.id, queue_entry_id)
2220
2221
showardb2e2c322008-10-14 17:33:55 +00002222 def test_run_asynchronous(self):
2223 self._create_job(hosts=[1, 2])
2224
showard8cc058f2009-09-08 16:26:33 +00002225 task = self._test_pre_job_tasks_helper()
showardb2e2c322008-10-14 17:33:55 +00002226
showard8cc058f2009-09-08 16:26:33 +00002227 self._check_special_task(task, models.SpecialTask.Task.VERIFY, 1)
showardb2e2c322008-10-14 17:33:55 +00002228
showardb2e2c322008-10-14 17:33:55 +00002229
showard9976ce92008-10-15 20:28:13 +00002230 def test_run_asynchronous_skip_verify(self):
2231 job = self._create_job(hosts=[1, 2])
2232 job.run_verify = False
2233 job.save()
2234
showard8cc058f2009-09-08 16:26:33 +00002235 task = self._test_pre_job_tasks_helper()
showard9976ce92008-10-15 20:28:13 +00002236
showard8cc058f2009-09-08 16:26:33 +00002237 self.assertEquals(task, None)
showard9976ce92008-10-15 20:28:13 +00002238
2239
showardb2e2c322008-10-14 17:33:55 +00002240 def test_run_synchronous_verify(self):
2241 self._create_job(hosts=[1, 2], synchronous=True)
2242
showard8cc058f2009-09-08 16:26:33 +00002243 task = self._test_pre_job_tasks_helper()
2244
2245 self._check_special_task(task, models.SpecialTask.Task.VERIFY, 1)
showardb2e2c322008-10-14 17:33:55 +00002246
2247
showard9976ce92008-10-15 20:28:13 +00002248 def test_run_synchronous_skip_verify(self):
2249 job = self._create_job(hosts=[1, 2], synchronous=True)
2250 job.run_verify = False
2251 job.save()
2252
showard8cc058f2009-09-08 16:26:33 +00002253 task = self._test_pre_job_tasks_helper()
2254
2255 self.assertEquals(task, None)
showard9976ce92008-10-15 20:28:13 +00002256
2257
showardb2e2c322008-10-14 17:33:55 +00002258 def test_run_synchronous_ready(self):
2259 self._create_job(hosts=[1, 2], synchronous=True)
showardd9ac4452009-02-07 02:04:37 +00002260 self._update_hqe("status='Pending', execution_subdir=''")
showardb2e2c322008-10-14 17:33:55 +00002261
showard8cc058f2009-09-08 16:26:33 +00002262 queue_task = self._test_run_helper(expect_starting=True)
showardb2e2c322008-10-14 17:33:55 +00002263
2264 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
2265 self.assertEquals(queue_task.job.id, 1)
2266 hqe_ids = [hqe.id for hqe in queue_task.queue_entries]
2267 self.assertEquals(hqe_ids, [1, 2])
2268
2269
showard77182562009-06-10 00:16:05 +00002270 def test_run_atomic_group_already_started(self):
2271 self._create_job(hosts=[5, 6], atomic_group=1, synchronous=True)
2272 self._update_hqe("status='Starting', execution_subdir=''")
2273
showard8cc058f2009-09-08 16:26:33 +00002274 job = monitor_db.Job.fetch('id = 1')[0]
2275 queue_entry = monitor_db.HostQueueEntry.fetch('id = 1')[0]
showard77182562009-06-10 00:16:05 +00002276 assert queue_entry.job is job
2277 self.assertEqual(None, job.run(queue_entry))
2278
2279 self.god.check_playback()
2280
2281
showardf1ae3542009-05-11 19:26:02 +00002282 def test_run_synchronous_atomic_group_ready(self):
2283 self._create_job(hosts=[5, 6], atomic_group=1, synchronous=True)
2284 self._update_hqe("status='Pending', execution_subdir=''")
2285
showard8cc058f2009-09-08 16:26:33 +00002286 queue_task = self._test_run_helper(expect_starting=True)
showardf1ae3542009-05-11 19:26:02 +00002287
2288 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
showard77182562009-06-10 00:16:05 +00002289 # Atomic group jobs that do not depend on a specific label in the
2290 # atomic group will use the atomic group name as their group name.
showardf1ae3542009-05-11 19:26:02 +00002291 self.assertEquals(queue_task.group_name, 'atomic1')
2292
2293
2294 def test_run_synchronous_atomic_group_with_label_ready(self):
2295 job = self._create_job(hosts=[5, 6], atomic_group=1, synchronous=True)
2296 job.dependency_labels.add(self.label4)
2297 self._update_hqe("status='Pending', execution_subdir=''")
2298
showard8cc058f2009-09-08 16:26:33 +00002299 queue_task = self._test_run_helper(expect_starting=True)
showardf1ae3542009-05-11 19:26:02 +00002300
2301 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
2302 # Atomic group jobs that also specify a label in the atomic group
2303 # will use the label name as their group name.
2304 self.assertEquals(queue_task.group_name, 'label4')
2305
2306
showard21baa452008-10-21 00:08:39 +00002307 def test_reboot_before_always(self):
2308 job = self._create_job(hosts=[1])
showard0fc38302008-10-23 00:44:07 +00002309 job.reboot_before = models.RebootBefore.ALWAYS
showard21baa452008-10-21 00:08:39 +00002310 job.save()
2311
showard8cc058f2009-09-08 16:26:33 +00002312 task = self._test_pre_job_tasks_helper()
2313
2314 self._check_special_task(task, models.SpecialTask.Task.CLEANUP)
showard21baa452008-10-21 00:08:39 +00002315
2316
2317 def _test_reboot_before_if_dirty_helper(self, expect_reboot):
2318 job = self._create_job(hosts=[1])
showard0fc38302008-10-23 00:44:07 +00002319 job.reboot_before = models.RebootBefore.IF_DIRTY
showard21baa452008-10-21 00:08:39 +00002320 job.save()
2321
showard8cc058f2009-09-08 16:26:33 +00002322 task = self._test_pre_job_tasks_helper()
showard21baa452008-10-21 00:08:39 +00002323 if expect_reboot:
showard8cc058f2009-09-08 16:26:33 +00002324 task_type = models.SpecialTask.Task.CLEANUP
2325 else:
2326 task_type = models.SpecialTask.Task.VERIFY
2327 self._check_special_task(task, task_type)
showard21baa452008-10-21 00:08:39 +00002328
showard77182562009-06-10 00:16:05 +00002329
showard21baa452008-10-21 00:08:39 +00002330 def test_reboot_before_if_dirty(self):
2331 models.Host.smart_get(1).update_object(dirty=True)
2332 self._test_reboot_before_if_dirty_helper(True)
2333
2334
2335 def test_reboot_before_not_dirty(self):
2336 models.Host.smart_get(1).update_object(dirty=False)
2337 self._test_reboot_before_if_dirty_helper(False)
2338
2339
showardf1ae3542009-05-11 19:26:02 +00002340 def test_next_group_name(self):
2341 django_job = self._create_job(metahosts=[1])
2342 job = monitor_db.Job(id=django_job.id)
2343 self.assertEqual('group0', job._next_group_name())
2344
2345 for hqe in django_job.hostqueueentry_set.filter():
2346 hqe.execution_subdir = 'my_rack.group0'
2347 hqe.save()
2348 self.assertEqual('my_rack.group1', job._next_group_name('my/rack'))
2349
2350
2351class TopLevelFunctionsTest(unittest.TestCase):
mblighe7d9c602009-07-02 19:02:33 +00002352 def setUp(self):
2353 self.god = mock.mock_god()
2354
2355
2356 def tearDown(self):
2357 self.god.unstub_all()
2358
2359
showardf1ae3542009-05-11 19:26:02 +00002360 def test_autoserv_command_line(self):
2361 machines = 'abcd12,efgh34'
showardf1ae3542009-05-11 19:26:02 +00002362 extra_args = ['-Z', 'hello']
2363 expected_command_line = [monitor_db._autoserv_path, '-p',
showarded2afea2009-07-07 20:54:07 +00002364 '-m', machines, '-r',
2365 drone_manager.WORKING_DIRECTORY]
showardf1ae3542009-05-11 19:26:02 +00002366
showarded2afea2009-07-07 20:54:07 +00002367 command_line = monitor_db._autoserv_command_line(machines, extra_args)
showarde9c69362009-06-30 01:58:03 +00002368 self.assertEqual(expected_command_line + ['--verbose'] + extra_args,
2369 command_line)
showardf1ae3542009-05-11 19:26:02 +00002370
2371 class FakeJob(object):
2372 owner = 'Bob'
2373 name = 'fake job name'
mblighe7d9c602009-07-02 19:02:33 +00002374 id = 1337
2375
2376 class FakeHQE(object):
2377 job = FakeJob
showardf1ae3542009-05-11 19:26:02 +00002378
2379 command_line = monitor_db._autoserv_command_line(
showarded2afea2009-07-07 20:54:07 +00002380 machines, extra_args=[], queue_entry=FakeHQE, verbose=False)
showardf1ae3542009-05-11 19:26:02 +00002381 self.assertEqual(expected_command_line +
2382 ['-u', FakeJob.owner, '-l', FakeJob.name],
2383 command_line)
2384
showard21baa452008-10-21 00:08:39 +00002385
showardce38e0c2008-05-29 19:36:16 +00002386if __name__ == '__main__':
jadmanski0afbb632008-06-06 21:10:57 +00002387 unittest.main()