blob: 0b58e79d07770fa01d914dd42873247761fc77ea [file] [log] [blame]
showard26b7ec72009-12-21 22:43:57 +00001from django.db import models as dbmodels
2import common
3from autotest_lib.frontend.afe import models as afe_models
4from autotest_lib.frontend.afe import model_logic, rpc_utils
showard250d84d2010-01-12 21:59:48 +00005from autotest_lib.frontend.tko import models as tko_models
jamesren3e9f6092010-03-11 21:32:10 +00006from autotest_lib.frontend.planner import model_attributes
7from autotest_lib.client.common_lib import utils
showard26b7ec72009-12-21 22:43:57 +00008
9
jamesrenc3940222010-02-19 21:57:37 +000010class Plan(dbmodels.Model, model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +000011 """A test plan
12
13 Required:
14 name: Plan name, unique
15 complete: True if the plan is completed
16 dirty: True if the plan has been changed since the execution engine has
17 last seen it
18 initialized: True if the plan has started
19
20 Optional:
21 label_override: A label to apply to each Autotest job.
jamesrendbeebf82010-04-08 22:58:26 +000022 support: The global support script to apply to this plan
showard26b7ec72009-12-21 22:43:57 +000023 """
24 name = dbmodels.CharField(max_length=255, unique=True)
25 label_override = dbmodels.CharField(max_length=255, null=True, blank=True)
26 support = dbmodels.TextField(blank=True)
27 complete = dbmodels.BooleanField(default=False)
28 dirty = dbmodels.BooleanField(default=False)
29 initialized = dbmodels.BooleanField(default=False)
30
31 owners = dbmodels.ManyToManyField(afe_models.User,
32 db_table='planner_plan_owners')
33 hosts = dbmodels.ManyToManyField(afe_models.Host, through='Host')
jamesrenc3940222010-02-19 21:57:37 +000034 host_labels = dbmodels.ManyToManyField(afe_models.Label,
35 db_table='planner_plan_host_labels')
36
37 name_field = 'name'
showard26b7ec72009-12-21 22:43:57 +000038
39 class Meta:
40 db_table = 'planner_plans'
41
42
43 def __unicode__(self):
44 return unicode(self.name)
45
46
47class ModelWithPlan(dbmodels.Model):
48 """Superclass for models that have a plan_id
49
50 Required:
51 plan: The associated test plan
52 """
53 plan = dbmodels.ForeignKey(Plan)
54
55 class Meta:
56 abstract = True
57
58
59 def __unicode__(self):
60 return u'%s (%s)' % (self._get_details_unicode(), self.plan.name)
61
62
63 def _get_details_unicode(self):
64 """Gets the first part of the unicode string
65
66 subclasses must override this method
67 """
68 raise NotImplementedError(
69 'Subclasses must override _get_details_unicode()')
70
71
jamesrenc3940222010-02-19 21:57:37 +000072class Host(ModelWithPlan, model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +000073 """A plan host
74
75 Required:
76 host: The AFE host
77 complete: True if and only if this host is finished in the test plan
78 blocked: True if and only if the host is blocked (not executing tests)
jamesren3e9f6092010-03-11 21:32:10 +000079 added_by_label: True if and only if the host was added because of a host
80 label (as opposed to being explicitly added)
showard26b7ec72009-12-21 22:43:57 +000081 """
82 host = dbmodels.ForeignKey(afe_models.Host)
83 complete = dbmodels.BooleanField(default=False)
84 blocked = dbmodels.BooleanField(default=False)
jamesren3e9f6092010-03-11 21:32:10 +000085 added_by_label = dbmodels.BooleanField(default=False)
jamesrenc3940222010-02-19 21:57:37 +000086
showard26b7ec72009-12-21 22:43:57 +000087 class Meta:
88 db_table = 'planner_hosts'
89
90
jamesrenc3940222010-02-19 21:57:37 +000091 def status(self):
92 if self.complete:
jamesren3e9f6092010-03-11 21:32:10 +000093 return model_attributes.HostStatus.FINISHED
jamesrenc3940222010-02-19 21:57:37 +000094 if self.blocked:
jamesren3e9f6092010-03-11 21:32:10 +000095 return model_attributes.HostStatus.BLOCKED
96 return model_attributes.HostStatus.RUNNING
jamesrenc3940222010-02-19 21:57:37 +000097
98
showard26b7ec72009-12-21 22:43:57 +000099 def _get_details_unicode(self):
jamesren128d4182010-02-19 21:57:58 +0000100 return 'Host: %s' % self.host.hostname
showard26b7ec72009-12-21 22:43:57 +0000101
102
jamesren3e9f6092010-03-11 21:32:10 +0000103class ControlFile(model_logic.ModelWithHash,
104 model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +0000105 """A control file. Immutable once added to the table
106
107 Required:
108 contents: The text of the control file
109
110 Others:
jamesren3e9f6092010-03-11 21:32:10 +0000111 the_hash: The SHA1 hash of the control file, for duplicate detection
112 and fast search
showard26b7ec72009-12-21 22:43:57 +0000113 """
114 contents = dbmodels.TextField()
115
116 class Meta:
117 db_table = 'planner_test_control_files'
118
119
120 @classmethod
121 def _compute_hash(cls, **kwargs):
lmr6f80e7a2010-02-04 03:18:28 +0000122 return utils.hash('sha1', kwargs['contents']).hexdigest()
showard26b7ec72009-12-21 22:43:57 +0000123
124
125 def __unicode__(self):
126 return u'Control file id %s (SHA1: %s)' % (self.id, self.control_hash)
127
128
jamesren3e9f6092010-03-11 21:32:10 +0000129class TestConfig(ModelWithPlan, model_logic.ModelExtensions):
jamesrenb852bce2010-04-07 20:36:13 +0000130 """A configuration for a planned test
showard26b7ec72009-12-21 22:43:57 +0000131
132 Required:
jamesrenc3940222010-02-19 21:57:37 +0000133 alias: The name to give this test within the plan. Unique with plan id
showard26b7ec72009-12-21 22:43:57 +0000134 test_control_file: The control file to run
jamesren3e9f6092010-03-11 21:32:10 +0000135 is_server: True if this control file is a server-side test
showard26b7ec72009-12-21 22:43:57 +0000136 execution_order: An integer describing when this test should be run in
137 the test plan
jamesrenc3940222010-02-19 21:57:37 +0000138 estimated_runtime: Time in hours that the test is expected to run. Will
139 be automatically generated (on the frontend) for
140 tests in Autotest.
jamesrendbeebf82010-04-08 22:58:26 +0000141 skipped_hosts: Hosts that are going to skip this test.
showard26b7ec72009-12-21 22:43:57 +0000142 """
jamesrenc3940222010-02-19 21:57:37 +0000143 alias = dbmodels.CharField(max_length=255)
showard26b7ec72009-12-21 22:43:57 +0000144 control_file = dbmodels.ForeignKey(ControlFile)
jamesren3e9f6092010-03-11 21:32:10 +0000145 is_server = dbmodels.BooleanField(default=True)
showard26b7ec72009-12-21 22:43:57 +0000146 execution_order = dbmodels.IntegerField(blank=True)
jamesrenc3940222010-02-19 21:57:37 +0000147 estimated_runtime = dbmodels.IntegerField()
jamesrendbeebf82010-04-08 22:58:26 +0000148 skipped_hosts = dbmodels.ManyToManyField(
149 afe_models.Host, db_table='planner_test_configs_skipped_hosts')
showard26b7ec72009-12-21 22:43:57 +0000150
151 class Meta:
jamesren3e9f6092010-03-11 21:32:10 +0000152 db_table = 'planner_test_configs'
showard26b7ec72009-12-21 22:43:57 +0000153 ordering = ('execution_order',)
jamesrenc3940222010-02-19 21:57:37 +0000154 unique_together = (('plan', 'alias'),)
showard26b7ec72009-12-21 22:43:57 +0000155
156
157 def _get_details_unicode(self):
jamesren3e9f6092010-03-11 21:32:10 +0000158 return 'Planned test config - Control file id %s' % self.control_file.id
showard26b7ec72009-12-21 22:43:57 +0000159
160
jamesren3e9f6092010-03-11 21:32:10 +0000161class Job(ModelWithPlan, model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +0000162 """Represents an Autotest job initiated for a test plan
163
164 Required:
jamesren3e9f6092010-03-11 21:32:10 +0000165 test: The TestConfig associated with this Job
showard26b7ec72009-12-21 22:43:57 +0000166 afe_job: The Autotest job
167 """
jamesren3e9f6092010-03-11 21:32:10 +0000168 test_config = dbmodels.ForeignKey(TestConfig)
showard26b7ec72009-12-21 22:43:57 +0000169 afe_job = dbmodels.ForeignKey(afe_models.Job)
jamesren4be631f2010-04-08 23:01:22 +0000170 requires_rerun = dbmodels.BooleanField(default=False)
showard26b7ec72009-12-21 22:43:57 +0000171
172 class Meta:
173 db_table = 'planner_test_jobs'
174
175
176 def _get_details_unicode(self):
jamesren128d4182010-02-19 21:57:58 +0000177 return 'AFE job %s' % self.afe_job.id
showard26b7ec72009-12-21 22:43:57 +0000178
179
180class Bug(dbmodels.Model):
181 """Represents a bug ID
182
183 Required:
184 external_uid: External unique ID for the bug
185 """
186 external_uid = dbmodels.CharField(max_length=255, unique=True)
187
188 class Meta:
189 db_table = 'planner_bugs'
190
191
192 def __unicode__(self):
193 return u'Bug external ID %s' % self.external_uid
194
195
jamesren3e9f6092010-03-11 21:32:10 +0000196class TestRun(ModelWithPlan, model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +0000197 """An individual test run from an Autotest job for the test plan.
198
199 Each Job object may have multiple TestRun objects associated with it.
200
201 Required:
202 test_job: The Job object associated with this TestRun
203 tko_test: The TKO Test associated with this TestRun
204 status: One of 'Active', 'Passed', 'Failed'
205 finalized: True if and only if the TestRun is ready to be shown in
206 triage
207 invalidated: True if and only if a user has decided to invalidate this
208 TestRun's results
209 seen: True if and only if a user has marked this TestRun as "seen"
210 triaged: True if and only if the TestRun no longer requires any user
211 intervention
212
213 Optional:
214 bugs: Bugs filed that a relevant to this run
215 """
showard26b7ec72009-12-21 22:43:57 +0000216 test_job = dbmodels.ForeignKey(Job)
217 tko_test = dbmodels.ForeignKey(tko_models.Test)
jamesrenc3940222010-02-19 21:57:37 +0000218 host = dbmodels.ForeignKey(Host)
jamesren3e9f6092010-03-11 21:32:10 +0000219 status = dbmodels.CharField(
220 max_length=16,
221 choices=model_attributes.TestRunStatus.choices(),
222 default=model_attributes.TestRunStatus.ACTIVE)
showard26b7ec72009-12-21 22:43:57 +0000223 finalized = dbmodels.BooleanField(default=False)
224 seen = dbmodels.BooleanField(default=False)
225 triaged = dbmodels.BooleanField(default=False)
jamesren4be631f2010-04-08 23:01:22 +0000226 invalidated = dbmodels.BooleanField(default=False)
showard26b7ec72009-12-21 22:43:57 +0000227
228 bugs = dbmodels.ManyToManyField(Bug, null=True,
229 db_table='planner_test_run_bugs')
230
231 class Meta:
232 db_table = 'planner_test_runs'
jamesren3e9f6092010-03-11 21:32:10 +0000233 unique_together = (('plan', 'test_job', 'tko_test', 'host'),)
showard26b7ec72009-12-21 22:43:57 +0000234
235
236 def _get_details_unicode(self):
237 return 'Test Run: %s' % self.id
238
239
240class DataType(dbmodels.Model):
241 """Encodes the data model types
242
243 For use in the history table, to identify the type of object that was
244 changed.
245
246 Required:
247 name: The name of the data type
248 db_table: The name of the database table that stores this type
249 """
250 name = dbmodels.CharField(max_length=255)
251 db_table = dbmodels.CharField(max_length=255)
252
253 class Meta:
254 db_table = 'planner_data_types'
255
256
257 def __unicode__(self):
258 return u'Data type %s (stored in table %s)' % (self.name, self.db_table)
259
260
261class History(ModelWithPlan):
262 """Represents a history action
263
264 Required:
265 action_id: An arbitrary ID that uniquely identifies the user action
266 related to the history entry. One user action may result in
267 multiple history entries
268 user: The user who initiated the change
269 data_type: The type of object that was changed
270 object_id: Value of the primary key field for the changed object
271 old_object_repr: A string representation of the object before the change
272 new_object_repr: A string representation of the object after the change
273
274 Others:
275 time: A timestamp. Automatically generated.
276 """
277 action_id = dbmodels.IntegerField()
278 user = dbmodels.ForeignKey(afe_models.User)
279 data_type = dbmodels.ForeignKey(DataType)
280 object_id = dbmodels.IntegerField()
281 old_object_repr = dbmodels.TextField(blank=True)
282 new_object_repr = dbmodels.TextField(blank=True)
283
284 time = dbmodels.DateTimeField(auto_now_add=True)
285
286 class Meta:
287 db_table = 'planner_history'
288
289
290 def _get_details_unicode(self):
291 return 'History entry: %s => %s' % (self.old_object_repr,
292 self.new_object_repr)
293
294
295class SavedObject(dbmodels.Model):
296 """A saved object that can be recalled at certain points in the UI
297
298 Required:
299 user: The creator of the object
300 object_type: One of 'support', 'triage', 'autoprocess', 'custom_query'
301 name: The name given to the object
302 encoded_object: The actual object
303 """
showard26b7ec72009-12-21 22:43:57 +0000304 user = dbmodels.ForeignKey(afe_models.User)
jamesren3e9f6092010-03-11 21:32:10 +0000305 object_type = dbmodels.CharField(
306 max_length=16,
307 choices=model_attributes.SavedObjectType.choices(),
308 db_column='type')
showard26b7ec72009-12-21 22:43:57 +0000309 name = dbmodels.CharField(max_length=255)
310 encoded_object = dbmodels.TextField()
311
312 class Meta:
313 db_table = 'planner_saved_objects'
314 unique_together = ('user', 'object_type', 'name')
315
316
317 def __unicode__(self):
318 return u'Saved %s object: %s, by %s' % (self.object_type, self.name,
319 self.user.login)
320
321
322class CustomQuery(ModelWithPlan):
323 """A custom SQL query for the triage page
324
325 Required:
326 query: the SQL WHERE clause to attach to the main query
327 """
328 query = dbmodels.TextField()
329
330 class Meta:
331 db_table = 'planner_custom_queries'
332
333
334 def _get_details_unicode(self):
335 return 'Custom Query: %s' % self.query
336
337
338class KeyVal(model_logic.ModelWithHash):
339 """Represents a keyval. Immutable once added to the table.
340
341 Required:
342 key: The key
343 value: The value
344
345 Others:
jamesren3e9f6092010-03-11 21:32:10 +0000346 the_hash: The result of SHA1(SHA1(key) ++ value), for duplicate
347 detection and fast search.
showard26b7ec72009-12-21 22:43:57 +0000348 """
349 key = dbmodels.CharField(max_length=1024)
350 value = dbmodels.CharField(max_length=1024)
351
352 class Meta:
353 db_table = 'planner_keyvals'
354
355
356 @classmethod
357 def _compute_hash(cls, **kwargs):
lmr6f80e7a2010-02-04 03:18:28 +0000358 round1 = utils.hash('sha1', kwargs['key']).hexdigest()
359 return utils.hash('sha1', round1 + kwargs['value']).hexdigest()
showard26b7ec72009-12-21 22:43:57 +0000360
361
362 def __unicode__(self):
363 return u'Keyval: %s = %s' % (self.key, self.value)
364
365
366class AutoProcess(ModelWithPlan):
367 """An autoprocessing directive to perform on test runs that enter triage
368
369 Required:
370 condition: A SQL WHERE clause. The autoprocessing will be applied if the
371 test run matches this condition
372 enabled: If this is False, this autoprocessing entry will not be applied
373
374 Optional:
375 labels: Labels to apply to the TKO test
376 keyvals: Keyval overrides to apply to the TKO test
377 bugs: Bugs filed that a relevant to this run
378 reason_override: Override for the AFE reason
379 """
380 condition = dbmodels.TextField()
381 enabled = dbmodels.BooleanField(default=False)
382
383 labels = dbmodels.ManyToManyField(tko_models.TestLabel, null=True,
384 db_table='planner_autoprocess_labels')
385 keyvals = dbmodels.ManyToManyField(KeyVal, null=True,
386 db_table='planner_autoprocess_keyvals')
387 bugs = dbmodels.ManyToManyField(Bug, null=True,
388 db_table='planner_autoprocess_bugs')
389 reason_override = dbmodels.CharField(max_length=255, null=True, blank=True)
390
391 class Meta:
392 db_table = 'planner_autoprocess'
393
394
395 def _get_details_unicode(self):
396 return 'Autoprocessing condition: %s' % self.condition