blob: 71959210be00a77d25c3b49f40b4777206616338 [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
jamesren0cde1eb2010-04-09 20:45:49 +00006from autotest_lib.client.common_lib import global_config, utils, global_config
jamesrenc3940222010-02-19 21:57:37 +00007
8
9PLANNER_LABEL_PREFIX = 'planner_'
10PLANNER_ATOMIC_GROUP_NAME = 'planner_global_atomic_group'
11SERVER = global_config.global_config.get_config_value('SERVER', 'hostname')
12LAZY_LOADED_FILES = {}
13
14
15def create_plan_label(plan):
16 """
17 Creates the host label to apply on the plan hosts
18 """
19 group, _ = afe_models.AtomicGroup.objects.get_or_create(
20 name=PLANNER_ATOMIC_GROUP_NAME)
21 if group.invalid:
22 group.invalid = False
23 group.save()
24
25 name = PLANNER_LABEL_PREFIX + plan.name
26 if bool(afe_models.Label.valid_objects.filter(name=name)):
27 raise model_logic.ValidationError('Label %s already exists, '
28 'cannot start plan' % name)
29 label = afe_models.Label(name=name, atomic_group=group)
30 label.save()
31
32 return label
33
34
35def start_plan(plan, label):
36 """
37 Takes the necessary steps to start a test plan in Autotest
38 """
jamesren0cde1eb2010-04-09 20:45:49 +000039 timeout = global_config.global_config.get_config_value(
40 'PLANNER', 'execution_engine_timeout')
jamesrene38a0a72010-04-19 18:05:31 +000041 control = _get_execution_engine_control(
42 server=SERVER,
43 plan_id=plan.id,
44 label_name=label.name,
45 owner=afe_models.User.current_user().login)
jamesren3e9f6092010-03-11 21:32:10 +000046 options = {'name': plan.name + '_execution_engine',
47 'priority': afe_models.Job.Priority.MEDIUM,
jamesren30f06c72010-04-12 18:23:56 +000048 'control_file': control,
jamesren3e9f6092010-03-11 21:32:10 +000049 'control_type': afe_models.Job.ControlType.SERVER,
50 'synch_count': None,
jamesren0cde1eb2010-04-09 20:45:49 +000051 'timeout': timeout,
jamesren16e1bbb2010-04-12 18:23:46 +000052 'max_runtime_hrs': timeout,
jamesren3e9f6092010-03-11 21:32:10 +000053 'run_verify': False,
54 'reboot_before': False,
55 'reboot_after': False,
jamesrenab9e11b2010-04-16 23:44:51 +000056 'dependencies': ()}
jamesren3e9f6092010-03-11 21:32:10 +000057 job = afe_models.Job.create(owner=afe_models.User.current_user().login,
58 options=options, hosts=())
59 job.queue(hosts=())
jamesrenc3940222010-02-19 21:57:37 +000060
61
jamesrene38a0a72010-04-19 18:05:31 +000062def _get_execution_engine_control(server, plan_id, label_name, owner):
jamesrenc3940222010-02-19 21:57:37 +000063 """
64 Gets the control file to run the execution engine
65 """
jamesren30f06c72010-04-12 18:23:56 +000066 control = lazy_load(os.path.join(os.path.dirname(__file__),
67 'execution_engine_control.srv'))
jamesrene38a0a72010-04-19 18:05:31 +000068 return control % dict(server=server, plan_id=plan_id,
69 label_name=label_name, owner=owner)
jamesrenc3940222010-02-19 21:57:37 +000070
71
72def lazy_load(path):
73 """
74 Lazily loads the file indicated by the path given, and caches the result
75 """
76 if path not in LAZY_LOADED_FILES:
77 LAZY_LOADED_FILES[path] = utils.read_file(path)
78
79 return LAZY_LOADED_FILES[path]
jamesren3e9f6092010-03-11 21:32:10 +000080
81
82def update_hosts_table(plan):
83 """
84 Resolves the host labels into host objects
85
86 Adds or removes hosts from the planner Hosts model based on changes to the
87 host label
88 """
89 label_hosts = set()
90
91 for label in plan.host_labels.all():
92 for afe_host in label.host_set.all():
93 host, created = models.Host.objects.get_or_create(plan=plan,
94 host=afe_host)
95 if created:
96 host.added_by_label = True
97 host.save()
98
99 label_hosts.add(host.host.id)
100
101 deleted_hosts = models.Host.objects.filter(
102 plan=plan, added_by_label=True).exclude(host__id__in=label_hosts)
103 deleted_hosts.delete()
104
105
106def compute_next_test_config(plan, host):
107 """
108 Gets the next test config that should be run for this plan and host
109
110 Returns None if the host is already running a job. Also sets the host's
111 complete bit if the host is finished running tests.
112 """
113 if host.blocked:
114 return None
115
116 test_configs = plan.testconfig_set.order_by('execution_order')
117 for test_config in test_configs:
118 afe_jobs = plan.job_set.filter(test_config=test_config)
119 afe_job_ids = afe_jobs.values_list('afe_job', flat=True)
120 hqes = afe_models.HostQueueEntry.objects.filter(job__id__in=afe_job_ids,
121 host=host.host)
jamesrendbeebf82010-04-08 22:58:26 +0000122 if not hqes and not bool(test_config.skipped_hosts.filter(host=host)):
123 return test_config
jamesren3e9f6092010-03-11 21:32:10 +0000124 for hqe in hqes:
125 if not hqe.complete:
126 # HostQueueEntry still active for this host,
127 # should not run another test
128 return None
129
130 # All HQEs related to this host are complete
131 host.complete = True
132 host.save()
133 return None
134
135
136def check_for_completion(plan):
137 """
138 Checks if a plan is actually complete. Sets complete=True if so
139 """
140 if not models.Host.objects.filter(plan=plan, complete=False):
141 plan.complete = True
142 plan.save()
143
144
145def compute_test_run_status(status):
146 """
147 Converts a TKO test status to a Planner test run status
148 """
149 Status = model_attributes.TestRunStatus
150 if status == 'GOOD':
151 return Status.PASSED
152 if status == 'RUNNING':
153 return Status.ACTIVE
154 return Status.FAILED
155
156
157def add_test_run(plan, planner_job, tko_test, hostname, status):
158 """
159 Adds a TKO test to the Planner Test Run tables
160 """
161 host = afe_models.Host.objects.get(hostname=hostname)
162
163 planner_host = models.Host.objects.get(plan=plan, host=host)
164 test_run, _ = models.TestRun.objects.get_or_create(plan=plan,
165 test_job=planner_job,
166 tko_test=tko_test,
167 host=planner_host)
168 test_run.status = status
169 test_run.save()
jamesren4be631f2010-04-08 23:01:22 +0000170
171
172def _site_process_host_action_dummy(host, action):
173 return False
174
175
176def process_host_action(host, action):
177 """
178 Takes the specified action on the host
179 """
180 HostAction = failure_actions.HostAction
181 if action not in HostAction.values:
182 raise ValueError('Unexpected host action %s' % action)
183
184 site_process = utils.import_site_function(
185 __file__, 'autotest_lib.frontend.planner.site_rpc_utils',
186 'site_process_host_action', _site_process_host_action_dummy)
187
188 if not site_process(host, action):
189 # site_process_host_action returns True and and only if it matched a
190 # site-specific processing option
191 if action == HostAction.BLOCK:
192 host.blocked = True
193 elif action == HostAction.UNBLOCK:
194 host.blocked = False
195 else:
196 assert action == HostAction.REINSTALL
197 raise NotImplemented('TODO: implement reinstall')
198
199 host.save()
200
201
202def process_test_action(planner_job, action):
203 """
204 Takes the specified action for this planner job
205 """
206 TestAction = failure_actions.TestAction
207 if action not in TestAction.values:
208 raise ValueError('Unexpected test action %s' % action)
209
210 if action == TestAction.SKIP:
211 # Do nothing
212 pass
213 else:
214 assert action == TestAction.RERUN
215 planner_job.requires_rerun = True
216 planner_job.save()