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