Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(854)

Side by Side Diff: client/bin/job.py

Issue 6246035: Merge remote branch 'cros/upstream' into master (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/autotest.git@master
Patch Set: patch Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 """The main job wrapper 1 """The main job wrapper
2 2
3 This is the core infrastructure. 3 This is the core infrastructure.
4 4
5 Copyright Andy Whitcroft, Martin J. Bligh 2006 5 Copyright Andy Whitcroft, Martin J. Bligh 2006
6 """ 6 """
7 7
8 import copy, os, platform, re, shutil, sys, time, traceback, types, glob 8 import copy, os, platform, re, shutil, sys, time, traceback, types, glob
9 import logging, getpass, errno, weakref 9 import logging, getpass, errno, weakref
10 import cPickle as pickle 10 import cPickle as pickle
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 @classmethod 128 @classmethod
129 def _find_base_directories(cls): 129 def _find_base_directories(cls):
130 """ 130 """
131 Determine locations of autodir and clientdir (which are the same) 131 Determine locations of autodir and clientdir (which are the same)
132 using os.environ. Serverdir does not exist in this context. 132 using os.environ. Serverdir does not exist in this context.
133 """ 133 """
134 autodir = clientdir = cls._get_environ_autodir() 134 autodir = clientdir = cls._get_environ_autodir()
135 return autodir, clientdir, None 135 return autodir, clientdir, None
136 136
137 137
138 @classmethod
139 def _parse_args(cls, args):
140 return re.findall("[^\s]*?['|\"].*?['|\"]|[^\s]+", args)
141
142
138 def _find_resultdir(self, options): 143 def _find_resultdir(self, options):
139 """ 144 """
140 Determine the directory for storing results. On a client this is 145 Determine the directory for storing results. On a client this is
141 always <autodir>/results/<tag>, where tag is passed in on the command 146 always <autodir>/results/<tag>, where tag is passed in on the command
142 line as an option. 147 line as an option.
143 """ 148 """
144 return os.path.join(self.autodir, 'results', options.tag) 149 return os.path.join(self.autodir, 'results', options.tag)
145 150
146 151
147 def _get_status_logger(self): 152 def _get_status_logger(self):
148 """Return a reference to the status logger.""" 153 """Return a reference to the status logger."""
149 return self._logger 154 return self._logger
150 155
151 156
152 def _pre_record_init(self, control, options): 157 def _pre_record_init(self, control, options):
153 """ 158 """
154 Initialization function that should peform ONLY the required 159 Initialization function that should peform ONLY the required
155 setup so that the self.record() method works. 160 setup so that the self.record() method works.
156 161
157 As of now self.record() needs self.resultdir, self._group_level, 162 As of now self.record() needs self.resultdir, self._group_level,
158 self.harness and of course self._logger. 163 self.harness and of course self._logger.
159 """ 164 """
160 if not options.cont: 165 if not options.cont:
161 self._cleanup_debugdir_files() 166 self._cleanup_debugdir_files()
162 self._cleanup_results_dir() 167 self._cleanup_results_dir()
163 168
164 logging_manager.configure_logging( 169 logging_manager.configure_logging(
165 client_logging_config.ClientLoggingConfig(), 170 client_logging_config.ClientLoggingConfig(),
166 results_dir=self.resultdir, 171 results_dir=self.resultdir,
167 verbose=options.verbose) 172 verbose=options.verbose)
168 logging.info('Writing results to %s', self.resultdir) 173 logging.info('Writing results to %s', self.resultdir)
169 174
170 # init_group_level needs the state 175 # init_group_level needs the state
171 self.control = os.path.realpath(control) 176 self.control = os.path.realpath(control)
172 self._is_continuation = options.cont 177 self._is_continuation = options.cont
173 self._current_step_ancestry = [] 178 self._current_step_ancestry = []
174 self._next_step_index = 0 179 self._next_step_index = 0
175 self._load_state() 180 self._load_state()
176 181
177 self.harness = harness.select(options.harness, self) 182 # harness is chosen by following rules:
183 # 1. explicitly specified via command line
184 # 2. harness stored in state file (if continuing job '-c')
185 # 3. default harness
186 selected_harness = None
187 if options.harness:
188 selected_harness = options.harness
189 self._state.set('client', 'harness', selected_harness)
190 else:
191 stored_harness = self._state.get('client', 'harness', None)
192 if stored_harness:
193 selected_harness = stored_harness
194
195 self.harness = harness.select(selected_harness, self)
178 196
179 # set up the status logger 197 # set up the status logger
180 def client_job_record_hook(entry): 198 def client_job_record_hook(entry):
181 msg_tag = '' 199 msg_tag = ''
182 if '.' in self._logger.global_filename: 200 if '.' in self._logger.global_filename:
183 msg_tag = self._logger.global_filename.split('.', 1)[1] 201 msg_tag = self._logger.global_filename.split('.', 1)[1]
184 # send the entry to the job harness 202 # send the entry to the job harness
185 message = '\n'.join([entry.message] + entry.extra_message_lines) 203 message = '\n'.join([entry.message] + entry.extra_message_lines)
186 rendered_entry = self._logger.render_entry(entry) 204 rendered_entry = self._logger.render_entry(entry)
187 self.harness.test_status_detail(entry.status_code, entry.subdir, 205 self.harness.test_status_detail(entry.status_code, entry.subdir,
188 entry.operation, message, msg_tag) 206 entry.operation, message, msg_tag)
189 self.harness.test_status(rendered_entry, msg_tag) 207 self.harness.test_status(rendered_entry, msg_tag)
190 # send the entry to stdout, if it's enabled 208 # send the entry to stdout, if it's enabled
191 logging.info(rendered_entry) 209 logging.info(rendered_entry)
192 self._logger = base_job.status_logger( 210 self._logger = base_job.status_logger(
193 self, status_indenter(self), record_hook=client_job_record_hook) 211 self, status_indenter(self), record_hook=client_job_record_hook,
194 212 tap_writer=self._tap)
195 213
196 def _post_record_init(self, control, options, drop_caches, 214 def _post_record_init(self, control, options, drop_caches,
197 extra_copy_cmdline): 215 extra_copy_cmdline):
198 """ 216 """
199 Perform job initialization not required by self.record(). 217 Perform job initialization not required by self.record().
200 """ 218 """
201 self._init_drop_caches(drop_caches) 219 self._init_drop_caches(drop_caches)
202 220
203 self._init_packages() 221 self._init_packages()
204 222
(...skipping 18 matching lines...) Expand all
223 self.profilers = profilers.profilers(self) 241 self.profilers = profilers.profilers(self)
224 242
225 self._init_bootloader() 243 self._init_bootloader()
226 244
227 self.machines = [options.hostname] 245 self.machines = [options.hostname]
228 self.hosts = set([local_host.LocalHost(hostname=options.hostname, 246 self.hosts = set([local_host.LocalHost(hostname=options.hostname,
229 bootloader=self.bootloader)]) 247 bootloader=self.bootloader)])
230 248
231 self.args = [] 249 self.args = []
232 if options.args: 250 if options.args:
233 self.args = options.args.split() 251 self.args = self._parse_args(options.args)
234 252
235 if options.user: 253 if options.user:
236 self.user = options.user 254 self.user = options.user
237 else: 255 else:
238 self.user = getpass.getuser() 256 self.user = getpass.getuser()
239 257
240 self.sysinfo.log_per_reboot_data() 258 self.sysinfo.log_per_reboot_data()
241 259
242 if not options.cont: 260 if not options.cont:
243 self.record('START', None, None) 261 self.record('START', None, None)
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 function: 593 function:
576 subroutine to run 594 subroutine to run
577 *args: 595 *args:
578 arguments for the function 596 arguments for the function
579 597
580 Returns the result of the passed in function 598 Returns the result of the passed in function
581 """ 599 """
582 600
583 try: 601 try:
584 self.record('START', subdir, testname) 602 self.record('START', subdir, testname)
603 self._state.set('client', 'unexpected_reboot', (subdir, testname))
585 result = function(*args, **dargs) 604 result = function(*args, **dargs)
586 self.record('END GOOD', subdir, testname) 605 self.record('END GOOD', subdir, testname)
587 return result 606 return result
588 except error.TestBaseException, e: 607 except error.TestBaseException, e:
589 self.record('END %s' % e.exit_status, subdir, testname) 608 self.record('END %s' % e.exit_status, subdir, testname)
590 raise 609 raise
591 except error.JobError, e: 610 except error.JobError, e:
592 self.record('END ABORT', subdir, testname) 611 self.record('END ABORT', subdir, testname)
593 raise 612 raise
594 except Exception, e: 613 except Exception, e:
595 # This should only ever happen due to a bug in the given 614 # This should only ever happen due to a bug in the given
596 # function's code. The common case of being called by 615 # function's code. The common case of being called by
597 # run_test() will never reach this. If a control file called 616 # run_test() will never reach this. If a control file called
598 # run_group() itself, bugs in its function will be caught 617 # run_group() itself, bugs in its function will be caught
599 # here. 618 # here.
600 err_msg = str(e) + '\n' + traceback.format_exc() 619 err_msg = str(e) + '\n' + traceback.format_exc()
601 self.record('END ERROR', subdir, testname, err_msg) 620 self.record('END ERROR', subdir, testname, err_msg)
602 raise 621 raise
622 finally:
623 self._state.discard('client', 'unexpected_reboot')
603 624
604 625
605 def run_group(self, function, tag=None, **dargs): 626 def run_group(self, function, tag=None, **dargs):
606 """ 627 """
607 Run a function nested within a group level. 628 Run a function nested within a group level.
608 629
609 function: 630 function:
610 Callable to run. 631 Callable to run.
611 tag: 632 tag:
612 An optional tag name for the group. If None (default) 633 An optional tag name for the group. If None (default)
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
657 @param running_id: An optional running_id to include in the reboot 678 @param running_id: An optional running_id to include in the reboot
658 failure log message 679 failure log message
659 680
660 @raise JobError: Raised if the current configuration does not match the 681 @raise JobError: Raised if the current configuration does not match the
661 pre-reboot configuration. 682 pre-reboot configuration.
662 """ 683 """
663 # check to see if any partitions have changed 684 # check to see if any partitions have changed
664 partition_list = partition_lib.get_partition_list(self, 685 partition_list = partition_lib.get_partition_list(self,
665 exclude_swap=False) 686 exclude_swap=False)
666 mount_info = partition_lib.get_mount_info(partition_list) 687 mount_info = partition_lib.get_mount_info(partition_list)
667
668 old_mount_info = self._state.get('client', 'mount_info') 688 old_mount_info = self._state.get('client', 'mount_info')
669 if mount_info != old_mount_info: 689 if mount_info != old_mount_info:
670 new_entries = mount_info - old_mount_info 690 new_entries = mount_info - old_mount_info
671 old_entries = old_mount_info - mount_info 691 old_entries = old_mount_info - mount_info
672 description = ("mounted partitions are different after reboot " 692 description = ("mounted partitions are different after reboot "
673 "(old entries: %s, new entries: %s)" % 693 "(old entries: %s, new entries: %s)" %
674 (old_entries, new_entries)) 694 (old_entries, new_entries))
675 self._record_reboot_failure(subdir, "reboot.verify_config", 695 self._record_reboot_failure(subdir, "reboot.verify_config",
676 description, running_id=running_id) 696 description, running_id=running_id)
677 raise error.JobError("Reboot failed: %s" % description) 697 raise error.JobError("Reboot failed: %s" % description)
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
863 raise error.JobError(msg) 883 raise error.JobError(msg)
864 884
865 885
866 def quit(self): 886 def quit(self):
867 # XXX: should have a better name. 887 # XXX: should have a better name.
868 self.harness.run_pause() 888 self.harness.run_pause()
869 raise error.JobContinue("more to come") 889 raise error.JobContinue("more to come")
870 890
871 891
872 def complete(self, status): 892 def complete(self, status):
873 """Clean up and exit""" 893 """Write pending TAP reports, clean up, and exit"""
894 # write out TAP reports
895 if self._tap.do_tap_report:
896 self._tap.write()
897 self._tap._write_tap_archive()
898
874 # We are about to exit 'complete' so clean up the control file. 899 # We are about to exit 'complete' so clean up the control file.
875 dest = os.path.join(self.resultdir, os.path.basename(self._state_file)) 900 dest = os.path.join(self.resultdir, os.path.basename(self._state_file))
876 shutil.move(self._state_file, dest) 901 shutil.move(self._state_file, dest)
877 902
878 self.harness.run_complete() 903 self.harness.run_complete()
879 self.disable_external_logging() 904 self.disable_external_logging()
880 sys.exit(status) 905 sys.exit(status)
881 906
882 907
883 def _load_state(self): 908 def _load_state(self):
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
1036 except Exception, detail: 1061 except Exception, detail:
1037 # Syntax errors or other general Python exceptions coming out of 1062 # Syntax errors or other general Python exceptions coming out of
1038 # the top level of the control file itself go through here. 1063 # the top level of the control file itself go through here.
1039 raise error.UnhandledJobError(detail) 1064 raise error.UnhandledJobError(detail)
1040 1065
1041 # If we loaded in a mid-job state file, then we presumably 1066 # If we loaded in a mid-job state file, then we presumably
1042 # know what steps we have yet to run. 1067 # know what steps we have yet to run.
1043 if not self._is_continuation: 1068 if not self._is_continuation:
1044 if 'step_init' in global_control_vars: 1069 if 'step_init' in global_control_vars:
1045 self.next_step(global_control_vars['step_init']) 1070 self.next_step(global_control_vars['step_init'])
1071 else:
1072 # if last job failed due to unexpected reboot, record it as fail
1073 # so harness gets called
1074 last_job = self._state.get('client', 'unexpected_reboot', None)
1075 if last_job:
1076 subdir, testname = last_job
1077 self.record('FAIL', subdir, testname, 'unexpected reboot')
1078 self.record('END FAIL', subdir, testname)
1046 1079
1047 # Iterate through the steps. If we reboot, we'll simply 1080 # Iterate through the steps. If we reboot, we'll simply
1048 # continue iterating on the next step. 1081 # continue iterating on the next step.
1049 while len(self._state.get('client', 'steps')) > 0: 1082 while len(self._state.get('client', 'steps')) > 0:
1050 steps = self._state.get('client', 'steps') 1083 steps = self._state.get('client', 'steps')
1051 (ancestry, fn_name, args, dargs) = steps.pop(0) 1084 (ancestry, fn_name, args, dargs) = steps.pop(0)
1052 self._state.set('client', 'steps', steps) 1085 self._state.set('client', 'steps', steps)
1053 1086
1054 self._next_step_index = 0 1087 self._next_step_index = 0
1055 ret = self._create_frame(global_control_vars, ancestry, fn_name) 1088 ret = self._create_frame(global_control_vars, ancestry, fn_name)
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
1175 # 2) define steps, and select the first via next_step() 1208 # 2) define steps, and select the first via next_step()
1176 myjob.step_engine() 1209 myjob.step_engine()
1177 1210
1178 except error.JobContinue: 1211 except error.JobContinue:
1179 sys.exit(5) 1212 sys.exit(5)
1180 1213
1181 except error.JobComplete: 1214 except error.JobComplete:
1182 sys.exit(1) 1215 sys.exit(1)
1183 1216
1184 except error.JobError, instance: 1217 except error.JobError, instance:
1185 logging.error("JOB ERROR: " + instance.args[0]) 1218 logging.error("JOB ERROR: " + str(instance))
1186 if myjob: 1219 if myjob:
1187 command = None 1220 command = None
1188 if len(instance.args) > 1: 1221 if len(instance.args) > 1:
1189 command = instance.args[1] 1222 command = instance.args[1]
1190 myjob.record('ABORT', None, command, instance.args[0]) 1223 myjob.record('ABORT', None, command, str(instance))
1191 myjob.record('END ABORT', None, None, instance.args[0]) 1224 myjob.record('END ABORT', None, None, str(instance))
1192 assert myjob._record_indent == 0 1225 assert myjob._record_indent == 0
1193 myjob.complete(1) 1226 myjob.complete(1)
1194 else: 1227 else:
1195 sys.exit(1) 1228 sys.exit(1)
1196 1229
1197 except Exception, e: 1230 except Exception, e:
1198 # NOTE: job._run_step_fn and job.step_engine will turn things into 1231 # NOTE: job._run_step_fn and job.step_engine will turn things into
1199 # a JobError for us. If we get here, its likely an autotest bug. 1232 # a JobError for us. If we get here, its likely an autotest bug.
1200 msg = str(e) + '\n' + traceback.format_exc() 1233 msg = str(e) + '\n' + traceback.format_exc()
1201 logging.critical("JOB ERROR (autotest bug?): " + msg) 1234 logging.critical("JOB ERROR (autotest bug?): " + msg)
1202 if myjob: 1235 if myjob:
1203 myjob.record('END ABORT', None, None, msg) 1236 myjob.record('END ABORT', None, None, msg)
1204 assert myjob._record_indent == 0 1237 assert myjob._record_indent == 0
1205 myjob.complete(1) 1238 myjob.complete(1)
1206 else: 1239 else:
1207 sys.exit(1) 1240 sys.exit(1)
1208 1241
1209 # If we get here, then we assume the job is complete and good. 1242 # If we get here, then we assume the job is complete and good.
1210 myjob.record('END GOOD', None, None) 1243 myjob.record('END GOOD', None, None)
1211 assert myjob._record_indent == 0 1244 assert myjob._record_indent == 0
1212 1245
1213 myjob.complete(0) 1246 myjob.complete(0)
1214 1247
1215 1248
1216 site_job = utils.import_site_class( 1249 site_job = utils.import_site_class(
1217 __file__, "autotest_lib.client.bin.site_job", "site_job", base_client_job) 1250 __file__, "autotest_lib.client.bin.site_job", "site_job", base_client_job)
1218 1251
1219 class job(site_job): 1252 class job(site_job):
1220 pass 1253 pass
OLDNEW
« cli/job.py ('K') | « client/bin/harness.py ('k') | client/bin/job_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698