blob: 4db57290ae49545fe961c6f298d90183e27c9a8e [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
7from autotest_lib.frontend import setup_test_environment
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
showardb2e2c322008-10-14 17:33:55 +000076class BaseSchedulerTest(unittest.TestCase):
showard50c0e712008-09-22 16:20:37 +000077 _config_section = 'AUTOTEST_WEB'
showardb1e51872008-10-07 11:08:18 +000078 _test_db_initialized = False
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
showardb1e51872008-10-07 11:08:18 +000084 @classmethod
85 def _initialize_test_db(cls):
86 if cls._test_db_initialized:
87 return
88 temp_fd, cls._test_db_file = tempfile.mkstemp(suffix='.monitor_test')
89 os.close(temp_fd)
showard364fe862008-10-17 02:01:16 +000090 setup_test_environment.set_test_database(cls._test_db_file)
91 setup_test_environment.run_syncdb()
92 cls._test_db_backup = setup_test_environment.backup_test_database()
showardb1e51872008-10-07 11:08:18 +000093 cls._test_db_initialized = True
showardce38e0c2008-05-29 19:36:16 +000094
95
showard50c0e712008-09-22 16:20:37 +000096 def _open_test_db(self):
showardb1e51872008-10-07 11:08:18 +000097 self._initialize_test_db()
showard364fe862008-10-17 02:01:16 +000098 setup_test_environment.restore_test_database(self._test_db_backup)
showardb1e51872008-10-07 11:08:18 +000099 self._database = (
100 database_connection.DatabaseConnection.get_test_database(
101 self._test_db_file))
102 self._database.connect()
103 self._database.debug = _DEBUG
showardce38e0c2008-05-29 19:36:16 +0000104
105
jadmanski0afbb632008-06-06 21:10:57 +0000106 def _close_test_db(self):
showardb1e51872008-10-07 11:08:18 +0000107 self._database.disconnect()
showardce38e0c2008-05-29 19:36:16 +0000108
109
showard56193bb2008-08-13 20:07:41 +0000110 def _set_monitor_stubs(self):
showarda3c58572009-03-12 20:36:59 +0000111 # Clear the instance cache as this is a brand new database.
112 monitor_db.DBObject._clear_instance_cache()
showardb1e51872008-10-07 11:08:18 +0000113 monitor_db._db = self._database
showard170873e2009-01-07 00:22:26 +0000114 monitor_db._drone_manager._results_dir = '/test/path'
115 monitor_db._drone_manager._temporary_directory = '/test/path/tmp'
showard56193bb2008-08-13 20:07:41 +0000116
117
jadmanski0afbb632008-06-06 21:10:57 +0000118 def _fill_in_test_data(self):
showard89f84db2009-03-12 20:39:13 +0000119 """Populate the test database with some hosts and labels."""
showardb1e51872008-10-07 11:08:18 +0000120 user = models.User.objects.create(login='my_user')
121 acl_group = models.AclGroup.objects.create(name='my_acl')
122 acl_group.users.add(user)
123
124 hosts = [models.Host.objects.create(hostname=hostname) for hostname in
showard89f84db2009-03-12 20:39:13 +0000125 ('host1', 'host2', 'host3', 'host4', 'host5', 'host6',
126 'host7', 'host8', 'host9')]
127
showardb1e51872008-10-07 11:08:18 +0000128 acl_group.hosts = hosts
showard98863972008-10-29 21:14:56 +0000129 models.AclGroup.smart_get('Everyone').hosts = []
showardb1e51872008-10-07 11:08:18 +0000130
131 labels = [models.Label.objects.create(name=name) for name in
showard89f84db2009-03-12 20:39:13 +0000132 ('label1', 'label2', 'label3', 'label4', 'label5', 'label6',
133 'label7')]
134
135 atomic_group1 = models.AtomicGroup.objects.create(
136 name='atomic1', max_number_of_machines=2)
137 atomic_group2 = models.AtomicGroup.objects.create(
138 name='atomic2', max_number_of_machines=2)
139
140 self.label3 = labels[2]
141 self.label3.only_if_needed = True
142 self.label3.save()
143 self.label4 = labels[3]
144 self.label4.atomic_group = atomic_group1
145 self.label4.save()
146 self.label5 = labels[4]
147 self.label5.atomic_group = atomic_group1
148 self.label5.save()
149 hosts[0].labels.add(labels[0]) # label1
150 hosts[1].labels.add(labels[1]) # label2
151 self.label6 = labels[5]
152 self.label7 = labels[6]
153 for hostnum in xrange(4,7): # host5..host7
154 hosts[hostnum].labels.add(self.label4) # an atomic group lavel
155 hosts[hostnum].labels.add(self.label6) # a normal label
156 hosts[6].labels.add(self.label7)
157 for hostnum in xrange(7,9): # host8..host9
158 hosts[hostnum].labels.add(self.label5) # an atomic group lavel
159 hosts[hostnum].labels.add(self.label6) # a normal label
160 hosts[hostnum].labels.add(self.label7)
showardce38e0c2008-05-29 19:36:16 +0000161
162
showard21baa452008-10-21 00:08:39 +0000163 def _setup_dummy_user(self):
164 user = models.User.objects.create(login='dummy', access_level=100)
165 thread_local.set_user(user)
166
167
showard56193bb2008-08-13 20:07:41 +0000168 def setUp(self):
169 self.god = mock.mock_god()
showard50c0e712008-09-22 16:20:37 +0000170 self._open_test_db()
showard56193bb2008-08-13 20:07:41 +0000171 self._fill_in_test_data()
172 self._set_monitor_stubs()
173 self._dispatcher = monitor_db.Dispatcher()
showard21baa452008-10-21 00:08:39 +0000174 self._setup_dummy_user()
showardce38e0c2008-05-29 19:36:16 +0000175
176
showard56193bb2008-08-13 20:07:41 +0000177 def tearDown(self):
178 self._close_test_db()
179 self.god.unstub_all()
showardce38e0c2008-05-29 19:36:16 +0000180
181
showard2bab8f42008-11-12 18:15:22 +0000182 def _create_job(self, hosts=[], metahosts=[], priority=0, active=False,
showard89f84db2009-03-12 20:39:13 +0000183 synchronous=False, atomic_group=None):
184 """
185 Create a job row in the test database.
186
187 @param hosts - A list of explicit host ids for this job to be
188 scheduled on.
189 @param metahosts - A list of label ids for each host that this job
190 should be scheduled on (meta host scheduling).
191 @param priority - The job priority (integer).
192 @param active - bool, mark this job as running or not in the database?
193 @param synchronous - bool, if True use synch_count=2 otherwise use
194 synch_count=1.
195 @param atomic_group - An atomic group id for this job to schedule on
196 or None if atomic scheduling is not required. Each metahost
197 becomes a request to schedule an entire atomic group.
198 This does not support creating an active atomic group job.
199 """
200 assert not (atomic_group and active) # TODO(gps): support this
showard2bab8f42008-11-12 18:15:22 +0000201 synch_count = synchronous and 2 or 1
showard12bc8a82008-10-09 16:49:53 +0000202 created_on = datetime.datetime(2008, 1, 1)
showard2bab8f42008-11-12 18:15:22 +0000203 status = models.HostQueueEntry.Status.QUEUED
204 if active:
205 status = models.HostQueueEntry.Status.RUNNING
showard21baa452008-10-21 00:08:39 +0000206 job = models.Job.objects.create(
207 name='test', owner='my_user', priority=priority,
showard2bab8f42008-11-12 18:15:22 +0000208 synch_count=synch_count, created_on=created_on,
showard0fc38302008-10-23 00:44:07 +0000209 reboot_before=models.RebootBefore.NEVER)
jadmanski0afbb632008-06-06 21:10:57 +0000210 for host_id in hosts:
showard25cbdbd2009-02-17 20:57:21 +0000211 models.HostQueueEntry.objects.create(job=job, host_id=host_id,
showard89f84db2009-03-12 20:39:13 +0000212 status=status,
213 atomic_group_id=atomic_group)
showardb1e51872008-10-07 11:08:18 +0000214 models.IneligibleHostQueue.objects.create(job=job, host_id=host_id)
jadmanski0afbb632008-06-06 21:10:57 +0000215 for label_id in metahosts:
showard25cbdbd2009-02-17 20:57:21 +0000216 models.HostQueueEntry.objects.create(job=job, meta_host_id=label_id,
showard89f84db2009-03-12 20:39:13 +0000217 status=status,
218 atomic_group_id=atomic_group)
219 if atomic_group and not (metahosts or hosts):
220 # Create a single HQE to request the atomic group of hosts even if
221 # no metahosts or hosts are supplied.
222 models.HostQueueEntry.objects.create(job=job,
223 status=status,
224 atomic_group_id=atomic_group)
showardb1e51872008-10-07 11:08:18 +0000225 return job
showardce38e0c2008-05-29 19:36:16 +0000226
227
jadmanski0afbb632008-06-06 21:10:57 +0000228 def _create_job_simple(self, hosts, use_metahost=False,
showard2bab8f42008-11-12 18:15:22 +0000229 priority=0, active=False):
showard89f84db2009-03-12 20:39:13 +0000230 """An alternative interface to _create_job"""
jadmanski0afbb632008-06-06 21:10:57 +0000231 args = {'hosts' : [], 'metahosts' : []}
232 if use_metahost:
233 args['metahosts'] = hosts
234 else:
235 args['hosts'] = hosts
showardb1e51872008-10-07 11:08:18 +0000236 return self._create_job(priority=priority, active=active, **args)
showardce38e0c2008-05-29 19:36:16 +0000237
238
showard56193bb2008-08-13 20:07:41 +0000239 def _update_hqe(self, set, where=''):
240 query = 'UPDATE host_queue_entries SET ' + set
241 if where:
242 query += ' WHERE ' + where
243 self._do_query(query)
244
245
showarda3c58572009-03-12 20:36:59 +0000246class DBObjectTest(BaseSchedulerTest):
247 # It may seem odd to subclass BaseSchedulerTest for this but it saves us
248 # duplicating some setup work for what we want to test.
249
250
251 def test_compare_fields_in_row(self):
252 host = monitor_db.Host(id=1)
253 fields = list(host._fields)
254 row_data = [getattr(host, fieldname) for fieldname in fields]
255 self.assertEqual({}, host._compare_fields_in_row(row_data))
256 row_data[fields.index('hostname')] = 'spam'
257 self.assertEqual({'hostname': ('host1', 'spam')},
258 host._compare_fields_in_row(row_data))
259 row_data[fields.index('id')] = 23
260 self.assertEqual({'hostname': ('host1', 'spam'), 'id': (1, 23)},
261 host._compare_fields_in_row(row_data))
262
263
264 def test_always_query(self):
265 host_a = monitor_db.Host(id=2)
266 self.assertEqual(host_a.hostname, 'host2')
267 self._do_query('UPDATE hosts SET hostname="host2-updated" WHERE id=2')
268 host_b = monitor_db.Host(id=2, always_query=True)
269 self.assert_(host_a is host_b, 'Cached instance not returned.')
270 self.assertEqual(host_a.hostname, 'host2-updated',
271 'Database was not re-queried')
272
273 # If either of these are called, a query was made when it shouldn't be.
274 host_a._compare_fields_in_row = lambda _: self.fail('eek! a query!')
275 host_a._update_fields_from_row = host_a._compare_fields_in_row
276 host_c = monitor_db.Host(id=2, always_query=False)
277 self.assert_(host_a is host_c, 'Cached instance not returned')
278
279
280 def test_delete(self):
281 host = monitor_db.Host(id=3)
282 host.delete()
283 host = self.assertRaises(monitor_db.DBError, monitor_db.Host, id=3,
284 always_query=False)
285 host = self.assertRaises(monitor_db.DBError, monitor_db.Host, id=3,
286 always_query=True)
287
showard76e29d12009-04-15 21:53:10 +0000288 def test_save(self):
289 # Dummy Job to avoid creating a one in the HostQueueEntry __init__.
290 class MockJob(object):
291 def __init__(self, id):
292 pass
293 def tag(self):
294 return 'MockJob'
295 self.god.stub_with(monitor_db, 'Job', MockJob)
296 hqe = monitor_db.HostQueueEntry(
297 new_record=True,
showardd3dc1992009-04-22 21:01:40 +0000298 row=[0, 1, 2, 'Queued', None, 0, 0, 0, '.', None, False])
showard76e29d12009-04-15 21:53:10 +0000299 hqe.save()
300 new_id = hqe.id
301 # Force a re-query and verify that the correct data was stored.
302 monitor_db.DBObject._clear_instance_cache()
303 hqe = monitor_db.HostQueueEntry(id=new_id)
304 self.assertEqual(hqe.id, new_id)
305 self.assertEqual(hqe.job_id, 1)
306 self.assertEqual(hqe.host_id, 2)
307 self.assertEqual(hqe.status, 'Queued')
308 self.assertEqual(hqe.meta_host, None)
309 self.assertEqual(hqe.active, False)
310 self.assertEqual(hqe.complete, False)
311 self.assertEqual(hqe.deleted, False)
312 self.assertEqual(hqe.execution_subdir, '.')
313 self.assertEqual(hqe.atomic_group_id, None)
showarda3c58572009-03-12 20:36:59 +0000314
315
showardb2e2c322008-10-14 17:33:55 +0000316class DispatcherSchedulingTest(BaseSchedulerTest):
showard56193bb2008-08-13 20:07:41 +0000317 _jobs_scheduled = []
318
showard89f84db2009-03-12 20:39:13 +0000319
320 def tearDown(self):
321 super(DispatcherSchedulingTest, self).tearDown()
322
323
showard56193bb2008-08-13 20:07:41 +0000324 def _set_monitor_stubs(self):
325 super(DispatcherSchedulingTest, self)._set_monitor_stubs()
showard89f84db2009-03-12 20:39:13 +0000326
327 def job_run_stub(job_self, queue_entry):
328 """Return a dummy for testing. Called by HostQueueEntry.run()."""
329 self._record_job_scheduled(job_self.id, queue_entry.host.id)
330 queue_entry.set_status('Starting')
showard170873e2009-01-07 00:22:26 +0000331 return DummyAgent()
showard89f84db2009-03-12 20:39:13 +0000332
333 self.god.stub_with(monitor_db.Job, 'run', job_run_stub)
334
335 def hqe_queue_log_record_stub(self, log_line):
336 """No-Op to avoid calls down to the _drone_manager during tests."""
337
338 self.god.stub_with(monitor_db.HostQueueEntry, 'queue_log_record',
339 hqe_queue_log_record_stub)
showard56193bb2008-08-13 20:07:41 +0000340
341
342 def _record_job_scheduled(self, job_id, host_id):
343 record = (job_id, host_id)
344 self.assert_(record not in self._jobs_scheduled,
345 'Job %d scheduled on host %d twice' %
346 (job_id, host_id))
347 self._jobs_scheduled.append(record)
348
349
350 def _assert_job_scheduled_on(self, job_id, host_id):
351 record = (job_id, host_id)
352 self.assert_(record in self._jobs_scheduled,
353 'Job %d not scheduled on host %d as expected\n'
354 'Jobs scheduled: %s' %
355 (job_id, host_id, self._jobs_scheduled))
356 self._jobs_scheduled.remove(record)
357
358
showard89f84db2009-03-12 20:39:13 +0000359 def _assert_job_scheduled_on_number_of(self, job_id, host_ids, number):
360 """Assert job was scheduled on exactly number hosts out of a set."""
361 found = []
362 for host_id in host_ids:
363 record = (job_id, host_id)
364 if record in self._jobs_scheduled:
365 found.append(record)
366 self._jobs_scheduled.remove(record)
367 if len(found) < number:
368 self.fail('Job %d scheduled on fewer than %d hosts in %s.\n'
369 'Jobs scheduled: %s' % (job_id, number, host_ids, found))
370 elif len(found) > number:
371 self.fail('Job %d scheduled on more than %d hosts in %s.\n'
372 'Jobs scheduled: %s' % (job_id, number, host_ids, found))
373
374
showard56193bb2008-08-13 20:07:41 +0000375 def _check_for_extra_schedulings(self):
376 if len(self._jobs_scheduled) != 0:
377 self.fail('Extra jobs scheduled: ' +
378 str(self._jobs_scheduled))
379
380
jadmanski0afbb632008-06-06 21:10:57 +0000381 def _convert_jobs_to_metahosts(self, *job_ids):
382 sql_tuple = '(' + ','.join(str(i) for i in job_ids) + ')'
383 self._do_query('UPDATE host_queue_entries SET '
384 'meta_host=host_id, host_id=NULL '
385 'WHERE job_id IN ' + sql_tuple)
showardce38e0c2008-05-29 19:36:16 +0000386
387
jadmanski0afbb632008-06-06 21:10:57 +0000388 def _lock_host(self, host_id):
389 self._do_query('UPDATE hosts SET locked=1 WHERE id=' +
390 str(host_id))
showardce38e0c2008-05-29 19:36:16 +0000391
392
jadmanski0afbb632008-06-06 21:10:57 +0000393 def setUp(self):
showard56193bb2008-08-13 20:07:41 +0000394 super(DispatcherSchedulingTest, self).setUp()
jadmanski0afbb632008-06-06 21:10:57 +0000395 self._jobs_scheduled = []
showardce38e0c2008-05-29 19:36:16 +0000396
397
jadmanski0afbb632008-06-06 21:10:57 +0000398 def _test_basic_scheduling_helper(self, use_metahosts):
399 'Basic nonmetahost scheduling'
400 self._create_job_simple([1], use_metahosts)
401 self._create_job_simple([2], use_metahosts)
402 self._dispatcher._schedule_new_jobs()
403 self._assert_job_scheduled_on(1, 1)
404 self._assert_job_scheduled_on(2, 2)
405 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000406
407
jadmanski0afbb632008-06-06 21:10:57 +0000408 def _test_priorities_helper(self, use_metahosts):
409 'Test prioritization ordering'
410 self._create_job_simple([1], use_metahosts)
411 self._create_job_simple([2], use_metahosts)
412 self._create_job_simple([1,2], use_metahosts)
413 self._create_job_simple([1], use_metahosts, priority=1)
414 self._dispatcher._schedule_new_jobs()
415 self._assert_job_scheduled_on(4, 1) # higher priority
416 self._assert_job_scheduled_on(2, 2) # earlier job over later
417 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000418
419
jadmanski0afbb632008-06-06 21:10:57 +0000420 def _test_hosts_ready_helper(self, use_metahosts):
421 """
422 Only hosts that are status=Ready, unlocked and not invalid get
423 scheduled.
424 """
425 self._create_job_simple([1], use_metahosts)
426 self._do_query('UPDATE hosts SET status="Running" WHERE id=1')
427 self._dispatcher._schedule_new_jobs()
428 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000429
jadmanski0afbb632008-06-06 21:10:57 +0000430 self._do_query('UPDATE hosts SET status="Ready", locked=1 '
431 'WHERE id=1')
432 self._dispatcher._schedule_new_jobs()
433 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000434
jadmanski0afbb632008-06-06 21:10:57 +0000435 self._do_query('UPDATE hosts SET locked=0, invalid=1 '
436 'WHERE id=1')
437 self._dispatcher._schedule_new_jobs()
showard5df2b192008-07-03 19:51:57 +0000438 if not use_metahosts:
439 self._assert_job_scheduled_on(1, 1)
jadmanski0afbb632008-06-06 21:10:57 +0000440 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000441
442
jadmanski0afbb632008-06-06 21:10:57 +0000443 def _test_hosts_idle_helper(self, use_metahosts):
444 'Only idle hosts get scheduled'
showard2bab8f42008-11-12 18:15:22 +0000445 self._create_job(hosts=[1], active=True)
jadmanski0afbb632008-06-06 21:10:57 +0000446 self._create_job_simple([1], use_metahosts)
447 self._dispatcher._schedule_new_jobs()
448 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000449
450
showard63a34772008-08-18 19:32:50 +0000451 def _test_obey_ACLs_helper(self, use_metahosts):
452 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id=1')
453 self._create_job_simple([1], use_metahosts)
454 self._dispatcher._schedule_new_jobs()
455 self._check_for_extra_schedulings()
456
457
jadmanski0afbb632008-06-06 21:10:57 +0000458 def test_basic_scheduling(self):
459 self._test_basic_scheduling_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000460
461
jadmanski0afbb632008-06-06 21:10:57 +0000462 def test_priorities(self):
463 self._test_priorities_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000464
465
jadmanski0afbb632008-06-06 21:10:57 +0000466 def test_hosts_ready(self):
467 self._test_hosts_ready_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000468
469
jadmanski0afbb632008-06-06 21:10:57 +0000470 def test_hosts_idle(self):
471 self._test_hosts_idle_helper(False)
showardce38e0c2008-05-29 19:36:16 +0000472
473
showard63a34772008-08-18 19:32:50 +0000474 def test_obey_ACLs(self):
475 self._test_obey_ACLs_helper(False)
476
477
478 def test_non_metahost_on_invalid_host(self):
479 """
480 Non-metahost entries can get scheduled on invalid hosts (this is how
481 one-time hosts work).
482 """
483 self._do_query('UPDATE hosts SET invalid=1')
484 self._test_basic_scheduling_helper(False)
485
486
jadmanski0afbb632008-06-06 21:10:57 +0000487 def test_metahost_scheduling(self):
showard63a34772008-08-18 19:32:50 +0000488 """
489 Basic metahost scheduling
490 """
jadmanski0afbb632008-06-06 21:10:57 +0000491 self._test_basic_scheduling_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000492
493
jadmanski0afbb632008-06-06 21:10:57 +0000494 def test_metahost_priorities(self):
495 self._test_priorities_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000496
497
jadmanski0afbb632008-06-06 21:10:57 +0000498 def test_metahost_hosts_ready(self):
499 self._test_hosts_ready_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000500
501
jadmanski0afbb632008-06-06 21:10:57 +0000502 def test_metahost_hosts_idle(self):
503 self._test_hosts_idle_helper(True)
showardce38e0c2008-05-29 19:36:16 +0000504
505
showard63a34772008-08-18 19:32:50 +0000506 def test_metahost_obey_ACLs(self):
507 self._test_obey_ACLs_helper(True)
508
509
showard89f84db2009-03-12 20:39:13 +0000510 def _setup_test_only_if_needed_labels(self):
showardade14e22009-01-26 22:38:32 +0000511 # apply only_if_needed label3 to host1
showard89f84db2009-03-12 20:39:13 +0000512 models.Host.smart_get('host1').labels.add(self.label3)
513 return self._create_job_simple([1], use_metahost=True)
showardade14e22009-01-26 22:38:32 +0000514
showard89f84db2009-03-12 20:39:13 +0000515
516 def test_only_if_needed_labels_avoids_host(self):
517 job = self._setup_test_only_if_needed_labels()
showardade14e22009-01-26 22:38:32 +0000518 # if the job doesn't depend on label3, there should be no scheduling
519 self._dispatcher._schedule_new_jobs()
520 self._check_for_extra_schedulings()
521
showard89f84db2009-03-12 20:39:13 +0000522
523 def test_only_if_needed_labels_schedules(self):
524 job = self._setup_test_only_if_needed_labels()
525 job.dependency_labels.add(self.label3)
showardade14e22009-01-26 22:38:32 +0000526 self._dispatcher._schedule_new_jobs()
527 self._assert_job_scheduled_on(1, 1)
528 self._check_for_extra_schedulings()
529
showard89f84db2009-03-12 20:39:13 +0000530
531 def test_only_if_needed_labels_via_metahost(self):
532 job = self._setup_test_only_if_needed_labels()
533 job.dependency_labels.add(self.label3)
showardade14e22009-01-26 22:38:32 +0000534 # should also work if the metahost is the only_if_needed label
535 self._do_query('DELETE FROM jobs_dependency_labels')
536 self._create_job(metahosts=[3])
537 self._dispatcher._schedule_new_jobs()
538 self._assert_job_scheduled_on(2, 1)
539 self._check_for_extra_schedulings()
showard989f25d2008-10-01 11:38:11 +0000540
541
jadmanski0afbb632008-06-06 21:10:57 +0000542 def test_nonmetahost_over_metahost(self):
543 """
544 Non-metahost entries should take priority over metahost entries
545 for the same host
546 """
547 self._create_job(metahosts=[1])
548 self._create_job(hosts=[1])
549 self._dispatcher._schedule_new_jobs()
550 self._assert_job_scheduled_on(2, 1)
551 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000552
553
jadmanski0afbb632008-06-06 21:10:57 +0000554 def test_metahosts_obey_blocks(self):
555 """
556 Metahosts can't get scheduled on hosts already scheduled for
557 that job.
558 """
559 self._create_job(metahosts=[1], hosts=[1])
560 # make the nonmetahost entry complete, so the metahost can try
561 # to get scheduled
showard56193bb2008-08-13 20:07:41 +0000562 self._update_hqe(set='complete = 1', where='host_id=1')
jadmanski0afbb632008-06-06 21:10:57 +0000563 self._dispatcher._schedule_new_jobs()
564 self._check_for_extra_schedulings()
showardce38e0c2008-05-29 19:36:16 +0000565
566
showard89f84db2009-03-12 20:39:13 +0000567 # TODO(gps): These should probably live in their own TestCase class
568 # specific to testing HostScheduler methods directly. It was convenient
569 # to put it here for now to share existing test environment setup code.
570 def test_HostScheduler_check_atomic_group_labels(self):
571 normal_job = self._create_job(metahosts=[0])
572 atomic_job = self._create_job(atomic_group=1)
573 # Indirectly initialize the internal state of the host scheduler.
574 self._dispatcher._refresh_pending_queue_entries()
575
576 atomic_hqe = monitor_db.HostQueueEntry(id=atomic_job.id)
577 normal_hqe = monitor_db.HostQueueEntry(id=normal_job.id)
578
579 host_scheduler = self._dispatcher._host_scheduler
580 self.assertTrue(host_scheduler._check_atomic_group_labels(
581 [self.label4.id], atomic_hqe))
582 self.assertFalse(host_scheduler._check_atomic_group_labels(
583 [self.label4.id], normal_hqe))
584 self.assertFalse(host_scheduler._check_atomic_group_labels(
585 [self.label5.id, self.label6.id, self.label7.id], normal_hqe))
586 self.assertTrue(host_scheduler._check_atomic_group_labels(
587 [self.label4.id, self.label6.id], atomic_hqe))
588 self.assertRaises(monitor_db.SchedulerError,
589 host_scheduler._check_atomic_group_labels,
590 [self.label4.id, self.label5.id],
591 atomic_hqe)
592
593
594 def test_HostScheduler_get_host_atomic_group_id(self):
595 self._create_job(metahosts=[self.label6.id])
596 # Indirectly initialize the internal state of the host scheduler.
597 self._dispatcher._refresh_pending_queue_entries()
598
599 # Test the host scheduler
600 host_scheduler = self._dispatcher._host_scheduler
601 self.assertRaises(monitor_db.SchedulerError,
602 host_scheduler._get_host_atomic_group_id,
603 [self.label4.id, self.label5.id])
604 self.assertEqual(None, host_scheduler._get_host_atomic_group_id([]))
605 self.assertEqual(None, host_scheduler._get_host_atomic_group_id(
606 [self.label3.id, self.label7.id, self.label6.id]))
607 self.assertEqual(1, host_scheduler._get_host_atomic_group_id(
608 [self.label4.id, self.label7.id, self.label6.id]))
609 self.assertEqual(1, host_scheduler._get_host_atomic_group_id(
610 [self.label7.id, self.label5.id]))
611
612
613 def test_atomic_group_hosts_blocked_from_non_atomic_jobs(self):
614 # Create a job scheduled to run on label6.
615 self._create_job(metahosts=[self.label6.id])
616 self._dispatcher._schedule_new_jobs()
617 # label6 only has hosts that are in atomic groups associated with it,
618 # there should be no scheduling.
619 self._check_for_extra_schedulings()
620
621
622 def test_atomic_group_hosts_blocked_from_non_atomic_jobs_explicit(self):
623 # Create a job scheduled to run on label5. This is an atomic group
624 # label but this job does not request atomic group scheduling.
625 self._create_job(metahosts=[self.label5.id])
626 self._dispatcher._schedule_new_jobs()
627 # label6 only has hosts that are in atomic groups associated with it,
628 # there should be no scheduling.
629 self._check_for_extra_schedulings()
630
631
632 def test_atomic_group_scheduling_basics(self):
633 # Create jobs scheduled to run on an atomic group.
634 job_a = self._create_job(synchronous=True, metahosts=[self.label4.id],
635 atomic_group=1)
636 job_b = self._create_job(synchronous=True, metahosts=[self.label5.id],
637 atomic_group=1)
638 self._dispatcher._schedule_new_jobs()
639 # atomic_group.max_number_of_machines was 2 so we should run on 2.
640 self._assert_job_scheduled_on_number_of(job_a.id, (5, 6, 7), 2)
641 self._assert_job_scheduled_on(job_b.id, 8) # label5
642 self._assert_job_scheduled_on(job_b.id, 9) # label5
643 self._check_for_extra_schedulings()
644
645 # The three host label4 atomic group still has one host available.
646 # That means a job with a synch_count of 1 asking to be scheduled on
647 # the atomic group can still use the final machine.
648 #
649 # This may seem like a somewhat odd use case. It allows the use of an
650 # atomic group as a set of machines to run smaller jobs within (a set
651 # of hosts configured for use in network tests with eachother perhaps?)
652 onehost_job = self._create_job(atomic_group=1)
653 self._dispatcher._schedule_new_jobs()
654 self._assert_job_scheduled_on_number_of(onehost_job.id, (5, 6, 7), 1)
655 self._check_for_extra_schedulings()
656
657 # No more atomic groups have hosts available, no more jobs should
658 # be scheduled.
659 self._create_job(atomic_group=1)
660 self._dispatcher._schedule_new_jobs()
661 self._check_for_extra_schedulings()
662
663
664 def test_atomic_group_scheduling_obeys_acls(self):
665 # Request scheduling on a specific atomic label but be denied by ACLs.
666 self._do_query('DELETE FROM acl_groups_hosts WHERE host_id in (8,9)')
667 job = self._create_job(metahosts=[self.label5.id], atomic_group=1)
668 self._dispatcher._schedule_new_jobs()
669 self._check_for_extra_schedulings()
670
671
672 def test_atomic_group_scheduling_dependency_label_exclude(self):
673 # A dependency label that matches no hosts in the atomic group.
674 job_a = self._create_job(atomic_group=1)
675 job_a.dependency_labels.add(self.label3)
676 self._dispatcher._schedule_new_jobs()
677 self._check_for_extra_schedulings()
678
679
680 def test_atomic_group_scheduling_metahost_dependency_label_exclude(self):
681 # A metahost and dependency label that excludes too many hosts.
682 job_b = self._create_job(synchronous=True, metahosts=[self.label4.id],
683 atomic_group=1)
684 job_b.dependency_labels.add(self.label7)
685 self._dispatcher._schedule_new_jobs()
686 self._check_for_extra_schedulings()
687
688
689 def test_atomic_group_scheduling_dependency_label_match(self):
690 # A dependency label that exists on enough atomic group hosts in only
691 # one of the two atomic group labels.
692 job_c = self._create_job(synchronous=True, atomic_group=1)
693 job_c.dependency_labels.add(self.label7)
694 self._dispatcher._schedule_new_jobs()
695 self._assert_job_scheduled_on_number_of(job_c.id, (8, 9), 2)
696 self._check_for_extra_schedulings()
697
698
699 def test_atomic_group_scheduling_no_metahost(self):
700 # Force it to schedule on the other group for a reliable test.
701 self._do_query('UPDATE hosts SET invalid=1 WHERE id=9')
702 # An atomic job without a metahost.
703 job = self._create_job(synchronous=True, atomic_group=1)
704 self._dispatcher._schedule_new_jobs()
705 self._assert_job_scheduled_on_number_of(job.id, (5, 6, 7), 2)
706 self._check_for_extra_schedulings()
707
708
709 def test_atomic_group_scheduling_partial_group(self):
710 # Make one host in labels[3] unavailable so that there are only two
711 # hosts left in the group.
712 self._do_query('UPDATE hosts SET status="Repair Failed" WHERE id=5')
713 job = self._create_job(synchronous=True, metahosts=[self.label4.id],
714 atomic_group=1)
715 self._dispatcher._schedule_new_jobs()
716 # Verify that it was scheduled on the 2 ready hosts in that group.
717 self._assert_job_scheduled_on(job.id, 6)
718 self._assert_job_scheduled_on(job.id, 7)
719 self._check_for_extra_schedulings()
720
721
722 def test_atomic_group_scheduling_not_enough_available(self):
723 # Mark some hosts in each atomic group label as not usable.
724 # One host running, another invalid in the first group label.
725 self._do_query('UPDATE hosts SET status="Running" WHERE id=5')
726 self._do_query('UPDATE hosts SET invalid=1 WHERE id=6')
727 # One host invalid in the second group label.
728 self._do_query('UPDATE hosts SET invalid=1 WHERE id=9')
729 # Nothing to schedule when no group label has enough (2) good hosts..
730 self._create_job(atomic_group=1, synchronous=True)
731 self._dispatcher._schedule_new_jobs()
732 # There are not enough hosts in either atomic group,
733 # No more scheduling should occur.
734 self._check_for_extra_schedulings()
735
736 # Now create an atomic job that has a synch count of 1. It should
737 # schedule on exactly one of the hosts.
738 onehost_job = self._create_job(atomic_group=1)
739 self._dispatcher._schedule_new_jobs()
740 self._assert_job_scheduled_on_number_of(onehost_job.id, (7, 8), 1)
741
742
743 def test_atomic_group_scheduling_no_valid_hosts(self):
744 self._do_query('UPDATE hosts SET invalid=1 WHERE id in (8,9)')
745 self._create_job(synchronous=True, metahosts=[self.label5.id],
746 atomic_group=1)
747 self._dispatcher._schedule_new_jobs()
748 # no hosts in the selected group and label are valid. no schedulings.
749 self._check_for_extra_schedulings()
750
751
752 def test_atomic_group_scheduling_metahost_works(self):
753 # Test that atomic group scheduling also obeys metahosts.
754 self._create_job(metahosts=[0], atomic_group=1)
755 self._dispatcher._schedule_new_jobs()
756 # There are no atomic group hosts that also have that metahost.
757 self._check_for_extra_schedulings()
758
759 job_b = self._create_job(metahosts=[self.label5.id], atomic_group=1)
760 self._dispatcher._schedule_new_jobs()
761 self._assert_job_scheduled_on(job_b.id, 8)
762 self._assert_job_scheduled_on(job_b.id, 9)
763 self._check_for_extra_schedulings()
764
765
766 def test_atomic_group_skips_ineligible_hosts(self):
767 # Test hosts marked ineligible for this job are not eligible.
768 # How would this ever happen anyways?
769 job = self._create_job(metahosts=[self.label4.id], atomic_group=1)
770 models.IneligibleHostQueue.objects.create(job=job, host_id=5)
771 models.IneligibleHostQueue.objects.create(job=job, host_id=6)
772 models.IneligibleHostQueue.objects.create(job=job, host_id=7)
773 self._dispatcher._schedule_new_jobs()
774 # No scheduling should occur as all desired hosts were ineligible.
775 self._check_for_extra_schedulings()
776
777
778 def test_atomic_group_scheduling_fail(self):
779 # If synch_count is > the atomic group number of machines, the job
780 # should be aborted immediately.
781 model_job = self._create_job(synchronous=True, atomic_group=1)
782 model_job.synch_count = 4
783 model_job.save()
784 job = monitor_db.Job(id=model_job.id)
785 self._dispatcher._schedule_new_jobs()
786 self._check_for_extra_schedulings()
787 queue_entries = job.get_host_queue_entries()
788 self.assertEqual(1, len(queue_entries))
789 self.assertEqual(queue_entries[0].status,
790 models.HostQueueEntry.Status.ABORTED)
791
792
showard205fd602009-03-21 00:17:35 +0000793 def test_atomic_group_no_labels_no_scheduling(self):
794 # Never schedule on atomic groups marked invalid.
795 job = self._create_job(metahosts=[self.label5.id], synchronous=True,
796 atomic_group=1)
797 # Deleting an atomic group via the frontend marks it invalid and
798 # removes all label references to the group. The job now references
799 # an invalid atomic group with no labels associated with it.
800 self.label5.atomic_group.invalid = True
801 self.label5.atomic_group.save()
802 self.label5.atomic_group = None
803 self.label5.save()
804
805 self._dispatcher._schedule_new_jobs()
806 self._check_for_extra_schedulings()
807
808
showard89f84db2009-03-12 20:39:13 +0000809 def test_schedule_directly_on_atomic_group_host_fail(self):
810 # Scheduling a job directly on hosts in an atomic group must
811 # fail to avoid users inadvertently holding up the use of an
812 # entire atomic group by using the machines individually.
813 job = self._create_job(hosts=[5])
814 self._dispatcher._schedule_new_jobs()
815 self._check_for_extra_schedulings()
816
817
818 def test_schedule_directly_on_atomic_group_host(self):
819 # Scheduling a job directly on one host in an atomic group will
820 # work when the atomic group is listed on the HQE in addition
821 # to the host (assuming the sync count is 1).
822 job = self._create_job(hosts=[5], atomic_group=1)
823 self._dispatcher._schedule_new_jobs()
824 self._assert_job_scheduled_on(job.id, 5)
825 self._check_for_extra_schedulings()
826
827
828 def test_schedule_directly_on_atomic_group_hosts_sync2(self):
829 job = self._create_job(hosts=[5,8], atomic_group=1, synchronous=True)
830 self._dispatcher._schedule_new_jobs()
831 self._assert_job_scheduled_on(job.id, 5)
832 self._assert_job_scheduled_on(job.id, 8)
833 self._check_for_extra_schedulings()
834
835
836 def test_schedule_directly_on_atomic_group_hosts_wrong_group(self):
837 job = self._create_job(hosts=[5,8], atomic_group=2, synchronous=True)
838 self._dispatcher._schedule_new_jobs()
839 self._check_for_extra_schedulings()
840
841
showard56193bb2008-08-13 20:07:41 +0000842 def test_only_schedule_queued_entries(self):
843 self._create_job(metahosts=[1])
844 self._update_hqe(set='active=1, host_id=2')
845 self._dispatcher._schedule_new_jobs()
846 self._check_for_extra_schedulings()
847
848
showardfa8629c2008-11-04 16:51:23 +0000849 def test_no_ready_hosts(self):
850 self._create_job(hosts=[1])
851 self._do_query('UPDATE hosts SET status="Repair Failed"')
852 self._dispatcher._schedule_new_jobs()
853 self._check_for_extra_schedulings()
854
855
showardb2e2c322008-10-14 17:33:55 +0000856class DispatcherThrottlingTest(BaseSchedulerTest):
showard4c5374f2008-09-04 17:02:56 +0000857 """
858 Test that the dispatcher throttles:
859 * total number of running processes
860 * number of processes started per cycle
861 """
862 _MAX_RUNNING = 3
863 _MAX_STARTED = 2
864
865 def setUp(self):
866 super(DispatcherThrottlingTest, self).setUp()
showard324bf812009-01-20 23:23:38 +0000867 scheduler_config.config.max_processes_per_drone = self._MAX_RUNNING
showardd1ee1dd2009-01-07 21:33:08 +0000868 scheduler_config.config.max_processes_started_per_cycle = (
869 self._MAX_STARTED)
showard4c5374f2008-09-04 17:02:56 +0000870
showard324bf812009-01-20 23:23:38 +0000871 def fake_max_runnable_processes(fake_self):
872 running = sum(agent.num_processes
873 for agent in self._agents
874 if agent.is_running())
875 return self._MAX_RUNNING - running
876 self.god.stub_with(drone_manager.DroneManager, 'max_runnable_processes',
877 fake_max_runnable_processes)
showard2fa51692009-01-13 23:48:08 +0000878
showard4c5374f2008-09-04 17:02:56 +0000879
showard4c5374f2008-09-04 17:02:56 +0000880 def _setup_some_agents(self, num_agents):
showard170873e2009-01-07 00:22:26 +0000881 self._agents = [DummyAgent() for i in xrange(num_agents)]
showard4c5374f2008-09-04 17:02:56 +0000882 self._dispatcher._agents = list(self._agents)
883
884
885 def _run_a_few_cycles(self):
886 for i in xrange(4):
887 self._dispatcher._handle_agents()
888
889
890 def _assert_agents_started(self, indexes, is_started=True):
891 for i in indexes:
892 self.assert_(self._agents[i].is_running() == is_started,
893 'Agent %d %sstarted' %
894 (i, is_started and 'not ' or ''))
895
896
897 def _assert_agents_not_started(self, indexes):
898 self._assert_agents_started(indexes, False)
899
900
901 def test_throttle_total(self):
902 self._setup_some_agents(4)
903 self._run_a_few_cycles()
904 self._assert_agents_started([0, 1, 2])
905 self._assert_agents_not_started([3])
906
907
908 def test_throttle_per_cycle(self):
909 self._setup_some_agents(3)
910 self._dispatcher._handle_agents()
911 self._assert_agents_started([0, 1])
912 self._assert_agents_not_started([2])
913
914
915 def test_throttle_with_synchronous(self):
916 self._setup_some_agents(2)
917 self._agents[0].num_processes = 3
918 self._run_a_few_cycles()
919 self._assert_agents_started([0])
920 self._assert_agents_not_started([1])
921
922
923 def test_large_agent_starvation(self):
924 """
925 Ensure large agents don't get starved by lower-priority agents.
926 """
927 self._setup_some_agents(3)
928 self._agents[1].num_processes = 3
929 self._run_a_few_cycles()
930 self._assert_agents_started([0])
931 self._assert_agents_not_started([1, 2])
932
933 self._agents[0].set_done(True)
934 self._run_a_few_cycles()
935 self._assert_agents_started([1])
936 self._assert_agents_not_started([2])
937
938
939 def test_zero_process_agent(self):
940 self._setup_some_agents(5)
941 self._agents[4].num_processes = 0
942 self._run_a_few_cycles()
943 self._assert_agents_started([0, 1, 2, 4])
944 self._assert_agents_not_started([3])
945
946
showard1be97432008-10-17 15:30:45 +0000947class FindAbortTest(BaseSchedulerTest):
showard56193bb2008-08-13 20:07:41 +0000948 """
showard1be97432008-10-17 15:30:45 +0000949 Test the dispatcher abort functionality.
showard56193bb2008-08-13 20:07:41 +0000950 """
showard170873e2009-01-07 00:22:26 +0000951 def _check_host_agent(self, agent, host_id):
952 self.assert_(isinstance(agent, monitor_db.Agent))
953 tasks = list(agent.queue.queue)
954 self.assertEquals(len(tasks), 2)
955 cleanup, verify = tasks
showard9d9ffd52008-11-09 23:14:35 +0000956
showard45ae8192008-11-05 19:32:53 +0000957 self.assert_(isinstance(cleanup, monitor_db.CleanupTask))
showard170873e2009-01-07 00:22:26 +0000958 self.assertEquals(cleanup.host.id, host_id)
showard1be97432008-10-17 15:30:45 +0000959
960 self.assert_(isinstance(verify, monitor_db.VerifyTask))
showard170873e2009-01-07 00:22:26 +0000961 self.assertEquals(verify.host.id, host_id)
showard56193bb2008-08-13 20:07:41 +0000962
963
showardd3dc1992009-04-22 21:01:40 +0000964 def _check_agents(self, agents):
showard170873e2009-01-07 00:22:26 +0000965 agents = list(agents)
showardd3dc1992009-04-22 21:01:40 +0000966 self.assertEquals(len(agents), 3)
967 self.assertEquals(agents[0], self._agent)
968 self._check_host_agent(agents[1], 1)
969 self._check_host_agent(agents[2], 2)
showard56193bb2008-08-13 20:07:41 +0000970
971
showardd3dc1992009-04-22 21:01:40 +0000972 def _common_setup(self):
showard56193bb2008-08-13 20:07:41 +0000973 self._create_job(hosts=[1, 2])
showardd3dc1992009-04-22 21:01:40 +0000974 self._update_hqe(set='aborted=1')
975 self._agent = self.god.create_mock_class(monitor_db.Agent, 'old_agent')
showard6b733412009-04-27 20:09:18 +0000976 _set_host_and_qe_ids(self._agent, [1, 2])
showardd3dc1992009-04-22 21:01:40 +0000977 self._agent.abort.expect_call()
978 self._agent.abort.expect_call() # gets called once for each HQE
979 self._dispatcher.add_agent(self._agent)
showard56193bb2008-08-13 20:07:41 +0000980
showardd3dc1992009-04-22 21:01:40 +0000981
982 def test_find_aborting(self):
983 self._common_setup()
showard56193bb2008-08-13 20:07:41 +0000984 self._dispatcher._find_aborting()
showard56193bb2008-08-13 20:07:41 +0000985 self.god.check_playback()
986
987
showardd3dc1992009-04-22 21:01:40 +0000988 def test_find_aborting_verifying(self):
989 self._common_setup()
990 self._update_hqe(set='active=1, status="Verifying"')
showard56193bb2008-08-13 20:07:41 +0000991
showard56193bb2008-08-13 20:07:41 +0000992 self._dispatcher._find_aborting()
993
showardd3dc1992009-04-22 21:01:40 +0000994 self._check_agents(self._dispatcher._agents)
showard56193bb2008-08-13 20:07:41 +0000995 self.god.check_playback()
996
997
showard98863972008-10-29 21:14:56 +0000998class JobTimeoutTest(BaseSchedulerTest):
showard2bab8f42008-11-12 18:15:22 +0000999 def _test_synch_start_timeout_helper(self, expect_abort,
1000 set_created_on=True, set_active=True,
1001 set_acl=True):
showardd1ee1dd2009-01-07 21:33:08 +00001002 scheduler_config.config.synch_job_start_timeout_minutes = 60
showard98863972008-10-29 21:14:56 +00001003 job = self._create_job(hosts=[1, 2])
showard98863972008-10-29 21:14:56 +00001004 if set_active:
1005 hqe = job.hostqueueentry_set.filter(host__id=1)[0]
1006 hqe.status = 'Pending'
1007 hqe.active = 1
1008 hqe.save()
1009
1010 everyone_acl = models.AclGroup.smart_get('Everyone')
1011 host1 = models.Host.smart_get(1)
1012 if set_acl:
1013 everyone_acl.hosts.add(host1)
1014 else:
1015 everyone_acl.hosts.remove(host1)
1016
1017 job.created_on = datetime.datetime.now()
1018 if set_created_on:
1019 job.created_on -= datetime.timedelta(minutes=100)
1020 job.save()
1021
showard915958d2009-04-22 21:00:58 +00001022 cleanup = self._dispatcher._periodic_cleanup
1023 cleanup._abort_jobs_past_synch_start_timeout()
showard98863972008-10-29 21:14:56 +00001024
1025 for hqe in job.hostqueueentry_set.all():
showardd3dc1992009-04-22 21:01:40 +00001026 self.assertEquals(hqe.aborted, expect_abort)
showard98863972008-10-29 21:14:56 +00001027
1028
1029 def test_synch_start_timeout_helper(self):
1030 # no abort if any of the condition aren't met
showard2bab8f42008-11-12 18:15:22 +00001031 self._test_synch_start_timeout_helper(False, set_created_on=False)
1032 self._test_synch_start_timeout_helper(False, set_active=False)
1033 self._test_synch_start_timeout_helper(False, set_acl=False)
showard98863972008-10-29 21:14:56 +00001034 # abort if all conditions are met
showard2bab8f42008-11-12 18:15:22 +00001035 self._test_synch_start_timeout_helper(True)
showard98863972008-10-29 21:14:56 +00001036
1037
jadmanski3d161b02008-06-06 15:43:36 +00001038class PidfileRunMonitorTest(unittest.TestCase):
showard170873e2009-01-07 00:22:26 +00001039 execution_tag = 'test_tag'
jadmanski0afbb632008-06-06 21:10:57 +00001040 pid = 12345
showard170873e2009-01-07 00:22:26 +00001041 process = drone_manager.Process('myhost', pid)
showard21baa452008-10-21 00:08:39 +00001042 num_tests_failed = 1
jadmanski3d161b02008-06-06 15:43:36 +00001043
jadmanski0afbb632008-06-06 21:10:57 +00001044 def setUp(self):
1045 self.god = mock.mock_god()
showard170873e2009-01-07 00:22:26 +00001046 self.mock_drone_manager = self.god.create_mock_class(
1047 drone_manager.DroneManager, 'drone_manager')
1048 self.god.stub_with(monitor_db, '_drone_manager',
1049 self.mock_drone_manager)
1050 self.god.stub_function(email_manager.manager, 'enqueue_notify_email')
1051
1052 self.pidfile_id = object()
1053
showardd3dc1992009-04-22 21:01:40 +00001054 (self.mock_drone_manager.get_pidfile_id_from
1055 .expect_call(self.execution_tag,
1056 pidfile_name=monitor_db._AUTOSERV_PID_FILE)
1057 .and_return(self.pidfile_id))
showard170873e2009-01-07 00:22:26 +00001058 self.mock_drone_manager.register_pidfile.expect_call(self.pidfile_id)
1059
1060 self.monitor = monitor_db.PidfileRunMonitor()
1061 self.monitor.attach_to_existing_process(self.execution_tag)
jadmanski3d161b02008-06-06 15:43:36 +00001062
1063
jadmanski0afbb632008-06-06 21:10:57 +00001064 def tearDown(self):
1065 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001066
1067
showard170873e2009-01-07 00:22:26 +00001068 def setup_pidfile(self, pid=None, exit_code=None, tests_failed=None,
1069 use_second_read=False):
1070 contents = drone_manager.PidfileContents()
1071 if pid is not None:
1072 contents.process = drone_manager.Process('myhost', pid)
1073 contents.exit_status = exit_code
1074 contents.num_tests_failed = tests_failed
1075 self.mock_drone_manager.get_pidfile_contents.expect_call(
1076 self.pidfile_id, use_second_read=use_second_read).and_return(
1077 contents)
1078
1079
jadmanski0afbb632008-06-06 21:10:57 +00001080 def set_not_yet_run(self):
showard170873e2009-01-07 00:22:26 +00001081 self.setup_pidfile()
jadmanski3d161b02008-06-06 15:43:36 +00001082
1083
showard3dd6b882008-10-27 19:21:39 +00001084 def set_empty_pidfile(self):
showard170873e2009-01-07 00:22:26 +00001085 self.setup_pidfile()
showard3dd6b882008-10-27 19:21:39 +00001086
1087
showard170873e2009-01-07 00:22:26 +00001088 def set_running(self, use_second_read=False):
1089 self.setup_pidfile(self.pid, use_second_read=use_second_read)
jadmanski3d161b02008-06-06 15:43:36 +00001090
1091
showard170873e2009-01-07 00:22:26 +00001092 def set_complete(self, error_code, use_second_read=False):
1093 self.setup_pidfile(self.pid, error_code, self.num_tests_failed,
1094 use_second_read=use_second_read)
1095
1096
1097 def _check_monitor(self, expected_pid, expected_exit_status,
1098 expected_num_tests_failed):
1099 if expected_pid is None:
1100 self.assertEquals(self.monitor._state.process, None)
1101 else:
1102 self.assertEquals(self.monitor._state.process.pid, expected_pid)
1103 self.assertEquals(self.monitor._state.exit_status, expected_exit_status)
1104 self.assertEquals(self.monitor._state.num_tests_failed,
1105 expected_num_tests_failed)
1106
1107
1108 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001109
1110
showard21baa452008-10-21 00:08:39 +00001111 def _test_read_pidfile_helper(self, expected_pid, expected_exit_status,
1112 expected_num_tests_failed):
1113 self.monitor._read_pidfile()
showard170873e2009-01-07 00:22:26 +00001114 self._check_monitor(expected_pid, expected_exit_status,
1115 expected_num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001116
1117
showard21baa452008-10-21 00:08:39 +00001118 def _get_expected_tests_failed(self, expected_exit_status):
1119 if expected_exit_status is None:
1120 expected_tests_failed = None
1121 else:
1122 expected_tests_failed = self.num_tests_failed
1123 return expected_tests_failed
1124
1125
jadmanski0afbb632008-06-06 21:10:57 +00001126 def test_read_pidfile(self):
1127 self.set_not_yet_run()
showard21baa452008-10-21 00:08:39 +00001128 self._test_read_pidfile_helper(None, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001129
showard3dd6b882008-10-27 19:21:39 +00001130 self.set_empty_pidfile()
1131 self._test_read_pidfile_helper(None, None, None)
1132
jadmanski0afbb632008-06-06 21:10:57 +00001133 self.set_running()
showard21baa452008-10-21 00:08:39 +00001134 self._test_read_pidfile_helper(self.pid, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001135
jadmanski0afbb632008-06-06 21:10:57 +00001136 self.set_complete(123)
showard21baa452008-10-21 00:08:39 +00001137 self._test_read_pidfile_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001138
1139
jadmanski0afbb632008-06-06 21:10:57 +00001140 def test_read_pidfile_error(self):
showard170873e2009-01-07 00:22:26 +00001141 self.mock_drone_manager.get_pidfile_contents.expect_call(
1142 self.pidfile_id, use_second_read=False).and_return(
1143 drone_manager.InvalidPidfile('error'))
1144 self.assertRaises(monitor_db.PidfileRunMonitor._PidfileException,
showard21baa452008-10-21 00:08:39 +00001145 self.monitor._read_pidfile)
jadmanski0afbb632008-06-06 21:10:57 +00001146 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001147
1148
showard170873e2009-01-07 00:22:26 +00001149 def setup_is_running(self, is_running):
1150 self.mock_drone_manager.is_process_running.expect_call(
1151 self.process).and_return(is_running)
jadmanski3d161b02008-06-06 15:43:36 +00001152
1153
showard21baa452008-10-21 00:08:39 +00001154 def _test_get_pidfile_info_helper(self, expected_pid, expected_exit_status,
1155 expected_num_tests_failed):
1156 self.monitor._get_pidfile_info()
showard170873e2009-01-07 00:22:26 +00001157 self._check_monitor(expected_pid, expected_exit_status,
1158 expected_num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001159
1160
jadmanski0afbb632008-06-06 21:10:57 +00001161 def test_get_pidfile_info(self):
showard21baa452008-10-21 00:08:39 +00001162 """
1163 normal cases for get_pidfile_info
1164 """
jadmanski0afbb632008-06-06 21:10:57 +00001165 # running
1166 self.set_running()
showard170873e2009-01-07 00:22:26 +00001167 self.setup_is_running(True)
showard21baa452008-10-21 00:08:39 +00001168 self._test_get_pidfile_info_helper(self.pid, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001169
jadmanski0afbb632008-06-06 21:10:57 +00001170 # exited during check
1171 self.set_running()
showard170873e2009-01-07 00:22:26 +00001172 self.setup_is_running(False)
1173 self.set_complete(123, use_second_read=True) # pidfile gets read again
showard21baa452008-10-21 00:08:39 +00001174 self._test_get_pidfile_info_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001175
jadmanski0afbb632008-06-06 21:10:57 +00001176 # completed
1177 self.set_complete(123)
showard21baa452008-10-21 00:08:39 +00001178 self._test_get_pidfile_info_helper(self.pid, 123, self.num_tests_failed)
jadmanski3d161b02008-06-06 15:43:36 +00001179
1180
jadmanski0afbb632008-06-06 21:10:57 +00001181 def test_get_pidfile_info_running_no_proc(self):
showard21baa452008-10-21 00:08:39 +00001182 """
1183 pidfile shows process running, but no proc exists
1184 """
jadmanski0afbb632008-06-06 21:10:57 +00001185 # running but no proc
1186 self.set_running()
showard170873e2009-01-07 00:22:26 +00001187 self.setup_is_running(False)
1188 self.set_running(use_second_read=True)
1189 email_manager.manager.enqueue_notify_email.expect_call(
jadmanski0afbb632008-06-06 21:10:57 +00001190 mock.is_string_comparator(), mock.is_string_comparator())
showard21baa452008-10-21 00:08:39 +00001191 self._test_get_pidfile_info_helper(self.pid, 1, 0)
jadmanski0afbb632008-06-06 21:10:57 +00001192 self.assertTrue(self.monitor.lost_process)
jadmanski3d161b02008-06-06 15:43:36 +00001193
1194
jadmanski0afbb632008-06-06 21:10:57 +00001195 def test_get_pidfile_info_not_yet_run(self):
showard21baa452008-10-21 00:08:39 +00001196 """
1197 pidfile hasn't been written yet
1198 """
jadmanski0afbb632008-06-06 21:10:57 +00001199 self.set_not_yet_run()
showard21baa452008-10-21 00:08:39 +00001200 self._test_get_pidfile_info_helper(None, None, None)
jadmanski3d161b02008-06-06 15:43:36 +00001201
jadmanski3d161b02008-06-06 15:43:36 +00001202
showard170873e2009-01-07 00:22:26 +00001203 def test_process_failed_to_write_pidfile(self):
jadmanski0afbb632008-06-06 21:10:57 +00001204 self.set_not_yet_run()
showard170873e2009-01-07 00:22:26 +00001205 email_manager.manager.enqueue_notify_email.expect_call(
1206 mock.is_string_comparator(), mock.is_string_comparator())
showard170873e2009-01-07 00:22:26 +00001207 self.monitor._start_time = time.time() - monitor_db.PIDFILE_TIMEOUT - 1
showard35162b02009-03-03 02:17:30 +00001208 self._test_get_pidfile_info_helper(None, 1, 0)
1209 self.assertTrue(self.monitor.lost_process)
jadmanski3d161b02008-06-06 15:43:36 +00001210
1211
1212class AgentTest(unittest.TestCase):
jadmanski0afbb632008-06-06 21:10:57 +00001213 def setUp(self):
1214 self.god = mock.mock_god()
showard6b733412009-04-27 20:09:18 +00001215 self._dispatcher = self.god.create_mock_class(monitor_db.Dispatcher,
1216 'dispatcher')
jadmanski3d161b02008-06-06 15:43:36 +00001217
1218
jadmanski0afbb632008-06-06 21:10:57 +00001219 def tearDown(self):
1220 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001221
1222
showard170873e2009-01-07 00:22:26 +00001223 def _create_mock_task(self, name):
1224 task = self.god.create_mock_class(monitor_db.AgentTask, name)
showard6b733412009-04-27 20:09:18 +00001225 _set_host_and_qe_ids(task)
showard170873e2009-01-07 00:22:26 +00001226 return task
1227
showard6b733412009-04-27 20:09:18 +00001228 def _create_agent(self, tasks):
1229 agent = monitor_db.Agent(tasks)
1230 agent.dispatcher = self._dispatcher
1231 return agent
1232
1233
1234 def _finish_agent(self, agent):
1235 while not agent.is_done():
1236 agent.tick()
1237
showard170873e2009-01-07 00:22:26 +00001238
jadmanski0afbb632008-06-06 21:10:57 +00001239 def test_agent(self):
showard170873e2009-01-07 00:22:26 +00001240 task1 = self._create_mock_task('task1')
1241 task2 = self._create_mock_task('task2')
1242 task3 = self._create_mock_task('task3')
jadmanski3d161b02008-06-06 15:43:36 +00001243
jadmanski0afbb632008-06-06 21:10:57 +00001244 task1.start.expect_call()
1245 task1.is_done.expect_call().and_return(False)
1246 task1.poll.expect_call()
1247 task1.is_done.expect_call().and_return(True)
1248 task1.is_done.expect_call().and_return(True)
1249 task1.success = True
jadmanski3d161b02008-06-06 15:43:36 +00001250
jadmanski0afbb632008-06-06 21:10:57 +00001251 task2.start.expect_call()
1252 task2.is_done.expect_call().and_return(True)
1253 task2.is_done.expect_call().and_return(True)
1254 task2.success = False
1255 task2.failure_tasks = [task3]
jadmanski3d161b02008-06-06 15:43:36 +00001256
showardd3dc1992009-04-22 21:01:40 +00001257 self._dispatcher.add_agent.expect_call(IsAgentWithTask(task3))
jadmanski3d161b02008-06-06 15:43:36 +00001258
showard6b733412009-04-27 20:09:18 +00001259 agent = self._create_agent([task1, task2])
jadmanski0afbb632008-06-06 21:10:57 +00001260 agent.start()
showard6b733412009-04-27 20:09:18 +00001261 self._finish_agent(agent)
jadmanski0afbb632008-06-06 21:10:57 +00001262 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001263
1264
showard6b733412009-04-27 20:09:18 +00001265 def _test_agent_abort_helper(self, ignore_abort=False):
1266 task1 = self._create_mock_task('task1')
1267 task2 = self._create_mock_task('task2')
1268 task1.start.expect_call()
1269 task1.is_done.expect_call().and_return(False)
1270 task1.poll.expect_call()
1271 task1.is_done.expect_call().and_return(False)
1272 task1.abort.expect_call()
1273 if ignore_abort:
1274 task1.aborted = False # task ignores abort; execution continues
1275
1276 task1.is_done.expect_call().and_return(True)
1277 task1.is_done.expect_call().and_return(True)
1278 task1.success = True
1279
1280 task2.start.expect_call()
1281 task2.is_done.expect_call().and_return(True)
1282 task2.is_done.expect_call().and_return(True)
1283 task2.success = True
1284 else:
1285 task1.aborted = True # execution halts, no further expectations
1286
1287 agent = self._create_agent([task1, task2])
1288 agent.start()
1289 agent.tick()
1290 agent.abort()
1291 self._finish_agent(agent)
1292 self.god.check_playback()
1293
1294
1295 def test_agent_abort(self):
1296 self._test_agent_abort_helper()
1297 self._test_agent_abort_helper(True)
1298
1299
jadmanski3d161b02008-06-06 15:43:36 +00001300class AgentTasksTest(unittest.TestCase):
showard170873e2009-01-07 00:22:26 +00001301 TEMP_DIR = '/abspath/tempdir'
showard97aed502008-11-04 02:01:24 +00001302 RESULTS_DIR = '/results/dir'
jadmanski0afbb632008-06-06 21:10:57 +00001303 HOSTNAME = 'myhost'
showard170873e2009-01-07 00:22:26 +00001304 DUMMY_PROCESS = object()
jadmanskifb7cfb12008-07-09 14:13:21 +00001305 HOST_PROTECTION = host_protections.default
showard170873e2009-01-07 00:22:26 +00001306 PIDFILE_ID = object()
showard87ba02a2009-04-20 19:37:32 +00001307 JOB_OWNER = 'test_owner'
1308 JOB_NAME = 'test_job_name'
1309 JOB_AUTOSERV_PARAMS = set(['-u', JOB_OWNER, '-l', JOB_NAME])
jadmanski3d161b02008-06-06 15:43:36 +00001310
jadmanski0afbb632008-06-06 21:10:57 +00001311 def setUp(self):
1312 self.god = mock.mock_god()
showard170873e2009-01-07 00:22:26 +00001313 self.god.stub_with(drone_manager.DroneManager, 'get_temporary_path',
1314 mock.mock_function('get_temporary_path',
1315 default_return_val='tempdir'))
1316 self.god.stub_function(drone_manager.DroneManager,
showard678df4f2009-02-04 21:36:39 +00001317 'copy_results_on_drone')
1318 self.god.stub_function(drone_manager.DroneManager,
showard170873e2009-01-07 00:22:26 +00001319 'copy_to_results_repository')
1320 self.god.stub_function(drone_manager.DroneManager,
1321 'get_pidfile_id_from')
1322
1323 def dummy_absolute_path(self, path):
1324 return '/abspath/' + path
1325 self.god.stub_with(drone_manager.DroneManager, 'absolute_path',
1326 dummy_absolute_path)
1327
1328 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'run')
1329 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'exit_code')
1330 self.god.stub_class_method(monitor_db.PidfileRunMonitor, 'get_process')
showard6b733412009-04-27 20:09:18 +00001331 def mock_has_process(unused):
1332 return True
1333 self.god.stub_with(monitor_db.PidfileRunMonitor, 'has_process',
1334 mock_has_process)
jadmanski0afbb632008-06-06 21:10:57 +00001335 self.host = self.god.create_mock_class(monitor_db.Host, 'host')
showard170873e2009-01-07 00:22:26 +00001336 self.host.id = 1
jadmanski0afbb632008-06-06 21:10:57 +00001337 self.host.hostname = self.HOSTNAME
jadmanskifb7cfb12008-07-09 14:13:21 +00001338 self.host.protection = self.HOST_PROTECTION
jadmanski0afbb632008-06-06 21:10:57 +00001339 self.queue_entry = self.god.create_mock_class(
1340 monitor_db.HostQueueEntry, 'queue_entry')
showard97aed502008-11-04 02:01:24 +00001341 self.job = self.god.create_mock_class(monitor_db.Job, 'job')
showard87ba02a2009-04-20 19:37:32 +00001342 self.job.owner = self.JOB_OWNER
1343 self.job.name = self.JOB_NAME
showard170873e2009-01-07 00:22:26 +00001344 self.queue_entry.id = 1
showard97aed502008-11-04 02:01:24 +00001345 self.queue_entry.job = self.job
jadmanski0afbb632008-06-06 21:10:57 +00001346 self.queue_entry.host = self.host
1347 self.queue_entry.meta_host = None
showardd3dc1992009-04-22 21:01:40 +00001348 self._dispatcher = self.god.create_mock_class(monitor_db.Dispatcher,
1349 'dispatcher')
1350
jadmanski3d161b02008-06-06 15:43:36 +00001351
jadmanski0afbb632008-06-06 21:10:57 +00001352 def tearDown(self):
1353 self.god.unstub_all()
jadmanski3d161b02008-06-06 15:43:36 +00001354
1355
jadmanski0afbb632008-06-06 21:10:57 +00001356 def run_task(self, task, success):
1357 """
1358 Do essentially what an Agent would do, but protect againt
1359 infinite looping from test errors.
1360 """
1361 if not getattr(task, 'agent', None):
1362 task.agent = object()
1363 task.start()
1364 count = 0
1365 while not task.is_done():
1366 count += 1
1367 if count > 10:
1368 print 'Task failed to finish'
1369 # in case the playback has clues to why it
1370 # failed
1371 self.god.check_playback()
1372 self.fail()
1373 task.poll()
1374 self.assertEquals(task.success, success)
jadmanski3d161b02008-06-06 15:43:36 +00001375
1376
showard170873e2009-01-07 00:22:26 +00001377 def setup_run_monitor(self, exit_status, copy_log_file=True):
1378 monitor_db.PidfileRunMonitor.run.expect_call(
1379 mock.is_instance_comparator(list),
1380 'tempdir',
1381 nice_level=monitor_db.AUTOSERV_NICE_LEVEL,
showardd3dc1992009-04-22 21:01:40 +00001382 log_file=mock.anything_comparator(),
1383 pidfile_name=monitor_db._AUTOSERV_PID_FILE,
1384 paired_with_pidfile=None)
showard170873e2009-01-07 00:22:26 +00001385 monitor_db.PidfileRunMonitor.exit_code.expect_call()
1386 monitor_db.PidfileRunMonitor.exit_code.expect_call().and_return(
jadmanski0afbb632008-06-06 21:10:57 +00001387 exit_status)
jadmanski3d161b02008-06-06 15:43:36 +00001388
showard170873e2009-01-07 00:22:26 +00001389 if copy_log_file:
1390 self._setup_move_logfile()
1391
1392
showard678df4f2009-02-04 21:36:39 +00001393 def _setup_move_logfile(self, copy_on_drone=False,
1394 include_destination=False):
showard170873e2009-01-07 00:22:26 +00001395 monitor_db.PidfileRunMonitor.get_process.expect_call().and_return(
1396 self.DUMMY_PROCESS)
showard678df4f2009-02-04 21:36:39 +00001397 if copy_on_drone:
1398 self.queue_entry.execution_tag.expect_call().and_return('tag')
1399 drone_manager.DroneManager.copy_results_on_drone.expect_call(
1400 self.DUMMY_PROCESS, source_path=mock.is_string_comparator(),
1401 destination_path=mock.is_string_comparator())
1402 elif include_destination:
showard170873e2009-01-07 00:22:26 +00001403 drone_manager.DroneManager.copy_to_results_repository.expect_call(
1404 self.DUMMY_PROCESS, mock.is_string_comparator(),
1405 destination_path=mock.is_string_comparator())
1406 else:
1407 drone_manager.DroneManager.copy_to_results_repository.expect_call(
1408 self.DUMMY_PROCESS, mock.is_string_comparator())
1409
jadmanski3d161b02008-06-06 15:43:36 +00001410
jadmanski0afbb632008-06-06 21:10:57 +00001411 def _test_repair_task_helper(self, success):
1412 self.host.set_status.expect_call('Repairing')
1413 if success:
1414 self.setup_run_monitor(0)
1415 self.host.set_status.expect_call('Ready')
1416 else:
1417 self.setup_run_monitor(1)
1418 self.host.set_status.expect_call('Repair Failed')
jadmanski3d161b02008-06-06 15:43:36 +00001419
jadmanski0afbb632008-06-06 21:10:57 +00001420 task = monitor_db.RepairTask(self.host)
showard56193bb2008-08-13 20:07:41 +00001421 self.assertEquals(task.failure_tasks, [])
jadmanski0afbb632008-06-06 21:10:57 +00001422 self.run_task(task, success)
jadmanskifb7cfb12008-07-09 14:13:21 +00001423
1424 expected_protection = host_protections.Protection.get_string(
1425 host_protections.default)
mbligh3e0f7e02008-07-28 19:42:01 +00001426 expected_protection = host_protections.Protection.get_attr_name(
1427 expected_protection)
1428
showard170873e2009-01-07 00:22:26 +00001429 self.assertTrue(set(task.cmd) >=
1430 set([monitor_db._autoserv_path, '-p', '-R', '-m',
1431 self.HOSTNAME, '-r', self.TEMP_DIR,
1432 '--host-protection', expected_protection]))
jadmanski0afbb632008-06-06 21:10:57 +00001433 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001434
1435
jadmanski0afbb632008-06-06 21:10:57 +00001436 def test_repair_task(self):
1437 self._test_repair_task_helper(True)
1438 self._test_repair_task_helper(False)
jadmanski3d161b02008-06-06 15:43:36 +00001439
1440
jadmanski0afbb632008-06-06 21:10:57 +00001441 def test_repair_task_with_queue_entry(self):
showardde634ee2009-01-30 01:44:24 +00001442 self.god.stub_class(monitor_db, 'FinalReparseTask')
1443 self.god.stub_class(monitor_db, 'Agent')
showardde634ee2009-01-30 01:44:24 +00001444 agent = DummyAgent()
showardd3dc1992009-04-22 21:01:40 +00001445 agent.dispatcher = self._dispatcher
showardde634ee2009-01-30 01:44:24 +00001446
jadmanski0afbb632008-06-06 21:10:57 +00001447 self.host.set_status.expect_call('Repairing')
showarde788ea62008-11-17 21:02:47 +00001448 self.queue_entry.requeue.expect_call()
jadmanski0afbb632008-06-06 21:10:57 +00001449 self.setup_run_monitor(1)
1450 self.host.set_status.expect_call('Repair Failed')
showardccbd6c52009-03-21 00:10:21 +00001451 self.queue_entry.update_from_database.expect_call()
showardde634ee2009-01-30 01:44:24 +00001452 self.queue_entry.set_execution_subdir.expect_call()
showard678df4f2009-02-04 21:36:39 +00001453 self._setup_move_logfile(copy_on_drone=True)
showardde634ee2009-01-30 01:44:24 +00001454 self.queue_entry.execution_tag.expect_call().and_return('tag')
showard678df4f2009-02-04 21:36:39 +00001455 self._setup_move_logfile()
showardde634ee2009-01-30 01:44:24 +00001456 reparse_task = monitor_db.FinalReparseTask.expect_new(
1457 [self.queue_entry])
1458 reparse_agent = monitor_db.Agent.expect_new([reparse_task],
1459 num_processes=0)
showardd3dc1992009-04-22 21:01:40 +00001460 self._dispatcher.add_agent.expect_call(reparse_agent)
showarde788ea62008-11-17 21:02:47 +00001461 self.queue_entry.handle_host_failure.expect_call()
jadmanski3d161b02008-06-06 15:43:36 +00001462
showarde788ea62008-11-17 21:02:47 +00001463 task = monitor_db.RepairTask(self.host, self.queue_entry)
showardde634ee2009-01-30 01:44:24 +00001464 task.agent = agent
showardccbd6c52009-03-21 00:10:21 +00001465 self.queue_entry.status = 'Queued'
jadmanski0afbb632008-06-06 21:10:57 +00001466 self.run_task(task, False)
showard87ba02a2009-04-20 19:37:32 +00001467 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
jadmanski0afbb632008-06-06 21:10:57 +00001468 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001469
1470
jadmanski0afbb632008-06-06 21:10:57 +00001471 def setup_verify_expects(self, success, use_queue_entry):
1472 if use_queue_entry:
showard8fe93b52008-11-18 17:53:22 +00001473 self.queue_entry.set_status.expect_call('Verifying')
jadmanski0afbb632008-06-06 21:10:57 +00001474 self.host.set_status.expect_call('Verifying')
1475 if success:
1476 self.setup_run_monitor(0)
1477 self.host.set_status.expect_call('Ready')
1478 else:
1479 self.setup_run_monitor(1)
showard8fe93b52008-11-18 17:53:22 +00001480 if use_queue_entry and not self.queue_entry.meta_host:
1481 self.queue_entry.set_execution_subdir.expect_call()
showard170873e2009-01-07 00:22:26 +00001482 self.queue_entry.execution_tag.expect_call().and_return('tag')
1483 self._setup_move_logfile(include_destination=True)
jadmanski3d161b02008-06-06 15:43:36 +00001484
1485
showard56193bb2008-08-13 20:07:41 +00001486 def _check_verify_failure_tasks(self, verify_task):
1487 self.assertEquals(len(verify_task.failure_tasks), 1)
1488 repair_task = verify_task.failure_tasks[0]
1489 self.assert_(isinstance(repair_task, monitor_db.RepairTask))
1490 self.assertEquals(verify_task.host, repair_task.host)
showard8fe93b52008-11-18 17:53:22 +00001491 if verify_task.queue_entry:
showardccbd6c52009-03-21 00:10:21 +00001492 self.assertEquals(repair_task.queue_entry_to_fail,
1493 verify_task.queue_entry)
showard56193bb2008-08-13 20:07:41 +00001494 else:
showardccbd6c52009-03-21 00:10:21 +00001495 self.assertEquals(repair_task.queue_entry_to_fail, None)
showard56193bb2008-08-13 20:07:41 +00001496
1497
1498 def _test_verify_task_helper(self, success, use_queue_entry=False,
1499 use_meta_host=False):
jadmanski0afbb632008-06-06 21:10:57 +00001500 self.setup_verify_expects(success, use_queue_entry)
jadmanski3d161b02008-06-06 15:43:36 +00001501
jadmanski0afbb632008-06-06 21:10:57 +00001502 if use_queue_entry:
showard170873e2009-01-07 00:22:26 +00001503 task = monitor_db.VerifyTask(queue_entry=self.queue_entry)
jadmanski0afbb632008-06-06 21:10:57 +00001504 else:
1505 task = monitor_db.VerifyTask(host=self.host)
showard56193bb2008-08-13 20:07:41 +00001506 self._check_verify_failure_tasks(task)
jadmanski0afbb632008-06-06 21:10:57 +00001507 self.run_task(task, success)
showard170873e2009-01-07 00:22:26 +00001508 self.assertTrue(set(task.cmd) >=
1509 set([monitor_db._autoserv_path, '-p', '-v', '-m',
1510 self.HOSTNAME, '-r', self.TEMP_DIR]))
showard87ba02a2009-04-20 19:37:32 +00001511 if use_queue_entry:
1512 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
jadmanski0afbb632008-06-06 21:10:57 +00001513 self.god.check_playback()
jadmanski3d161b02008-06-06 15:43:36 +00001514
1515
jadmanski0afbb632008-06-06 21:10:57 +00001516 def test_verify_task_with_host(self):
showard56193bb2008-08-13 20:07:41 +00001517 self._test_verify_task_helper(True)
1518 self._test_verify_task_helper(False)
jadmanski3d161b02008-06-06 15:43:36 +00001519
1520
jadmanski0afbb632008-06-06 21:10:57 +00001521 def test_verify_task_with_queue_entry(self):
showard56193bb2008-08-13 20:07:41 +00001522 self._test_verify_task_helper(True, use_queue_entry=True)
1523 self._test_verify_task_helper(False, use_queue_entry=True)
1524
1525
1526 def test_verify_task_with_metahost(self):
showard8fe93b52008-11-18 17:53:22 +00001527 self.queue_entry.meta_host = 1
1528 self.test_verify_task_with_queue_entry()
jadmanski3d161b02008-06-06 15:43:36 +00001529
1530
showard6b733412009-04-27 20:09:18 +00001531 def _setup_post_job_task_expects(self, autoserv_success, hqe_status,
1532 hqe_aborted=False):
showard170873e2009-01-07 00:22:26 +00001533 self.queue_entry.execution_tag.expect_call().and_return('tag')
1534 self.pidfile_monitor = monitor_db.PidfileRunMonitor.expect_new()
1535 self.pidfile_monitor.pidfile_id = self.PIDFILE_ID
1536 self.pidfile_monitor.attach_to_existing_process.expect_call('tag')
1537 if autoserv_success:
1538 code = 0
1539 else:
1540 code = 1
showardd3dc1992009-04-22 21:01:40 +00001541 self.queue_entry.update_from_database.expect_call()
showard6b733412009-04-27 20:09:18 +00001542 self.queue_entry.aborted = hqe_aborted
1543 if not hqe_aborted:
1544 self.pidfile_monitor.exit_code.expect_call().and_return(code)
showard170873e2009-01-07 00:22:26 +00001545
showardd3dc1992009-04-22 21:01:40 +00001546 self.queue_entry.set_status.expect_call(hqe_status)
1547
1548
1549 def _setup_pre_parse_expects(self, autoserv_success):
1550 self._setup_post_job_task_expects(autoserv_success, 'Parsing')
showard97aed502008-11-04 02:01:24 +00001551
1552
1553 def _setup_post_parse_expects(self, autoserv_success):
showard97aed502008-11-04 02:01:24 +00001554 if autoserv_success:
showard170873e2009-01-07 00:22:26 +00001555 status = 'Completed'
showard97aed502008-11-04 02:01:24 +00001556 else:
showard170873e2009-01-07 00:22:26 +00001557 status = 'Failed'
showard97aed502008-11-04 02:01:24 +00001558 self.queue_entry.set_status.expect_call(status)
1559
1560
showardd3dc1992009-04-22 21:01:40 +00001561 def _setup_post_job_run_monitor(self, pidfile_name):
showard678df4f2009-02-04 21:36:39 +00001562 self.pidfile_monitor.has_process.expect_call().and_return(True)
showard170873e2009-01-07 00:22:26 +00001563 autoserv_pidfile_id = object()
showardd3dc1992009-04-22 21:01:40 +00001564 self.monitor = monitor_db.PidfileRunMonitor.expect_new()
1565 self.monitor.run.expect_call(
showard170873e2009-01-07 00:22:26 +00001566 mock.is_instance_comparator(list),
1567 'tag',
showardd3dc1992009-04-22 21:01:40 +00001568 nice_level=monitor_db.AUTOSERV_NICE_LEVEL,
showard170873e2009-01-07 00:22:26 +00001569 log_file=mock.anything_comparator(),
showardd3dc1992009-04-22 21:01:40 +00001570 pidfile_name=pidfile_name,
showard170873e2009-01-07 00:22:26 +00001571 paired_with_pidfile=self.PIDFILE_ID)
showardd3dc1992009-04-22 21:01:40 +00001572 self.monitor.exit_code.expect_call()
1573 self.monitor.exit_code.expect_call().and_return(0)
1574 self._expect_copy_results()
1575
1576
showard6b733412009-04-27 20:09:18 +00001577 def _expect_copy_results(self, monitor=None, queue_entry=None):
1578 if monitor is None:
1579 monitor = self.monitor
1580 monitor.has_process.expect_call().and_return(True)
1581 if queue_entry:
1582 queue_entry.execution_tag.expect_call().and_return('tag')
1583 monitor.get_process.expect_call().and_return(self.DUMMY_PROCESS)
showard170873e2009-01-07 00:22:26 +00001584 drone_manager.DroneManager.copy_to_results_repository.expect_call(
1585 self.DUMMY_PROCESS, mock.is_string_comparator())
showard97aed502008-11-04 02:01:24 +00001586
showard170873e2009-01-07 00:22:26 +00001587
1588 def _test_final_reparse_task_helper(self, autoserv_success=True):
1589 self._setup_pre_parse_expects(autoserv_success)
showardd3dc1992009-04-22 21:01:40 +00001590 self._setup_post_job_run_monitor(monitor_db._PARSER_PID_FILE)
showard97aed502008-11-04 02:01:24 +00001591 self._setup_post_parse_expects(autoserv_success)
1592
1593 task = monitor_db.FinalReparseTask([self.queue_entry])
1594 self.run_task(task, True)
1595
1596 self.god.check_playback()
showard170873e2009-01-07 00:22:26 +00001597 cmd = [monitor_db._parser_path, '--write-pidfile', '-l', '2', '-r',
1598 '-o', '/abspath/tag']
showard97aed502008-11-04 02:01:24 +00001599 self.assertEquals(task.cmd, cmd)
1600
1601
1602 def test_final_reparse_task(self):
1603 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1604 self._test_final_reparse_task_helper()
showard97aed502008-11-04 02:01:24 +00001605 self._test_final_reparse_task_helper(autoserv_success=False)
1606
1607
1608 def test_final_reparse_throttling(self):
1609 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1610 self.god.stub_function(monitor_db.FinalReparseTask,
1611 '_can_run_new_parse')
1612
showard170873e2009-01-07 00:22:26 +00001613 self._setup_pre_parse_expects(True)
showard97aed502008-11-04 02:01:24 +00001614 monitor_db.FinalReparseTask._can_run_new_parse.expect_call().and_return(
1615 False)
1616 monitor_db.FinalReparseTask._can_run_new_parse.expect_call().and_return(
1617 True)
showardd3dc1992009-04-22 21:01:40 +00001618 self._setup_post_job_run_monitor(monitor_db._PARSER_PID_FILE)
showard97aed502008-11-04 02:01:24 +00001619 self._setup_post_parse_expects(True)
1620
1621 task = monitor_db.FinalReparseTask([self.queue_entry])
1622 self.run_task(task, True)
1623 self.god.check_playback()
showard1be97432008-10-17 15:30:45 +00001624
1625
showard6b733412009-04-27 20:09:18 +00001626 def _setup_gather_logs_expects(self, hqe_aborted=False):
showardd3dc1992009-04-22 21:01:40 +00001627 self.god.stub_class(monitor_db, 'PidfileRunMonitor')
1628 self.god.stub_class(monitor_db, 'FinalReparseTask')
showard6b733412009-04-27 20:09:18 +00001629 self._setup_post_job_task_expects(True, 'Gathering', hqe_aborted)
showardd3dc1992009-04-22 21:01:40 +00001630 self._setup_post_job_run_monitor('.collect_crashinfo_execute')
showard6b733412009-04-27 20:09:18 +00001631 self._expect_copy_results(monitor=self.pidfile_monitor,
1632 queue_entry=self.queue_entry)
showardd3dc1992009-04-22 21:01:40 +00001633 parse_task = monitor_db.FinalReparseTask.expect_new([self.queue_entry])
showard6b733412009-04-27 20:09:18 +00001634 _set_host_and_qe_ids(parse_task)
showardd3dc1992009-04-22 21:01:40 +00001635 self._dispatcher.add_agent.expect_call(IsAgentWithTask(parse_task))
1636
showardd3dc1992009-04-22 21:01:40 +00001637
showard6b733412009-04-27 20:09:18 +00001638 def _run_gather_logs_task(self):
showardd3dc1992009-04-22 21:01:40 +00001639 task = monitor_db.GatherLogsTask(self.job, [self.queue_entry])
1640 task.agent = DummyAgent()
1641 task.agent.dispatcher = self._dispatcher
1642 self.run_task(task, True)
showardd3dc1992009-04-22 21:01:40 +00001643 self.god.check_playback()
1644
1645
showard6b733412009-04-27 20:09:18 +00001646 def test_gather_logs_task(self):
1647 self._setup_gather_logs_expects()
1648 # no rebooting for this basic test
1649 self.job.reboot_after = models.RebootAfter.NEVER
1650 self.host.set_status.expect_call('Ready')
1651
1652 self._run_gather_logs_task()
1653
1654
1655 def _setup_gather_task_cleanup_expects(self):
1656 self.god.stub_class(monitor_db, 'CleanupTask')
1657 cleanup_task = monitor_db.CleanupTask.expect_new(host=self.host)
1658 _set_host_and_qe_ids(cleanup_task)
1659 self._dispatcher.add_agent.expect_call(IsAgentWithTask(cleanup_task))
1660
1661
1662 def test_gather_logs_reboot_hosts(self):
1663 self._setup_gather_logs_expects()
1664 self.job.reboot_after = models.RebootAfter.ALWAYS
1665 self._setup_gather_task_cleanup_expects()
1666
1667 self._run_gather_logs_task()
1668
1669
1670 def test_gather_logs_reboot_on_abort(self):
1671 self._setup_gather_logs_expects(hqe_aborted=True)
1672 self.job.reboot_after = models.RebootAfter.NEVER
1673 self._setup_gather_task_cleanup_expects()
1674
1675 self._run_gather_logs_task()
1676
1677
showard45ae8192008-11-05 19:32:53 +00001678 def _test_cleanup_task_helper(self, success, use_queue_entry=False):
showardfa8629c2008-11-04 16:51:23 +00001679 if use_queue_entry:
1680 self.queue_entry.get_host.expect_call().and_return(self.host)
showard45ae8192008-11-05 19:32:53 +00001681 self.host.set_status.expect_call('Cleaning')
showardfa8629c2008-11-04 16:51:23 +00001682 if success:
1683 self.setup_run_monitor(0)
1684 self.host.set_status.expect_call('Ready')
1685 self.host.update_field.expect_call('dirty', 0)
1686 else:
1687 self.setup_run_monitor(1)
showard8fe93b52008-11-18 17:53:22 +00001688 if use_queue_entry and not self.queue_entry.meta_host:
1689 self.queue_entry.set_execution_subdir.expect_call()
showard170873e2009-01-07 00:22:26 +00001690 self.queue_entry.execution_tag.expect_call().and_return('tag')
1691 self._setup_move_logfile(include_destination=True)
showardfa8629c2008-11-04 16:51:23 +00001692
1693 if use_queue_entry:
showard45ae8192008-11-05 19:32:53 +00001694 task = monitor_db.CleanupTask(queue_entry=self.queue_entry)
showardfa8629c2008-11-04 16:51:23 +00001695 else:
showard45ae8192008-11-05 19:32:53 +00001696 task = monitor_db.CleanupTask(host=self.host)
showardfa8629c2008-11-04 16:51:23 +00001697 self.assertEquals(len(task.failure_tasks), 1)
1698 repair_task = task.failure_tasks[0]
1699 self.assert_(isinstance(repair_task, monitor_db.RepairTask))
1700 if use_queue_entry:
showardccbd6c52009-03-21 00:10:21 +00001701 self.assertEquals(repair_task.queue_entry_to_fail, self.queue_entry)
showardfa8629c2008-11-04 16:51:23 +00001702
1703 self.run_task(task, success)
1704
1705 self.god.check_playback()
showard170873e2009-01-07 00:22:26 +00001706 self.assert_(set(task.cmd) >=
1707 set([monitor_db._autoserv_path, '-p', '--cleanup', '-m',
1708 self.HOSTNAME, '-r', self.TEMP_DIR]))
showard87ba02a2009-04-20 19:37:32 +00001709 if use_queue_entry:
1710 self.assertTrue(set(task.cmd) >= self.JOB_AUTOSERV_PARAMS)
showardfa8629c2008-11-04 16:51:23 +00001711
showard45ae8192008-11-05 19:32:53 +00001712 def test_cleanup_task(self):
1713 self._test_cleanup_task_helper(True)
1714 self._test_cleanup_task_helper(False)
showardfa8629c2008-11-04 16:51:23 +00001715
1716
showard45ae8192008-11-05 19:32:53 +00001717 def test_cleanup_task_with_queue_entry(self):
1718 self._test_cleanup_task_helper(False, True)
showardfa8629c2008-11-04 16:51:23 +00001719
1720
showardb2e2c322008-10-14 17:33:55 +00001721class JobTest(BaseSchedulerTest):
showard2bab8f42008-11-12 18:15:22 +00001722 def setUp(self):
1723 super(JobTest, self).setUp()
showard170873e2009-01-07 00:22:26 +00001724 self.god.stub_with(
1725 drone_manager.DroneManager, 'attach_file_to_execution',
1726 mock.mock_function('attach_file_to_execution',
1727 default_return_val='/test/path/tmp/foo'))
showard2bab8f42008-11-12 18:15:22 +00001728
1729
1730 def _setup_directory_expects(self, execution_subdir):
1731 job_path = os.path.join('.', '1-my_user')
1732 results_dir = os.path.join(job_path, execution_subdir)
showard2bab8f42008-11-12 18:15:22 +00001733
1734
showarde58e3f82008-11-20 19:04:59 +00001735 def _test_run_helper(self, expect_agent=True, expect_starting=False,
1736 expect_pending=False):
1737 if expect_starting:
1738 expected_status = models.HostQueueEntry.Status.STARTING
1739 elif expect_pending:
1740 expected_status = models.HostQueueEntry.Status.PENDING
1741 else:
1742 expected_status = models.HostQueueEntry.Status.VERIFYING
showardb2e2c322008-10-14 17:33:55 +00001743 job = monitor_db.Job.fetch('id = 1').next()
1744 queue_entry = monitor_db.HostQueueEntry.fetch('id = 1').next()
1745 agent = job.run(queue_entry)
1746
showard2bab8f42008-11-12 18:15:22 +00001747 self.god.check_playback()
showarde77ac672008-11-14 22:42:33 +00001748 self.assertEquals(models.HostQueueEntry.smart_get(1).status,
1749 expected_status)
showard2bab8f42008-11-12 18:15:22 +00001750
showard9976ce92008-10-15 20:28:13 +00001751 if not expect_agent:
1752 self.assertEquals(agent, None)
1753 return
1754
showardb2e2c322008-10-14 17:33:55 +00001755 self.assert_(isinstance(agent, monitor_db.Agent))
1756 tasks = list(agent.queue.queue)
1757 return tasks
1758
1759
showardc9ae1782009-01-30 01:42:37 +00001760 def _check_verify_task(self, verify_task):
1761 self.assert_(isinstance(verify_task, monitor_db.VerifyTask))
1762 self.assertEquals(verify_task.queue_entry.id, 1)
1763
1764
1765 def _check_pending_task(self, pending_task):
1766 self.assert_(isinstance(pending_task, monitor_db.SetEntryPendingTask))
1767 self.assertEquals(pending_task._queue_entry.id, 1)
1768
1769
showardb2e2c322008-10-14 17:33:55 +00001770 def test_run_asynchronous(self):
1771 self._create_job(hosts=[1, 2])
1772
1773 tasks = self._test_run_helper()
1774
showardc9ae1782009-01-30 01:42:37 +00001775 self.assertEquals(len(tasks), 2)
1776 verify_task, pending_task = tasks
1777 self._check_verify_task(verify_task)
1778 self._check_pending_task(pending_task)
showardb2e2c322008-10-14 17:33:55 +00001779
showardb2e2c322008-10-14 17:33:55 +00001780
showard9976ce92008-10-15 20:28:13 +00001781 def test_run_asynchronous_skip_verify(self):
1782 job = self._create_job(hosts=[1, 2])
1783 job.run_verify = False
1784 job.save()
showard2bab8f42008-11-12 18:15:22 +00001785 self._setup_directory_expects('host1')
showard9976ce92008-10-15 20:28:13 +00001786
showardc9ae1782009-01-30 01:42:37 +00001787 tasks = self._test_run_helper()
showard9976ce92008-10-15 20:28:13 +00001788
1789 self.assertEquals(len(tasks), 1)
showardc9ae1782009-01-30 01:42:37 +00001790 pending_task = tasks[0]
1791 self._check_pending_task(pending_task)
showard9976ce92008-10-15 20:28:13 +00001792
1793
showardb2e2c322008-10-14 17:33:55 +00001794 def test_run_synchronous_verify(self):
1795 self._create_job(hosts=[1, 2], synchronous=True)
1796
1797 tasks = self._test_run_helper()
showardc9ae1782009-01-30 01:42:37 +00001798 self.assertEquals(len(tasks), 2)
1799 verify_task, pending_task = tasks
1800 self._check_verify_task(verify_task)
1801 self._check_pending_task(pending_task)
showardb2e2c322008-10-14 17:33:55 +00001802
1803
showard9976ce92008-10-15 20:28:13 +00001804 def test_run_synchronous_skip_verify(self):
1805 job = self._create_job(hosts=[1, 2], synchronous=True)
1806 job.run_verify = False
1807 job.save()
1808
showardc9ae1782009-01-30 01:42:37 +00001809 tasks = self._test_run_helper()
1810 self.assertEquals(len(tasks), 1)
1811 self._check_pending_task(tasks[0])
showard9976ce92008-10-15 20:28:13 +00001812
1813
showardb2e2c322008-10-14 17:33:55 +00001814 def test_run_synchronous_ready(self):
1815 self._create_job(hosts=[1, 2], synchronous=True)
showardd9ac4452009-02-07 02:04:37 +00001816 self._update_hqe("status='Pending', execution_subdir=''")
showard2bab8f42008-11-12 18:15:22 +00001817 self._setup_directory_expects('group0')
showardb2e2c322008-10-14 17:33:55 +00001818
showarde58e3f82008-11-20 19:04:59 +00001819 tasks = self._test_run_helper(expect_starting=True)
showardb2e2c322008-10-14 17:33:55 +00001820 self.assertEquals(len(tasks), 1)
1821 queue_task = tasks[0]
1822
1823 self.assert_(isinstance(queue_task, monitor_db.QueueTask))
1824 self.assertEquals(queue_task.job.id, 1)
1825 hqe_ids = [hqe.id for hqe in queue_task.queue_entries]
1826 self.assertEquals(hqe_ids, [1, 2])
1827
1828
showard21baa452008-10-21 00:08:39 +00001829 def test_reboot_before_always(self):
1830 job = self._create_job(hosts=[1])
showard0fc38302008-10-23 00:44:07 +00001831 job.reboot_before = models.RebootBefore.ALWAYS
showard21baa452008-10-21 00:08:39 +00001832 job.save()
1833
1834 tasks = self._test_run_helper()
showardc9ae1782009-01-30 01:42:37 +00001835 self.assertEquals(len(tasks), 3)
showard45ae8192008-11-05 19:32:53 +00001836 cleanup_task = tasks[0]
1837 self.assert_(isinstance(cleanup_task, monitor_db.CleanupTask))
1838 self.assertEquals(cleanup_task.host.id, 1)
showard21baa452008-10-21 00:08:39 +00001839
1840
1841 def _test_reboot_before_if_dirty_helper(self, expect_reboot):
1842 job = self._create_job(hosts=[1])
showard0fc38302008-10-23 00:44:07 +00001843 job.reboot_before = models.RebootBefore.IF_DIRTY
showard21baa452008-10-21 00:08:39 +00001844 job.save()
1845
1846 tasks = self._test_run_helper()
showardc9ae1782009-01-30 01:42:37 +00001847 self.assertEquals(len(tasks), expect_reboot and 3 or 2)
showard21baa452008-10-21 00:08:39 +00001848 if expect_reboot:
showard45ae8192008-11-05 19:32:53 +00001849 cleanup_task = tasks[0]
1850 self.assert_(isinstance(cleanup_task, monitor_db.CleanupTask))
1851 self.assertEquals(cleanup_task.host.id, 1)
showard21baa452008-10-21 00:08:39 +00001852
1853 def test_reboot_before_if_dirty(self):
1854 models.Host.smart_get(1).update_object(dirty=True)
1855 self._test_reboot_before_if_dirty_helper(True)
1856
1857
1858 def test_reboot_before_not_dirty(self):
1859 models.Host.smart_get(1).update_object(dirty=False)
1860 self._test_reboot_before_if_dirty_helper(False)
1861
1862
1863
showardce38e0c2008-05-29 19:36:16 +00001864if __name__ == '__main__':
jadmanski0afbb632008-06-06 21:10:57 +00001865 unittest.main()