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