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