summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/orm/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/toaster/orm/models.py')
-rw-r--r--bitbake/lib/toaster/orm/models.py775
1 files changed, 775 insertions, 0 deletions
diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py
new file mode 100644
index 0000000000..77afe35861
--- /dev/null
+++ b/bitbake/lib/toaster/orm/models.py
@@ -0,0 +1,775 @@
1#
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# BitBake Toaster Implementation
6#
7# Copyright (C) 2013 Intel Corporation
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22from django.db import models
23from django.db.models import F
24from django.utils.encoding import python_2_unicode_compatible
25from django.utils import timezone
26
27class ToasterSetting(models.Model):
28 name = models.CharField(max_length=63)
29 helptext = models.TextField()
30 value = models.CharField(max_length=255)
31
32class ToasterSettingDefaultLayer(models.Model):
33 layer_version = models.ForeignKey('Layer_Version')
34
35class ProjectManager(models.Manager):
36 def create_project(self, name, release):
37 prj = self.model(name = name, bitbake_version = release.bitbake_version, release = release)
38 prj.save()
39
40 for defaultconf in ToasterSetting.objects.filter(name__startswith="DEFCONF_"):
41 name = defaultconf.name[8:]
42 ProjectVariable.objects.create( project = prj,
43 name = name,
44 value = defaultconf.value)
45
46 for layer in map(lambda x: x.layer, ReleaseDefaultLayer.objects.filter(release = release)):
47 for branches in Branch.objects.filter(name = release.branch):
48 for lv in Layer_Version.objects.filter(layer = layer, up_branch = branches ):
49 ProjectLayer.objects.create( project = prj,
50 layercommit = lv,
51 optional = False )
52
53 return prj
54
55 def create(self, *args, **kwargs):
56 raise Exception("Invalid call to Project.objects.create. Use Project.objects.create_project() to create a project")
57
58 def get_or_create(self, *args, **kwargs):
59 raise Exception("Invalid call to Project.objects.get_or_create. Use Project.objects.create_project() to create a project")
60
61class Project(models.Model):
62 name = models.CharField(max_length=100)
63 short_description = models.CharField(max_length=50, blank=True)
64 bitbake_version = models.ForeignKey('BitbakeVersion')
65 release = models.ForeignKey("Release")
66 created = models.DateTimeField(auto_now_add = True)
67 updated = models.DateTimeField(auto_now = True)
68 # This is a horrible hack; since Toaster has no "User" model available when
69 # running in interactive mode, we can't reference the field here directly
70 # Instead, we keep a possible null reference to the User id, as not to force
71 # hard links to possibly missing models
72 user_id = models.IntegerField(null = True)
73 objects = ProjectManager()
74
75
76 def schedule_build(self):
77 from bldcontrol.models import BuildRequest, BRTarget, BRLayer, BRVariable, BRBitbake
78 br = BuildRequest.objects.create(project = self)
79
80 BRBitbake.objects.create(req = br,
81 giturl = self.bitbake_version.giturl,
82 commit = self.bitbake_version.branch,
83 dirpath = self.bitbake_version.dirpath)
84
85 for l in self.projectlayer_set.all():
86 BRLayer.objects.create(req = br, name = l.layercommit.layer.name, giturl = l.layercommit.layer.vcs_url, commit = l.layercommit.commit, dirpath = l.layercommit.dirpath)
87 for t in self.projecttarget_set.all():
88 BRTarget.objects.create(req = br, target = t.target, task = t.task)
89 for v in self.projectvariable_set.all():
90 BRVariable.objects.create(req = br, name = v.name, value = v.value)
91
92 br.state = BuildRequest.REQ_QUEUED
93 br.save()
94 return br
95
96class Build(models.Model):
97 SUCCEEDED = 0
98 FAILED = 1
99 IN_PROGRESS = 2
100
101 BUILD_OUTCOME = (
102 (SUCCEEDED, 'Succeeded'),
103 (FAILED, 'Failed'),
104 (IN_PROGRESS, 'In Progress'),
105 )
106
107 search_allowed_fields = ['machine', 'cooker_log_path', "target__target", "target__target_image_file__file_name"]
108
109 project = models.ForeignKey(Project, null = True)
110 machine = models.CharField(max_length=100)
111 distro = models.CharField(max_length=100)
112 distro_version = models.CharField(max_length=100)
113 started_on = models.DateTimeField()
114 completed_on = models.DateTimeField()
115 timespent = models.IntegerField(default=0)
116 outcome = models.IntegerField(choices=BUILD_OUTCOME, default=IN_PROGRESS)
117 errors_no = models.IntegerField(default=0)
118 warnings_no = models.IntegerField(default=0)
119 cooker_log_path = models.CharField(max_length=500)
120 build_name = models.CharField(max_length=100)
121 bitbake_version = models.CharField(max_length=50)
122
123 def completeper(self):
124 tf = Task.objects.filter(build = self)
125 tfc = tf.count()
126 if tfc > 0:
127 completeper = tf.exclude(order__isnull=True).count()*100/tf.count()
128 else:
129 completeper = 0
130 return completeper
131
132 def eta(self):
133 from django.utils import timezone
134 eta = 0
135 completeper = self.completeper()
136 if self.completeper() > 0:
137 eta = timezone.now() + ((timezone.now() - self.started_on)*(100-completeper)/completeper)
138 return eta
139
140
141 def get_sorted_target_list(self):
142 tgts = Target.objects.filter(build_id = self.id).order_by( 'target' );
143 return( tgts );
144
145class ProjectTarget(models.Model):
146 project = models.ForeignKey(Project)
147 target = models.CharField(max_length=100)
148 task = models.CharField(max_length=100, null=True)
149
150@python_2_unicode_compatible
151class Target(models.Model):
152 search_allowed_fields = ['target', 'file_name']
153 build = models.ForeignKey(Build)
154 target = models.CharField(max_length=100)
155 is_image = models.BooleanField(default = False)
156 image_size = models.IntegerField(default=0)
157 license_manifest_path = models.CharField(max_length=500, null=True)
158
159 def package_count(self):
160 return Target_Installed_Package.objects.filter(target_id__exact=self.id).count()
161
162 def __str__(self):
163 return self.target
164
165class Target_Image_File(models.Model):
166 target = models.ForeignKey(Target)
167 file_name = models.FilePathField(max_length=254)
168 file_size = models.IntegerField()
169
170class Target_File(models.Model):
171 ITYPE_REGULAR = 1
172 ITYPE_DIRECTORY = 2
173 ITYPE_SYMLINK = 3
174 ITYPE_SOCKET = 4
175 ITYPE_FIFO = 5
176 ITYPE_CHARACTER = 6
177 ITYPE_BLOCK = 7
178 ITYPES = ( (ITYPE_REGULAR ,'regular'),
179 ( ITYPE_DIRECTORY ,'directory'),
180 ( ITYPE_SYMLINK ,'symlink'),
181 ( ITYPE_SOCKET ,'socket'),
182 ( ITYPE_FIFO ,'fifo'),
183 ( ITYPE_CHARACTER ,'character'),
184 ( ITYPE_BLOCK ,'block'),
185 )
186
187 target = models.ForeignKey(Target)
188 path = models.FilePathField()
189 size = models.IntegerField()
190 inodetype = models.IntegerField(choices = ITYPES)
191 permission = models.CharField(max_length=16)
192 owner = models.CharField(max_length=128)
193 group = models.CharField(max_length=128)
194 directory = models.ForeignKey('Target_File', related_name="directory_set", null=True)
195 sym_target = models.ForeignKey('Target_File', related_name="symlink_set", null=True)
196
197
198class TaskManager(models.Manager):
199 def related_setscene(self, task_object):
200 return Task.objects.filter(task_executed=True, build = task_object.build, recipe = task_object.recipe, task_name=task_object.task_name+"_setscene")
201
202class Task(models.Model):
203
204 SSTATE_NA = 0
205 SSTATE_MISS = 1
206 SSTATE_FAILED = 2
207 SSTATE_RESTORED = 3
208
209 SSTATE_RESULT = (
210 (SSTATE_NA, 'Not Applicable'), # For rest of tasks, but they still need checking.
211 (SSTATE_MISS, 'File not in cache'), # the sstate object was not found
212 (SSTATE_FAILED, 'Failed'), # there was a pkg, but the script failed
213 (SSTATE_RESTORED, 'Succeeded'), # successfully restored
214 )
215
216 CODING_NA = 0
217 CODING_PYTHON = 2
218 CODING_SHELL = 3
219
220 TASK_CODING = (
221 (CODING_NA, 'N/A'),
222 (CODING_PYTHON, 'Python'),
223 (CODING_SHELL, 'Shell'),
224 )
225
226 OUTCOME_NA = -1
227 OUTCOME_SUCCESS = 0
228 OUTCOME_COVERED = 1
229 OUTCOME_CACHED = 2
230 OUTCOME_PREBUILT = 3
231 OUTCOME_FAILED = 4
232 OUTCOME_EMPTY = 5
233
234 TASK_OUTCOME = (
235 (OUTCOME_NA, 'Not Available'),
236 (OUTCOME_SUCCESS, 'Succeeded'),
237 (OUTCOME_COVERED, 'Covered'),
238 (OUTCOME_CACHED, 'Cached'),
239 (OUTCOME_PREBUILT, 'Prebuilt'),
240 (OUTCOME_FAILED, 'Failed'),
241 (OUTCOME_EMPTY, 'Empty'),
242 )
243
244 TASK_OUTCOME_HELP = (
245 (OUTCOME_SUCCESS, 'This task successfully completed'),
246 (OUTCOME_COVERED, 'This task did not run because its output is provided by another task'),
247 (OUTCOME_CACHED, 'This task restored output from the sstate-cache directory or mirrors'),
248 (OUTCOME_PREBUILT, 'This task did not run because its outcome was reused from a previous build'),
249 (OUTCOME_FAILED, 'This task did not complete'),
250 (OUTCOME_EMPTY, 'This task has no executable content'),
251 (OUTCOME_NA, ''),
252 )
253
254 search_allowed_fields = [ "recipe__name", "recipe__version", "task_name", "logfile" ]
255
256 objects = TaskManager()
257
258 def get_related_setscene(self):
259 return Task.objects.related_setscene(self)
260
261 def get_outcome_text(self):
262 return Task.TASK_OUTCOME[self.outcome + 1][1]
263
264 def get_outcome_help(self):
265 return Task.TASK_OUTCOME_HELP[self.outcome][1]
266
267 def get_sstate_text(self):
268 if self.sstate_result==Task.SSTATE_NA:
269 return ''
270 else:
271 return Task.SSTATE_RESULT[self.sstate_result][1]
272
273 def get_executed_display(self):
274 if self.task_executed:
275 return "Executed"
276 return "Not Executed"
277
278 def get_description(self):
279 helptext = HelpText.objects.filter(key=self.task_name, area=HelpText.VARIABLE, build=self.build)
280 try:
281 return helptext[0].text
282 except IndexError:
283 return ''
284
285 build = models.ForeignKey(Build, related_name='task_build')
286 order = models.IntegerField(null=True)
287 task_executed = models.BooleanField(default=False) # True means Executed, False means Not/Executed
288 outcome = models.IntegerField(choices=TASK_OUTCOME, default=OUTCOME_NA)
289 sstate_checksum = models.CharField(max_length=100, blank=True)
290 path_to_sstate_obj = models.FilePathField(max_length=500, blank=True)
291 recipe = models.ForeignKey('Recipe', related_name='build_recipe')
292 task_name = models.CharField(max_length=100)
293 source_url = models.FilePathField(max_length=255, blank=True)
294 work_directory = models.FilePathField(max_length=255, blank=True)
295 script_type = models.IntegerField(choices=TASK_CODING, default=CODING_NA)
296 line_number = models.IntegerField(default=0)
297 disk_io = models.IntegerField(null=True)
298 cpu_usage = models.DecimalField(max_digits=6, decimal_places=2, null=True)
299 elapsed_time = models.DecimalField(max_digits=6, decimal_places=2, null=True)
300 sstate_result = models.IntegerField(choices=SSTATE_RESULT, default=SSTATE_NA)
301 message = models.CharField(max_length=240)
302 logfile = models.FilePathField(max_length=255, blank=True)
303
304 outcome_text = property(get_outcome_text)
305 sstate_text = property(get_sstate_text)
306
307 class Meta:
308 ordering = ('order', 'recipe' ,)
309 unique_together = ('build', 'recipe', 'task_name', )
310
311
312class Task_Dependency(models.Model):
313 task = models.ForeignKey(Task, related_name='task_dependencies_task')
314 depends_on = models.ForeignKey(Task, related_name='task_dependencies_depends')
315
316class Package(models.Model):
317 search_allowed_fields = ['name', 'version', 'revision', 'recipe__name', 'recipe__version', 'recipe__license', 'recipe__layer_version__layer__name', 'recipe__layer_version__branch', 'recipe__layer_version__commit', 'recipe__layer_version__layer__local_path', 'installed_name']
318 build = models.ForeignKey('Build')
319 recipe = models.ForeignKey('Recipe', null=True)
320 name = models.CharField(max_length=100)
321 installed_name = models.CharField(max_length=100, default='')
322 version = models.CharField(max_length=100, blank=True)
323 revision = models.CharField(max_length=32, blank=True)
324 summary = models.CharField(max_length=200, blank=True)
325 description = models.TextField(blank=True)
326 size = models.IntegerField(default=0)
327 installed_size = models.IntegerField(default=0)
328 section = models.CharField(max_length=80, blank=True)
329 license = models.CharField(max_length=80, blank=True)
330
331class Package_DependencyManager(models.Manager):
332 use_for_related_fields = True
333
334 def get_query_set(self):
335 return super(Package_DependencyManager, self).get_query_set().exclude(package_id = F('depends_on__id'))
336
337class Package_Dependency(models.Model):
338 TYPE_RDEPENDS = 0
339 TYPE_TRDEPENDS = 1
340 TYPE_RRECOMMENDS = 2
341 TYPE_TRECOMMENDS = 3
342 TYPE_RSUGGESTS = 4
343 TYPE_RPROVIDES = 5
344 TYPE_RREPLACES = 6
345 TYPE_RCONFLICTS = 7
346 ' TODO: bpackage should be changed to remove the DEPENDS_TYPE access '
347 DEPENDS_TYPE = (
348 (TYPE_RDEPENDS, "depends"),
349 (TYPE_TRDEPENDS, "depends"),
350 (TYPE_TRECOMMENDS, "recommends"),
351 (TYPE_RRECOMMENDS, "recommends"),
352 (TYPE_RSUGGESTS, "suggests"),
353 (TYPE_RPROVIDES, "provides"),
354 (TYPE_RREPLACES, "replaces"),
355 (TYPE_RCONFLICTS, "conflicts"),
356 )
357 ''' Indexed by dep_type, in view order, key for short name and help
358 description which when viewed will be printf'd with the
359 package name.
360 '''
361 DEPENDS_DICT = {
362 TYPE_RDEPENDS : ("depends", "%s is required to run %s"),
363 TYPE_TRDEPENDS : ("depends", "%s is required to run %s"),
364 TYPE_TRECOMMENDS : ("recommends", "%s extends the usability of %s"),
365 TYPE_RRECOMMENDS : ("recommends", "%s extends the usability of %s"),
366 TYPE_RSUGGESTS : ("suggests", "%s is suggested for installation with %s"),
367 TYPE_RPROVIDES : ("provides", "%s is provided by %s"),
368 TYPE_RREPLACES : ("replaces", "%s is replaced by %s"),
369 TYPE_RCONFLICTS : ("conflicts", "%s conflicts with %s, which will not be installed if this package is not first removed"),
370 }
371
372 package = models.ForeignKey(Package, related_name='package_dependencies_source')
373 depends_on = models.ForeignKey(Package, related_name='package_dependencies_target') # soft dependency
374 dep_type = models.IntegerField(choices=DEPENDS_TYPE)
375 target = models.ForeignKey(Target, null=True)
376 objects = Package_DependencyManager()
377
378class Target_Installed_Package(models.Model):
379 target = models.ForeignKey(Target)
380 package = models.ForeignKey(Package, related_name='buildtargetlist_package')
381
382class Package_File(models.Model):
383 package = models.ForeignKey(Package, related_name='buildfilelist_package')
384 path = models.FilePathField(max_length=255, blank=True)
385 size = models.IntegerField()
386
387class Recipe(models.Model):
388 search_allowed_fields = ['name', 'version', 'file_path', 'section', 'license', 'layer_version__layer__name', 'layer_version__branch', 'layer_version__commit', 'layer_version__layer__local_path']
389
390 layer_source = models.ForeignKey('LayerSource', default = None, null = True) # from where did we get this recipe
391 up_id = models.IntegerField(null = True, default = None) # id of entry in the source
392 up_date = models.DateTimeField(null = True, default = None)
393
394 name = models.CharField(max_length=100, blank=True) # pn
395 version = models.CharField(max_length=100, blank=True) # pv
396 layer_version = models.ForeignKey('Layer_Version', related_name='recipe_layer_version')
397 summary = models.CharField(max_length=100, blank=True)
398 description = models.TextField(blank=True)
399 section = models.CharField(max_length=100, blank=True)
400 license = models.CharField(max_length=200, blank=True)
401 homepage = models.URLField(blank=True)
402 bugtracker = models.URLField(blank=True)
403 file_path = models.FilePathField(max_length=255)
404
405 def get_vcs_link_url(self):
406 if self.layer_version.layer.vcs_web_file_base_url is None:
407 return ""
408 return self.layer_version.layer.vcs_web_file_base_url.replace('%path%', self.file_path).replace('%branch%', self.layer_version.up_branch.name)
409
410 def get_layersource_view_url(self):
411 if self.layer_source is None:
412 return ""
413
414 url = self.layer_source.get_object_view(self.layer_version.up_branch, "recipes", self.name)
415 return url
416
417 def __unicode__(self):
418 return "Recipe " + self.name + ":" + self.version
419
420class Recipe_DependencyManager(models.Manager):
421 use_for_related_fields = True
422
423 def get_query_set(self):
424 return super(Recipe_DependencyManager, self).get_query_set().exclude(recipe_id = F('depends_on__id'))
425
426class Recipe_Dependency(models.Model):
427 TYPE_DEPENDS = 0
428 TYPE_RDEPENDS = 1
429
430 DEPENDS_TYPE = (
431 (TYPE_DEPENDS, "depends"),
432 (TYPE_RDEPENDS, "rdepends"),
433 )
434 recipe = models.ForeignKey(Recipe, related_name='r_dependencies_recipe')
435 depends_on = models.ForeignKey(Recipe, related_name='r_dependencies_depends')
436 dep_type = models.IntegerField(choices=DEPENDS_TYPE)
437 objects = Recipe_DependencyManager()
438
439
440class Machine(models.Model):
441 layer_source = models.ForeignKey('LayerSource', default = None, null = True) # from where did we get this machine
442 up_id = models.IntegerField(null = True, default = None) # id of entry in the source
443 up_date = models.DateTimeField(null = True, default = None)
444
445 layer_version = models.ForeignKey('Layer_Version')
446 name = models.CharField(max_length=255)
447 description = models.CharField(max_length=255)
448
449 def __unicode__(self):
450 return "Machine " + self.name + "(" + self.description + ")"
451
452 class Meta:
453 unique_together = ("layer_source", "up_id")
454
455
456from django.db.models.base import ModelBase
457
458class InheritanceMetaclass(ModelBase):
459 def __call__(cls, *args, **kwargs):
460 obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs)
461 return obj.get_object()
462
463
464class LayerSource(models.Model):
465 __metaclass__ = InheritanceMetaclass
466
467 class Meta:
468 unique_together = (('sourcetype', 'apiurl'), )
469
470 TYPE_LOCAL = 0
471 TYPE_LAYERINDEX = 1
472 SOURCE_TYPE = (
473 (TYPE_LOCAL, "local"),
474 (TYPE_LAYERINDEX, "layerindex"),
475 )
476
477 name = models.CharField(max_length=63)
478 sourcetype = models.IntegerField(choices=SOURCE_TYPE)
479 apiurl = models.CharField(max_length=255, null=True, default=None)
480
481 def save(self, *args, **kwargs):
482 if isinstance(self, LocalLayerSource):
483 self.sourcetype = LayerSource.TYPE_LOCAL
484 elif isinstance(self, LayerIndexLayerSource):
485 self.sourcetype = LayerSource.TYPE_LAYERINDEX
486 elif self.sourcetype == None:
487 raise Exception("Invalid LayerSource type")
488 return super(LayerSource, self).save(*args, **kwargs)
489
490 def get_object(self):
491 if self.sourcetype is not None:
492 if self.sourcetype == LayerSource.TYPE_LOCAL:
493 self.__class__ = LocalLayerSource
494 if self.sourcetype == LayerSource.TYPE_LAYERINDEX:
495 self.__class__ = LayerIndexLayerSource
496 return self
497
498 return "LS " + self.sourcetype + " " + self.name
499
500
501class LocalLayerSource(LayerSource):
502 class Meta(LayerSource._meta.__class__):
503 proxy = True
504
505 def __init__(self, *args, **kwargs):
506 super(LocalLayerSource, self).__init__(args, kwargs)
507 self.sourcetype = LayerSource.TYPE_LOCAL
508
509 def update(self):
510 '''
511 Fetches layer, recipe and machine information from local repository
512 '''
513 pass
514
515class LayerIndexLayerSource(LayerSource):
516 class Meta(LayerSource._meta.__class__):
517 proxy = True
518
519 def __init__(self, *args, **kwargs):
520 super(LayerIndexLayerSource, self).__init__(args, kwargs)
521 self.sourcetype = LayerSource.TYPE_LAYERINDEX
522
523 def get_object_view(self, branch, objectype, upid):
524 if self != branch.layer_source:
525 raise Exception("Invalid branch specification")
526 return self.apiurl + "../branch/" + branch.name + "/" + objectype + "/?q=" + str(upid)
527
528 def update(self):
529 '''
530 Fetches layer, recipe and machine information from remote repository
531 '''
532 assert self.apiurl is not None
533
534 def _get_json_response(apiurl = self.apiurl):
535 import httplib, urlparse, json
536 parsedurl = urlparse.urlparse(apiurl)
537 (host, port) = parsedurl.netloc.split(":")
538 if port is None:
539 port = 80
540 else:
541 port = int(port)
542 #print "-- connect to: http://%s:%s%s?%s" % (host, port, parsedurl.path, parsedurl.query)
543 conn = httplib.HTTPConnection(host, port)
544 conn.request("GET", parsedurl.path + "?" + parsedurl.query)
545 r = conn.getresponse()
546 if r.status != 200:
547 raise Exception("Failed to read " + parsedurl.path + ": %d %s" % (r.status, r.reason))
548 return json.loads(r.read())
549
550 # verify we can get the basic api
551 try:
552 apilinks = _get_json_response()
553 except:
554 print "EE: could not connect to %s, skipping update" % self.apiurl
555 return
556
557 # update branches; only those that we already have names listed in the database
558 whitelist_branch_names = map(lambda x: x.name, Branch.objects.all())
559
560 branches_info = _get_json_response(apilinks['branches']
561 + "?filter=name:%s" % "OR".join(whitelist_branch_names))
562 for bi in branches_info:
563 b, created = Branch.objects.get_or_create(layer_source = self, name = bi['name'])
564 b.up_id = bi['id']
565 b.up_date = bi['updated']
566 b.name = bi['name']
567 b.bitbake_branch = bi['bitbake_branch']
568 b.short_description = bi['short_description']
569 b.save()
570
571 # update layers
572 layers_info = _get_json_response(apilinks['layerItems'])
573 for li in layers_info:
574 l, created = Layer.objects.get_or_create(layer_source = self, up_id = li['id'])
575 l.up_date = li['updated']
576 l.name = li['name']
577 l.vcs_url = li['vcs_url']
578 l.vcs_web_file_base_url = li['vcs_web_file_base_url']
579 l.summary = li['summary']
580 l.description = li['description']
581 l.save()
582
583 # update layerbranches/layer_versions
584 layerbranches_info = _get_json_response(apilinks['layerBranches']
585 + "?filter=branch:%s" % "OR".join(map(lambda x: str(x.up_id), Branch.objects.filter(layer_source = self)))
586 )
587 for lbi in layerbranches_info:
588 lv, created = Layer_Version.objects.get_or_create(layer_source = self, up_id = lbi['id'])
589
590 lv.up_date = lbi['updated']
591 lv.layer = Layer.objects.get(layer_source = self, up_id = lbi['layer'])
592 lv.up_branch = Branch.objects.get(layer_source = self, up_id = lbi['branch'])
593 lv.branch = lbi['actual_branch']
594 lv.commit = lbi['vcs_last_rev']
595 lv.dirpath = lbi['vcs_subdir']
596 lv.save()
597
598
599 # update machines
600 machines_info = _get_json_response(apilinks['machines']
601 + "?filter=layerbranch:%s" % "OR".join(map(lambda x: str(x.up_id), Layer_Version.objects.filter(layer_source = self)))
602 )
603 for mi in machines_info:
604 mo, created = Machine.objects.get_or_create(layer_source = self, up_id = mi['id'])
605 mo.up_date = mi['updated']
606 mo.layer_version = Layer_Version.objects.get(layer_source = self, up_id = mi['layerbranch'])
607 mo.name = mi['name']
608 mo.description = mi['description']
609 mo.save()
610
611 # update recipes; paginate by layer version / layer branch
612 recipes_info = _get_json_response(apilinks['recipes']
613 + "?filter=layerbranch:%s" % "OR".join(map(lambda x: str(x.up_id), Layer_Version.objects.filter(layer_source = self)))
614 )
615 for ri in recipes_info:
616 ro, created = Recipe.objects.get_or_create(layer_source = self, up_id = ri['id'])
617
618 ro.up_date = ri['updated']
619 ro.layer_version = Layer_Version.objects.get(layer_source = self, up_id = mi['layerbranch'])
620
621 ro.name = ri['pn']
622 ro.version = ri['pv']
623 ro.summary = ri['summary']
624 ro.description = ri['description']
625 ro.section = ri['section']
626 ro.license = ri['license']
627 ro.homepage = ri['homepage']
628 ro.bugtracker = ri['bugtracker']
629 ro.file_path = ri['filepath'] + ri['filename']
630 ro.save()
631
632 pass
633
634class BitbakeVersion(models.Model):
635 name = models.CharField(max_length=32, unique = True)
636 giturl = models.URLField()
637 branch = models.CharField(max_length=32)
638 dirpath = models.CharField(max_length=255)
639
640
641class Release(models.Model):
642 name = models.CharField(max_length=32, unique = True)
643 description = models.CharField(max_length=255)
644 bitbake_version = models.ForeignKey(BitbakeVersion)
645 branch = models.CharField(max_length=32)
646
647
648class ReleaseDefaultLayer(models.Model):
649 release = models.ForeignKey(Release)
650 layer = models.ForeignKey('Layer')
651
652
653# Branch class is synced with layerindex.Branch, branches can only come from remote layer indexes
654class Branch(models.Model):
655 layer_source = models.ForeignKey('LayerSource', null = True, default = True)
656 up_id = models.IntegerField(null = True, default = None) # id of branch in the source
657 up_date = models.DateTimeField(null = True, default = None)
658
659 name = models.CharField(max_length=50)
660 bitbake_branch = models.CharField(max_length=50, blank=True)
661 short_description = models.CharField(max_length=50, blank=True)
662
663 class Meta:
664 verbose_name_plural = "Branches"
665 unique_together = (('layer_source', 'name'),('layer_source', 'up_id'))
666
667 def __unicode__(self):
668 return self.name
669
670
671# Layer class synced with layerindex.LayerItem
672class Layer(models.Model):
673 layer_source = models.ForeignKey(LayerSource, null = True, default = None) # from where did we got this layer
674 up_id = models.IntegerField(null = True, default = None) # id of layer in the remote source
675 up_date = models.DateTimeField(null = True, default = None)
676
677 name = models.CharField(max_length=100)
678 local_path = models.FilePathField(max_length=255, null = True, default = None)
679 layer_index_url = models.URLField()
680 vcs_url = models.URLField(default = None, null = True)
681 vcs_web_file_base_url = models.URLField(null = True, default = None)
682
683 summary = models.CharField(max_length=200, help_text='One-line description of the layer', null = True, default = None)
684 description = models.TextField(null = True, default = None)
685
686 def __unicode__(self):
687 return "L " + self.name
688
689 class Meta:
690 unique_together = (("layer_source", "up_id"), ("layer_source", "name"))
691
692
693# LayerCommit class is synced with layerindex.LayerBranch
694class Layer_Version(models.Model):
695 search_allowed_fields = ["layer__name", "layer__summary",]
696 build = models.ForeignKey(Build, related_name='layer_version_build', default = None, null = True)
697 layer = models.ForeignKey(Layer, related_name='layer_version_layer')
698
699 layer_source = models.ForeignKey(LayerSource, null = True, default = None) # from where did we get this Layer Version
700 up_id = models.IntegerField(null = True, default = None) # id of layerbranch in the remote source
701 up_date = models.DateTimeField(null = True, default = None)
702 up_branch = models.ForeignKey(Branch, null = True, default = None)
703
704 branch = models.CharField(max_length=80) # LayerBranch.actual_branch
705 commit = models.CharField(max_length=100) # LayerBranch.vcs_last_rev
706 dirpath = models.CharField(max_length=255, null = True, default = None) # LayerBranch.vcs_subdir
707 priority = models.IntegerField(default = 0) # if -1, this is a default layer
708
709 def __unicode__(self):
710 return "LV " + str(self.layer) + " " + self.commit
711
712 class Meta:
713 unique_together = ("layer_source", "up_id")
714
715class LayerVersionDependency(models.Model):
716 layer_source = models.ForeignKey(LayerSource, null = True, default = None) # from where did we got this layer
717 up_id = models.IntegerField(null = True, default = None) # id of layerbranch in the remote source
718
719 layer_version = models.ForeignKey(Layer_Version, related_name="dependencies")
720 depends_on = models.ForeignKey(Layer_Version, related_name="dependees")
721
722 class Meta:
723 unique_together = ("layer_source", "up_id")
724
725class ProjectLayer(models.Model):
726 project = models.ForeignKey(Project)
727 layercommit = models.ForeignKey(Layer_Version, null=True)
728 optional = models.BooleanField(default = True)
729
730class ProjectVariable(models.Model):
731 project = models.ForeignKey(Project)
732 name = models.CharField(max_length=100)
733 value = models.TextField(blank = True)
734
735class Variable(models.Model):
736 search_allowed_fields = ['variable_name', 'variable_value',
737 'vhistory__file_name', "description"]
738 build = models.ForeignKey(Build, related_name='variable_build')
739 variable_name = models.CharField(max_length=100)
740 variable_value = models.TextField(blank=True)
741 changed = models.BooleanField(default=False)
742 human_readable_name = models.CharField(max_length=200)
743 description = models.TextField(blank=True)
744
745class VariableHistory(models.Model):
746 variable = models.ForeignKey(Variable, related_name='vhistory')
747 value = models.TextField(blank=True)
748 file_name = models.FilePathField(max_length=255)
749 line_number = models.IntegerField(null=True)
750 operation = models.CharField(max_length=64)
751
752class HelpText(models.Model):
753 VARIABLE = 0
754 HELPTEXT_AREA = ((VARIABLE, 'variable'), )
755
756 build = models.ForeignKey(Build, related_name='helptext_build')
757 area = models.IntegerField(choices=HELPTEXT_AREA)
758 key = models.CharField(max_length=100)
759 text = models.TextField()
760
761class LogMessage(models.Model):
762 INFO = 0
763 WARNING = 1
764 ERROR = 2
765
766 LOG_LEVEL = ( (INFO, "info"),
767 (WARNING, "warn"),
768 (ERROR, "error") )
769
770 build = models.ForeignKey(Build)
771 task = models.ForeignKey(Task, blank = True, null=True)
772 level = models.IntegerField(choices=LOG_LEVEL, default=INFO)
773 message=models.CharField(max_length=240)
774 pathname = models.FilePathField(max_length=255, blank=True)
775 lineno = models.IntegerField(null=True)