blob: 51ec9c05e3e08ebbcfa11cccd3f644d380818101 [file] [log] [blame]
jamesrenc3940222010-02-19 21:57:37 +00001import common
2import os
3from autotest_lib.frontend.afe import models as afe_models, model_logic
jamesren3e9f6092010-03-11 21:32:10 +00004from autotest_lib.frontend.planner import models, model_attributes
jamesren4be631f2010-04-08 23:01:22 +00005from autotest_lib.frontend.planner import failure_actions
jamesren62758242010-04-28 18:08:25 +00006from autotest_lib.frontend.tko import models as tko_models
jamesren0cde1eb2010-04-09 20:45:49 +00007from autotest_lib.client.common_lib import global_config, utils, global_config
jamesrenc3940222010-02-19 21:57:37 +00008
9
10PLANNER_LABEL_PREFIX = 'planner_'
11PLANNER_ATOMIC_GROUP_NAME = 'planner_global_atomic_group'
12SERVER = global_config.global_config.get_config_value('SERVER', 'hostname')
13LAZY_LOADED_FILES = {}
14
15
16def create_plan_label(plan):
17 """
18 Creates the host label to apply on the plan hosts
19 """
20 group, _ = afe_models.AtomicGroup.objects.get_or_create(
21 name=PLANNER_ATOMIC_GROUP_NAME)
22 if group.invalid:
23 group.invalid = False
24 group.save()
25
26 name = PLANNER_LABEL_PREFIX + plan.name
27 if bool(afe_models.Label.valid_objects.filter(name=name)):
28 raise model_logic.ValidationError('Label %s already exists, '
29 'cannot start plan' % name)
30 label = afe_models.Label(name=name, atomic_group=group)
31 label.save()
32
33 return label
34
35
36def start_plan(plan, label):
37 """
38 Takes the necessary steps to start a test plan in Autotest
39 """
jamesren0cde1eb2010-04-09 20:45:49 +000040 timeout = global_config.global_config.get_config_value(
41 'PLANNER', 'execution_engine_timeout')
jamesrene38a0a72010-04-19 18:05:31 +000042 control = _get_execution_engine_control(
43 server=SERVER,
44 plan_id=plan.id,
45 label_name=label.name,
46 owner=afe_models.User.current_user().login)
jamesren3e9f6092010-03-11 21:32:10 +000047 options = {'name': plan.name + '_execution_engine',
48 'priority': afe_models.Job.Priority.MEDIUM,
jamesren30f06c72010-04-12 18:23:56 +000049 'control_file': control,
jamesren3e9f6092010-03-11 21:32:10 +000050 'control_type': afe_models.Job.ControlType.SERVER,
51 'synch_count': None,
jamesren0cde1eb2010-04-09 20:45:49 +000052 'timeout': timeout,
jamesren16e1bbb2010-04-12 18:23:46 +000053 'max_runtime_hrs': timeout,
jamesren3e9f6092010-03-11 21:32:10 +000054 'run_verify': False,
55 'reboot_before': False,
56 'reboot_after': False,
jamesrenab9e11b2010-04-16 23:44:51 +000057 'dependencies': ()}
jamesren3e9f6092010-03-11 21:32:10 +000058 job = afe_models.Job.create(owner=afe_models.User.current_user().login,
59 options=options, hosts=())
60 job.queue(hosts=())
jamesrenc3940222010-02-19 21:57:37 +000061
62
jamesrene38a0a72010-04-19 18:05:31 +000063def _get_execution_engine_control(server, plan_id, label_name, owner):
jamesrenc3940222010-02-19 21:57:37 +000064 """
65 Gets the control file to run the execution engine
66 """
jamesren30f06c72010-04-12 18:23:56 +000067 control = lazy_load(os.path.join(os.path.dirname(__file__),
68 'execution_engine_control.srv'))
jamesrene38a0a72010-04-19 18:05:31 +000069 return control % dict(server=server, plan_id=plan_id,
70 label_name=label_name, owner=owner)
jamesrenc3940222010-02-19 21:57:37 +000071
72
73def lazy_load(path):
74 """
75 Lazily loads the file indicated by the path given, and caches the result
76 """
77 if path not in LAZY_LOADED_FILES:
78 LAZY_LOADED_FILES[path] = utils.read_file(path)
79
80 return LAZY_LOADED_FILES[path]
jamesren3e9f6092010-03-11 21:32:10 +000081
82
83def update_hosts_table(plan):
84 """
85 Resolves the host labels into host objects
86
87 Adds or removes hosts from the planner Hosts model based on changes to the
88 host label
89 """
90 label_hosts = set()
91
92 for label in plan.host_labels.all():
93 for afe_host in label.host_set.all():
94 host, created = models.Host.objects.get_or_create(plan=plan,
95 host=afe_host)
96 if created:
97 host.added_by_label = True
98 host.save()
99
100 label_hosts.add(host.host.id)
101
102 deleted_hosts = models.Host.objects.filter(
103 plan=plan, added_by_label=True).exclude(host__id__in=label_hosts)
104 deleted_hosts.delete()
105
106
107def compute_next_test_config(plan, host):
108 """
109 Gets the next test config that should be run for this plan and host
110
111 Returns None if the host is already running a job. Also sets the host's
112 complete bit if the host is finished running tests.
113 """
114 if host.blocked:
115 return None
116
117 test_configs = plan.testconfig_set.order_by('execution_order')
118 for test_config in test_configs:
119 afe_jobs = plan.job_set.filter(test_config=test_config)
120 afe_job_ids = afe_jobs.values_list('afe_job', flat=True)
121 hqes = afe_models.HostQueueEntry.objects.filter(job__id__in=afe_job_ids,
122 host=host.host)
jamesrendbeebf82010-04-08 22:58:26 +0000123 if not hqes and not bool(test_config.skipped_hosts.filter(host=host)):
124 return test_config
jamesren3e9f6092010-03-11 21:32:10 +0000125 for hqe in hqes:
126 if not hqe.complete:
127 # HostQueueEntry still active for this host,
128 # should not run another test
129 return None
130
131 # All HQEs related to this host are complete
132 host.complete = True
133 host.save()
134 return None
135
136
137def check_for_completion(plan):
138 """
139 Checks if a plan is actually complete. Sets complete=True if so
140 """
141 if not models.Host.objects.filter(plan=plan, complete=False):
142 plan.complete = True
143 plan.save()
144
145
146def compute_test_run_status(status):
147 """
148 Converts a TKO test status to a Planner test run status
149 """
150 Status = model_attributes.TestRunStatus
151 if status == 'GOOD':
152 return Status.PASSED
153 if status == 'RUNNING':
154 return Status.ACTIVE
155 return Status.FAILED
156
157
158def add_test_run(plan, planner_job, tko_test, hostname, status):
159 """
160 Adds a TKO test to the Planner Test Run tables
161 """
162 host = afe_models.Host.objects.get(hostname=hostname)
163
164 planner_host = models.Host.objects.get(plan=plan, host=host)
165 test_run, _ = models.TestRun.objects.get_or_create(plan=plan,
166 test_job=planner_job,
167 tko_test=tko_test,
168 host=planner_host)
169 test_run.status = status
170 test_run.save()
jamesren4be631f2010-04-08 23:01:22 +0000171
172
jamesren62758242010-04-28 18:08:25 +0000173def process_failure(failure_id, host_action, test_action, labels, keyvals,
174 bugs, reason, invalidate):
175 if keyvals is None:
176 keyvals = {}
177
178 failure = models.TestRun.objects.get(id=failure_id)
179
180 _process_host_action(failure.host, host_action)
181 _process_test_action(failure.test_job, test_action)
182
183 # Add the test labels
184 for label in labels:
185 tko_test_label, _ = (
186 tko_models.TestLabel.objects.get_or_create(name=label))
187 failure.tko_test.testlabel_set.add(tko_test_label)
188
189 # Set the job keyvals
190 for key, value in keyvals.iteritems():
191 keyval, created = tko_models.JobKeyval.objects.get_or_create(
192 job=failure.tko_test.job, key=key)
193 if not created:
194 tko_models.JobKeyval.objects.create(job=failure.tko_test.job,
195 key='original_' + key,
196 value=keyval.value)
197 keyval.value = value
198 keyval.save()
199
200 # Add the bugs
201 for bug_id in bugs:
202 bug, _ = models.Bug.objects.get_or_create(external_uid=bug_id)
203 failure.bugs.add(bug)
204
205 # Set the failure reason
206 if reason is not None:
207 tko_models.TestAttribute.objects.create(test=failure.tko_test,
208 attribute='original_reason',
209 value=failure.tko_test.reason)
210 failure.tko_test.reason = reason
211 failure.tko_test.save()
212
213 # Set 'invalidated', 'seen', and 'triaged'
214 failure.invalidated = invalidate
215 failure.seen = True
216 failure.triaged = True
217 failure.save()
218
219
jamesren4be631f2010-04-08 23:01:22 +0000220def _site_process_host_action_dummy(host, action):
221 return False
222
223
jamesren62758242010-04-28 18:08:25 +0000224def _process_host_action(host, action):
jamesren4be631f2010-04-08 23:01:22 +0000225 """
226 Takes the specified action on the host
227 """
228 HostAction = failure_actions.HostAction
229 if action not in HostAction.values:
230 raise ValueError('Unexpected host action %s' % action)
231
232 site_process = utils.import_site_function(
233 __file__, 'autotest_lib.frontend.planner.site_rpc_utils',
234 'site_process_host_action', _site_process_host_action_dummy)
235
236 if not site_process(host, action):
237 # site_process_host_action returns True and and only if it matched a
238 # site-specific processing option
239 if action == HostAction.BLOCK:
240 host.blocked = True
241 elif action == HostAction.UNBLOCK:
242 host.blocked = False
243 else:
244 assert action == HostAction.REINSTALL
245 raise NotImplemented('TODO: implement reinstall')
246
247 host.save()
248
249
jamesren62758242010-04-28 18:08:25 +0000250def _process_test_action(planner_job, action):
jamesren4be631f2010-04-08 23:01:22 +0000251 """
252 Takes the specified action for this planner job
253 """
254 TestAction = failure_actions.TestAction
255 if action not in TestAction.values:
256 raise ValueError('Unexpected test action %s' % action)
257
258 if action == TestAction.SKIP:
259 # Do nothing
260 pass
261 else:
262 assert action == TestAction.RERUN
263 planner_job.requires_rerun = True
264 planner_job.save()