blob: d2ee2a25096f202ad27e5773d640048a49ee39db [file] [log] [blame]
showarde39ebe92009-06-18 23:14:48 +00001#!/usr/bin/python
2
showard42d44982009-10-12 20:34:03 +00003import os, unittest
showarde39ebe92009-06-18 23:14:48 +00004import common
showardac5b0002009-10-19 18:34:00 +00005from autotest_lib.client.common_lib import global_config
showard42d44982009-10-12 20:34:03 +00006from autotest_lib.client.common_lib.test_utils import mock
showard2aafd902009-10-14 16:20:14 +00007from autotest_lib.scheduler import drone_manager, drone_utility, drones
Simran Basi882f15b2013-10-29 14:59:34 -07008from autotest_lib.scheduler import scheduler_config, site_drone_manager
showarde39ebe92009-06-18 23:14:48 +00009
10class MockDrone(drones._AbstractDrone):
showard9bb960b2009-11-19 01:02:11 +000011 def __init__(self, name, active_processes=0, max_processes=10,
12 allowed_users=None):
showarde39ebe92009-06-18 23:14:48 +000013 super(MockDrone, self).__init__()
14 self.name = name
jamesren76fcf192010-04-21 20:39:50 +000015 self.hostname = name
showarde39ebe92009-06-18 23:14:48 +000016 self.active_processes = active_processes
17 self.max_processes = max_processes
showard9bb960b2009-11-19 01:02:11 +000018 self.allowed_users = allowed_users
showard42d44982009-10-12 20:34:03 +000019 # maps method names list of tuples containing method arguments
20 self._recorded_calls = {'queue_call': [],
21 'send_file_to': []}
22
23
24 def queue_call(self, method, *args, **kwargs):
25 self._recorded_calls['queue_call'].append((method, args, kwargs))
26
27
showard2aafd902009-10-14 16:20:14 +000028 def call(self, method, *args, **kwargs):
29 # don't bother differentiating between call() and queue_call()
30 return self.queue_call(method, *args, **kwargs)
31
32
showard42d44982009-10-12 20:34:03 +000033 def send_file_to(self, drone, source_path, destination_path,
34 can_fail=False):
35 self._recorded_calls['send_file_to'].append(
36 (drone, source_path, destination_path))
37
38
39 # method for use by tests
40 def _check_for_recorded_call(self, method_name, arguments):
41 recorded_arg_list = self._recorded_calls[method_name]
42 was_called = arguments in recorded_arg_list
43 if not was_called:
44 print 'Recorded args:', recorded_arg_list
45 print 'Expected:', arguments
46 return was_called
47
48
49 def was_call_queued(self, method, *args, **kwargs):
50 return self._check_for_recorded_call('queue_call',
51 (method, args, kwargs))
52
53
54 def was_file_sent(self, drone, source_path, destination_path):
55 return self._check_for_recorded_call('send_file_to',
56 (drone, source_path,
57 destination_path))
showarde39ebe92009-06-18 23:14:48 +000058
59
60class DroneManager(unittest.TestCase):
showard42d44982009-10-12 20:34:03 +000061 _DRONE_INSTALL_DIR = '/drone/install/dir'
showardc75fded2009-10-14 16:20:02 +000062 _DRONE_RESULTS_DIR = os.path.join(_DRONE_INSTALL_DIR, 'results')
showard42d44982009-10-12 20:34:03 +000063 _RESULTS_DIR = '/results/dir'
64 _SOURCE_PATH = 'source/path'
65 _DESTINATION_PATH = 'destination/path'
showard2aafd902009-10-14 16:20:14 +000066 _WORKING_DIRECTORY = 'working/directory'
showard9bb960b2009-11-19 01:02:11 +000067 _USERNAME = 'my_user'
showard42d44982009-10-12 20:34:03 +000068
showarde39ebe92009-06-18 23:14:48 +000069 def setUp(self):
showard42d44982009-10-12 20:34:03 +000070 self.god = mock.mock_god()
71 self.god.stub_with(drones, 'AUTOTEST_INSTALL_DIR',
72 self._DRONE_INSTALL_DIR)
showarde39ebe92009-06-18 23:14:48 +000073 self.manager = drone_manager.DroneManager()
showard42d44982009-10-12 20:34:03 +000074 self.god.stub_with(self.manager, '_results_dir', self._RESULTS_DIR)
75
showard2aafd902009-10-14 16:20:14 +000076 # we don't want this to ever actually get called
77 self.god.stub_function(drones, 'get_drone')
showardac5b0002009-10-19 18:34:00 +000078 # we don't want the DroneManager to go messing with global config
79 def do_nothing():
80 pass
81 self.god.stub_with(self.manager, 'refresh_drone_configs', do_nothing)
showard2aafd902009-10-14 16:20:14 +000082
showard42d44982009-10-12 20:34:03 +000083 # set up some dummy drones
showard202343e2009-10-14 16:20:24 +000084 self.mock_drone = MockDrone('mock_drone')
showard42d44982009-10-12 20:34:03 +000085 self.manager._drones[self.mock_drone.name] = self.mock_drone
86 self.results_drone = MockDrone('results_drone', 0, 10)
87 self.manager._results_drone = self.results_drone
88
89 self.mock_drone_process = drone_manager.Process(self.mock_drone.name, 0)
90
91
92 def tearDown(self):
93 self.god.unstub_all()
showarde39ebe92009-06-18 23:14:48 +000094
95
96 def _test_choose_drone_for_execution_helper(self, processes_info_list,
97 requested_processes):
98 for index, process_info in enumerate(processes_info_list):
99 active_processes, max_processes = process_info
100 self.manager._enqueue_drone(MockDrone(index, active_processes,
101 max_processes))
102
showard9bb960b2009-11-19 01:02:11 +0000103 return self.manager._choose_drone_for_execution(requested_processes,
jamesren76fcf192010-04-21 20:39:50 +0000104 self._USERNAME, None)
showarde39ebe92009-06-18 23:14:48 +0000105
106
107 def test_choose_drone_for_execution(self):
108 drone = self._test_choose_drone_for_execution_helper([(1, 2), (0, 2)],
109 1)
110 self.assertEquals(drone.name, 1)
111
112
113 def test_choose_drone_for_execution_some_full(self):
114 drone = self._test_choose_drone_for_execution_helper([(0, 1), (1, 3)],
115 2)
116 self.assertEquals(drone.name, 1)
117
118
119 def test_choose_drone_for_execution_all_full(self):
120 drone = self._test_choose_drone_for_execution_helper([(2, 1), (3, 2)],
121 1)
122 self.assertEquals(drone.name, 1)
123
124
jamesren37b50452010-03-25 20:38:56 +0000125 def test_choose_drone_for_execution_all_full_same_percentage_capacity(self):
126 drone = self._test_choose_drone_for_execution_helper([(5, 3), (10, 6)],
127 1)
128 self.assertEquals(drone.name, 1)
129
130
showard9bb960b2009-11-19 01:02:11 +0000131 def test_user_restrictions(self):
132 # this drone is restricted to a different user
133 self.manager._enqueue_drone(MockDrone(1, max_processes=10,
134 allowed_users=['fakeuser']))
135 # this drone is allowed but has lower capacity
136 self.manager._enqueue_drone(MockDrone(2, max_processes=2,
137 allowed_users=[self._USERNAME]))
138
139 self.assertEquals(2,
jamesren76fcf192010-04-21 20:39:50 +0000140 self.manager.max_runnable_processes(self._USERNAME,
141 None))
showard9bb960b2009-11-19 01:02:11 +0000142 drone = self.manager._choose_drone_for_execution(
jamesren76fcf192010-04-21 20:39:50 +0000143 1, username=self._USERNAME, drone_hostnames_allowed=None)
showard9bb960b2009-11-19 01:02:11 +0000144 self.assertEquals(drone.name, 2)
145
146
jamesren37b50452010-03-25 20:38:56 +0000147 def test_user_restrictions_with_full_drone(self):
148 # this drone is restricted to a different user
149 self.manager._enqueue_drone(MockDrone(1, max_processes=10,
150 allowed_users=['fakeuser']))
151 # this drone is allowed but is full
152 self.manager._enqueue_drone(MockDrone(2, active_processes=3,
153 max_processes=2,
154 allowed_users=[self._USERNAME]))
155
156 self.assertEquals(0,
jamesren76fcf192010-04-21 20:39:50 +0000157 self.manager.max_runnable_processes(self._USERNAME,
158 None))
jamesren37b50452010-03-25 20:38:56 +0000159 drone = self.manager._choose_drone_for_execution(
jamesren76fcf192010-04-21 20:39:50 +0000160 1, username=self._USERNAME, drone_hostnames_allowed=None)
jamesren37b50452010-03-25 20:38:56 +0000161 self.assertEquals(drone.name, 2)
162
163
jamesren76fcf192010-04-21 20:39:50 +0000164 def _setup_test_drone_restrictions(self, active_processes=0):
165 self.manager._enqueue_drone(MockDrone(
166 1, active_processes=active_processes, max_processes=10))
167 self.manager._enqueue_drone(MockDrone(
168 2, active_processes=active_processes, max_processes=5))
169 self.manager._enqueue_drone(MockDrone(
170 3, active_processes=active_processes, max_processes=2))
171
172
173 def test_drone_restrictions_allow_any(self):
174 self._setup_test_drone_restrictions()
175 self.assertEquals(10,
176 self.manager.max_runnable_processes(self._USERNAME,
177 None))
178 drone = self.manager._choose_drone_for_execution(
179 1, username=self._USERNAME, drone_hostnames_allowed=None)
180 self.assertEqual(drone.name, 1)
181
182
183 def test_drone_restrictions_under_capacity(self):
184 self._setup_test_drone_restrictions()
185 drone_hostnames_allowed = (2, 3)
186 self.assertEquals(
187 5, self.manager.max_runnable_processes(self._USERNAME,
188 drone_hostnames_allowed))
189 drone = self.manager._choose_drone_for_execution(
190 1, username=self._USERNAME,
191 drone_hostnames_allowed=drone_hostnames_allowed)
192
193 self.assertEqual(drone.name, 2)
194
195
196 def test_drone_restrictions_over_capacity(self):
197 self._setup_test_drone_restrictions(active_processes=6)
198 drone_hostnames_allowed = (2, 3)
199 self.assertEquals(
200 0, self.manager.max_runnable_processes(self._USERNAME,
201 drone_hostnames_allowed))
202 drone = self.manager._choose_drone_for_execution(
203 7, username=self._USERNAME,
204 drone_hostnames_allowed=drone_hostnames_allowed)
205 self.assertEqual(drone.name, 2)
206
207
208 def test_drone_restrictions_allow_none(self):
209 self._setup_test_drone_restrictions()
210 drone_hostnames_allowed = ()
211 self.assertEquals(
212 0, self.manager.max_runnable_processes(self._USERNAME,
213 drone_hostnames_allowed))
214 drone = self.manager._choose_drone_for_execution(
215 1, username=self._USERNAME,
216 drone_hostnames_allowed=drone_hostnames_allowed)
217 self.assertEqual(drone, None)
218
219
showard2aafd902009-10-14 16:20:14 +0000220 def test_initialize(self):
showard2aafd902009-10-14 16:20:14 +0000221 results_hostname = 'results_repo'
showardac5b0002009-10-19 18:34:00 +0000222 results_install_dir = '/results/install'
223 global_config.global_config.override_config_value(
224 scheduler_config.CONFIG_SECTION,
225 'results_host_installation_directory', results_install_dir)
showard2aafd902009-10-14 16:20:14 +0000226
showard2aafd902009-10-14 16:20:14 +0000227 (drones.get_drone.expect_call(self.mock_drone.name)
228 .and_return(self.mock_drone))
showard202343e2009-10-14 16:20:24 +0000229
230 results_drone = MockDrone('results_drone')
231 self.god.stub_function(results_drone, 'set_autotest_install_dir')
232 drones.get_drone.expect_call(results_hostname).and_return(results_drone)
showardac5b0002009-10-19 18:34:00 +0000233 results_drone.set_autotest_install_dir.expect_call(results_install_dir)
showard2aafd902009-10-14 16:20:14 +0000234
235 self.manager.initialize(base_results_dir=self._RESULTS_DIR,
236 drone_hostnames=[self.mock_drone.name],
237 results_repository_hostname=results_hostname)
238
239 self.assert_(self.mock_drone.was_call_queued(
240 'initialize', self._DRONE_RESULTS_DIR + '/'))
showardac5b0002009-10-19 18:34:00 +0000241 self.god.check_playback()
showard2aafd902009-10-14 16:20:14 +0000242
243
showard42d44982009-10-12 20:34:03 +0000244 def test_execute_command(self):
245 self.manager._enqueue_drone(self.mock_drone)
246
showard42d44982009-10-12 20:34:03 +0000247 pidfile_name = 'my_pidfile'
248 log_file = 'log_file'
249
250 pidfile_id = self.manager.execute_command(
251 command=['test', drone_manager.WORKING_DIRECTORY],
showard2aafd902009-10-14 16:20:14 +0000252 working_directory=self._WORKING_DIRECTORY,
showard42d44982009-10-12 20:34:03 +0000253 pidfile_name=pidfile_name,
showard418785b2009-11-23 20:19:59 +0000254 num_processes=1,
showard42d44982009-10-12 20:34:03 +0000255 log_file=log_file)
256
showardc75fded2009-10-14 16:20:02 +0000257 full_working_directory = os.path.join(self._DRONE_RESULTS_DIR,
showard2aafd902009-10-14 16:20:14 +0000258 self._WORKING_DIRECTORY)
showard42d44982009-10-12 20:34:03 +0000259 self.assertEquals(pidfile_id.path,
260 os.path.join(full_working_directory, pidfile_name))
261 self.assert_(self.mock_drone.was_call_queued(
262 'execute_command', ['test', full_working_directory],
263 full_working_directory,
showardc75fded2009-10-14 16:20:02 +0000264 os.path.join(self._DRONE_RESULTS_DIR, log_file), pidfile_name))
showard42d44982009-10-12 20:34:03 +0000265
266
showard2aafd902009-10-14 16:20:14 +0000267 def test_attach_file_to_execution(self):
268 self.manager._enqueue_drone(self.mock_drone)
269
270 contents = 'my\ncontents'
271 attached_path = self.manager.attach_file_to_execution(
272 self._WORKING_DIRECTORY, contents)
273 self.manager.execute_command(command=['test'],
274 working_directory=self._WORKING_DIRECTORY,
showard418785b2009-11-23 20:19:59 +0000275 pidfile_name='mypidfile',
jamesren76fcf192010-04-21 20:39:50 +0000276 num_processes=1,
277 drone_hostnames_allowed=None)
showard2aafd902009-10-14 16:20:14 +0000278
279 self.assert_(self.mock_drone.was_call_queued(
280 'write_to_file',
281 os.path.join(self._DRONE_RESULTS_DIR, attached_path),
282 contents))
283
284
showard42d44982009-10-12 20:34:03 +0000285 def test_copy_results_on_drone(self):
286 self.manager.copy_results_on_drone(self.mock_drone_process,
287 self._SOURCE_PATH,
288 self._DESTINATION_PATH)
289 self.assert_(self.mock_drone.was_call_queued(
290 'copy_file_or_directory',
showardc75fded2009-10-14 16:20:02 +0000291 os.path.join(self._DRONE_RESULTS_DIR, self._SOURCE_PATH),
292 os.path.join(self._DRONE_RESULTS_DIR, self._DESTINATION_PATH)))
showard42d44982009-10-12 20:34:03 +0000293
294
295 def test_copy_to_results_repository(self):
Simran Basi882f15b2013-10-29 14:59:34 -0700296 site_drone_manager.ENABLE_ARCHIVING = True
showard42d44982009-10-12 20:34:03 +0000297 self.manager.copy_to_results_repository(self.mock_drone_process,
298 self._SOURCE_PATH)
299 self.assert_(self.mock_drone.was_file_sent(
300 self.results_drone,
showardc75fded2009-10-14 16:20:02 +0000301 os.path.join(self._DRONE_RESULTS_DIR, self._SOURCE_PATH),
showard42d44982009-10-12 20:34:03 +0000302 os.path.join(self._RESULTS_DIR, self._SOURCE_PATH)))
303
304
305 def test_write_lines_to_file(self):
306 file_path = 'file/path'
307 lines = ['line1', 'line2']
308 written_data = 'line1\nline2\n'
309
310 # write to results repository
311 self.manager.write_lines_to_file(file_path, lines)
312 self.assert_(self.results_drone.was_call_queued(
313 'write_to_file', os.path.join(self._RESULTS_DIR, file_path),
314 written_data))
315
316 # write to a drone
317 self.manager.write_lines_to_file(
318 file_path, lines, paired_with_process=self.mock_drone_process)
319 self.assert_(self.mock_drone.was_call_queued(
320 'write_to_file',
showardc75fded2009-10-14 16:20:02 +0000321 os.path.join(self._DRONE_RESULTS_DIR, file_path), written_data))
showard42d44982009-10-12 20:34:03 +0000322
323
showardd3496242009-12-10 21:41:43 +0000324 def test_pidfile_expiration(self):
325 self.god.stub_with(self.manager, '_get_max_pidfile_refreshes',
326 lambda: 0)
327 pidfile_id = self.manager.get_pidfile_id_from('tag', 'name')
328 self.manager.register_pidfile(pidfile_id)
329 self.manager._drop_old_pidfiles()
330 self.manager._drop_old_pidfiles()
331 self.assertFalse(self.manager._registered_pidfile_info)
332
333
showarde39ebe92009-06-18 23:14:48 +0000334if __name__ == '__main__':
335 unittest.main()