blob: bd078c151b0a82aa38f99e26b421676d9ddf35c2 [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
showardac5b0002009-10-19 18:34:00 +00008from autotest_lib.scheduler import scheduler_config
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
15 self.active_processes = active_processes
16 self.max_processes = max_processes
showard9bb960b2009-11-19 01:02:11 +000017 self.allowed_users = allowed_users
showard42d44982009-10-12 20:34:03 +000018 # maps method names list of tuples containing method arguments
19 self._recorded_calls = {'queue_call': [],
20 'send_file_to': []}
21
22
23 def queue_call(self, method, *args, **kwargs):
24 self._recorded_calls['queue_call'].append((method, args, kwargs))
25
26
showard2aafd902009-10-14 16:20:14 +000027 def call(self, method, *args, **kwargs):
28 # don't bother differentiating between call() and queue_call()
29 return self.queue_call(method, *args, **kwargs)
30
31
showard42d44982009-10-12 20:34:03 +000032 def send_file_to(self, drone, source_path, destination_path,
33 can_fail=False):
34 self._recorded_calls['send_file_to'].append(
35 (drone, source_path, destination_path))
36
37
38 # method for use by tests
39 def _check_for_recorded_call(self, method_name, arguments):
40 recorded_arg_list = self._recorded_calls[method_name]
41 was_called = arguments in recorded_arg_list
42 if not was_called:
43 print 'Recorded args:', recorded_arg_list
44 print 'Expected:', arguments
45 return was_called
46
47
48 def was_call_queued(self, method, *args, **kwargs):
49 return self._check_for_recorded_call('queue_call',
50 (method, args, kwargs))
51
52
53 def was_file_sent(self, drone, source_path, destination_path):
54 return self._check_for_recorded_call('send_file_to',
55 (drone, source_path,
56 destination_path))
showarde39ebe92009-06-18 23:14:48 +000057
58
59class DroneManager(unittest.TestCase):
showard42d44982009-10-12 20:34:03 +000060 _DRONE_INSTALL_DIR = '/drone/install/dir'
showardc75fded2009-10-14 16:20:02 +000061 _DRONE_RESULTS_DIR = os.path.join(_DRONE_INSTALL_DIR, 'results')
showard42d44982009-10-12 20:34:03 +000062 _RESULTS_DIR = '/results/dir'
63 _SOURCE_PATH = 'source/path'
64 _DESTINATION_PATH = 'destination/path'
showard2aafd902009-10-14 16:20:14 +000065 _WORKING_DIRECTORY = 'working/directory'
showard9bb960b2009-11-19 01:02:11 +000066 _USERNAME = 'my_user'
showard42d44982009-10-12 20:34:03 +000067
showarde39ebe92009-06-18 23:14:48 +000068 def setUp(self):
showard42d44982009-10-12 20:34:03 +000069 self.god = mock.mock_god()
70 self.god.stub_with(drones, 'AUTOTEST_INSTALL_DIR',
71 self._DRONE_INSTALL_DIR)
showarde39ebe92009-06-18 23:14:48 +000072 self.manager = drone_manager.DroneManager()
showard42d44982009-10-12 20:34:03 +000073 self.god.stub_with(self.manager, '_results_dir', self._RESULTS_DIR)
74
showard2aafd902009-10-14 16:20:14 +000075 # we don't want this to ever actually get called
76 self.god.stub_function(drones, 'get_drone')
showardac5b0002009-10-19 18:34:00 +000077 # we don't want the DroneManager to go messing with global config
78 def do_nothing():
79 pass
80 self.god.stub_with(self.manager, 'refresh_drone_configs', do_nothing)
showard2aafd902009-10-14 16:20:14 +000081
showard42d44982009-10-12 20:34:03 +000082 # set up some dummy drones
showard202343e2009-10-14 16:20:24 +000083 self.mock_drone = MockDrone('mock_drone')
showard42d44982009-10-12 20:34:03 +000084 self.manager._drones[self.mock_drone.name] = self.mock_drone
85 self.results_drone = MockDrone('results_drone', 0, 10)
86 self.manager._results_drone = self.results_drone
87
88 self.mock_drone_process = drone_manager.Process(self.mock_drone.name, 0)
89
90
91 def tearDown(self):
92 self.god.unstub_all()
showarde39ebe92009-06-18 23:14:48 +000093
94
95 def _test_choose_drone_for_execution_helper(self, processes_info_list,
96 requested_processes):
97 for index, process_info in enumerate(processes_info_list):
98 active_processes, max_processes = process_info
99 self.manager._enqueue_drone(MockDrone(index, active_processes,
100 max_processes))
101
showard9bb960b2009-11-19 01:02:11 +0000102 return self.manager._choose_drone_for_execution(requested_processes,
103 self._USERNAME)
showarde39ebe92009-06-18 23:14:48 +0000104
105
106 def test_choose_drone_for_execution(self):
107 drone = self._test_choose_drone_for_execution_helper([(1, 2), (0, 2)],
108 1)
109 self.assertEquals(drone.name, 1)
110
111
112 def test_choose_drone_for_execution_some_full(self):
113 drone = self._test_choose_drone_for_execution_helper([(0, 1), (1, 3)],
114 2)
115 self.assertEquals(drone.name, 1)
116
117
118 def test_choose_drone_for_execution_all_full(self):
119 drone = self._test_choose_drone_for_execution_helper([(2, 1), (3, 2)],
120 1)
121 self.assertEquals(drone.name, 1)
122
123
jamesren37b50452010-03-25 20:38:56 +0000124 def test_choose_drone_for_execution_all_full_same_percentage_capacity(self):
125 drone = self._test_choose_drone_for_execution_helper([(5, 3), (10, 6)],
126 1)
127 self.assertEquals(drone.name, 1)
128
129
showard9bb960b2009-11-19 01:02:11 +0000130 def test_user_restrictions(self):
131 # this drone is restricted to a different user
132 self.manager._enqueue_drone(MockDrone(1, max_processes=10,
133 allowed_users=['fakeuser']))
134 # this drone is allowed but has lower capacity
135 self.manager._enqueue_drone(MockDrone(2, max_processes=2,
136 allowed_users=[self._USERNAME]))
137
138 self.assertEquals(2,
139 self.manager.max_runnable_processes(self._USERNAME))
140 drone = self.manager._choose_drone_for_execution(
141 1, username=self._USERNAME)
142 self.assertEquals(drone.name, 2)
143
144
jamesren37b50452010-03-25 20:38:56 +0000145 def test_user_restrictions_with_full_drone(self):
146 # this drone is restricted to a different user
147 self.manager._enqueue_drone(MockDrone(1, max_processes=10,
148 allowed_users=['fakeuser']))
149 # this drone is allowed but is full
150 self.manager._enqueue_drone(MockDrone(2, active_processes=3,
151 max_processes=2,
152 allowed_users=[self._USERNAME]))
153
154 self.assertEquals(0,
155 self.manager.max_runnable_processes(self._USERNAME))
156 drone = self.manager._choose_drone_for_execution(
157 1, username=self._USERNAME)
158 self.assertEquals(drone.name, 2)
159
160
showard2aafd902009-10-14 16:20:14 +0000161 def test_initialize(self):
showard2aafd902009-10-14 16:20:14 +0000162 results_hostname = 'results_repo'
showardac5b0002009-10-19 18:34:00 +0000163 results_install_dir = '/results/install'
164 global_config.global_config.override_config_value(
165 scheduler_config.CONFIG_SECTION,
166 'results_host_installation_directory', results_install_dir)
showard2aafd902009-10-14 16:20:14 +0000167
showard2aafd902009-10-14 16:20:14 +0000168 (drones.get_drone.expect_call(self.mock_drone.name)
169 .and_return(self.mock_drone))
showard202343e2009-10-14 16:20:24 +0000170
171 results_drone = MockDrone('results_drone')
172 self.god.stub_function(results_drone, 'set_autotest_install_dir')
173 drones.get_drone.expect_call(results_hostname).and_return(results_drone)
showardac5b0002009-10-19 18:34:00 +0000174 results_drone.set_autotest_install_dir.expect_call(results_install_dir)
showard2aafd902009-10-14 16:20:14 +0000175
176 self.manager.initialize(base_results_dir=self._RESULTS_DIR,
177 drone_hostnames=[self.mock_drone.name],
178 results_repository_hostname=results_hostname)
179
180 self.assert_(self.mock_drone.was_call_queued(
181 'initialize', self._DRONE_RESULTS_DIR + '/'))
showardac5b0002009-10-19 18:34:00 +0000182 self.god.check_playback()
showard2aafd902009-10-14 16:20:14 +0000183
184
showard42d44982009-10-12 20:34:03 +0000185 def test_execute_command(self):
186 self.manager._enqueue_drone(self.mock_drone)
187
showard42d44982009-10-12 20:34:03 +0000188 pidfile_name = 'my_pidfile'
189 log_file = 'log_file'
190
191 pidfile_id = self.manager.execute_command(
192 command=['test', drone_manager.WORKING_DIRECTORY],
showard2aafd902009-10-14 16:20:14 +0000193 working_directory=self._WORKING_DIRECTORY,
showard42d44982009-10-12 20:34:03 +0000194 pidfile_name=pidfile_name,
showard418785b2009-11-23 20:19:59 +0000195 num_processes=1,
showard42d44982009-10-12 20:34:03 +0000196 log_file=log_file)
197
showardc75fded2009-10-14 16:20:02 +0000198 full_working_directory = os.path.join(self._DRONE_RESULTS_DIR,
showard2aafd902009-10-14 16:20:14 +0000199 self._WORKING_DIRECTORY)
showard42d44982009-10-12 20:34:03 +0000200 self.assertEquals(pidfile_id.path,
201 os.path.join(full_working_directory, pidfile_name))
202 self.assert_(self.mock_drone.was_call_queued(
203 'execute_command', ['test', full_working_directory],
204 full_working_directory,
showardc75fded2009-10-14 16:20:02 +0000205 os.path.join(self._DRONE_RESULTS_DIR, log_file), pidfile_name))
showard42d44982009-10-12 20:34:03 +0000206
207
showard2aafd902009-10-14 16:20:14 +0000208 def test_attach_file_to_execution(self):
209 self.manager._enqueue_drone(self.mock_drone)
210
211 contents = 'my\ncontents'
212 attached_path = self.manager.attach_file_to_execution(
213 self._WORKING_DIRECTORY, contents)
214 self.manager.execute_command(command=['test'],
215 working_directory=self._WORKING_DIRECTORY,
showard418785b2009-11-23 20:19:59 +0000216 pidfile_name='mypidfile',
217 num_processes=1)
showard2aafd902009-10-14 16:20:14 +0000218
219 self.assert_(self.mock_drone.was_call_queued(
220 'write_to_file',
221 os.path.join(self._DRONE_RESULTS_DIR, attached_path),
222 contents))
223
224
showard42d44982009-10-12 20:34:03 +0000225 def test_copy_results_on_drone(self):
226 self.manager.copy_results_on_drone(self.mock_drone_process,
227 self._SOURCE_PATH,
228 self._DESTINATION_PATH)
229 self.assert_(self.mock_drone.was_call_queued(
230 'copy_file_or_directory',
showardc75fded2009-10-14 16:20:02 +0000231 os.path.join(self._DRONE_RESULTS_DIR, self._SOURCE_PATH),
232 os.path.join(self._DRONE_RESULTS_DIR, self._DESTINATION_PATH)))
showard42d44982009-10-12 20:34:03 +0000233
234
235 def test_copy_to_results_repository(self):
236 self.manager.copy_to_results_repository(self.mock_drone_process,
237 self._SOURCE_PATH)
238 self.assert_(self.mock_drone.was_file_sent(
239 self.results_drone,
showardc75fded2009-10-14 16:20:02 +0000240 os.path.join(self._DRONE_RESULTS_DIR, self._SOURCE_PATH),
showard42d44982009-10-12 20:34:03 +0000241 os.path.join(self._RESULTS_DIR, self._SOURCE_PATH)))
242
243
244 def test_write_lines_to_file(self):
245 file_path = 'file/path'
246 lines = ['line1', 'line2']
247 written_data = 'line1\nline2\n'
248
249 # write to results repository
250 self.manager.write_lines_to_file(file_path, lines)
251 self.assert_(self.results_drone.was_call_queued(
252 'write_to_file', os.path.join(self._RESULTS_DIR, file_path),
253 written_data))
254
255 # write to a drone
256 self.manager.write_lines_to_file(
257 file_path, lines, paired_with_process=self.mock_drone_process)
258 self.assert_(self.mock_drone.was_call_queued(
259 'write_to_file',
showardc75fded2009-10-14 16:20:02 +0000260 os.path.join(self._DRONE_RESULTS_DIR, file_path), written_data))
showard42d44982009-10-12 20:34:03 +0000261
262
showardd3496242009-12-10 21:41:43 +0000263 def test_pidfile_expiration(self):
264 self.god.stub_with(self.manager, '_get_max_pidfile_refreshes',
265 lambda: 0)
266 pidfile_id = self.manager.get_pidfile_id_from('tag', 'name')
267 self.manager.register_pidfile(pidfile_id)
268 self.manager._drop_old_pidfiles()
269 self.manager._drop_old_pidfiles()
270 self.assertFalse(self.manager._registered_pidfile_info)
271
272
showarde39ebe92009-06-18 23:14:48 +0000273if __name__ == '__main__':
274 unittest.main()