blob: 5c754c0b48ff9d5802f65fe3633eef263053f443 [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
showardce38e0c2008-05-29 19:36:16 +00004import common
mbligh8bcd23a2009-02-03 19:14:06 +00005import MySQLdb
showard364fe862008-10-17 02:01:16 +00006from autotest_lib.frontend import setup_django_environment
showardb6d16622009-05-26 19:35:29 +00007from autotest_lib.frontend.afe import frontend_test_utils
jadmanskifb7cfb12008-07-09 14:13:21 +00008from autotest_lib.client.common_lib import global_config, host_protections
jadmanski3d161b02008-06-06 15:43:36 +00009from autotest_lib.client.common_lib.test_utils import mock
showard442e71e2008-10-06 10:05:20 +000010from autotest_lib.database import database_connection, migrate
showard21baa452008-10-21 00:08:39 +000011from autotest_lib.frontend import thread_local
showardb1e51872008-10-07 11:08:18 +000012from autotest_lib.frontend.afe import models
showard170873e2009-01-07 00:22:26 +000013from autotest_lib.scheduler import monitor_db, drone_manager, email_manager
showardd1ee1dd2009-01-07 21:33:08 +000014from autotest_lib.scheduler import scheduler_config
showardce38e0c2008-05-29 19:36:16 +000015
16_DEBUG = False
17
showarda3c58572009-03-12 20:36:59 +000018
showard170873e2009-01-07 00:22:26 +000019class DummyAgent(object):
20 _is_running = False
21 _is_done = False
22 num_processes = 1
23 host_ids = []
24 queue_entry_ids = []
25
26 def is_running(self):
27 return self._is_running
28
29
30 def tick(self):
31 self._is_running = True
32
33
34 def is_done(self):
35 return self._is_done
36
37
38 def set_done(self, done):
39 self._is_done = done
40 self._is_running = not done
showard04c82c52008-05-29 19:38:12 +000041
showard56193bb2008-08-13 20:07:41 +000042
43class IsRow(mock.argument_comparator):
44 def __init__(self, row_id):
45 self.row_id = row_id
showardce38e0c2008-05-29 19:36:16 +000046
47
showard56193bb2008-08-13 20:07:41 +000048 def is_satisfied_by(self, parameter):
49 return list(parameter)[0] == self.row_id
50
51
52 def __str__(self):
53 return 'row with id %s' % self.row_id
54
55
showardd3dc1992009-04-22 21:01:40 +000056class IsAgentWithTask(mock.argument_comparator):
57 def __init__(self, task):
58 self._task = task
59
60
61 def is_satisfied_by(self, parameter):
62 if not isinstance(parameter, monitor_db.Agent):
63 return False
64 tasks = list(parameter.queue.queue)
65 if len(tasks) != 1:
66 return False
67 return tasks[0] == self._task
68
69
showard6b733412009-04-27 20:09:18 +000070def _set_host_and_qe_ids(agent_or_task, id_list=None):
71 if id_list is None:
72 id_list = []
73 agent_or_task.host_ids = agent_or_task.queue_entry_ids = id_list
74
75
showardb6d16622009-05-26 19:35:29 +000076class BaseSchedulerTest(unittest.TestCase,
77 frontend_test_utils.FrontendTestMixin):
showard50c0e712008-09-22 16:20:37 +000078 _config_section = 'AUTOTEST_WEB'
showardce38e0c2008-05-29 19:36:16 +000079
jadmanski0afbb632008-06-06 21:10:57 +000080 def _do_query(self, sql):
showardb1e51872008-10-07 11:08:18 +000081 self._database.execute(sql)
showardce38e0c2008-05-29 19:36:16 +000082
83
showardb6d16622009-05-26 19:35:29 +000084 def _set_monitor_stubs(self):
85 # Clear the instance cache as this is a brand new database.
86 monitor_db.DBObject._clear_instance_cache()
showardce38e0c2008-05-29 19:36:16 +000087
showardb1e51872008-10-07 11:08:18 +000088 self._database = (
89 database_connection.DatabaseConnection.get_test_database(
90 self._test_db_file))
91 self._database.connect()
92 self._database.debug = _DEBUG
showardce38e0c2008-05-29 19:36:16 +000093
showardb1e51872008-10-07 11:08:18 +000094 monitor_db._db = self._database
showard170873e2009-01-07 00:22:26 +000095 monitor_db._drone_manager._results_dir = '/test/path'
96 monitor_db._drone_manager._temporary_directory = '/test/path/tmp'
showard56193bb2008-08-13 20:07:41 +000097
98
showard56193bb2008-08-13 20:07:41 +000099 def setUp(self):
showardb6d16622009-05-26 19:35:29 +0000100 self._frontend_common_setup()
showard56193bb2008-08-13 20:07:41 +0000101 self._set_monitor_stubs()
102 self._dispatcher = monitor_db.Dispatcher()
showardce38e0c2008-05-29 19:36:16 +0000103
104
showard56193bb2008-08-13 20:07:41 +0000105 def tearDown(self):
showardb6d16622009-05-26 19:35:29 +0000106 self._database.disconnect()
107 self._frontend_common_teardown()
showardce38e0c2008-05-29 19:36:16 +0000108
109
showard56193bb2008-08-13 20:07:41 +0000110 def _update_hqe(self, set, where=''):
111 query = 'UPDATE host_queue_entries SET ' + set
112 if where:
113 query += ' WHERE ' + where
114 self._do_query(query)
115
116
showarda3c58572009-03-12 20:36:59 +0000117class DBObjectTest(BaseSchedulerTest):
118 # It may seem odd to subclass BaseSchedulerTest for this but it saves us
119 # duplicating some setup work for what we want to test.
120
121
122 def test_compare_fields_in_row(self):
123 host = monitor_db.Host(id=1)
124 fields = list(host._fields)
125 row_data = [getattr(host, fieldname) for fieldname in fields]
126 self.assertEqual({}, host._compare_fields_in_row(row_data))
127 row_data[fields.index('hostname')] = 'spam'
128 self.assertEqual({'hostname': ('host1', 'spam')},
129 host._compare_fields_in_row(row_data))
130 row_data[fields.index('id')] = 23
131 self.assertEqual({'hostname': ('host1', 'spam'), 'id': (1, 23)},
132 host._compare_fields_in_row(row_data))
133
134
135 def test_always_query(self):
136 host_a = monitor_db.Host(id=2)
137 self.assertEqual(host_a.hostname, 'host2')
138 self._do_query('UPDATE hosts SET hostname="host2-updated" WHERE id=2')
139 host_b = monitor_db.Host(id=2, always_query=True)
140 self.assert_(host_a is host_b, 'Cached instance not returned.')
141 self.assertEqual(host_a.hostname, 'host2-updated',
142 'Database was not re-queried')
143
144 # If either of these are called, a query was made when it shouldn't be.
145 host_a._compare_fields_in_row = lambda _: self.fail('eek! a query!')
showard12f3e322009-05-13 21:27:42 +0000146 host_a._update_fields_from_row = host_a._compare_fields_in_row
showarda3c58572009-03-12 20:36:59 +0000147 host_c = monitor_db.Host(id=2, always_query=False)
148 self.assert_(host_a is host_c, 'Cached instance not returned')
149
150
151 def test_delete(self):
152 host = monitor_db.Host(id=3)
153 host.delete()
154 host = self.assertRaises(monitor_db.DBError, monitor_db.Host, id=3,
155 always_query=False)
156 host = self.assertRaises(monitor_db.DBError, monitor_db.Host, id=3,
157 always_query=True)
158
showard76e29d12009-04-15 21:53:10 +0000159 def test_save(self):
160 # Dummy Job to avoid creating a one in the HostQueueEntry __init__.
161 class MockJob(object):
162 def __init__(self, id):
163 pass
164 def tag(self):
165 return 'MockJob'
166 self.god.stub_with(monitor_db, 'Job', MockJob)
167 hqe = monitor_db.HostQueueEntry(
168 new_record=True,
showard12f3e322009-05-13 21:27:42 +0000169 row=[0, 1, 2, 'Queued', None, 0, 0, 0, '.', None, False, None])
showard76e29d12009-04-15 21:53:10 +0000170 hqe.save()
171 new_id = hqe.id
172 # Force a re-query and verify that the correct data was stored.
173 monitor_db.DBObject._clear_instance_cache()
174 hqe = monitor_db.HostQueueEntry(id=new_id)
175 self.assertEqual(hqe.id, new_id)
176 self.assertEqual(hqe.job_id, 1)
177 self.assertEqual(hqe.host_id, 2)
178 self.assertEqual(hqe.status, 'Queued')
179 self.assertEqual(hqe.meta_host, None)
180 self.assertEqual(hqe.active, False)
181 self.assertEqual(hqe.complete, False)
182 self.assertEqual(hqe.deleted, False)
183 self.assertEqual(hqe.execution_subdir, '.')
184 self.assertEqual(hqe.atomic_group_id, None)
showard12f3e322009-05-13 21:27:42 +0000185 self.assertEqual(hqe.started_on, None)
showarda3c58572009-03-12 20:36:59 +0000186
187
showardb2e2c322008-10-14 17:33:55 +0000188class DispatcherSchedulingTest(BaseSchedulerTest):
showard56193bb2008-08-13 20:07:41 +0000189 _jobs_scheduled = []
190
showard89f84db2009-03-12 20:39:13 +0000191
192 def tearDown(self):
193 super(DispatcherSchedulingTest, self).tearDown()
194
195
showard56193bb2008-08-13 20:07:41 +0000196 def _set_monitor_stubs(self):
197 super(DispatcherSchedulingTest, self)._set_monitor_stubs()
showard89f84db2009-03-12 20:39:13 +0000198
199 def job_run_stub(job_self, queue_entry):
200 """Return a dummy for testing. Called by HostQueueEntry.run()."""
201 self._record_job_scheduled(job_self.id, queue_entry.host.id)
202 queue_entry.set_status('Starting')
showard170873e2009-01-07 00:22:26 +0000203 return DummyAgent()
showard89f84db2009-03-12 20:39:13 +0000204
205 self.god.stub_with(monitor_db.Job, 'run', job_run_stub)
206
207 def hqe_queue_log_record_stub(self, log_line):
208 """No-Op to avoid calls down to the _drone_manager during tests."""
209
210 self.god.stub_with(monitor_db.HostQueueEntry, 'queue_log_record',
211 hqe_queue_log_record_stub)
showard56193bb2008-08-13 20:07:41 +0000212
213
214 def _record_job_scheduled(self, job_id, host_id):
215 record = (job_id, host_id)
216 self.assert_(record not in self._jobs_scheduled,
217 'Job %d scheduled on host %d twice' %
218 (job_id, host_id))
219 self._jobs_scheduled.append(record)
220
221
222 def _assert_job_scheduled_on(self, job_id, host_id):
223 record = (job_id, host_id)
224 self.assert_(record in self._jobs_scheduled,
225 'Job %d not scheduled on host %d as expected\n'
226 'Jobs scheduled: %s' %
227 (job_id, host_id, self._jobs_scheduled))
228 self._jobs_scheduled.remove(record)
229
230
showard89f84db2009-03-12 20:39:13 +0000231 def _assert_job_scheduled_on_number_of(self, job_id, host_ids, number):
232 """Assert job was scheduled on exactly number hosts out of a set."""
233 found = []
234 for host_id in host_ids:
235 record = (job_id, host_id)
236 if record in self._jobs_scheduled:
237 found.append(record)
238 self._jobs_scheduled.remove(record)
239 if len(found) < number:
240 self.fail('Job %d scheduled on fewer than %d hosts in %s.\n'
241 'Jobs scheduled: %s' % (job_id, number, host_ids, found))
242 elif len(found) > number:
243 self.fail('Job %d scheduled on more than %d hosts in %s.\n'
244 'Jobs scheduled: %s' % (job_id, number, host_ids, found))
245
246
showard56193bb2008-08-13 20:07:41 +0000247 def _check_for_extra_schedulings(self):
248 if len(self._jobs_scheduled) != 0:
249 self.fail('Extra jobs scheduled: ' +
250 str(self._jobs_scheduled))
251
252
jadmanski0afbb632008-06-06 21:10:57 +0000253 def _convert_jobs_to_metahosts(self, *job_ids):
254 sql_tuple = '(' + ','.join(str(i) for i in job_ids) + ')'
255 self._do_query('UPDATE host_queue_entries SET '
256 'meta_host=host_id, host_id=NULL '
257 'WHERE job_id IN ' + sql_tuple)
showardce38e0c2008-05-29 19:36:16 +0000258
259
jadmanski0afbb632008-06-06 21:10:57 +0000260 def _lock_host(self, host_id):
261 self._do_query('UPDATE hosts SET locked=1 WHERE id=' +
262 str(host_id))
showardce38e0c2008-05-29 19:36:16 +0000263
264
jadmanski0afbb632008-06-06 21:10:57 +0000265 def setUp(self):
showard56193bb2008-08-13 20:07:41 +0000266 super(DispatcherSchedulingTest, self).setUp()
jadmanski0afbb632008-06-06 21:10:57 +0000267 self._jobs_scheduled = []
showardce38e0c2008-05-29 19:36:16 +0000268
269
jadmanski0afbb632008-06-06 21:10:57 +0000270 def _test_basic_scheduling_helper(self, use_metahosts):
271 'Basic nonmetahost scheduling'
272 self._create_job_simple([1], use_metahosts)
273 self._create_job_simple([2], use_metahosts)
274 self._dispatcher._schedule_new_jobs()
275 self._assert_job_scheduled_on(1, 1)
276 self._assert_job_scheduled_on(2, 2)
277 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000278
279
jadmanski0afbb632008-06-06 21:10:57 +0000280 def _test_priorities_helper(self, use_metahosts):
281 'Test prioritization ordering'
282 self._create_job_simple([1], use_metahosts)
283 self._create_job_simple([2], use_metahosts)
284 self._create_job_simple([1,2], use_metahosts)
285 self._create_job_simple([1], use_metahosts, priority=1)
286 self._dispatcher._schedule_new_jobs()
287 self._assert_job_scheduled_on(4, 1) # higher priority
288 self._assert_job_scheduled_on(2, 2) # earlier job over later
289 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000290
291
jadmanski0afbb632008-06-06 21:10:57 +0000292 def _test_hosts_ready_helper(self, use_metahosts):
293 """
294 Only hosts that are status=Ready, unlocked and not invalid get
295 scheduled.
296 """
297 self._create_job_simple([1], use_metahosts)
298 self._do_query('UPDATE hosts SET status="Running" WHERE id=1')
299 self._dispatcher._schedule_new_jobs()
300 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000301
jadmanski0afbb632008-06-06 21:10:57 +0000302 self._do_query('UPDATE hosts SET status="Ready", locked=1 '
303 'WHERE id=1')
304 self._dispatcher._schedule_new_jobs()
305 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000306
jadmanski0afbb632008-06-06 21:10:57 +0000307 self._do_query('UPDATE hosts SET locked=0, invalid=1 '
308 'WHERE id=1')
309 self._dispatcher._schedule_new_jobs()
showard5df2b192008-07-03 19:51:57 +0000310 if not use_metahosts:
311 self._assert_job_scheduled_on(1, 1)
jadmanski0afbb632008-06-06 21:10:57 +0000312 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000313
314
jadmanski0afbb632008-06-06 21:10:57 +0000315 def _test_hosts_idle_helper(self, use_metahosts):
316 'Only idle hosts get scheduled'
showard2bab8f42008-11-12 18:15:22 +0000317 self._create_job(hosts=[1], active=True)
jadmanski0afbb632008-06-06 21:10:57 +0000318 self._create_job_simple([1], use_metahosts)
319 self._dispatcher._schedule_new_jobs()
320 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000321
322
showard63a34772008-08-18 19:32:50 +0000323 def _test_obey_ACLs_helper(self, use_metahosts):
324 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id=1')
325 self._create_job_simple([1], use_metahosts)
326 self._dispatcher._schedule_new_jobs()
327 self._check_for_extra_schedulings()
328
329
jadmanski0afbb632008-06-06 21:10:57 +0000330 def test_basic_scheduling(self):
331 self._test_basic_scheduling_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000332
333
jadmanski0afbb632008-06-06 21:10:57 +0000334 def test_priorities(self):
335 self._test_priorities_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000336
337
jadmanski0afbb632008-06-06 21:10:57 +0000338 def test_hosts_ready(self):
339 self._test_hosts_ready_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000340
341
jadmanski0afbb632008-06-06 21:10:57 +0000342 def test_hosts_idle(self):
343 self._test_hosts_idle_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000344
345
showard63a34772008-08-18 19:32:50 +0000346 def test_obey_ACLs(self):
347 self._test_obey_ACLs_helper(False)
348
349
350 def test_non_metahost_on_invalid_host(self):
351 """
352 Non-metahost entries can get scheduled on invalid hosts (this is how
353 one-time hosts work).
354 """
355 self._do_query('UPDATE hosts SET invalid=1')
356 self._test_basic_scheduling_helper(False)
357
358
jadmanski0afbb632008-06-06 21:10:57 +0000359 def test_metahost_scheduling(self):
showard63a34772008-08-18 19:32:50 +0000360 """
361 Basic metahost scheduling
362 """
jadmanski0afbb632008-06-06 21:10:57 +0000363 self._test_basic_scheduling_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000364
365
jadmanski0afbb632008-06-06 21:10:57 +0000366 def test_metahost_priorities(self):
367 self._test_priorities_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000368
369
jadmanski0afbb632008-06-06 21:10:57 +0000370 def test_metahost_hosts_ready(self):
371 self._test_hosts_ready_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000372
373
jadmanski0afbb632008-06-06 21:10:57 +0000374 def test_metahost_hosts_idle(self):
375 self._test_hosts_idle_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000376
377
showard63a34772008-08-18 19:32:50 +0000378 def test_metahost_obey_ACLs(self):
379 self._test_obey_ACLs_helper(True)
380
381
showard89f84db2009-03-12 20:39:13 +0000382 def _setup_test_only_if_needed_labels(self):
showardade14e22009-01-26 22:38:32 +0000383 # apply only_if_needed label3 to host1
showard89f84db2009-03-12 20:39:13 +0000384 models.Host.smart_get('host1').labels.add(self.label3)
385 return self._create_job_simple([1], use_metahost=True)
showardade14e22009-01-26 22:38:32 +0000386
showard89f84db2009-03-12 20:39:13 +0000387
388 def test_only_if_needed_labels_avoids_host(self):
389 job = self._setup_test_only_if_needed_labels()
showardade14e22009-01-26 22:38:32 +0000390 # if the job doesn't depend on label3, there should be no scheduling
391 self._dispatcher._schedule_new_jobs()
392 self._check_for_extra_schedulings()
393
showard89f84db2009-03-12 20:39:13 +0000394
395 def test_only_if_needed_labels_schedules(self):
396 job = self._setup_test_only_if_needed_labels()
397 job.dependency_labels.add(self.label3)
showardade14e22009-01-26 22:38:32 +0000398 self._dispatcher._schedule_new_jobs()
399 self._assert_job_scheduled_on(1, 1)
400 self._check_for_extra_schedulings()
401
showard89f84db2009-03-12 20:39:13 +0000402
403 def test_only_if_needed_labels_via_metahost(self):
404 job = self._setup_test_only_if_needed_labels()
405 job.dependency_labels.add(self.label3)
showardade14e22009-01-26 22:38:32 +0000406 # should also work if the metahost is the only_if_needed label
407 self._do_query('DELETE FROM jobs_dependency_labels')
408 self._create_job(metahosts=[3])
409 self._dispatcher._schedule_new_jobs()
410 self._assert_job_scheduled_on(2, 1)
411 self._check_for_extra_schedulings()
showard989f25d2008-10-01 11:38:11 +0000412
413
jadmanski0afbb632008-06-06 21:10:57 +0000414 def test_nonmetahost_over_metahost(self):
415 """
416 Non-metahost entries should take priority over metahost entries
417 for the same host
418 """
419 self._create_job(metahosts=[1])
420 self._create_job(hosts=[1])
421 self._dispatcher._schedule_new_jobs()
422 self._assert_job_scheduled_on(2, 1)
423 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000424
425
jadmanski0afbb632008-06-06 21:10:57 +0000426 def test_metahosts_obey_blocks(self):
427 """
428 Metahosts can't get scheduled on hosts already scheduled for
429 that job.
430 """
431 self._create_job(metahosts=[1], hosts=[1])
432 # make the nonmetahost entry complete, so the metahost can try
433 # to get scheduled
showard56193bb2008-08-13 20:07:41 +0000434 self._update_hqe(set='complete = 1', where='host_id=1')
jadmanski0afbb632008-06-06 21:10:57 +0000435 self._dispatcher._schedule_new_jobs()
436 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000437
438
showard89f84db2009-03-12 20:39:13 +0000439 # TODO(gps): These should probably live in their own TestCase class
440 # specific to testing HostScheduler methods directly. It was convenient
441 # to put it here for now to share existing test environment setup code.
442 def test_HostScheduler_check_atomic_group_labels(self):
443 normal_job = self._create_job(metahosts=[0])
444 atomic_job = self._create_job(atomic_group=1)
445 # Indirectly initialize the internal state of the host scheduler.
446 self._dispatcher._refresh_pending_queue_entries()
447
448 atomic_hqe = monitor_db.HostQueueEntry(id=atomic_job.id)
449 normal_hqe = monitor_db.HostQueueEntry(id=normal_job.id)
450
451 host_scheduler = self._dispatcher._host_scheduler
452 self.assertTrue(host_scheduler._check_atomic_group_labels(
453 [self.label4.id], atomic_hqe))
454 self.assertFalse(host_scheduler._check_atomic_group_labels(
455 [self.label4.id], normal_hqe))
456 self.assertFalse(host_scheduler._check_atomic_group_labels(
457 [self.label5.id, self.label6.id, self.label7.id], normal_hqe))
458 self.assertTrue(host_scheduler._check_atomic_group_labels(
459 [self.label4.id, self.label6.id], atomic_hqe))
460 self.assertRaises(monitor_db.SchedulerError,
461 host_scheduler._check_atomic_group_labels,
462 [self.label4.id, self.label5.id],
463 atomic_hqe)
464
465
466 def test_HostScheduler_get_host_atomic_group_id(self):
467 self._create_job(metahosts=[self.label6.id])
468 # Indirectly initialize the internal state of the host scheduler.
469 self._dispatcher._refresh_pending_queue_entries()
470
471 # Test the host scheduler
472 host_scheduler = self._dispatcher._host_scheduler
473 self.assertRaises(monitor_db.SchedulerError,
474 host_scheduler._get_host_atomic_group_id,
475 [self.label4.id, self.label5.id])
476 self.assertEqual(None, host_scheduler._get_host_atomic_group_id([]))
477 self.assertEqual(None, host_scheduler._get_host_atomic_group_id(
478 [self.label3.id, self.label7.id, self.label6.id]))
479 self.assertEqual(1, host_scheduler._get_host_atomic_group_id(
480 [self.label4.id, self.label7.id, self.label6.id]))
481 self.assertEqual(1, host_scheduler._get_host_atomic_group_id(
482 [self.label7.id, self.label5.id]))
483
484
485 def test_atomic_group_hosts_blocked_from_non_atomic_jobs(self):
486 # Create a job scheduled to run on label6.
487 self._create_job(metahosts=[self.label6.id])
488 self._dispatcher._schedule_new_jobs()
489 # label6 only has hosts that are in atomic groups associated with it,
490 # there should be no scheduling.
491 self._check_for_extra_schedulings()
492
493
494 def test_atomic_group_hosts_blocked_from_non_atomic_jobs_explicit(self):
495 # Create a job scheduled to run on label5. This is an atomic group
496 # label but this job does not request atomic group scheduling.
497 self._create_job(metahosts=[self.label5.id])
498 self._dispatcher._schedule_new_jobs()
499 # label6 only has hosts that are in atomic groups associated with it,
500 # there should be no scheduling.
501 self._check_for_extra_schedulings()
502
503
504 def test_atomic_group_scheduling_basics(self):
505 # Create jobs scheduled to run on an atomic group.
506 job_a = self._create_job(synchronous=True, metahosts=[self.label4.id],
507 atomic_group=1)
508 job_b = self._create_job(synchronous=True, metahosts=[self.label5.id],
509 atomic_group=1)
510 self._dispatcher._schedule_new_jobs()
511 # atomic_group.max_number_of_machines was 2 so we should run on 2.
512 self._assert_job_scheduled_on_number_of(job_a.id, (5, 6, 7), 2)
513 self._assert_job_scheduled_on(job_b.id, 8) # label5
514 self._assert_job_scheduled_on(job_b.id, 9) # label5
515 self._check_for_extra_schedulings()
516
517 # The three host label4 atomic group still has one host available.
518 # That means a job with a synch_count of 1 asking to be scheduled on
519 # the atomic group can still use the final machine.
520 #
521 # This may seem like a somewhat odd use case. It allows the use of an
522 # atomic group as a set of machines to run smaller jobs within (a set
523 # of hosts configured for use in network tests with eachother perhaps?)
524 onehost_job = self._create_job(atomic_group=1)
525 self._dispatcher._schedule_new_jobs()
526 self._assert_job_scheduled_on_number_of(onehost_job.id, (5, 6, 7), 1)
527 self._check_for_extra_schedulings()
528
529 # No more atomic groups have hosts available, no more jobs should
530 # be scheduled.
531 self._create_job(atomic_group=1)
532 self._dispatcher._schedule_new_jobs()
533 self._check_for_extra_schedulings()
534
535
536 def test_atomic_group_scheduling_obeys_acls(self):
537 # Request scheduling on a specific atomic label but be denied by ACLs.
538 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id in (8,9)')
539 job = self._create_job(metahosts=[self.label5.id], atomic_group=1)
540 self._dispatcher._schedule_new_jobs()
541 self._check_for_extra_schedulings()
542
543
544 def test_atomic_group_scheduling_dependency_label_exclude(self):
545 # A dependency label that matches no hosts in the atomic group.
546 job_a = self._create_job(atomic_group=1)
547 job_a.dependency_labels.add(self.label3)
548 self._dispatcher._schedule_new_jobs()
549 self._check_for_extra_schedulings()
550
551
552 def test_atomic_group_scheduling_metahost_dependency_label_exclude(self):
553 # A metahost and dependency label that excludes too many hosts.
554 job_b = self._create_job(synchronous=True, metahosts=[self.label4.id],
555 atomic_group=1)
556 job_b.dependency_labels.add(self.label7)
557 self._dispatcher._schedule_new_jobs()
558 self._check_for_extra_schedulings()
559
560
561 def test_atomic_group_scheduling_dependency_label_match(self):
562 # A dependency label that exists on enough atomic group hosts in only
563 # one of the two atomic group labels.
564 job_c = self._create_job(synchronous=True, atomic_group=1)
565 job_c.dependency_labels.add(self.label7)
566 self._dispatcher._schedule_new_jobs()
567 self._assert_job_scheduled_on_number_of(job_c.id, (8, 9), 2)
568 self._check_for_extra_schedulings()
569
570
571 def test_atomic_group_scheduling_no_metahost(self):
572 # Force it to schedule on the other group for a reliable test.
573 self._do_query('UPDATE hosts SET invalid=1 WHERE id=9')
574 # An atomic job without a metahost.
575 job = self._create_job(synchronous=True, atomic_group=1)
576 self._dispatcher._schedule_new_jobs()
577 self._assert_job_scheduled_on_number_of(job.id, (5, 6, 7), 2)
578 self._check_for_extra_schedulings()
579
580
581 def test_atomic_group_scheduling_partial_group(self):
582 # Make one host in labels[3] unavailable so that there are only two
583 # hosts left in the group.
584 self._do_query('UPDATE hosts SET status="Repair Failed" WHERE id=5')
585 job = self._create_job(synchronous=True, metahosts=[self.label4.id],
586 atomic_group=1)
587 self._dispatcher._schedule_new_jobs()
588 # Verify that it was scheduled on the 2 ready hosts in that group.
589 self._assert_job_scheduled_on(job.id, 6)
590 self._assert_job_scheduled_on(job.id, 7)
591 self._check_for_extra_schedulings()
592
593
594 def test_atomic_group_scheduling_not_enough_available(self):
595 # Mark some hosts in each atomic group label as not usable.
596 # One host running, another invalid in the first group label.
597 self._do_query('UPDATE hosts SET status="Running" WHERE id=5')
598 self._do_query('UPDATE hosts SET invalid=1 WHERE id=6')
599 # One host invalid in the second group label.
600 self._do_query('UPDATE hosts SET invalid=1 WHERE id=9')
601 # Nothing to schedule when no group label has enough (2) good hosts..
602 self._create_job(atomic_group=1, synchronous=True)
603 self._dispatcher._schedule_new_jobs()
604 # There are not enough hosts in either atomic group,
605 # No more scheduling should occur.
606 self._check_for_extra_schedulings()
607
608 # Now create an atomic job that has a synch count of 1. It should
609 # schedule on exactly one of the hosts.
610 onehost_job = self._create_job(atomic_group=1)
611 self._dispatcher._schedule_new_jobs()
612 self._assert_job_scheduled_on_number_of(onehost_job.id, (7, 8), 1)
613
614
615 def test_atomic_group_scheduling_no_valid_hosts(self):
616 self._do_query('UPDATE hosts SET invalid=1 WHERE id in (8,9)')
617 self._create_job(synchronous=True, metahosts=[self.label5.id],
618 atomic_group=1)
619 self._dispatcher._schedule_new_jobs()
620 # no hosts in the selected group and label are valid. no schedulings.
621 self._check_for_extra_schedulings()
622
623
624 def test_atomic_group_scheduling_metahost_works(self):
625 # Test that atomic group scheduling also obeys metahosts.
626 self._create_job(metahosts=[0], atomic_group=1)
627 self._dispatcher._schedule_new_jobs()
628 # There are no atomic group hosts that also have that metahost.
629 self._check_for_extra_schedulings()
630
631 job_b = self._create_job(metahosts=[self.label5.id], atomic_group=1)
632 self._dispatcher._schedule_new_jobs()
633 self._assert_job_scheduled_on(job_b.id, 8)
634 self._assert_job_scheduled_on(job_b.id, 9)
635 self._check_for_extra_schedulings()
636
637
638 def test_atomic_group_skips_ineligible_hosts(self):
639 # Test hosts marked ineligible for this job are not eligible.
640 # How would this ever happen anyways?
641 job = self._create_job(metahosts=[self.label4.id], atomic_group=1)
642 models.IneligibleHostQueue.objects.create(job=job, host_id=5)
643 models.IneligibleHostQueue.objects.create(job=job, host_id=6)
644 models.IneligibleHostQueue.objects.create(job=job, host_id=7)
645 self._dispatcher._schedule_new_jobs()
646 # No scheduling should occur as all desired hosts were ineligible.
647 self._check_for_extra_schedulings()
648
649
650 def test_atomic_group_scheduling_fail(self):
651 # If synch_count is > the atomic group number of machines, the job
652 # should be aborted immediately.
653 model_job = self._create_job(synchronous=True, atomic_group=1)
654 model_job.synch_count = 4
655 model_job.save()
656 job = monitor_db.Job(id=model_job.id)
657 self._dispatcher._schedule_new_jobs()
658 self._check_for_extra_schedulings()
659 queue_entries = job.get_host_queue_entries()
660 self.assertEqual(1, len(queue_entries))
661 self.assertEqual(queue_entries[0].status,
662 models.HostQueueEntry.Status.ABORTED)
663
664
showard205fd602009-03-21 00:17:35 +0000665 def test_atomic_group_no_labels_no_scheduling(self):
666 # Never schedule on atomic groups marked invalid.
667 job = self._create_job(metahosts=[self.label5.id], synchronous=True,
668 atomic_group=1)
669 # Deleting an atomic group via the frontend marks it invalid and
670 # removes all label references to the group. The job now references
671 # an invalid atomic group with no labels associated with it.
672 self.label5.atomic_group.invalid = True
673 self.label5.atomic_group.save()
674 self.label5.atomic_group = None
675 self.label5.save()
676
677 self._dispatcher._schedule_new_jobs()
678 self._check_for_extra_schedulings()
679
680
showard89f84db2009-03-12 20:39:13 +0000681 def test_schedule_directly_on_atomic_group_host_fail(self):
682 # Scheduling a job directly on hosts in an atomic group must
683 # fail to avoid users inadvertently holding up the use of an
684 # entire atomic group by using the machines individually.
685 job = self._create_job(hosts=[5])
686 self._dispatcher._schedule_new_jobs()
687 self._check_for_extra_schedulings()
688
689
690 def test_schedule_directly_on_atomic_group_host(self):
691 # Scheduling a job directly on one host in an atomic group will
692 # work when the atomic group is listed on the HQE in addition
693 # to the host (assuming the sync count is 1).
694 job = self._create_job(hosts=[5], atomic_group=1)
695 self._dispatcher._schedule_new_jobs()
696 self._assert_job_scheduled_on(job.id, 5)
697 self._check_for_extra_schedulings()
698
699
700 def test_schedule_directly_on_atomic_group_hosts_sync2(self):
701 job = self._create_job(hosts=[5,8], atomic_group=1, synchronous=True)
702 self._dispatcher._schedule_new_jobs()
703 self._assert_job_scheduled_on(job.id, 5)
704 self._assert_job_scheduled_on(job.id, 8)
705 self._check_for_extra_schedulings()
706
707
708 def test_schedule_directly_on_atomic_group_hosts_wrong_group(self):
709 job = self._create_job(hosts=[5,8], atomic_group=2, synchronous=True)
710 self._dispatcher._schedule_new_jobs()
711 self._check_for_extra_schedulings()
712
713
showard56193bb2008-08-13 20:07:41 +0000714 def test_only_schedule_queued_entries(self):
715 self._create_job(metahosts=[1])
716 self._update_hqe(set='active=1, host_id=2')
717 self._dispatcher._schedule_new_jobs()
718 self._check_for_extra_schedulings()
719
720
showardfa8629c2008-11-04 16:51:23 +0000721 def test_no_ready_hosts(self):
722 self._create_job(hosts=[1])
723 self._do_query('UPDATE hosts SET status="Repair Failed"')
724 self._dispatcher._schedule_new_jobs()
725 self._check_for_extra_schedulings()
726
727
showardb2e2c322008-10-14 17:33:55 +0000728class DispatcherThrottlingTest(BaseSchedulerTest):
showard4c5374f2008-09-04 17:02:56 +0000729 """
730 Test that the dispatcher throttles:
731 * total number of running processes
732 * number of processes started per cycle
733 """
734 _MAX_RUNNING = 3
735 _MAX_STARTED = 2
736
737 def setUp(self):
738 super(DispatcherThrottlingTest, self).setUp()
showard324bf812009-01-20 23:23:38 +0000739 scheduler_config.config.max_processes_per_drone = self._MAX_RUNNING
showardd1ee1dd2009-01-07 21:33:08 +0000740 scheduler_config.config.max_processes_started_per_cycle = (
741 self._MAX_STARTED)
showard4c5374f2008-09-04 17:02:56 +0000742
showard324bf812009-01-20 23:23:38 +0000743 def fake_max_runnable_processes(fake_self):
744 running = sum(agent.num_processes
745 for agent in self._agents
746 if agent.is_running())
747 return self._MAX_RUNNING - running
748 self.god.stub_with(drone_manager.DroneManager, 'max_runnable_processes',
749 fake_max_runnable_processes)
showard2fa51692009-01-13 23:48:08 +0000750
showard4c5374f2008-09-04 17:02:56 +0000751
showard4c5374f2008-09-04 17:02:56 +0000752 def _setup_some_agents(self, num_agents):
showard170873e2009-01-07 00:22:26 +0000753 self._agents = [DummyAgent() for i in xrange(num_agents)]
showard4c5374f2008-09-04 17:02:56 +0000754 self._dispatcher._agents = list(self._agents)
755
756
757 def _run_a_few_cycles(self):
758 for i in xrange(4):
759 self._dispatcher._handle_agents()
760
761
762 def _assert_agents_started(self, indexes, is_started=True):
763 for i in indexes:
764 self.assert_(self._agents[i].is_running() == is_started,
765 'Agent %d %sstarted' %
766 (i, is_started and 'not ' or ''))
767
768
769 def _assert_agents_not_started(self, indexes):
770 self._assert_agents_started(indexes, False)
771
772
773 def test_throttle_total(self):
774 self._setup_some_agents(4)
775 self._run_a_few_cycles()
776 self._assert_agents_started([0, 1, 2])
777 self._assert_agents_not_started([3])
778
779
780 def test_throttle_per_cycle(self):
781 self._setup_some_agents(3)
782 self._dispatcher._handle_agents()
783 self._assert_agents_started([0, 1])
784 self._assert_agents_not_started([2])
785
786
787 def test_throttle_with_synchronous(self):
788 self._setup_some_agents(2)
789 self._agents[0].num_processes = 3
790 self._run_a_few_cycles()
791 self._assert_agents_started([0])
792 self._assert_agents_not_started([1])
793
794
795 def test_large_agent_starvation(self):
796 """
797 Ensure large agents don't get starved by lower-priority agents.
798 """
799 self._setup_some_agents(3)
800 self._agents[1].num_processes = 3
801 self._run_a_few_cycles()
802 self._assert_agents_started([0])
803 self._assert_agents_not_started([1, 2])
804
805 self._agents[0].set_done(True)
806 self._run_a_few_cycles()
807 self._assert_agents_started([1])
808 self._assert_agents_not_started([2])
809
810
811 def test_zero_process_agent(self):
812 self._setup_some_agents(5)
813 self._agents[4].num_processes = 0
814 self._run_a_few_cycles()
815 self._assert_agents_started([0, 1, 2, 4])
816 self._assert_agents_not_started([3])
817
818
showard1be97432008-10-17 15:30:45 +0000819class FindAbortTest(BaseSchedulerTest):
showard56193bb2008-08-13 20:07:41 +0000820 """
showard1be97432008-10-17 15:30:45 +0000821 Test the dispatcher abort functionality.
showard56193bb2008-08-13 20:07:41 +0000822 """
showard170873e2009-01-07 00:22:26 +0000823 def _check_host_agent(self, agent, host_id):
824 self.assert_(isinstance(agent, monitor_db.Agent))
825 tasks = list(agent.queue.queue)
826 self.assertEquals(len(tasks), 2)
827 cleanup, verify = tasks
showard9d9ffd52008-11-09 23:14:35 +0000828
showard45ae8192008-11-05 19:32:53 +0000829 self.assert_(isinstance(cleanup, monitor_db.CleanupTask))
showard170873e2009-01-07 00:22:26 +0000830 self.assertEquals(cleanup.host.id, host_id)
showard1be97432008-10-17 15:30:45 +0000831
832 self.assert_(isinstance(verify, monitor_db.VerifyTask))
showard170873e2009-01-07 00:22:26 +0000833 self.assertEquals(verify.host.id, host_id)
showard56193bb2008-08-13 20:07:41 +0000834
835
showardd3dc1992009-04-22 21:01:40 +0000836 def _check_agents(self, agents):
showard170873e2009-01-07 00:22:26 +0000837 agents = list(agents)
showardd3dc1992009-04-22 21:01:40 +0000838 self.assertEquals(len(agents), 3)
839 self.assertEquals(agents[0], self._agent)
840 self._check_host_agent(agents[1], 1)
841 self._check_host_agent(agents[2], 2)
showard56193bb2008-08-13 20:07:41 +0000842
843
showardd3dc1992009-04-22 21:01:40 +0000844 def _common_setup(self):
showard56193bb2008-08-13 20:07:41 +0000845 self._create_job(hosts=[1, 2])
showardd3dc1992009-04-22 21:01:40 +0000846 self._update_hqe(set='aborted=1')
847 self._agent = self.god.create_mock_class(monitor_db.Agent, 'old_agent')
showard6b733412009-04-27 20:09:18 +0000848 _set_host_and_qe_ids(self._agent, [1, 2])
showardd3dc1992009-04-22 21:01:40 +0000849 self._agent.abort.expect_call()
850 self._agent.abort.expect_call() # gets called once for each HQE
851 self._dispatcher.add_agent(self._agent)
showard56193bb2008-08-13 20:07:41 +0000852
showardd3dc1992009-04-22 21:01:40 +0000853
854 def test_find_aborting(self):
855 self._common_setup()
showard56193bb2008-08-13 20:07:41 +0000856 self._dispatcher._find_aborting()
showard56193bb2008-08-13 20:07:41 +0000857 self.god.check_playback()
858
859
showardd3dc1992009-04-22 21:01:40 +0000860 def test_find_aborting_verifying(self):
861 self._common_setup()
862 self._update_hqe(set='active=1, status="Verifying"')
showard56193bb2008-08-13 20:07:41 +0000863
showard56193bb2008-08-13 20:07:41 +0000864 self._dispatcher._find_aborting()
865
showardd3dc1992009-04-22 21:01:40 +0000866 self._check_agents(self._dispatcher._agents)
showard56193bb2008-08-13 20:07:41 +0000867 self.god.check_playback()
868
869
showard98863972008-10-29 21:14:56 +0000870class JobTimeoutTest(BaseSchedulerTest):
showard2bab8f42008-11-12 18:15:22 +0000871 def _test_synch_start_timeout_helper(self, expect_abort,
872 set_created_on=True, set_active=True,
873 set_acl=True):
showardd1ee1dd2009-01-07 21:33:08 +0000874 scheduler_config.config.synch_job_start_timeout_minutes = 60
showard98863972008-10-29 21:14:56 +0000875 job = self._create_job(hosts=[1, 2])
showard98863972008-10-29 21:14:56 +0000876 if set_active:
877 hqe = job.hostqueueentry_set.filter(host__id=1)[0]
878 hqe.status = 'Pending'
879 hqe.active = 1
880 hqe.save()
881
882 everyone_acl = models.AclGroup.smart_get('Everyone')
883 host1 = models.Host.smart_get(1)
884 if set_acl:
885 everyone_acl.hosts.add(host1)
886 else:
887 everyone_acl.hosts.remove(host1)
888
889 job.created_on = datetime.datetime.now()
890 if set_created_on:
891 job.created_on -= datetime.timedelta(minutes=100)
892 job.save()
893
showard915958d2009-04-22 21:00:58 +0000894 cleanup = self._dispatcher._periodic_cleanup
895 cleanup._abort_jobs_past_synch_start_timeout()
showard98863972008-10-29 21:14:56 +0000896
897 for hqe in job.hostqueueentry_set.all():
showardd3dc1992009-04-22 21:01:40 +0000898 self.assertEquals(hqe.aborted, expect_abort)
showard98863972008-10-29 21:14:56 +0000899
900
901 def test_synch_start_timeout_helper(self):
902 # no abort if any of the condition aren't met
showard2bab8f42008-11-12 18:15:22 +0000903 self._test_synch_start_timeout_helper(False, set_created_on=False)
904 self._test_synch_start_timeout_helper(False, set_active=False)
905 self._test_synch_start_timeout_helper(False, set_acl=False)
showard98863972008-10-29 21:14:56 +0000906 # abort if all conditions are met
showard2bab8f42008-11-12 18:15:22 +0000907 self._test_synch_start_timeout_helper(True)
showard98863972008-10-29 21:14:56 +0000908
909
jadmanski3d161b02008-06-06 15:43:36 +0000910class PidfileRunMonitorTest(unittest.TestCase):
showard170873e2009-01-07 00:22:26 +0000911 execution_tag = 'test_tag'
jadmanski0afbb632008-06-06 21:10:57 +0000912 pid = 12345
showard170873e2009-01-07 00:22:26 +0000913 process = drone_manager.Process('myhost', pid)
showard21baa452008-10-21 00:08:39 +0000914 num_tests_failed = 1
jadmanski3d161b02008-06-06 15:43:36 +0000915
jadmanski0afbb632008-06-06 21:10:57 +0000916 def setUp(self):
917 self.god = mock.mock_god()
showard170873e2009-01-07 00:22:26 +0000918 self.mock_drone_manager = self.god.create_mock_class(
919 drone_manager.DroneManager, 'drone_manager')
920 self.god.stub_with(monitor_db, '_drone_manager',
921 self.mock_drone_manager)
922 self.god.stub_function(email_manager.manager, 'enqueue_notify_email')
923
924 self.pidfile_id = object()
925
showardd3dc1992009-04-22 21:01:40 +0000926 (self.mock_drone_manager.get_pidfile_id_from
927 .expect_call(self.execution_tag,
928 pidfile_name=monitor_db._AUTOSERV_PID_FILE)
929 .and_return(self.pidfile_id))
showard170873e2009-01-07 00:22:26 +0000930 self.mock_drone_manager.register_pidfile.expect_call(self.pidfile_id)
931
932 self.monitor = monitor_db.PidfileRunMonitor()
933 self.monitor.attach_to_existing_process(self.execution_tag)
jadmanski3d161b02008-06-06 15:43:36 +0000934
935
jadmanski0afbb632008-06-06 21:10:57 +0000936 def tearDown(self):
937 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +0000938
939
showard170873e2009-01-07 00:22:26 +0000940 def setup_pidfile(self, pid=None, exit_code=None, tests_failed=None,
941 use_second_read=False):
942 contents = drone_manager.PidfileContents()
943 if pid is not None:
944 contents.process = drone_manager.Process('myhost', pid)
945 contents.exit_status = exit_code
946 contents.num_tests_failed = tests_failed
947 self.mock_drone_manager.get_pidfile_contents.expect_call(
948 self.pidfile_id, use_second_read=use_second_read).and_return(
949 contents)
950
951
jadmanski0afbb632008-06-06 21:10:57 +0000952 def set_not_yet_run(self):
showard170873e2009-01-07 00:22:26 +0000953 self.setup_pidfile()
jadmanski3d161b02008-06-06 15:43:36 +0000954
955
showard3dd6b882008-10-27 19:21:39 +0000956 def set_empty_pidfile(self):
showard170873e2009-01-07 00:22:26 +0000957 self.setup_pidfile()
showard3dd6b882008-10-27 19:21:39 +0000958
959
showard170873e2009-01-07 00:22:26 +0000960 def set_running(self, use_second_read=False):
961 self.setup_pidfile(self.pid, use_second_read=use_second_read)
jadmanski3d161b02008-06-06 15:43:36 +0000962
963
showard170873e2009-01-07 00:22:26 +0000964 def set_complete(self, error_code, use_second_read=False):
965 self.setup_pidfile(self.pid, error_code, self.num_tests_failed,
966 use_second_read=use_second_read)
967
968
969 def _check_monitor(self, expected_pid, expected_exit_status,
970 expected_num_tests_failed):
971 if expected_pid is None:
972 self.assertEquals(self.monitor._state.process, None)
973 else:
974 self.assertEquals(self.monitor._state.process.pid, expected_pid)
975 self.assertEquals(self.monitor._state.exit_status, expected_exit_status)
976 self.assertEquals(self.monitor._state.num_tests_failed,
977 expected_num_tests_failed)
978
979
980 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +0000981
982
showard21baa452008-10-21 00:08:39 +0000983 def _test_read_pidfile_helper(self, expected_pid, expected_exit_status,
984 expected_num_tests_failed):
985 self.monitor._read_pidfile()
showard170873e2009-01-07 00:22:26 +0000986 self._check_monitor(expected_pid, expected_exit_status,
987 expected_num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +0000988
989
showard21baa452008-10-21 00:08:39 +0000990 def _get_expected_tests_failed(self, expected_exit_status):
991 if expected_exit_status is None:
992 expected_tests_failed = None
993 else:
994 expected_tests_failed = self.num_tests_failed
995 return expected_tests_failed
996
997
jadmanski0afbb632008-06-06 21:10:57 +0000998 def test_read_pidfile(self):
999 self.set_not_yet_run()
showard21baa452008-10-21 00:08:39 +00001000 self._test_read_pidfile_helper(None, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001001
showard3dd6b882008-10-27 19:21:39 +00001002 self.set_empty_pidfile()
1003 self._test_read_pidfile_helper(None, None, None)
1004
jadmanski0afbb632008-06-06 21:10:57 +00001005 self.set_running()
showard21baa452008-10-21 00:08:39 +00001006 self._test_read_pidfile_helper(self.pid, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001007
jadmanski0afbb632008-06-06 21:10:57 +00001008 self.set_complete(123)
showard21baa452008-10-21 00:08:39 +00001009 self._test_read_pidfile_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001010
1011
jadmanski0afbb632008-06-06 21:10:57 +00001012 def test_read_pidfile_error(self):
showard170873e2009-01-07 00:22:26 +00001013 self.mock_drone_manager.get_pidfile_contents.expect_call(
1014 self.pidfile_id, use_second_read=False).and_return(
1015 drone_manager.InvalidPidfile('error'))
1016 self.assertRaises(monitor_db.PidfileRunMonitor._PidfileException,
showard21baa452008-10-21 00:08:39 +00001017 self.monitor._read_pidfile)
jadmanski0afbb632008-06-06 21:10:57 +00001018 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001019
1020
showard170873e2009-01-07 00:22:26 +00001021 def setup_is_running(self, is_running):
1022 self.mock_drone_manager.is_process_running.expect_call(
1023 self.process).and_return(is_running)
jadmanski3d161b02008-06-06 15:43:36 +00001024
1025
showard21baa452008-10-21 00:08:39 +00001026 def _test_get_pidfile_info_helper(self, expected_pid, expected_exit_status,
1027 expected_num_tests_failed):
1028 self.monitor._get_pidfile_info()
showard170873e2009-01-07 00:22:26 +00001029 self._check_monitor(expected_pid, expected_exit_status,
1030 expected_num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001031
1032
jadmanski0afbb632008-06-06 21:10:57 +00001033 def test_get_pidfile_info(self):
showard21baa452008-10-21 00:08:39 +00001034 """
1035 normal cases for get_pidfile_info
1036 """
jadmanski0afbb632008-06-06 21:10:57 +00001037 # running
1038 self.set_running()
showard170873e2009-01-07 00:22:26 +00001039 self.setup_is_running(True)
showard21baa452008-10-21 00:08:39 +00001040 self._test_get_pidfile_info_helper(self.pid, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001041
jadmanski0afbb632008-06-06 21:10:57 +00001042 # exited during check
1043 self.set_running()
showard170873e2009-01-07 00:22:26 +00001044 self.setup_is_running(False)
1045 self.set_complete(123, use_second_read=True) # pidfile gets read again
showard21baa452008-10-21 00:08:39 +00001046 self._test_get_pidfile_info_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001047
jadmanski0afbb632008-06-06 21:10:57 +00001048 # completed
1049 self.set_complete(123)
showard21baa452008-10-21 00:08:39 +00001050 self._test_get_pidfile_info_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001051
1052
jadmanski0afbb632008-06-06 21:10:57 +00001053 def test_get_pidfile_info_running_no_proc(self):
showard21baa452008-10-21 00:08:39 +00001054 """
1055 pidfile shows process running, but no proc exists
1056 """
jadmanski0afbb632008-06-06 21:10:57 +00001057 # running but no proc
1058 self.set_running()
showard170873e2009-01-07 00:22:26 +00001059 self.setup_is_running(False)
1060 self.set_running(use_second_read=True)
1061 email_manager.manager.enqueue_notify_email.expect_call(
jadmanski0afbb632008-06-06 21:10:57 +00001062 mock.is_string_comparator(), mock.is_string_comparator())
showard21baa452008-10-21 00:08:39 +00001063 self._test_get_pidfile_info_helper(self.pid, 1, 0)
jadmanski0afbb632008-06-06 21:10:57 +00001064 self.assertTrue(self.monitor.lost_process)
jadmanski3d161b02008-06-06 15:43:36 +00001065
1066
jadmanski0afbb632008-06-06 21:10:57 +00001067 def test_get_pidfile_info_not_yet_run(self):
showard21baa452008-10-21 00:08:39 +00001068 """
1069 pidfile hasn't been written yet
1070 """
jadmanski0afbb632008-06-06 21:10:57 +00001071 self.set_not_yet_run()
showard21baa452008-10-21 00:08:39 +00001072 self._test_get_pidfile_info_helper(None, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001073
jadmanski3d161b02008-06-06 15:43:36 +00001074
showard170873e2009-01-07 00:22:26 +00001075 def test_process_failed_to_write_pidfile(self):
jadmanski0afbb632008-06-06 21:10:57 +00001076 self.set_not_yet_run()
showard170873e2009-01-07 00:22:26 +00001077 email_manager.manager.enqueue_notify_email.expect_call(
1078 mock.is_string_comparator(), mock.is_string_comparator())
showard170873e2009-01-07 00:22:26 +00001079 self.monitor._start_time = time.time() - monitor_db.PIDFILE_TIMEOUT - 1
showard35162b02009-03-03 02:17:30 +00001080 self._test_get_pidfile_info_helper(None, 1, 0)
1081 self.assertTrue(self.monitor.lost_process)
jadmanski3d161b02008-06-06 15:43:36 +00001082
1083
1084class AgentTest(unittest.TestCase):
jadmanski0afbb632008-06-06 21:10:57 +00001085 def setUp(self):
1086 self.god = mock.mock_god()
showard6b733412009-04-27 20:09:18 +00001087 self._dispatcher = self.god.create_mock_class(monitor_db.Dispatcher,
1088 'dispatcher')
jadmanski3d161b02008-06-06 15:43:36 +00001089
1090
jadmanski0afbb632008-06-06 21:10:57 +00001091 def tearDown(self):
1092 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001093
1094
showard170873e2009-01-07 00:22:26 +00001095 def _create_mock_task(self, name):
1096 task = self.god.create_mock_class(monitor_db.AgentTask, name)
showard6b733412009-04-27 20:09:18 +00001097 _set_host_and_qe_ids(task)
showard170873e2009-01-07 00:22:26 +00001098 return task
1099
showard6b733412009-04-27 20:09:18 +00001100 def _create_agent(self, tasks):
1101 agent = monitor_db.Agent(tasks)
1102 agent.dispatcher = self._dispatcher
1103 return agent
1104
1105
1106 def _finish_agent(self, agent):
1107 while not agent.is_done():
1108 agent.tick()
1109
showard170873e2009-01-07 00:22:26 +00001110
jadmanski0afbb632008-06-06 21:10:57 +00001111 def test_agent(self):
showard170873e2009-01-07 00:22:26 +00001112 task1 = self._create_mock_task('task1')
1113 task2 = self._create_mock_task('task2')
1114 task3 = self._create_mock_task('task3')
showard08a36412009-05-05 01:01:13 +00001115 task1.poll.expect_call()
jadmanski0afbb632008-06-06 21:10:57 +00001116 task1.is_done.expect_call().and_return(False)
1117 task1.poll.expect_call()
1118 task1.is_done.expect_call().and_return(True)
1119 task1.is_done.expect_call().and_return(True)
1120 task1.success = True
jadmanski3d161b02008-06-06 15:43:36 +00001121
showard08a36412009-05-05 01:01:13 +00001122 task2.poll.expect_call()
jadmanski0afbb632008-06-06 21:10:57 +00001123 task2.is_done.expect_call().and_return(True)
1124 task2.is_done.expect_call().and_return(True)
1125 task2.success = False
1126 task2.failure_tasks = [task3]
jadmanski3d161b02008-06-06 15:43:36 +00001127
showardd3dc1992009-04-22 21:01:40 +00001128 self._dispatcher.add_agent.expect_call(IsAgentWithTask(task3))
jadmanski3d161b02008-06-06 15:43:36 +00001129
showard6b733412009-04-27 20:09:18 +00001130 agent = self._create_agent([task1, task2])
showard6b733412009-04-27 20:09:18 +00001131 self._finish_agent(agent)
jadmanski0afbb632008-06-06 21:10:57 +00001132 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001133
1134
showard6b733412009-04-27 20:09:18 +00001135 def _test_agent_abort_helper(self, ignore_abort=False):
1136 task1 = self._create_mock_task('task1')
1137 task2 = self._create_mock_task('task2')
showard6b733412009-04-27 20:09:18 +00001138 task1.poll.expect_call()
1139 task1.is_done.expect_call().and_return(False)
1140 task1.abort.expect_call()
1141 if ignore_abort:
1142 task1.aborted = False # task ignores abort; execution continues
1143
showard08a36412009-05-05 01:01:13 +00001144 task1.poll.expect_call()
showard6b733412009-04-27 20:09:18 +00001145 task1.is_done.expect_call().and_return(True)
1146 task1.is_done.expect_call().and_return(True)
1147 task1.success = True
1148
showard08a36412009-05-05 01:01:13 +00001149 task2.poll.expect_call()
showard6b733412009-04-27 20:09:18 +00001150 task2.is_done.expect_call().and_return(True)
1151 task2.is_done.expect_call().and_return(True)
1152 task2.success = True
1153 else:
showard08a36412009-05-05 01:01:13 +00001154 task1.aborted = True
1155 task2.abort.expect_call()
1156 task2.aborted = True
showard6b733412009-04-27 20:09:18 +00001157
1158 agent = self._create_agent([task1, task2])
showard6b733412009-04-27 20:09:18 +00001159 agent.tick()
1160 agent.abort()
1161 self._finish_agent(agent)
1162 self.god.check_playback()
1163
1164
1165 def test_agent_abort(self):
1166 self._test_agent_abort_helper()
1167 self._test_agent_abort_helper(True)
1168
1169
showard08a36412009-05-05 01:01:13 +00001170 def _test_agent_abort_before_started_helper(self, ignore_abort=False):
showard20f9bdd2009-04-29 19:48:33 +00001171 task = self._create_mock_task('task')
showard08a36412009-05-05 01:01:13 +00001172 task.abort.expect_call()
1173 if ignore_abort:
1174 task.aborted = False
1175 task.poll.expect_call()
1176 task.is_done.expect_call().and_return(True)
1177 task.is_done.expect_call().and_return(True)
1178 task.success = True
1179 else:
1180 task.aborted = True
1181
showard20f9bdd2009-04-29 19:48:33 +00001182 agent = self._create_agent([task])
1183 agent.abort()
showard20f9bdd2009-04-29 19:48:33 +00001184 self._finish_agent(agent)
1185 self.god.check_playback()
1186
1187
showard08a36412009-05-05 01:01:13 +00001188 def test_agent_abort_before_started(self):
1189 self._test_agent_abort_before_started_helper()
1190 self._test_agent_abort_before_started_helper(True)
1191
1192
showard184a5e82009-05-29 18:42:20 +00001193class AgentTasksTest(BaseSchedulerTest):
showard170873e2009-01-07 00:22:26 +00001194 TEMP_DIR = '/abspath/tempdir'
showard97aed502008-11-04 02:01:24 +00001195 RESULTS_DIR = '/results/dir'
jadmanski0afbb632008-06-06 21:10:57 +00001196 HOSTNAME = 'myhost'
showard170873e2009-01-07 00:22:26 +00001197 DUMMY_PROCESS = object()
jadmanskifb7cfb12008-07-09 14:13:21 +00001198 HOST_PROTECTION = host_protections.default
showard170873e2009-01-07 00:22:26 +00001199 PIDFILE_ID = object()
showard87ba02a2009-04-20 19:37:32 +00001200 JOB_OWNER = 'test_owner'
1201 JOB_NAME = 'test_job_name'
1202 JOB_AUTOSERV_PARAMS = set(['-u', JOB_OWNER, '-l', JOB_NAME])
jadmanski3d161b02008-06-06 15:43:36 +00001203
jadmanski0afbb632008-06-06 21:10:57 +00001204 def setUp(self):
showard184a5e82009-05-29 18:42:20 +00001205 super(AgentTasksTest, self).setUp()
jadmanski0afbb632008-06-06 21:10:57 +00001206 self.god = mock.mock_god()
showard170873e2009-01-07 00:22:26 +00001207 self.god.stub_with(drone_manager.DroneManager, 'get_temporary_path',
1208 mock.mock_function('get_temporary_path',
1209 default_return_val='tempdir'))
1210 self.god.stub_function(drone_manager.DroneManager,
showard678df4f2009-02-04 21:36:39 +00001211 'copy_results_on_drone')
1212 self.god.stub_function(drone_manager.DroneManager,
showard170873e2009-01-07 00:22:26 +00001213 'copy_to_results_repository')
1214 self.god.stub_function(drone_manager.DroneManager,
1215 'get_pidfile_id_from')
1216
1217 def dummy_absolute_path(self, path):
1218 return '/abspath/' + path
1219 self.god.stub_with(drone_manager.DroneManager, 'absolute_path',
1220 dummy_absolute_path)
1221
1222 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'run')
1223 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'exit_code')
1224 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'get_process')
showard6b733412009-04-27 20:09:18 +00001225 def mock_has_process(unused):
1226 return True
1227 self.god.stub_with(monitor_db.PidfileRunMonitor, 'has_process',
1228 mock_has_process)
jadmanski0afbb632008-06-06 21:10:57 +00001229 self.host = self.god.create_mock_class(monitor_db.Host, 'host')
showard170873e2009-01-07 00:22:26 +00001230 self.host.id = 1
jadmanski0afbb632008-06-06 21:10:57 +00001231 self.host.hostname = self.HOSTNAME
jadmanskifb7cfb12008-07-09 14:13:21 +00001232 self.host.protection = self.HOST_PROTECTION
jadmanski0afbb632008-06-06 21:10:57 +00001233 self.queue_entry = self.god.create_mock_class(
1234 monitor_db.HostQueueEntry, 'queue_entry')
showard97aed502008-11-04 02:01:24 +00001235 self.job = self.god.create_mock_class(monitor_db.Job, 'job')
showard87ba02a2009-04-20 19:37:32 +00001236 self.job.owner = self.JOB_OWNER
1237 self.job.name = self.JOB_NAME
showard170873e2009-01-07 00:22:26 +00001238 self.queue_entry.id = 1
showard97aed502008-11-04 02:01:24 +00001239 self.queue_entry.job = self.job
jadmanski0afbb632008-06-06 21:10:57 +00001240 self.queue_entry.host = self.host
1241 self.queue_entry.meta_host = None
showardd3dc1992009-04-22 21:01:40 +00001242 self._dispatcher = self.god.create_mock_class(monitor_db.Dispatcher,
1243 'dispatcher')
1244
jadmanski3d161b02008-06-06 15:43:36 +00001245
jadmanski0afbb632008-06-06 21:10:57 +00001246 def tearDown(self):
showard184a5e82009-05-29 18:42:20 +00001247 super(AgentTasksTest, self).tearDown()
jadmanski0afbb632008-06-06 21:10:57 +00001248 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001249
1250
jadmanski0afbb632008-06-06 21:10:57 +00001251 def run_task(self, task, success):
1252 """
1253 Do essentially what an Agent would do, but protect againt
1254 infinite looping from test errors.
1255 """
1256 if not getattr(task, 'agent', None):
1257 task.agent = object()
jadmanski0afbb632008-06-06 21:10:57 +00001258 count = 0
1259 while not task.is_done():
1260 count += 1
1261 if count > 10:
1262 print 'Task failed to finish'
1263 # in case the playback has clues to why it
1264 # failed
1265 self.god.check_playback()
1266 self.fail()
1267 task.poll()
1268 self.assertEquals(task.success, success)
jadmanski3d161b02008-06-06 15:43:36 +00001269
1270
showard170873e2009-01-07 00:22:26 +00001271 def setup_run_monitor(self, exit_status, copy_log_file=True):
1272 monitor_db.PidfileRunMonitor.run.expect_call(
1273 mock.is_instance_comparator(list),
1274 'tempdir',
1275 nice_level=monitor_db.AUTOSERV_NICE_LEVEL,
showardd3dc1992009-04-22 21:01:40 +00001276 log_file=mock.anything_comparator(),
1277 pidfile_name=monitor_db._AUTOSERV_PID_FILE,
1278 paired_with_pidfile=None)
showard170873e2009-01-07 00:22:26 +00001279 monitor_db.PidfileRunMonitor.exit_code.expect_call()
1280 monitor_db.PidfileRunMonitor.exit_code.expect_call().and_return(
jadmanski0afbb632008-06-06 21:10:57 +00001281 exit_status)
jadmanski3d161b02008-06-06 15:43:36 +00001282
showard170873e2009-01-07 00:22:26 +00001283 if copy_log_file:
1284 self._setup_move_logfile()
1285
1286
showard678df4f2009-02-04 21:36:39 +00001287 def _setup_move_logfile(self, copy_on_drone=False,
1288 include_destination=False):
showard170873e2009-01-07 00:22:26 +00001289 monitor_db.PidfileRunMonitor.get_process.expect_call().and_return(
1290 self.DUMMY_PROCESS)
showard678df4f2009-02-04 21:36:39 +00001291 if copy_on_drone:
1292 self.queue_entry.execution_tag.expect_call().and_return('tag')
1293 drone_manager.DroneManager.copy_results_on_drone.expect_call(
1294 self.DUMMY_PROCESS, source_path=mock.is_string_comparator(),
1295 destination_path=mock.is_string_comparator())
1296 elif include_destination:
showard170873e2009-01-07 00:22:26 +00001297 drone_manager.DroneManager.copy_to_results_repository.expect_call(
1298 self.DUMMY_PROCESS, mock.is_string_comparator(),
1299 destination_path=mock.is_string_comparator())
1300 else:
1301 drone_manager.DroneManager.copy_to_results_repository.expect_call(
1302 self.DUMMY_PROCESS, mock.is_string_comparator())
1303
jadmanski3d161b02008-06-06 15:43:36 +00001304
jadmanski0afbb632008-06-06 21:10:57 +00001305 def _test_repair_task_helper(self, success):
1306 self.host.set_status.expect_call('Repairing')
1307 if success:
1308 self.setup_run_monitor(0)
1309 self.host.set_status.expect_call('Ready')
1310 else:
1311 self.setup_run_monitor(1)
1312 self.host.set_status.expect_call('Repair Failed')
jadmanski3d161b02008-06-06 15:43:36 +00001313
jadmanski0afbb632008-06-06 21:10:57 +00001314 task = monitor_db.RepairTask(self.host)
showard56193bb2008-08-13 20:07:41 +00001315 self.assertEquals(task.failure_tasks, [])
jadmanski0afbb632008-06-06 21:10:57 +00001316 self.run_task(task, success)
jadmanskifb7cfb12008-07-09 14:13:21 +00001317
1318 expected_protection = host_protections.Protection.get_string(
1319 host_protections.default)
mbligh3e0f7e02008-07-28 19:42:01 +00001320 expected_protection = host_protections.Protection.get_attr_name(
1321 expected_protection)
1322
showard170873e2009-01-07 00:22:26 +00001323 self.assertTrue(set(task.cmd) >=
1324 set([monitor_db._autoserv_path, '-p', '-R', '-m',
1325 self.HOSTNAME, '-r', self.TEMP_DIR,
1326 '--host-protection', expected_protection]))
jadmanski0afbb632008-06-06 21:10:57 +00001327 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001328
1329
jadmanski0afbb632008-06-06 21:10:57 +00001330 def test_repair_task(self):
1331 self._test_repair_task_helper(True)
1332 self._test_repair_task_helper(False)
jadmanski3d161b02008-06-06 15:43:36 +00001333
1334
showarda1e74b32009-05-12 17:32:04 +00001335 def _test_repair_task_with_queue_entry_helper(self, parse_failed_repair):
showardde634ee2009-01-30 01:44:24 +00001336 self.god.stub_class(monitor_db, 'FinalReparseTask')
1337 self.god.stub_class(monitor_db, 'Agent')
showardd9205182009-04-27 20:09:55 +00001338 self.god.stub_class_method(monitor_db.TaskWithJobKeyvals,
1339 '_write_keyval_after_job')
showardde634ee2009-01-30 01:44:24 +00001340 agent = DummyAgent()
showardd3dc1992009-04-22 21:01:40 +00001341 agent.dispatcher = self._dispatcher
showardde634ee2009-01-30 01:44:24 +00001342
jadmanski0afbb632008-06-06 21:10:57 +00001343 self.host.set_status.expect_call('Repairing')
showarde788ea62008-11-17 21:02:47 +00001344 self.queue_entry.requeue.expect_call()
jadmanski0afbb632008-06-06 21:10:57 +00001345 self.setup_run_monitor(1)
1346 self.host.set_status.expect_call('Repair Failed')
showardccbd6c52009-03-21 00:10:21 +00001347 self.queue_entry.update_from_database.expect_call()
showardde634ee2009-01-30 01:44:24 +00001348 self.queue_entry.set_execution_subdir.expect_call()
showardd9205182009-04-27 20:09:55 +00001349 monitor_db.TaskWithJobKeyvals._write_keyval_after_job.expect_call(
1350 'job_queued', mock.is_instance_comparator(int))
1351 monitor_db.TaskWithJobKeyvals._write_keyval_after_job.expect_call(
1352 'job_finished', mock.is_instance_comparator(int))
showard678df4f2009-02-04 21:36:39 +00001353 self._setup_move_logfile(copy_on_drone=True)
showardde634ee2009-01-30 01:44:24 +00001354 self.queue_entry.execution_tag.expect_call().and_return('tag')
showard678df4f2009-02-04 21:36:39 +00001355 self._setup_move_logfile()
showarda1e74b32009-05-12 17:32:04 +00001356 self.job.parse_failed_repair = parse_failed_repair
1357 if parse_failed_repair:
1358 reparse_task = monitor_db.FinalReparseTask.expect_new(
1359 [self.queue_entry])
1360 reparse_agent = monitor_db.Agent.expect_new([reparse_task],
1361 num_processes=0)
1362 self._dispatcher.add_agent.expect_call(reparse_agent)
showarde788ea62008-11-17 21:02:47 +00001363 self.queue_entry.handle_host_failure.expect_call()
jadmanski3d161b02008-06-06 15:43:36 +00001364
showarde788ea62008-11-17 21:02:47 +00001365 task = monitor_db.RepairTask(self.host, self.queue_entry)
showardde634ee2009-01-30 01:44:24 +00001366 task.agent = agent
showardccbd6c52009-03-21 00:10:21 +00001367 self.queue_entry.status = 'Queued'
showardd9205182009-04-27 20:09:55 +00001368 self.job.created_on = datetime.datetime(2009, 1, 1)
jadmanski0afbb632008-06-06 21:10:57 +00001369 self.run_task(task, False)
showard87ba02a2009-04-20 19:37:32 +00001370 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
jadmanski0afbb632008-06-06 21:10:57 +00001371 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001372
1373
showarda1e74b32009-05-12 17:32:04 +00001374 def test_repair_task_with_queue_entry(self):
1375 self._test_repair_task_with_queue_entry_helper(True)
1376 self._test_repair_task_with_queue_entry_helper(False)
1377
1378
jadmanski0afbb632008-06-06 21:10:57 +00001379 def setup_verify_expects(self, success, use_queue_entry):
1380 if use_queue_entry:
showard8fe93b52008-11-18 17:53:22 +00001381 self.queue_entry.set_status.expect_call('Verifying')
jadmanski0afbb632008-06-06 21:10:57 +00001382 self.host.set_status.expect_call('Verifying')
1383 if success:
1384 self.setup_run_monitor(0)
1385 self.host.set_status.expect_call('Ready')
1386 else:
1387 self.setup_run_monitor(1)
showard8fe93b52008-11-18 17:53:22 +00001388 if use_queue_entry and not self.queue_entry.meta_host:
1389 self.queue_entry.set_execution_subdir.expect_call()
showard170873e2009-01-07 00:22:26 +00001390 self.queue_entry.execution_tag.expect_call().and_return('tag')
1391 self._setup_move_logfile(include_destination=True)
jadmanski3d161b02008-06-06 15:43:36 +00001392
1393
showard56193bb2008-08-13 20:07:41 +00001394 def _check_verify_failure_tasks(self, verify_task):
1395 self.assertEquals(len(verify_task.failure_tasks), 1)
1396 repair_task = verify_task.failure_tasks[0]
1397 self.assert_(isinstance(repair_task, monitor_db.RepairTask))
1398 self.assertEquals(verify_task.host, repair_task.host)
showard8fe93b52008-11-18 17:53:22 +00001399 if verify_task.queue_entry:
showardccbd6c52009-03-21 00:10:21 +00001400 self.assertEquals(repair_task.queue_entry_to_fail,
1401 verify_task.queue_entry)
showard56193bb2008-08-13 20:07:41 +00001402 else:
showardccbd6c52009-03-21 00:10:21 +00001403 self.assertEquals(repair_task.queue_entry_to_fail, None)
showard56193bb2008-08-13 20:07:41 +00001404
1405
1406 def _test_verify_task_helper(self, success, use_queue_entry=False,
1407 use_meta_host=False):
jadmanski0afbb632008-06-06 21:10:57 +00001408 self.setup_verify_expects(success, use_queue_entry)
jadmanski3d161b02008-06-06 15:43:36 +00001409
jadmanski0afbb632008-06-06 21:10:57 +00001410 if use_queue_entry:
showard170873e2009-01-07 00:22:26 +00001411 task = monitor_db.VerifyTask(queue_entry=self.queue_entry)
jadmanski0afbb632008-06-06 21:10:57 +00001412 else:
1413 task = monitor_db.VerifyTask(host=self.host)
showard56193bb2008-08-13 20:07:41 +00001414 self._check_verify_failure_tasks(task)
jadmanski0afbb632008-06-06 21:10:57 +00001415 self.run_task(task, success)
showard170873e2009-01-07 00:22:26 +00001416 self.assertTrue(set(task.cmd) >=
1417 set([monitor_db._autoserv_path, '-p', '-v', '-m',
1418 self.HOSTNAME, '-r', self.TEMP_DIR]))
showard87ba02a2009-04-20 19:37:32 +00001419 if use_queue_entry:
1420 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
jadmanski0afbb632008-06-06 21:10:57 +00001421 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001422
1423
jadmanski0afbb632008-06-06 21:10:57 +00001424 def test_verify_task_with_host(self):
showard56193bb2008-08-13 20:07:41 +00001425 self._test_verify_task_helper(True)
1426 self._test_verify_task_helper(False)
jadmanski3d161b02008-06-06 15:43:36 +00001427
1428
jadmanski0afbb632008-06-06 21:10:57 +00001429 def test_verify_task_with_queue_entry(self):
showard56193bb2008-08-13 20:07:41 +00001430 self._test_verify_task_helper(True, use_queue_entry=True)
1431 self._test_verify_task_helper(False, use_queue_entry=True)
1432
1433
1434 def test_verify_task_with_metahost(self):
showard8fe93b52008-11-18 17:53:22 +00001435 self.queue_entry.meta_host = 1
1436 self.test_verify_task_with_queue_entry()
jadmanski3d161b02008-06-06 15:43:36 +00001437
1438
showard5add1c82009-05-26 19:27:46 +00001439 def _setup_post_job_task_expects(self, autoserv_success, hqe_status=None,
showard6b733412009-04-27 20:09:18 +00001440 hqe_aborted=False):
showard170873e2009-01-07 00:22:26 +00001441 self.queue_entry.execution_tag.expect_call().and_return('tag')
1442 self.pidfile_monitor = monitor_db.PidfileRunMonitor.expect_new()
1443 self.pidfile_monitor.pidfile_id = self.PIDFILE_ID
1444 self.pidfile_monitor.attach_to_existing_process.expect_call('tag')
1445 if autoserv_success:
1446 code = 0
1447 else:
1448 code = 1
showardd3dc1992009-04-22 21:01:40 +00001449 self.queue_entry.update_from_database.expect_call()
showard6b733412009-04-27 20:09:18 +00001450 self.queue_entry.aborted = hqe_aborted
1451 if not hqe_aborted:
1452 self.pidfile_monitor.exit_code.expect_call().and_return(code)
showard170873e2009-01-07 00:22:26 +00001453
showard5add1c82009-05-26 19:27:46 +00001454 if hqe_status:
1455 self.queue_entry.set_status.expect_call(hqe_status)
showardd3dc1992009-04-22 21:01:40 +00001456
1457
1458 def _setup_pre_parse_expects(self, autoserv_success):
1459 self._setup_post_job_task_expects(autoserv_success, 'Parsing')
showard97aed502008-11-04 02:01:24 +00001460
1461
1462 def _setup_post_parse_expects(self, autoserv_success):
showard97aed502008-11-04 02:01:24 +00001463 if autoserv_success:
showard170873e2009-01-07 00:22:26 +00001464 status = 'Completed'
showard97aed502008-11-04 02:01:24 +00001465 else:
showard170873e2009-01-07 00:22:26 +00001466 status = 'Failed'
showard97aed502008-11-04 02:01:24 +00001467 self.queue_entry.set_status.expect_call(status)
1468
1469
showard5add1c82009-05-26 19:27:46 +00001470 def _expect_execute_run_monitor(self):
1471 self.monitor.exit_code.expect_call()
1472 self.monitor.exit_code.expect_call().and_return(0)
1473 self._expect_copy_results()
1474
1475
showardd3dc1992009-04-22 21:01:40 +00001476 def _setup_post_job_run_monitor(self, pidfile_name):
showard678df4f2009-02-04 21:36:39 +00001477 self.pidfile_monitor.has_process.expect_call().and_return(True)
showard170873e2009-01-07 00:22:26 +00001478 autoserv_pidfile_id = object()
showardd3dc1992009-04-22 21:01:40 +00001479 self.monitor = monitor_db.PidfileRunMonitor.expect_new()
1480 self.monitor.run.expect_call(
showard170873e2009-01-07 00:22:26 +00001481 mock.is_instance_comparator(list),
1482 'tag',
showardd3dc1992009-04-22 21:01:40 +00001483 nice_level=monitor_db.AUTOSERV_NICE_LEVEL,
showard170873e2009-01-07 00:22:26 +00001484 log_file=mock.anything_comparator(),
showardd3dc1992009-04-22 21:01:40 +00001485 pidfile_name=pidfile_name,
showard170873e2009-01-07 00:22:26 +00001486 paired_with_pidfile=self.PIDFILE_ID)
showard5add1c82009-05-26 19:27:46 +00001487 self._expect_execute_run_monitor()
showardd3dc1992009-04-22 21:01:40 +00001488
1489
showard6b733412009-04-27 20:09:18 +00001490 def _expect_copy_results(self, monitor=None, queue_entry=None):
1491 if monitor is None:
1492 monitor = self.monitor
1493 monitor.has_process.expect_call().and_return(True)
1494 if queue_entry:
1495 queue_entry.execution_tag.expect_call().and_return('tag')
1496 monitor.get_process.expect_call().and_return(self.DUMMY_PROCESS)
showard170873e2009-01-07 00:22:26 +00001497 drone_manager.DroneManager.copy_to_results_repository.expect_call(
1498 self.DUMMY_PROCESS, mock.is_string_comparator())
showard97aed502008-11-04 02:01:24 +00001499
showard170873e2009-01-07 00:22:26 +00001500
1501 def _test_final_reparse_task_helper(self, autoserv_success=True):
1502 self._setup_pre_parse_expects(autoserv_success)
showardd3dc1992009-04-22 21:01:40 +00001503 self._setup_post_job_run_monitor(monitor_db._PARSER_PID_FILE)
showard97aed502008-11-04 02:01:24 +00001504 self._setup_post_parse_expects(autoserv_success)
1505
1506 task = monitor_db.FinalReparseTask([self.queue_entry])
1507 self.run_task(task, True)
1508
1509 self.god.check_playback()
showard170873e2009-01-07 00:22:26 +00001510 cmd = [monitor_db._parser_path, '--write-pidfile', '-l', '2', '-r',
mbligh2d7c8bd2009-05-13 20:42:50 +00001511 '-o', '-P', '/abspath/tag']
showard97aed502008-11-04 02:01:24 +00001512 self.assertEquals(task.cmd, cmd)
1513
1514
1515 def test_final_reparse_task(self):
1516 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1517 self._test_final_reparse_task_helper()
showard97aed502008-11-04 02:01:24 +00001518 self._test_final_reparse_task_helper(autoserv_success=False)
1519
1520
1521 def test_final_reparse_throttling(self):
1522 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1523 self.god.stub_function(monitor_db.FinalReparseTask,
1524 '_can_run_new_parse')
1525
showard170873e2009-01-07 00:22:26 +00001526 self._setup_pre_parse_expects(True)
showard97aed502008-11-04 02:01:24 +00001527 monitor_db.FinalReparseTask._can_run_new_parse.expect_call().and_return(
1528 False)
1529 monitor_db.FinalReparseTask._can_run_new_parse.expect_call().and_return(
1530 True)
showardd3dc1992009-04-22 21:01:40 +00001531 self._setup_post_job_run_monitor(monitor_db._PARSER_PID_FILE)
showard97aed502008-11-04 02:01:24 +00001532 self._setup_post_parse_expects(True)
1533
1534 task = monitor_db.FinalReparseTask([self.queue_entry])
1535 self.run_task(task, True)
1536 self.god.check_playback()
showard1be97432008-10-17 15:30:45 +00001537
1538
showard5add1c82009-05-26 19:27:46 +00001539 def test_final_reparse_recovery(self):
1540 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1541 self.monitor = self.god.create_mock_class(monitor_db.PidfileRunMonitor,
1542 'run_monitor')
1543 self._setup_post_job_task_expects(True)
1544 self._expect_execute_run_monitor()
1545 self._setup_post_parse_expects(True)
1546
1547 task = monitor_db.FinalReparseTask([self.queue_entry],
1548 run_monitor=self.monitor)
1549 self.run_task(task, True)
1550 self.god.check_playback()
1551
1552
showard597bfd32009-05-08 18:22:50 +00001553 def _setup_gather_logs_expects(self, autoserv_killed=True,
showard0bbfc212009-04-29 21:06:13 +00001554 hqe_aborted=False):
showardd3dc1992009-04-22 21:01:40 +00001555 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1556 self.god.stub_class(monitor_db, 'FinalReparseTask')
showard597bfd32009-05-08 18:22:50 +00001557 self._setup_post_job_task_expects(not autoserv_killed, 'Gathering',
showard0bbfc212009-04-29 21:06:13 +00001558 hqe_aborted)
showard597bfd32009-05-08 18:22:50 +00001559 if hqe_aborted:
1560 exit_code = None
1561 elif autoserv_killed:
1562 exit_code = 271
1563 else:
1564 exit_code = 0
1565 self.pidfile_monitor.exit_code.expect_call().and_return(exit_code)
1566 if exit_code != 0:
showard0bbfc212009-04-29 21:06:13 +00001567 self._setup_post_job_run_monitor('.collect_crashinfo_execute')
showardebc0fb72009-05-13 21:28:07 +00001568 self.pidfile_monitor.has_process.expect_call().and_return(True)
showard6b733412009-04-27 20:09:18 +00001569 self._expect_copy_results(monitor=self.pidfile_monitor,
1570 queue_entry=self.queue_entry)
showardd3dc1992009-04-22 21:01:40 +00001571 parse_task = monitor_db.FinalReparseTask.expect_new([self.queue_entry])
showard6b733412009-04-27 20:09:18 +00001572 _set_host_and_qe_ids(parse_task)
showardd3dc1992009-04-22 21:01:40 +00001573 self._dispatcher.add_agent.expect_call(IsAgentWithTask(parse_task))
1574
showardd3dc1992009-04-22 21:01:40 +00001575
showard6b733412009-04-27 20:09:18 +00001576 def _run_gather_logs_task(self):
showardd3dc1992009-04-22 21:01:40 +00001577 task = monitor_db.GatherLogsTask(self.job, [self.queue_entry])
1578 task.agent = DummyAgent()
1579 task.agent.dispatcher = self._dispatcher
1580 self.run_task(task, True)
showardd3dc1992009-04-22 21:01:40 +00001581 self.god.check_playback()
1582
1583
showard6b733412009-04-27 20:09:18 +00001584 def test_gather_logs_task(self):
1585 self._setup_gather_logs_expects()
1586 # no rebooting for this basic test
1587 self.job.reboot_after = models.RebootAfter.NEVER
1588 self.host.set_status.expect_call('Ready')
1589
1590 self._run_gather_logs_task()
1591
1592
showard0bbfc212009-04-29 21:06:13 +00001593 def test_gather_logs_task_successful_autoserv(self):
showard597bfd32009-05-08 18:22:50 +00001594 # When Autoserv exits successfully, no collect_crashinfo stage runs
1595 self._setup_gather_logs_expects(autoserv_killed=False)
showard0bbfc212009-04-29 21:06:13 +00001596 self.job.reboot_after = models.RebootAfter.NEVER
1597 self.host.set_status.expect_call('Ready')
1598
1599 self._run_gather_logs_task()
1600
1601
showard6b733412009-04-27 20:09:18 +00001602 def _setup_gather_task_cleanup_expects(self):
1603 self.god.stub_class(monitor_db, 'CleanupTask')
1604 cleanup_task = monitor_db.CleanupTask.expect_new(host=self.host)
1605 _set_host_and_qe_ids(cleanup_task)
1606 self._dispatcher.add_agent.expect_call(IsAgentWithTask(cleanup_task))
1607
1608
1609 def test_gather_logs_reboot_hosts(self):
1610 self._setup_gather_logs_expects()
1611 self.job.reboot_after = models.RebootAfter.ALWAYS
1612 self._setup_gather_task_cleanup_expects()
1613
1614 self._run_gather_logs_task()
1615
1616
1617 def test_gather_logs_reboot_on_abort(self):
1618 self._setup_gather_logs_expects(hqe_aborted=True)
1619 self.job.reboot_after = models.RebootAfter.NEVER
1620 self._setup_gather_task_cleanup_expects()
1621
1622 self._run_gather_logs_task()
1623
1624
showard45ae8192008-11-05 19:32:53 +00001625 def _test_cleanup_task_helper(self, success, use_queue_entry=False):
showardfa8629c2008-11-04 16:51:23 +00001626 if use_queue_entry:
1627 self.queue_entry.get_host.expect_call().and_return(self.host)
showard45ae8192008-11-05 19:32:53 +00001628 self.host.set_status.expect_call('Cleaning')
showardfa8629c2008-11-04 16:51:23 +00001629 if success:
1630 self.setup_run_monitor(0)
1631 self.host.set_status.expect_call('Ready')
1632 self.host.update_field.expect_call('dirty', 0)
1633 else:
1634 self.setup_run_monitor(1)
showard8fe93b52008-11-18 17:53:22 +00001635 if use_queue_entry and not self.queue_entry.meta_host:
1636 self.queue_entry.set_execution_subdir.expect_call()
showard170873e2009-01-07 00:22:26 +00001637 self.queue_entry.execution_tag.expect_call().and_return('tag')
1638 self._setup_move_logfile(include_destination=True)
showardfa8629c2008-11-04 16:51:23 +00001639
1640 if use_queue_entry:
showard45ae8192008-11-05 19:32:53 +00001641 task = monitor_db.CleanupTask(queue_entry=self.queue_entry)
showardfa8629c2008-11-04 16:51:23 +00001642 else:
showard45ae8192008-11-05 19:32:53 +00001643 task = monitor_db.CleanupTask(host=self.host)
showardfa8629c2008-11-04 16:51:23 +00001644 self.assertEquals(len(task.failure_tasks), 1)
1645 repair_task = task.failure_tasks[0]
1646 self.assert_(isinstance(repair_task, monitor_db.RepairTask))
1647 if use_queue_entry:
showardccbd6c52009-03-21 00:10:21 +00001648 self.assertEquals(repair_task.queue_entry_to_fail, self.queue_entry)
showardfa8629c2008-11-04 16:51:23 +00001649
1650 self.run_task(task, success)
1651
1652 self.god.check_playback()
showard170873e2009-01-07 00:22:26 +00001653 self.assert_(set(task.cmd) >=
1654 set([monitor_db._autoserv_path, '-p', '--cleanup', '-m',
1655 self.HOSTNAME, '-r', self.TEMP_DIR]))
showard87ba02a2009-04-20 19:37:32 +00001656 if use_queue_entry:
1657 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
showardfa8629c2008-11-04 16:51:23 +00001658
showard45ae8192008-11-05 19:32:53 +00001659 def test_cleanup_task(self):
1660 self._test_cleanup_task_helper(True)
1661 self._test_cleanup_task_helper(False)
showardfa8629c2008-11-04 16:51:23 +00001662
1663
showard45ae8192008-11-05 19:32:53 +00001664 def test_cleanup_task_with_queue_entry(self):
1665 self._test_cleanup_task_helper(False, True)
showardfa8629c2008-11-04 16:51:23 +00001666
1667
showard5add1c82009-05-26 19:27:46 +00001668 def test_recovery_queue_task_aborted_early(self):
1669 # abort a RecoveryQueueTask right after it's created
1670 self.god.stub_class_method(monitor_db.QueueTask, '_log_abort')
1671 self.god.stub_class_method(monitor_db.QueueTask, '_finish_task')
1672 run_monitor = self.god.create_mock_class(monitor_db.PidfileRunMonitor,
1673 'run_monitor')
1674
1675 self.queue_entry.execution_tag.expect_call().and_return('tag')
1676 run_monitor.kill.expect_call()
1677 run_monitor.has_process.expect_call().and_return(True)
1678 monitor_db.QueueTask._log_abort.expect_call()
1679 monitor_db.QueueTask._finish_task.expect_call()
1680
1681 task = monitor_db.RecoveryQueueTask(self.job, [self.queue_entry],
1682 run_monitor)
1683 task.abort()
1684 self.assert_(task.aborted)
1685 self.god.check_playback()
1686
1687
showard54c1ea92009-05-20 00:32:58 +00001688class HostTest(BaseSchedulerTest):
1689 def test_cmp_for_sort(self):
1690 expected_order = [
1691 'alice', 'Host1', 'host2', 'host3', 'host09', 'HOST010',
1692 'host10', 'host11', 'yolkfolk']
1693 hostname_idx = list(monitor_db.Host._fields).index('hostname')
1694 row = [None] * len(monitor_db.Host._fields)
1695 hosts = []
1696 for hostname in expected_order:
1697 row[hostname_idx] = hostname
1698 hosts.append(monitor_db.Host(row=row, new_record=True))
1699
1700 host1 = hosts[expected_order.index('Host1')]
1701 host010 = hosts[expected_order.index('HOST010')]
1702 host10 = hosts[expected_order.index('host10')]
1703 host3 = hosts[expected_order.index('host3')]
1704 alice = hosts[expected_order.index('alice')]
1705 self.assertEqual(0, monitor_db.Host.cmp_for_sort(host10, host10))
1706 self.assertEqual(1, monitor_db.Host.cmp_for_sort(host10, host010))
1707 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host010, host10))
1708 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host1, host10))
1709 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host1, host010))
1710 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host3, host10))
1711 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host3, host010))
1712 self.assertEqual(1, monitor_db.Host.cmp_for_sort(host3, host1))
1713 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(host1, host3))
1714 self.assertEqual(-1, monitor_db.Host.cmp_for_sort(alice, host3))
1715 self.assertEqual(1, monitor_db.Host.cmp_for_sort(host3, alice))
1716 self.assertEqual(0, monitor_db.Host.cmp_for_sort(alice, alice))
1717
1718 hosts.sort(cmp=monitor_db.Host.cmp_for_sort)
1719 self.assertEqual(expected_order, [h.hostname for h in hosts])
1720
1721 hosts.reverse()
1722 hosts.sort(cmp=monitor_db.Host.cmp_for_sort)
1723 self.assertEqual(expected_order, [h.hostname for h in hosts])
1724
1725
showardf1ae3542009-05-11 19:26:02 +00001726class HostQueueEntryTest(BaseSchedulerTest):
1727 def _create_hqe(self, dependency_labels=(), **create_job_kwargs):
1728 job = self._create_job(**create_job_kwargs)
1729 for label in dependency_labels:
1730 job.dependency_labels.add(label)
1731 hqes = list(monitor_db.HostQueueEntry.fetch(where='job_id=%d' % job.id))
1732 self.assertEqual(1, len(hqes))
1733 return hqes[0]
1734
1735 def _check_hqe_labels(self, hqe, expected_labels):
1736 expected_labels = set(expected_labels)
1737 label_names = set(label.name for label in hqe.get_labels())
1738 self.assertEqual(expected_labels, label_names)
1739
1740 def test_get_labels_empty(self):
1741 hqe = self._create_hqe(hosts=[1])
1742 labels = list(hqe.get_labels())
1743 self.assertEqual([], labels)
1744
1745 def test_get_labels_metahost(self):
1746 hqe = self._create_hqe(metahosts=[2])
1747 self._check_hqe_labels(hqe, ['label2'])
1748
1749 def test_get_labels_dependancies(self):
1750 hqe = self._create_hqe(dependency_labels=(self.label3, self.label4),
1751 metahosts=[1])
1752 self._check_hqe_labels(hqe, ['label1', 'label3', 'label4'])
1753
1754
showardb2e2c322008-10-14 17:33:55 +00001755class JobTest(BaseSchedulerTest):
showard2bab8f42008-11-12 18:15:22 +00001756 def setUp(self):
1757 super(JobTest, self).setUp()
showard170873e2009-01-07 00:22:26 +00001758 self.god.stub_with(
1759 drone_manager.DroneManager, 'attach_file_to_execution',
1760 mock.mock_function('attach_file_to_execution',
1761 default_return_val='/test/path/tmp/foo'))
showard2bab8f42008-11-12 18:15:22 +00001762
1763
1764 def _setup_directory_expects(self, execution_subdir):
showardf1ae3542009-05-11 19:26:02 +00001765 # XXX(gps): um... this function does -nothing-
showard2bab8f42008-11-12 18:15:22 +00001766 job_path = os.path.join('.', '1-my_user')
1767 results_dir = os.path.join(job_path, execution_subdir)
showard2bab8f42008-11-12 18:15:22 +00001768
1769
showarde58e3f82008-11-20 19:04:59 +00001770 def _test_run_helper(self, expect_agent=True, expect_starting=False,
1771 expect_pending=False):
1772 if expect_starting:
1773 expected_status = models.HostQueueEntry.Status.STARTING
1774 elif expect_pending:
1775 expected_status = models.HostQueueEntry.Status.PENDING
1776 else:
1777 expected_status = models.HostQueueEntry.Status.VERIFYING
showardb2e2c322008-10-14 17:33:55 +00001778 job = monitor_db.Job.fetch('id = 1').next()
1779 queue_entry = monitor_db.HostQueueEntry.fetch('id = 1').next()
1780 agent = job.run(queue_entry)
1781
showard2bab8f42008-11-12 18:15:22 +00001782 self.god.check_playback()
showarde77ac672008-11-14 22:42:33 +00001783 self.assertEquals(models.HostQueueEntry.smart_get(1).status,
1784 expected_status)
showard2bab8f42008-11-12 18:15:22 +00001785
showard9976ce92008-10-15 20:28:13 +00001786 if not expect_agent:
1787 self.assertEquals(agent, None)
1788 return
1789
showardb2e2c322008-10-14 17:33:55 +00001790 self.assert_(isinstance(agent, monitor_db.Agent))
1791 tasks = list(agent.queue.queue)
1792 return tasks
1793
1794
showardc9ae1782009-01-30 01:42:37 +00001795 def _check_verify_task(self, verify_task):
1796 self.assert_(isinstance(verify_task, monitor_db.VerifyTask))
1797 self.assertEquals(verify_task.queue_entry.id, 1)
1798
1799
1800 def _check_pending_task(self, pending_task):
1801 self.assert_(isinstance(pending_task, monitor_db.SetEntryPendingTask))
1802 self.assertEquals(pending_task._queue_entry.id, 1)
1803
1804
showardb2e2c322008-10-14 17:33:55 +00001805 def test_run_asynchronous(self):
1806 self._create_job(hosts=[1, 2])
1807
1808 tasks = self._test_run_helper()
1809
showardc9ae1782009-01-30 01:42:37 +00001810 self.assertEquals(len(tasks), 2)
1811 verify_task, pending_task = tasks
1812 self._check_verify_task(verify_task)
1813 self._check_pending_task(pending_task)
showardb2e2c322008-10-14 17:33:55 +00001814
showardb2e2c322008-10-14 17:33:55 +00001815
showard9976ce92008-10-15 20:28:13 +00001816 def test_run_asynchronous_skip_verify(self):
1817 job = self._create_job(hosts=[1, 2])
1818 job.run_verify = False
1819 job.save()
showard2bab8f42008-11-12 18:15:22 +00001820 self._setup_directory_expects('host1')
showard9976ce92008-10-15 20:28:13 +00001821
showardc9ae1782009-01-30 01:42:37 +00001822 tasks = self._test_run_helper()
showard9976ce92008-10-15 20:28:13 +00001823
1824 self.assertEquals(len(tasks), 1)
showardc9ae1782009-01-30 01:42:37 +00001825 pending_task = tasks[0]
1826 self._check_pending_task(pending_task)
showard9976ce92008-10-15 20:28:13 +00001827
1828
showardb2e2c322008-10-14 17:33:55 +00001829 def test_run_synchronous_verify(self):
1830 self._create_job(hosts=[1, 2], synchronous=True)
1831
1832 tasks = self._test_run_helper()
showardc9ae1782009-01-30 01:42:37 +00001833 self.assertEquals(len(tasks), 2)
1834 verify_task, pending_task = tasks
1835 self._check_verify_task(verify_task)
1836 self._check_pending_task(pending_task)
showardb2e2c322008-10-14 17:33:55 +00001837
1838
showard9976ce92008-10-15 20:28:13 +00001839 def test_run_synchronous_skip_verify(self):
1840 job = self._create_job(hosts=[1, 2], synchronous=True)
1841 job.run_verify = False
1842 job.save()
1843
showardc9ae1782009-01-30 01:42:37 +00001844 tasks = self._test_run_helper()
1845 self.assertEquals(len(tasks), 1)
1846 self._check_pending_task(tasks[0])
showard9976ce92008-10-15 20:28:13 +00001847
1848
showardb2e2c322008-10-14 17:33:55 +00001849 def test_run_synchronous_ready(self):
1850 self._create_job(hosts=[1, 2], synchronous=True)
showardd9ac4452009-02-07 02:04:37 +00001851 self._update_hqe("status='Pending', execution_subdir=''")
showard2bab8f42008-11-12 18:15:22 +00001852 self._setup_directory_expects('group0')
showardb2e2c322008-10-14 17:33:55 +00001853
showarde58e3f82008-11-20 19:04:59 +00001854 tasks = self._test_run_helper(expect_starting=True)
showardb2e2c322008-10-14 17:33:55 +00001855 self.assertEquals(len(tasks), 1)
1856 queue_task = tasks[0]
1857
1858 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
1859 self.assertEquals(queue_task.job.id, 1)
1860 hqe_ids = [hqe.id for hqe in queue_task.queue_entries]
1861 self.assertEquals(hqe_ids, [1, 2])
1862
1863
showardf1ae3542009-05-11 19:26:02 +00001864 def test_run_synchronous_atomic_group_ready(self):
1865 self._create_job(hosts=[5, 6], atomic_group=1, synchronous=True)
1866 self._update_hqe("status='Pending', execution_subdir=''")
1867
1868 tasks = self._test_run_helper(expect_starting=True)
1869 self.assertEquals(len(tasks), 1)
1870 queue_task = tasks[0]
1871
1872 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
1873 # Atomic group jobs that do not a specific label in the atomic group
1874 # will use the atomic group name as their group name.
1875 self.assertEquals(queue_task.group_name, 'atomic1')
1876
1877
1878 def test_run_synchronous_atomic_group_with_label_ready(self):
1879 job = self._create_job(hosts=[5, 6], atomic_group=1, synchronous=True)
1880 job.dependency_labels.add(self.label4)
1881 self._update_hqe("status='Pending', execution_subdir=''")
1882
1883 tasks = self._test_run_helper(expect_starting=True)
1884 self.assertEquals(len(tasks), 1)
1885 queue_task = tasks[0]
1886
1887 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
1888 # Atomic group jobs that also specify a label in the atomic group
1889 # will use the label name as their group name.
1890 self.assertEquals(queue_task.group_name, 'label4')
1891
1892
showard21baa452008-10-21 00:08:39 +00001893 def test_reboot_before_always(self):
1894 job = self._create_job(hosts=[1])
showard0fc38302008-10-23 00:44:07 +00001895 job.reboot_before = models.RebootBefore.ALWAYS
showard21baa452008-10-21 00:08:39 +00001896 job.save()
1897
1898 tasks = self._test_run_helper()
showardc9ae1782009-01-30 01:42:37 +00001899 self.assertEquals(len(tasks), 3)
showard45ae8192008-11-05 19:32:53 +00001900 cleanup_task = tasks[0]
1901 self.assert_(isinstance(cleanup_task, monitor_db.CleanupTask))
1902 self.assertEquals(cleanup_task.host.id, 1)
showard21baa452008-10-21 00:08:39 +00001903
1904
1905 def _test_reboot_before_if_dirty_helper(self, expect_reboot):
1906 job = self._create_job(hosts=[1])
showard0fc38302008-10-23 00:44:07 +00001907 job.reboot_before = models.RebootBefore.IF_DIRTY
showard21baa452008-10-21 00:08:39 +00001908 job.save()
1909
1910 tasks = self._test_run_helper()
showardc9ae1782009-01-30 01:42:37 +00001911 self.assertEquals(len(tasks), expect_reboot and 3 or 2)
showard21baa452008-10-21 00:08:39 +00001912 if expect_reboot:
showard45ae8192008-11-05 19:32:53 +00001913 cleanup_task = tasks[0]
1914 self.assert_(isinstance(cleanup_task, monitor_db.CleanupTask))
1915 self.assertEquals(cleanup_task.host.id, 1)
showard21baa452008-10-21 00:08:39 +00001916
1917 def test_reboot_before_if_dirty(self):
1918 models.Host.smart_get(1).update_object(dirty=True)
1919 self._test_reboot_before_if_dirty_helper(True)
1920
1921
1922 def test_reboot_before_not_dirty(self):
1923 models.Host.smart_get(1).update_object(dirty=False)
1924 self._test_reboot_before_if_dirty_helper(False)
1925
1926
showardf1ae3542009-05-11 19:26:02 +00001927 def test_next_group_name(self):
1928 django_job = self._create_job(metahosts=[1])
1929 job = monitor_db.Job(id=django_job.id)
1930 self.assertEqual('group0', job._next_group_name())
1931
1932 for hqe in django_job.hostqueueentry_set.filter():
1933 hqe.execution_subdir = 'my_rack.group0'
1934 hqe.save()
1935 self.assertEqual('my_rack.group1', job._next_group_name('my/rack'))
1936
1937
1938class TopLevelFunctionsTest(unittest.TestCase):
1939 def test_autoserv_command_line(self):
1940 machines = 'abcd12,efgh34'
1941 results_dir = '/fake/path'
1942 extra_args = ['-Z', 'hello']
1943 expected_command_line = [monitor_db._autoserv_path, '-p',
1944 '-m', machines, '-r', results_dir]
1945
1946 command_line = monitor_db._autoserv_command_line(
1947 machines, results_dir, extra_args)
1948 self.assertEqual(expected_command_line + extra_args, command_line)
1949
1950 class FakeJob(object):
1951 owner = 'Bob'
1952 name = 'fake job name'
1953
1954 command_line = monitor_db._autoserv_command_line(
1955 machines, results_dir, extra_args=[], job=FakeJob())
1956 self.assertEqual(expected_command_line +
1957 ['-u', FakeJob.owner, '-l', FakeJob.name],
1958 command_line)
1959
showard21baa452008-10-21 00:08:39 +00001960
showardce38e0c2008-05-29 19:36:16 +00001961if __name__ == '__main__':
jadmanski0afbb632008-06-06 21:10:57 +00001962 unittest.main()