blob: 2236b11bdb7ee81077e8f27eb6de34cf4192a45d [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
lmr6f80e7a2010-02-04 03:18:28 +00006from autotest_lib.client.common_lib import enum, utils
showard26b7ec72009-12-21 22:43:57 +00007
8
jamesrenc3940222010-02-19 21:57:37 +00009class Plan(dbmodels.Model, model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +000010 """A test plan
11
12 Required:
13 name: Plan name, unique
14 complete: True if the plan is completed
15 dirty: True if the plan has been changed since the execution engine has
16 last seen it
17 initialized: True if the plan has started
18
19 Optional:
20 label_override: A label to apply to each Autotest job.
21 support: The global support object to apply to this plan
22 """
23 name = dbmodels.CharField(max_length=255, unique=True)
24 label_override = dbmodels.CharField(max_length=255, null=True, blank=True)
25 support = dbmodels.TextField(blank=True)
26 complete = dbmodels.BooleanField(default=False)
27 dirty = dbmodels.BooleanField(default=False)
28 initialized = dbmodels.BooleanField(default=False)
29
30 owners = dbmodels.ManyToManyField(afe_models.User,
31 db_table='planner_plan_owners')
32 hosts = dbmodels.ManyToManyField(afe_models.Host, through='Host')
jamesrenc3940222010-02-19 21:57:37 +000033 host_labels = dbmodels.ManyToManyField(afe_models.Label,
34 db_table='planner_plan_host_labels')
35
36 name_field = 'name'
showard26b7ec72009-12-21 22:43:57 +000037
38 class Meta:
39 db_table = 'planner_plans'
40
41
42 def __unicode__(self):
43 return unicode(self.name)
44
45
46class ModelWithPlan(dbmodels.Model):
47 """Superclass for models that have a plan_id
48
49 Required:
50 plan: The associated test plan
51 """
52 plan = dbmodels.ForeignKey(Plan)
53
54 class Meta:
55 abstract = True
56
57
58 def __unicode__(self):
59 return u'%s (%s)' % (self._get_details_unicode(), self.plan.name)
60
61
62 def _get_details_unicode(self):
63 """Gets the first part of the unicode string
64
65 subclasses must override this method
66 """
67 raise NotImplementedError(
68 'Subclasses must override _get_details_unicode()')
69
70
jamesrenc3940222010-02-19 21:57:37 +000071class Host(ModelWithPlan, model_logic.ModelExtensions):
showard26b7ec72009-12-21 22:43:57 +000072 """A plan host
73
74 Required:
75 host: The AFE host
76 complete: True if and only if this host is finished in the test plan
77 blocked: True if and only if the host is blocked (not executing tests)
78 """
79 host = dbmodels.ForeignKey(afe_models.Host)
80 complete = dbmodels.BooleanField(default=False)
81 blocked = dbmodels.BooleanField(default=False)
82
jamesrenc3940222010-02-19 21:57:37 +000083 Status = enum.Enum('Finished', 'Running', 'Blocked', string_values=True)
84
showard26b7ec72009-12-21 22:43:57 +000085 class Meta:
86 db_table = 'planner_hosts'
87
88
jamesrenc3940222010-02-19 21:57:37 +000089 def status(self):
90 if self.complete:
91 return Host.Status.FINISHED
92 if self.blocked:
93 return Host.Status.BLOCKED
94 return Host.Status.RUNNING
95
96
showard26b7ec72009-12-21 22:43:57 +000097 def _get_details_unicode(self):
98 return 'Host: %s' % host.hostname
99
100
jamesrenc3940222010-02-19 21:57:37 +0000101 @classmethod
102 def smart_get(cls, id):
103 raise NotImplementedError('Planner hosts do not support smart_get()')
104
105
showard26b7ec72009-12-21 22:43:57 +0000106class ControlFile(model_logic.ModelWithHash):
107 """A control file. Immutable once added to the table
108
109 Required:
110 contents: The text of the control file
111
112 Others:
113 control_hash: The SHA1 hash of the control file, for duplicate detection
114 and fast search
115 """
116 contents = dbmodels.TextField()
117
118 class Meta:
119 db_table = 'planner_test_control_files'
120
121
122 @classmethod
123 def _compute_hash(cls, **kwargs):
lmr6f80e7a2010-02-04 03:18:28 +0000124 return utils.hash('sha1', kwargs['contents']).hexdigest()
showard26b7ec72009-12-21 22:43:57 +0000125
126
127 def __unicode__(self):
128 return u'Control file id %s (SHA1: %s)' % (self.id, self.control_hash)
129
130
131class Test(ModelWithPlan):
132 """A planned test
133
134 Required:
jamesrenc3940222010-02-19 21:57:37 +0000135 alias: The name to give this test within the plan. Unique with plan id
showard26b7ec72009-12-21 22:43:57 +0000136 test_control_file: The control file to run
137 execution_order: An integer describing when this test should be run in
138 the test plan
jamesrenc3940222010-02-19 21:57:37 +0000139 estimated_runtime: Time in hours that the test is expected to run. Will
140 be automatically generated (on the frontend) for
141 tests in Autotest.
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)
145 execution_order = dbmodels.IntegerField(blank=True)
jamesrenc3940222010-02-19 21:57:37 +0000146 estimated_runtime = dbmodels.IntegerField()
showard26b7ec72009-12-21 22:43:57 +0000147
148 class Meta:
149 db_table = 'planner_tests'
150 ordering = ('execution_order',)
jamesrenc3940222010-02-19 21:57:37 +0000151 unique_together = (('plan', 'alias'),)
showard26b7ec72009-12-21 22:43:57 +0000152
153
154 def _get_details_unicode(self):
155 return 'Planned test - Control file id %s' % test_control_file.id
156
157
158class Job(ModelWithPlan):
159 """Represents an Autotest job initiated for a test plan
160
161 Required:
162 test: The Test associated with this Job
163 afe_job: The Autotest job
164 """
165 test = dbmodels.ForeignKey(Test)
166 afe_job = dbmodels.ForeignKey(afe_models.Job)
167
168 class Meta:
169 db_table = 'planner_test_jobs'
170
171
172 def _get_details_unicode(self):
173 return 'AFE job %s' % afe_job.id
174
175
176class Bug(dbmodels.Model):
177 """Represents a bug ID
178
179 Required:
180 external_uid: External unique ID for the bug
181 """
182 external_uid = dbmodels.CharField(max_length=255, unique=True)
183
184 class Meta:
185 db_table = 'planner_bugs'
186
187
188 def __unicode__(self):
189 return u'Bug external ID %s' % self.external_uid
190
191
192class TestRun(ModelWithPlan):
193 """An individual test run from an Autotest job for the test plan.
194
195 Each Job object may have multiple TestRun objects associated with it.
196
197 Required:
198 test_job: The Job object associated with this TestRun
199 tko_test: The TKO Test associated with this TestRun
200 status: One of 'Active', 'Passed', 'Failed'
201 finalized: True if and only if the TestRun is ready to be shown in
202 triage
203 invalidated: True if and only if a user has decided to invalidate this
204 TestRun's results
205 seen: True if and only if a user has marked this TestRun as "seen"
206 triaged: True if and only if the TestRun no longer requires any user
207 intervention
208
209 Optional:
210 bugs: Bugs filed that a relevant to this run
211 """
212 Status = enum.Enum('Active', 'Passed', 'Failed', string_values=True)
213
214 test_job = dbmodels.ForeignKey(Job)
215 tko_test = dbmodels.ForeignKey(tko_models.Test)
jamesrenc3940222010-02-19 21:57:37 +0000216 host = dbmodels.ForeignKey(Host)
showard26b7ec72009-12-21 22:43:57 +0000217 status = dbmodels.CharField(max_length=16, choices=Status.choices())
218 finalized = dbmodels.BooleanField(default=False)
219 seen = dbmodels.BooleanField(default=False)
220 triaged = dbmodels.BooleanField(default=False)
221
222 bugs = dbmodels.ManyToManyField(Bug, null=True,
223 db_table='planner_test_run_bugs')
224
225 class Meta:
226 db_table = 'planner_test_runs'
227
228
229 def _get_details_unicode(self):
230 return 'Test Run: %s' % self.id
231
232
233class DataType(dbmodels.Model):
234 """Encodes the data model types
235
236 For use in the history table, to identify the type of object that was
237 changed.
238
239 Required:
240 name: The name of the data type
241 db_table: The name of the database table that stores this type
242 """
243 name = dbmodels.CharField(max_length=255)
244 db_table = dbmodels.CharField(max_length=255)
245
246 class Meta:
247 db_table = 'planner_data_types'
248
249
250 def __unicode__(self):
251 return u'Data type %s (stored in table %s)' % (self.name, self.db_table)
252
253
254class History(ModelWithPlan):
255 """Represents a history action
256
257 Required:
258 action_id: An arbitrary ID that uniquely identifies the user action
259 related to the history entry. One user action may result in
260 multiple history entries
261 user: The user who initiated the change
262 data_type: The type of object that was changed
263 object_id: Value of the primary key field for the changed object
264 old_object_repr: A string representation of the object before the change
265 new_object_repr: A string representation of the object after the change
266
267 Others:
268 time: A timestamp. Automatically generated.
269 """
270 action_id = dbmodels.IntegerField()
271 user = dbmodels.ForeignKey(afe_models.User)
272 data_type = dbmodels.ForeignKey(DataType)
273 object_id = dbmodels.IntegerField()
274 old_object_repr = dbmodels.TextField(blank=True)
275 new_object_repr = dbmodels.TextField(blank=True)
276
277 time = dbmodels.DateTimeField(auto_now_add=True)
278
279 class Meta:
280 db_table = 'planner_history'
281
282
283 def _get_details_unicode(self):
284 return 'History entry: %s => %s' % (self.old_object_repr,
285 self.new_object_repr)
286
287
288class SavedObject(dbmodels.Model):
289 """A saved object that can be recalled at certain points in the UI
290
291 Required:
292 user: The creator of the object
293 object_type: One of 'support', 'triage', 'autoprocess', 'custom_query'
294 name: The name given to the object
295 encoded_object: The actual object
296 """
297 Type = enum.Enum('support', 'triage', 'autoprocess', 'custom_query',
298 string_values=True)
299
300 user = dbmodels.ForeignKey(afe_models.User)
301 object_type = dbmodels.CharField(max_length=16,
302 choices=Type.choices(), db_column='type')
303 name = dbmodels.CharField(max_length=255)
304 encoded_object = dbmodels.TextField()
305
306 class Meta:
307 db_table = 'planner_saved_objects'
308 unique_together = ('user', 'object_type', 'name')
309
310
311 def __unicode__(self):
312 return u'Saved %s object: %s, by %s' % (self.object_type, self.name,
313 self.user.login)
314
315
316class CustomQuery(ModelWithPlan):
317 """A custom SQL query for the triage page
318
319 Required:
320 query: the SQL WHERE clause to attach to the main query
321 """
322 query = dbmodels.TextField()
323
324 class Meta:
325 db_table = 'planner_custom_queries'
326
327
328 def _get_details_unicode(self):
329 return 'Custom Query: %s' % self.query
330
331
332class KeyVal(model_logic.ModelWithHash):
333 """Represents a keyval. Immutable once added to the table.
334
335 Required:
336 key: The key
337 value: The value
338
339 Others:
340 keyval_hash: The result of SHA1(SHA1(key) ++ value), for duplicate
341 detection and fast search.
342 """
343 key = dbmodels.CharField(max_length=1024)
344 value = dbmodels.CharField(max_length=1024)
345
346 class Meta:
347 db_table = 'planner_keyvals'
348
349
350 @classmethod
351 def _compute_hash(cls, **kwargs):
lmr6f80e7a2010-02-04 03:18:28 +0000352 round1 = utils.hash('sha1', kwargs['key']).hexdigest()
353 return utils.hash('sha1', round1 + kwargs['value']).hexdigest()
showard26b7ec72009-12-21 22:43:57 +0000354
355
356 def __unicode__(self):
357 return u'Keyval: %s = %s' % (self.key, self.value)
358
359
360class AutoProcess(ModelWithPlan):
361 """An autoprocessing directive to perform on test runs that enter triage
362
363 Required:
364 condition: A SQL WHERE clause. The autoprocessing will be applied if the
365 test run matches this condition
366 enabled: If this is False, this autoprocessing entry will not be applied
367
368 Optional:
369 labels: Labels to apply to the TKO test
370 keyvals: Keyval overrides to apply to the TKO test
371 bugs: Bugs filed that a relevant to this run
372 reason_override: Override for the AFE reason
373 """
374 condition = dbmodels.TextField()
375 enabled = dbmodels.BooleanField(default=False)
376
377 labels = dbmodels.ManyToManyField(tko_models.TestLabel, null=True,
378 db_table='planner_autoprocess_labels')
379 keyvals = dbmodels.ManyToManyField(KeyVal, null=True,
380 db_table='planner_autoprocess_keyvals')
381 bugs = dbmodels.ManyToManyField(Bug, null=True,
382 db_table='planner_autoprocess_bugs')
383 reason_override = dbmodels.CharField(max_length=255, null=True, blank=True)
384
385 class Meta:
386 db_table = 'planner_autoprocess'
387
388
389 def _get_details_unicode(self):
390 return 'Autoprocessing condition: %s' % self.condition