summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/orm/models.py
diff options
context:
space:
mode:
authorAlexandru DAMIAN <alexandru.damian@intel.com>2014-08-08 15:03:03 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-08-29 13:56:49 +0100
commit234226b9055a3b23addcdaa7a77812dc7aa79134 (patch)
tree979683201cb4f16cc9937aa30dadcf492edb442a /bitbake/lib/toaster/orm/models.py
parent10019a652f2894f8782cc9281c980edad701822a (diff)
downloadpoky-234226b9055a3b23addcdaa7a77812dc7aa79134.tar.gz
bitbake: toaster: update orm models for layerindex compatibility
We add a ToasterSettings table that will keep installation-wide settings. We update the models for the layer-related data storage to make them compatible with the layerindex application API. We add a LayerSource class that can update local data from a LayerIndex-like compatible API. Adding a command line option to perform information update from all upstream layer sources. Fair warning - there is no backward migration from 0013. (Bitbake rev: 89e13579e1b44b738f10fadec8454aa0e6f073af) Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/orm/models.py')
-rw-r--r--bitbake/lib/toaster/orm/models.py377
1 files changed, 339 insertions, 38 deletions
diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py
index f19a4370c8..5a6dcd72f6 100644
--- a/bitbake/lib/toaster/orm/models.py
+++ b/bitbake/lib/toaster/orm/models.py
@@ -22,30 +22,33 @@
22from django.db import models 22from django.db import models
23from django.db.models import F 23from django.db.models import F
24from django.utils.encoding import python_2_unicode_compatible 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')
25 34
26class ProjectManager(models.Manager): 35class ProjectManager(models.Manager):
27 def create_project(self, name, branch, short_description): 36 def create_project(self, name, release):
28 prj = self.model(name = name, branch = branch, short_description = short_description) 37 prj = self.model(name = name, bitbake_version = release.bitbake_version, release = release)
29 prj.save() 38 prj.save()
30 39
31 # create default variables 40 for defaultconf in ToasterSetting.objects.filter(name__startswith="DEFCONF_"):
32 ProjectVariable.objects.create(project = prj, name = "MACHINE", value = "qemux86") 41 name = defaultconf.name[8:]
33 ProjectVariable.objects.create(project = prj, name = "DISTRO", value = "poky") 42 ProjectVariable.objects.create( project = prj,
34 43 name = name,
35 # create default layers 44 value = defaultconf.value)
36 ProjectLayer.objects.create(project = prj, 45
37 name = "meta", 46 for layer in map(lambda x: x.layer, ReleaseDefaultLayer.objects.filter(release = release)):
38 giturl = "git://git.yoctoproject.org/poky", 47 for branches in Branch.objects.filter(name = release.branch):
39 commit = branch, 48 for lv in Layer_Version.objects.filter(layer = layer, up_branch = branches ):
40 dirpath = "meta", 49 ProjectLayer.objects.create( project = prj,
41 optional = False) 50 layercommit = lv,
42 51 optional = False )
43 ProjectLayer.objects.create(project = prj,
44 name = "meta-yocto",
45 giturl = "git://git.yoctoproject.org/poky",
46 commit = branch,
47 dirpath = "meta-yocto",
48 optional = False)
49 52
50 return prj 53 return prj
51 54
@@ -57,8 +60,9 @@ class ProjectManager(models.Manager):
57 60
58class Project(models.Model): 61class Project(models.Model):
59 name = models.CharField(max_length=100) 62 name = models.CharField(max_length=100)
60 branch = models.CharField(max_length=50)
61 short_description = models.CharField(max_length=50, blank=True) 63 short_description = models.CharField(max_length=50, blank=True)
64 bitbake_version = models.ForeignKey('BitbakeVersion')
65 release = models.ForeignKey("Release")
62 created = models.DateTimeField(auto_now_add = True) 66 created = models.DateTimeField(auto_now_add = True)
63 updated = models.DateTimeField(auto_now = True) 67 updated = models.DateTimeField(auto_now = True)
64 # This is a horrible hack; since Toaster has no "User" model available when 68 # This is a horrible hack; since Toaster has no "User" model available when
@@ -70,10 +74,16 @@ class Project(models.Model):
70 74
71 75
72 def schedule_build(self): 76 def schedule_build(self):
73 from bldcontrol.models import BuildRequest, BRTarget, BRLayer, BRVariable 77 from bldcontrol.models import BuildRequest, BRTarget, BRLayer, BRVariable, BRBitbake
74 br = BuildRequest.objects.create(project = self) 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
75 for l in self.projectlayer_set.all(): 85 for l in self.projectlayer_set.all():
76 BRLayer.objects.create(req = br, name = l.name, giturl = l.giturl, commit = l.commit, dirpath = l.dirpath) 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)
77 for t in self.projecttarget_set.all(): 87 for t in self.projecttarget_set.all():
78 BRTarget.objects.create(req = br, target = t.target, task = t.task) 88 BRTarget.objects.create(req = br, target = t.target, task = t.task)
79 for v in self.projectvariable_set.all(): 89 for v in self.projectvariable_set.all():
@@ -83,7 +93,6 @@ class Project(models.Model):
83 br.save() 93 br.save()
84 return br 94 return br
85 95
86
87class Build(models.Model): 96class Build(models.Model):
88 SUCCEEDED = 0 97 SUCCEEDED = 0
89 FAILED = 1 98 FAILED = 1
@@ -359,8 +368,13 @@ class Package_File(models.Model):
359 368
360class Recipe(models.Model): 369class Recipe(models.Model):
361 search_allowed_fields = ['name', 'version', 'file_path', 'section', 'license', 'layer_version__layer__name', 'layer_version__branch', 'layer_version__commit', 'layer_version__layer__local_path'] 370 search_allowed_fields = ['name', 'version', 'file_path', 'section', 'license', 'layer_version__layer__name', 'layer_version__branch', 'layer_version__commit', 'layer_version__layer__local_path']
362 name = models.CharField(max_length=100, blank=True) 371
363 version = models.CharField(max_length=100, blank=True) 372 layer_source = models.ForeignKey('LayerSource', default = None, null = True) # from where did we get this recipe
373 up_id = models.IntegerField(null = True, default = None) # id of entry in the source
374 up_date = models.DateTimeField(null = True, default = None)
375
376 name = models.CharField(max_length=100, blank=True) # pn
377 version = models.CharField(max_length=100, blank=True) # pv
364 layer_version = models.ForeignKey('Layer_Version', related_name='recipe_layer_version') 378 layer_version = models.ForeignKey('Layer_Version', related_name='recipe_layer_version')
365 summary = models.CharField(max_length=100, blank=True) 379 summary = models.CharField(max_length=100, blank=True)
366 description = models.TextField(blank=True) 380 description = models.TextField(blank=True)
@@ -370,6 +384,9 @@ class Recipe(models.Model):
370 bugtracker = models.URLField(blank=True) 384 bugtracker = models.URLField(blank=True)
371 file_path = models.FilePathField(max_length=255) 385 file_path = models.FilePathField(max_length=255)
372 386
387 def __unicode__(self):
388 return "Recipe " + self.name + ":" + self.version
389
373class Recipe_DependencyManager(models.Manager): 390class Recipe_DependencyManager(models.Manager):
374 use_for_related_fields = True 391 use_for_related_fields = True
375 392
@@ -389,27 +406,311 @@ class Recipe_Dependency(models.Model):
389 dep_type = models.IntegerField(choices=DEPENDS_TYPE) 406 dep_type = models.IntegerField(choices=DEPENDS_TYPE)
390 objects = Recipe_DependencyManager() 407 objects = Recipe_DependencyManager()
391 408
392class ProjectLayer(models.Model):
393 project = models.ForeignKey(Project)
394 name = models.CharField(max_length = 100)
395 giturl = models.CharField(max_length = 254)
396 commit = models.CharField(max_length = 254)
397 dirpath = models.CharField(max_length = 254)
398 optional = models.BooleanField(default = True)
399 409
410class Machine(models.Model):
411 layer_source = models.ForeignKey('LayerSource', default = None, null = True) # from where did we get this machine
412 up_id = models.IntegerField(null = True, default = None) # id of entry in the source
413 up_date = models.DateTimeField(null = True, default = None)
414
415 layer_version = models.ForeignKey('Layer_Version')
416 name = models.CharField(max_length=255)
417 description = models.CharField(max_length=255)
418
419 def __unicode__(self):
420 return "Machine " + self.name + "(" + self.description + ")"
421
422 class Meta:
423 unique_together = ("layer_source", "up_id")
424
425
426from django.db.models.base import ModelBase
427
428class InheritanceMetaclass(ModelBase):
429 def __call__(cls, *args, **kwargs):
430 obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs)
431 return obj.get_object()
432
433
434class LayerSource(models.Model):
435 __metaclass__ = InheritanceMetaclass
436
437 class Meta:
438 unique_together = (('sourcetype', 'apiurl'), )
439
440 TYPE_LOCAL = 0
441 TYPE_LAYERINDEX = 1
442 SOURCE_TYPE = (
443 (TYPE_LOCAL, "local"),
444 (TYPE_LAYERINDEX, "layerindex"),
445 )
446
447 name = models.CharField(max_length=63)
448 sourcetype = models.IntegerField(choices=SOURCE_TYPE)
449 apiurl = models.CharField(max_length=255, null=True, default=None)
450
451 def save(self, *args, **kwargs):
452 if isinstance(self, LocalLayerSource):
453 self.sourcetype = LayerSource.TYPE_LOCAL
454 elif isinstance(self, LayerIndexLayerSource):
455 self.sourcetype = LayerSource.TYPE_LAYERINDEX
456 elif self.sourcetype == None:
457 raise Exception("Invalid LayerSource type")
458 return super(LayerSource, self).save(*args, **kwargs)
459
460 def get_object(self):
461 if self.sourcetype is not None:
462 if self.sourcetype == LayerSource.TYPE_LOCAL:
463 self.__class__ = LocalLayerSource
464 if self.sourcetype == LayerSource.TYPE_LAYERINDEX:
465 self.__class__ = LayerIndexLayerSource
466 return self
467
468 return "LS " + self.sourcetype + " " + self.name
469
470
471class LocalLayerSource(LayerSource):
472 class Meta(LayerSource._meta.__class__):
473 proxy = True
474
475 def __init__(self, *args, **kwargs):
476 super(LocalLayerSource, self).__init__(args, kwargs)
477 self.sourcetype = LayerSource.TYPE_LOCAL
478
479 def update(self):
480 '''
481 Fetches layer, recipe and machine information from local repository
482 '''
483 pass
484
485class LayerIndexLayerSource(LayerSource):
486 class Meta(LayerSource._meta.__class__):
487 proxy = True
488
489 def __init__(self, *args, **kwargs):
490 super(LayerIndexLayerSource, self).__init__(args, kwargs)
491 self.sourcetype = LayerSource.TYPE_LAYERINDEX
492
493 def update(self):
494 '''
495 Fetches layer, recipe and machine information from remote repository
496 '''
497 assert self.apiurl is not None
498
499 def _get_json_response(apiurl = self.apiurl):
500 import httplib, urlparse, json
501 parsedurl = urlparse.urlparse(apiurl)
502 (host, port) = parsedurl.netloc.split(":")
503 if port is None:
504 port = 80
505 else:
506 port = int(port)
507 #print "-- connect to: http://%s:%s%s?%s" % (host, port, parsedurl.path, parsedurl.query)
508 conn = httplib.HTTPConnection(host, port)
509 conn.request("GET", parsedurl.path + "?" + parsedurl.query)
510 r = conn.getresponse()
511 if r.status != 200:
512 raise Exception("Failed to read " + parsedurl.path + ": %d %s" % (r.status, r.reason))
513 return json.loads(r.read())
514
515 # verify we can get the basic api
516 try:
517 apilinks = _get_json_response()
518 except:
519 print "EE: could not connect to %s, skipping update" % self.apiurl
520 return
521
522 # update branches; only those that we already have names listed in the database
523 whitelist_branch_names = self.branchnames.split(",")
524
525 branches_info = _get_json_response(apilinks['branches']
526 + "?filter=name:%s" % "OR".join(whitelist_branch_names))
527 for bi in branches_info:
528 try:
529 b = Branch.objects.get(layer_source = self, name = bi['name'])
530 b.up_id = bi['id']
531 b.up_date = bi['updated']
532 b.name = bi['name']
533 b.bitbake_branch = bi['bitbake_branch']
534 b.short_description = bi['short_description']
535 b.save()
536 except Branch.DoesNotExist:
537 b = Branch.objects.create(
538 layer_source = self,
539 up_id = bi['id'],
540 up_date = bi['updated'],
541 name = bi['name'],
542 bitbake_branch = bi['bitbake_branch'],
543 short_description = bi['short_description']
544 )
545
546 # update layers
547 layers_info = _get_json_response(apilinks['layerItems'])
548 for li in layers_info:
549 try:
550 l = Layer.objects.get(layer_source = self,
551 up_id = li['id'])
552 l.update(
553 up_date = li['updated'],
554 name = li['name'],
555 vcs_url = li['vcs_url'],
556 vcs_web_file_base_url = li['vcs_url'],
557 summary = li['summary'],
558 description = li['description'])
559 except Layer.DoesNotExist:
560 Layer.objects.create(layer_source = self,
561 up_id = li['id'],
562 up_date = li['updated'],
563 name = li['name'],
564 vcs_url = li['vcs_url'],
565 vcs_web_file_base_url = li['vcs_url'],
566 summary = li['summary'],
567 description = li['description']
568 )
569
570 # update layerbranches/layer_versions
571 layerbranches_info = _get_json_response(apilinks['layerBranches']
572 + "?filter=branch:%s" % "OR".join(map(lambda x: str(x.up_id), Branch.objects.filter(layer_source = self)))
573 )
574 for lbi in layerbranches_info:
575 Layer_Version.objects.get_or_create(layer_source = self,
576 up_id = lbi['id'],
577 up_date = lbi['updated'],
578 layer = Layer.objects.get(layer_source = self, up_id = lbi['layer']),
579 up_branch = Branch.objects.get(layer_source = self, up_id = lbi['branch']),
580 branch = lbi['actual_branch'],
581 commit = lbi['vcs_last_rev'],
582 dirpath = lbi['vcs_subdir'])
583
584 # update machines
585 machines_info = _get_json_response(apilinks['machines']
586 + "?filter=layerbranch:%s" % "OR".join(map(lambda x: str(x.up_id), Layer_Version.objects.filter(layer_source = self)))
587 )
588 for mi in machines_info:
589 Machine.objects.get_or_create(layer_source = self,
590 up_id = mi['id'],
591 up_date = mi['updated'],
592 layer_version = Layer_Version.objects.get(layer_source = self, up_id = mi['layerbranch']),
593 name = mi['name'],
594 description = mi['description'])
595
596 # update recipes; paginate by layer version / layer branch
597 recipes_info = _get_json_response(apilinks['recipes']
598 + "?filter=layerbranch:%s" % "OR".join(map(lambda x: str(x.up_id), Layer_Version.objects.filter(layer_source = self)))
599 )
600 for ri in recipes_info:
601 Recipe.objects.get_or_create(layer_source = self,
602 up_id = ri['id'],
603 up_date = ri['updated'],
604 layer_version = Layer_Version.objects.get(layer_source = self, up_id = mi['layerbranch']),
605
606 name = ri['pn'],
607 version = ri['pv'],
608 summary = ri['summary'],
609 description = ri['description'],
610 section = ri['section'],
611 license = ri['license'],
612 homepage = ri['homepage'],
613 bugtracker = ri['bugtracker'],
614 file_path = ri['filepath'] + ri['filename']
615 )
616
617 pass
618
619class BitbakeVersion(models.Model):
620 name = models.CharField(max_length=32, unique = True)
621 giturl = models.URLField()
622 branch = models.CharField(max_length=32)
623 dirpath = models.CharField(max_length=255)
624
625
626class Release(models.Model):
627 name = models.CharField(max_length=32, unique = True)
628 description = models.CharField(max_length=255)
629 bitbake_version = models.ForeignKey(BitbakeVersion)
630 branch = models.CharField(max_length=32)
631
632
633class ReleaseDefaultLayer(models.Model):
634 release = models.ForeignKey(Release)
635 layer = models.ForeignKey('Layer')
636
637
638# Branch class is synced with layerindex.Branch, branches can only come from remote layer indexes
639class Branch(models.Model):
640 layer_source = models.ForeignKey('LayerSource', null = True, default = True)
641 up_id = models.IntegerField(null = True, default = None) # id of branch in the source
642 up_date = models.DateTimeField(null = True, default = None)
643
644 name = models.CharField(max_length=50)
645 bitbake_branch = models.CharField(max_length=50, blank=True)
646 short_description = models.CharField(max_length=50, blank=True)
647
648 class Meta:
649 verbose_name_plural = "Branches"
650 unique_together = (('layer_source', 'name'),('layer_source', 'up_id'))
651
652 def __unicode__(self):
653 return self.name
654
655
656# Layer class synced with layerindex.LayerItem
400class Layer(models.Model): 657class Layer(models.Model):
658 layer_source = models.ForeignKey(LayerSource, null = True, default = None) # from where did we got this layer
659 up_id = models.IntegerField(null = True, default = None) # id of layer in the remote source
660 up_date = models.DateTimeField(null = True, default = None)
661
401 name = models.CharField(max_length=100) 662 name = models.CharField(max_length=100)
402 local_path = models.FilePathField(max_length=255) 663 local_path = models.FilePathField(max_length=255, null = True, default = None)
403 layer_index_url = models.URLField() 664 layer_index_url = models.URLField()
665 vcs_url = models.URLField(default = None, null = True)
666 vcs_web_file_base_url = models.URLField(null = True, default = None)
667
668 summary = models.CharField(max_length=200, help_text='One-line description of the layer', null = True, default = None)
669 description = models.TextField(null = True, default = None)
670
671 def __unicode__(self):
672 return "L " + self.name
673
674 class Meta:
675 unique_together = (("layer_source", "up_id"), ("layer_source", "name"))
404 676
405 677
678# LayerCommit class is synced with layerindex.LayerBranch
406class Layer_Version(models.Model): 679class Layer_Version(models.Model):
407 build = models.ForeignKey(Build, related_name='layer_version_build') 680 search_allowed_fields = ["layer__name", "layer__summary",]
681 build = models.ForeignKey(Build, related_name='layer_version_build', default = None, null = True)
408 layer = models.ForeignKey(Layer, related_name='layer_version_layer') 682 layer = models.ForeignKey(Layer, related_name='layer_version_layer')
409 branch = models.CharField(max_length=50)
410 commit = models.CharField(max_length=100)
411 priority = models.IntegerField()
412 683
684 layer_source = models.ForeignKey(LayerSource, null = True, default = None) # from where did we get this Layer Version
685 up_id = models.IntegerField(null = True, default = None) # id of layerbranch in the remote source
686 up_date = models.DateTimeField(null = True, default = None)
687 up_branch = models.ForeignKey(Branch, null = True, default = None)
688
689 branch = models.CharField(max_length=80) # LayerBranch.actual_branch
690 commit = models.CharField(max_length=100) # LayerBranch.vcs_last_rev
691 dirpath = models.CharField(max_length=255, null = True, default = None) # LayerBranch.vcs_subdir
692 priority = models.IntegerField(default = 0) # if -1, this is a default layer
693
694 def __unicode__(self):
695 return "LV " + str(self.layer) + " " + self.commit
696
697 class Meta:
698 unique_together = ("layer_source", "up_id")
699
700class LayerVersionDependency(models.Model):
701 layer_source = models.ForeignKey(LayerSource, null = True, default = None) # from where did we got this layer
702 up_id = models.IntegerField(null = True, default = None) # id of layerbranch in the remote source
703
704 layer_version = models.ForeignKey(Layer_Version, related_name="dependencies")
705 depends_on = models.ForeignKey(Layer_Version, related_name="dependees")
706
707 class Meta:
708 unique_together = ("layer_source", "up_id")
709
710class ProjectLayer(models.Model):
711 project = models.ForeignKey(Project)
712 layercommit = models.ForeignKey(Layer_Version, null=True)
713 optional = models.BooleanField(default = True)
413 714
414class ProjectVariable(models.Model): 715class ProjectVariable(models.Model):
415 project = models.ForeignKey(Project) 716 project = models.ForeignKey(Project)