Skip to contentSkip to conversation

toaster: cummulative 26062017 patch

PATCH New David Reyna 1 participant 6 messages Jun 27, 2017

lore
Share & tools
lore
#0 lore raw

cover toaster: cummulative 26062017 patch

From: David Reyna <[email protected]>

*** NOTE for MERGE: New Files: ***

 create mode 100644 bitbake/lib/toaster/orm/migrations/0016_clone_progress.py
 create mode 100644 bitbake/lib/toaster/orm/migrations/0017_distro_clone.py
 create mode 100644 bitbake/lib/toaster/toastergui/templates/distro_btn.html

I have been advised that: "If the patch creates any NEW files, the integration scripts that pull it into bitbake will fail. So, if new files are created as part of this patch set, you need to explicitly point that out in the body of the email for the patch set"

- David

=================

The following changes since commit b8358bc61435ec996ecd6931fb6d6d41bb4f710b:

bitbake: bitbake-selftest: add bb.tests.event to bitbake-selftest (2017-06-23 14:14:17 +0100)

are available in the git repository at:

git://git.yoctoproject.org/poky-contrib dreyna/submit/dreyna/toaster/cummulative_062017_patch
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=dreyna/submit/dreyna/toaster/cummulative_062017_patch

David Reyna (5):
toaster: address Django-1.10 API deprecations
toaster: git clone progress bar
toaster: Add distro selection support
toaster: large package set breaks sqlite query
toaster: test 'commit' first in get_vcs_reference

 bin/toaster                                        | 10 ++-
 lib/toaster/bldcollector/urls.py                   | 10 ++-
 lib/toaster/bldcontrol/localhostbecontroller.py    | 12 +++
 .../management/commands/checksettings.py           |  6 +-
 .../bldcontrol/management/commands/runbuilds.py    |  6 +-
 lib/toaster/orm/management/commands/lsupdates.py   | 27 ++++++-
 lib/toaster/orm/migrations/0016_clone_progress.py  | 24 ++++++
 lib/toaster/orm/migrations/0017_distro_clone.py    | 25 ++++++
 lib/toaster/orm/models.py                          | 50 +++++++++++-
 lib/toaster/toastergui/api.py                      | 13 +++-
 lib/toaster/toastergui/static/js/mrbsection.js     | 15 ++++
 lib/toaster/toastergui/static/js/projectpage.js    | 73 +++++++++++++++++
 lib/toaster/toastergui/tables.py                   | 91 ++++++++++++++++++++++
 lib/toaster/toastergui/templates/base.html         |  1 +
 .../toastergui/templates/baseprojectpage.html      |  1 +
 lib/toaster/toastergui/templates/distro_btn.html   | 20 +++++
 lib/toaster/toastergui/templates/mrb_section.html  | 29 ++++++-
 lib/toaster/toastergui/templates/project.html      | 16 ++++
 lib/toaster/toastergui/typeaheads.py               | 30 +++++++
 lib/toaster/toastergui/urls.py                     | 69 +++++++++-------
 lib/toaster/toastergui/views.py                    | 13 +++-
 lib/toaster/toastergui/widgets.py                  |  4 +
 .../toastermain/management/commands/buildslist.py  |  6 +-
 lib/toaster/toastermain/settings.py                | 70 ++++++++++-------
 lib/toaster/toastermain/urls.py                    |  8 +-
 toaster-requirements.txt                           |  2 +-
 26 files changed, 539 insertions(+), 92 deletions(-)
 create mode 100644 bitbake/lib/toaster/orm/migrations/0016_clone_progress.py
 create mode 100644 bitbake/lib/toaster/orm/migrations/0017_distro_clone.py
 create mode 100644 bitbake/lib/toaster/toastergui/templates/distro_btn.html
Signature · 1 line
1.9.1
#1 lore raw

PATCH 1/5 toaster: address Django-1.10 API deprecations

From: David Reyna <[email protected]>

There are four main API deprecations in Django-1.10:
(a) String view arguments to url() must be replaced by the explicit class reference
(b) New TEMPLATES stucture in settings.py consolidates TEMPLATE_DIRS, TEMPLATE_CONTEXT_PROCESSORS, TEMPLATE_LOADERS, TEMPLATE_STRING_IF_INVALID, and TEMPLATE_DEBUG
(c) patterns() wrapper in url() is removed, with urlpatterns now a simple list
(d) NoArgsCommand in commands() must be replace by BaseCommand, and handle_noargs() changed to handle()

Also, the Django version checker must be updated to accept two digit sub-version numbers (e.g. "1.8" < "1.10")

[YOCTO #11684]

---
 bin/toaster                                        | 10 +++-
 lib/toaster/bldcollector/urls.py                   | 10 ++--
 .../management/commands/checksettings.py           |  6 +-
 .../bldcontrol/management/commands/runbuilds.py    |  6 +-
 lib/toaster/orm/management/commands/lsupdates.py   |  8 +--
 lib/toaster/toastergui/urls.py                     | 61 +++++++++----------
 .../toastermain/management/commands/buildslist.py  |  6 +-
 lib/toaster/toastermain/settings.py                | 70 +++++++++++++---------
 lib/toaster/toastermain/urls.py                    |  8 ++-
 toaster-requirements.txt                           |  2 +-
 10 files changed, 105 insertions(+), 82 deletions(-)
bin/toaster +8 -2
index 61a4a0f..c2e33fe 100755
@@ -116,8 +116,14 @@ verify_prereq() {
     # Verify Django version
     reqfile=$(python3 -c "import os; print(os.path.realpath('$BBBASEDIR/toaster-requirements.txt'))")
     exp='s/Django\([><=]\+\)\([^,]\+\),\([><=]\+\)\(.\+\)/'
-    exp=$exp'import sys,django;version=django.get_version().split(".");'
-    exp=$exp'sys.exit(not (version \1 "\2".split(".") and version \3 "\4".split(".")))/p'
+    # expand version parts to 2 digits to support 1.10.x > 1.8
+    # (note:helper functions hard to insert in-line)
+    exp=$exp'import sys,django;'
+    exp=$exp'version=["%02d" % int(n) for n in django.get_version().split(".")];'
+    exp=$exp'vmin=["%02d" % int(n) for n in "\2".split(".")];'
+    exp=$exp'vmax=["%02d" % int(n) for n in "\4".split(".")];'
+    exp=$exp'sys.exit(not (version \1 vmin and version \3 vmax))'
+    exp=$exp'/p'
     if ! sed -n "$exp" $reqfile | python3 - ; then
         req=`grep ^Django $reqfile`
         echo "This program needs $req"
lib/toaster/bldcollector/urls.py +6 -4
index 64722f2..076afdb 100644
@@ -1,7 +1,7 @@
 #
 # BitBake Toaster Implementation
 #
-# Copyright (C) 2014        Intel Corporation
+# Copyright (C) 2014-2017   Intel Corporation
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 as
@@ -19,7 +19,9 @@
 
 from django.conf.urls import patterns, include, url
 
-urlpatterns = patterns('bldcollector.views',
+import bldcollector.views
+
+urlpatterns = [
         # landing point for pushing a bitbake_eventlog.json file to this toaster instace
-        url(r'^eventfile$', 'eventfile', name='eventfile'),
-        )
+        url(r'^eventfile$', bldcollector.views.eventfile, name='eventfile'),
+]
lib/toaster/bldcontrol/management/commands/checksettings.py +3 -3
index 2ed994f..d11166e 100644
@@ -1,4 +1,4 @@
-from django.core.management.base import NoArgsCommand, CommandError
+from django.core.management.base import BaseCommand, CommandError
 from django.db import transaction
 
 from django.core.management import call_command
@@ -18,7 +18,7 @@ def DN(path):
         return os.path.dirname(path)
 
 
-class Command(NoArgsCommand):
+class Command(BaseCommand):
     args = ""
     help = "Verifies that the configured settings are valid and usable, or prompts the user to fix the settings."
 
@@ -152,7 +152,7 @@ class Command(NoArgsCommand):
 
 
 
-    def handle_noargs(self, **options):
+    def handle(self, **options):
         retval = 0
         retval += self._verify_build_environment()
         retval += self._verify_default_settings()
lib/toaster/bldcontrol/management/commands/runbuilds.py +3 -3
index df11f9d..8eacb5e 100644
@@ -1,4 +1,4 @@
-from django.core.management.base import NoArgsCommand
+from django.core.management.base import BaseCommand
 from django.db import transaction
 from django.db.models import Q
 
@@ -16,7 +16,7 @@ import os
 logger = logging.getLogger("toaster")
 
 
-class Command(NoArgsCommand):
+class Command(BaseCommand):
     args = ""
     help = "Schedules and executes build requests as possible. "\
            "Does not return (interrupt with Ctrl-C)"
@@ -168,7 +168,7 @@ class Command(NoArgsCommand):
         except Exception as e:
             logger.warn("runbuilds: schedule exception %s" % str(e))
 
-    def handle_noargs(self, **options):
+    def handle(self, **options):
         pidfile_path = os.path.join(os.environ.get("BUILDDIR", "."),
                                     ".runbuilds.pid")
 
lib/toaster/orm/management/commands/lsupdates.py +4 -4
index 68c6c42..90f07c9 100644
@@ -4,7 +4,7 @@
 #
 # BitBake Toaster Implementation
 #
-# Copyright (C) 2016        Intel Corporation
+# Copyright (C) 2016-2017   Intel Corporation
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 as
@@ -19,7 +19,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-from django.core.management.base import NoArgsCommand
+from django.core.management.base import BaseCommand
 
 from orm.models import LayerSource, Layer, Release, Layer_Version
 from orm.models import LayerVersionDependency, Machine, Recipe
@@ -56,7 +56,7 @@ class Spinner(threading.Thread):
         self.signal = False
 
 
-class Command(NoArgsCommand):
+class Command(BaseCommand):
     args = ""
     help = "Updates locally cached information from a layerindex server"
 
@@ -307,5 +307,5 @@ class Command(NoArgsCommand):
 
         os.system('setterm -cursor on')
 
-    def handle_noargs(self, **options):
+    def handle(self, **options):
         self.update()
lib/toaster/toastergui/urls.py +31 -30

158 lines not on this page · raw message

lib/toaster/toastermain/management/commands/buildslist.py +3 -3
index 8dfef0a..70b5812 100644
@@ -1,13 +1,13 @@
-from django.core.management.base import NoArgsCommand, CommandError
+from django.core.management.base import BaseCommand, CommandError
 from orm.models import Build
 import os
 
 
 
-class Command(NoArgsCommand):
+class Command(BaseCommand):
     args    = ""
     help    = "Lists current builds"
 
-    def handle_noargs(self,**options):
+    def handle(self,**options):
         for b in Build.objects.all():
             print("%d: %s %s %s" % (b.pk, b.machine, b.distro, ",".join([x.target for x in b.target_set.all()])))
lib/toaster/toastermain/settings.py +41 -29

92 lines not on this page · raw message

lib/toaster/toastermain/urls.py +5 -3
index 1f8599e..bb32559 100644
@@ -23,6 +23,8 @@ from django.conf.urls import patterns, include, url
 from django.views.generic import RedirectView
 from django.views.decorators.cache import never_cache
 
+import bldcollector.views
+
 import logging
 
 logger = logging.getLogger("toaster")
@@ -31,7 +33,7 @@ logger = logging.getLogger("toaster")
 from django.contrib import admin
 admin.autodiscover()
 
-urlpatterns = patterns('',
+urlpatterns = [
 
     # Examples:
     # url(r'^toaster/', include('toaster.foo.urls')),
@@ -42,11 +44,11 @@ urlpatterns = patterns('',
 
     # This is here to maintain backward compatibility and will be deprecated
     # in the future.
-    url(r'^orm/eventfile$', 'bldcollector.views.eventfile'),
+    url(r'^orm/eventfile$', bldcollector.views.eventfile),
 
     # if no application is selected, we have the magic toastergui app here
     url(r'^$', never_cache(RedirectView.as_view(url='/toastergui/', permanent=True))),
-)
+]
 
 import toastermain.settings
 
toaster-requirements.txt +1 -1
index e61c8e2..3f47650 100644
@@ -1,3 +1,3 @@
-Django>1.8,<1.9
+Django>1.8,<1.9.9
 beautifulsoup4>=4.4.0
 pytz
Signature · 1 line
1.9.1
#2 lore raw

PATCH 2/5 toaster: git clone progress bar

In reply to David Reyna

From: David Reyna <[email protected]>

If a project has a lot of additional layers, the build may appear to hang while those layers are checked out. This patch adds a clone progress bar that is visible before the parsing progress appears.

[YOCTO #9916]

---
 lib/toaster/bldcontrol/localhostbecontroller.py   | 12 ++++++++++
 lib/toaster/orm/migrations/0016_clone_progress.py | 24 +++++++++++++++++++
 lib/toaster/orm/models.py                         | 15 ++++++++++++
 lib/toaster/toastergui/static/js/mrbsection.js    | 15 ++++++++++++
 lib/toaster/toastergui/templates/mrb_section.html | 29 ++++++++++++++++++++++-
 lib/toaster/toastergui/widgets.py                 |  4 ++++
 6 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100644 bitbake/lib/toaster/orm/migrations/0016_clone_progress.py
lib/toaster/bldcontrol/localhostbecontroller.py +12 -0
index 62e62fe..2916246 100644
@@ -85,6 +85,11 @@ class LocalhostBEController(BuildEnvironmentController):
         return local_checkout_path
 
 
+    def setCloneStatus(self,bitbake,status,total,current):
+        bitbake.req.build.repos_cloned=current
+        bitbake.req.build.repos_to_clone=total
+        bitbake.req.build.save()
+
     def setLayers(self, bitbake, layers, targets):
         """ a word of attention: by convention, the first layer for any build will be poky! """
 
@@ -147,7 +152,13 @@ class LocalhostBEController(BuildEnvironmentController):
         logger.info("Using pre-checked out source for layer %s", cached_layers)
 
         # 3. checkout the repositories
+        clone_count=0
+        clone_total=len(gitrepos.keys())
+        self.setCloneStatus(bitbake,'Started',clone_total,clone_count)
         for giturl, commit in gitrepos.keys():
+            self.setCloneStatus(bitbake,'progress',clone_total,clone_count)
+            clone_count += 1
+
             localdirname = os.path.join(self.be.sourcedir, self.getGitCloneDirectory(giturl, commit))
             logger.debug("localhostbecontroller: giturl %s:%s checking out in current directory %s" % (giturl, commit, localdirname))
 
@@ -198,6 +209,7 @@ class LocalhostBEController(BuildEnvironmentController):
                 if name != "bitbake":
                     layerlist.append(localdirpath.rstrip("/"))
 
+        self.setCloneStatus(bitbake,'complete',clone_total,clone_count)
         logger.debug("localhostbecontroller: current layer list %s " % pformat(layerlist))
 
         # 5. create custom layer and add custom recipes to it
lib/toaster/orm/migrations/0016_clone_progress.py new file +24 -0
new file mode 100644
index 0000000..852b878
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('orm', '0015_layer_local_source_dir'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='build',
+            name='repos_cloned',
+            field=models.IntegerField(default=0),
+        ),
+        migrations.AddField(
+            model_name='build',
+            name='repos_to_clone',
+            field=models.IntegerField(default=1),
+        ),
+    ]
+
lib/toaster/orm/models.py +15 -0
index a49f9a4..13bd117 100644
@@ -437,6 +437,12 @@ class Build(models.Model):
     # number of recipes parsed so far for this build
     recipes_parsed = models.IntegerField(default=0)
 
+    # number of repos to clone for this build
+    repos_to_clone = models.IntegerField(default=1)
+
+    # number of repos cloned so far for this build
+    repos_cloned = models.IntegerField(default=0)
+
     @staticmethod
     def get_recent(project=None):
         """
@@ -667,6 +673,13 @@ class Build(models.Model):
         else:
             return False
 
+    def is_cloning(self):
+        """
+        True if the build is still cloning repos
+        """
+        return self.outcome == Build.IN_PROGRESS and \
+            self.repos_cloned < self.repos_to_clone
+
     def is_parsing(self):
         """
         True if the build is still parsing recipes
@@ -698,6 +711,8 @@ class Build(models.Model):
             return 'Cancelling';
         elif self.is_queued():
             return 'Queued'
+        elif self.is_cloning():
+            return 'Cloning'
         elif self.is_parsing():
             return 'Parsing'
         elif self.is_starting():
lib/toaster/toastergui/static/js/mrbsection.js +15 -0
index 73d0935..c0c5fa9 100644
@@ -61,6 +61,12 @@ function mrbSectionInit(ctx){
     return (cached.recipes_parsed_percentage !== build.recipes_parsed_percentage);
   }
 
+  // returns true if the number of repos cloned/to clone changed
+  function cloneProgressChanged(build) {
+    var cached = getCached(build);
+    return (cached.repos_cloned_percentage !== build.repos_cloned_percentage);
+  }
+
   function refreshMostRecentBuilds(){
     libtoaster.getMostRecentBuilds(
       libtoaster.ctx.mostRecentBuildsUrl,
@@ -100,6 +106,15 @@ function mrbSectionInit(ctx){
 
             container.html(html);
           }
+          else if (cloneProgressChanged(build)) {
+            // update the clone progress text
+            selector = '#repos-cloned-percentage-' + build.id;
+            $(selector).html(build.repos_cloned_percentage);
+
+            // update the recipe progress bar
+            selector = '#repos-cloned-percentage-bar-' + build.id;
+            $(selector).width(build.repos_cloned_percentage + '%');
+          }
           else if (tasksProgressChanged(build)) {
             // update the task progress text
             selector = '#build-pc-done-' + build.id;
lib/toaster/toastergui/templates/mrb_section.html +28 -1
index b761ffe..c5b9fe9 100644
@@ -64,7 +64,9 @@
   </div>
 
   <div data-build-state="<%:state%>">
-    <%if state == 'Parsing'%>
+    <%if state == 'Cloning'%>
+      <%include tmpl='#cloning-repos-build-template'/%>
+    <%else state == 'Parsing'%>
       <%include tmpl='#parsing-recipes-build-template'/%>
     <%else state == 'Queued'%>
       <%include tmpl='#queued-build-template'/%>
@@ -98,6 +100,31 @@
   </div>
 </script>
 
+<!-- cloning repos build -->
+<script id="cloning-repos-build-template" type="text/x-jsrender">
+  <!-- progress bar and parse completion percentage -->
+  <div data-role="build-status" class="col-md-4 col-md-offset-1 progress-info">
+    <!-- progress bar -->
+    <div class="progress">
+      <div id="repos-cloned-percentage-bar-<%:id%>"
+           style="width: <%:repos_cloned_percentage%>%;"
+           class="progress-bar">
+      </div>
+    </div>
+  </div>
+
+  <div class="col-md-4 progress-info">
+    <!-- parse completion percentage -->
+    <span class="glyphicon glyphicon-question-sign get-help get-help-blue"
+          title="Toaster is cloning the repos required for your build">
+    </span>
+
+    Cloning <span id="repos-cloned-percentage-<%:id%>"><%:repos_cloned_percentage%></span>% complete
+
+    <%include tmpl='#cancel-template'/%>
+  </div>
+</script>
+
 <!-- parsing recipes build -->
 <script id="parsing-recipes-build-template" type="text/x-jsrender">
   <!-- progress bar and parse completion percentage -->
lib/toaster/toastergui/widgets.py +4 -0
index 6b7b981..67c1ff9 100644
@@ -511,6 +511,10 @@ class MostRecentBuildsView(View):
                 int((build_obj.recipes_parsed /
                      build_obj.recipes_to_parse) * 100)
 
+            build['repos_cloned_percentage'] = \
+                int((build_obj.repos_cloned /
+                     build_obj.repos_to_clone) * 100)
+
             tasks_complete_percentage = 0
             if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED):
                 tasks_complete_percentage = 100
Signature · 1 line
1.9.1
#3 lore raw

PATCH 3/5 toaster: Add distro selection support

In reply to David Reyna

From: David Reyna <[email protected]>

Add the ability to select a distro in the project page, based on values from the Layer Index. Add a distro selection page with the add layer feature, based on the add machine page.

[YOCTO #10632]

---
 lib/toaster/orm/management/commands/lsupdates.py   | 19 +++++
 lib/toaster/orm/migrations/0017_distro_clone.py    | 25 ++++++
 lib/toaster/orm/models.py                          | 31 ++++++++
 lib/toaster/toastergui/api.py                      | 13 +++-
 lib/toaster/toastergui/static/js/projectpage.js    | 73 +++++++++++++++++
 lib/toaster/toastergui/tables.py                   | 91 ++++++++++++++++++++++
 lib/toaster/toastergui/templates/base.html         |  1 +
 .../toastergui/templates/baseprojectpage.html      |  1 +
 lib/toaster/toastergui/templates/distro_btn.html   | 20 +++++
 lib/toaster/toastergui/templates/project.html      | 16 ++++
 lib/toaster/toastergui/typeaheads.py               | 30 +++++++
 lib/toaster/toastergui/urls.py                     |  8 ++
 12 files changed, 325 insertions(+), 3 deletions(-)
 create mode 100644 bitbake/lib/toaster/orm/migrations/0017_distro_clone.py
 create mode 100644 bitbake/lib/toaster/toastergui/templates/distro_btn.html
lib/toaster/orm/management/commands/lsupdates.py +19 -0
index 90f07c9..0b0d4ff 100644
@@ -23,6 +23,7 @@ from django.core.management.base import BaseCommand
 
 from orm.models import LayerSource, Layer, Release, Layer_Version
 from orm.models import LayerVersionDependency, Machine, Recipe
+from orm.models import Distro
 
 import os
 import sys
@@ -249,6 +250,24 @@ class Command(BaseCommand):
                                                              depends_on=lvd)
             self.mini_progress("Layer version dependencies", i, total)
 
+        # update Distros
+        logger.info("Fetching distro information")
+        distros_info = _get_json_response(
+            apilinks['distros'] + "?filter=layerbranch__branch__name:%s" %
+            "OR".join(whitelist_branch_names))
+
+        total = len(distros_info)
+        for i, di in enumerate(distros_info):
+            distro, created = Distro.objects.get_or_create(
+                name=di['name'],
+                layer_version=Layer_Version.objects.get(
+                    pk=li_layer_branch_id_to_toaster_lv_id[di['layerbranch']]))
+            distro.up_date = di['updated']
+            distro.name = di['name']
+            distro.description = di['description']
+            distro.save()
+            self.mini_progress("distros", i, total)
+
         # update machines
         logger.info("Fetching machine information")
         machines_info = _get_json_response(
lib/toaster/orm/migrations/0017_distro_clone.py new file +25 -0
new file mode 100644
index 0000000..d3c5901
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('orm', '0016_clone_progress'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Distro',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('up_id', models.IntegerField(default=None, null=True)),
+                ('up_date', models.DateTimeField(default=None, null=True)),
+                ('name', models.CharField(max_length=255)),
+                ('description', models.CharField(max_length=255)),
+                ('layer_version', models.ForeignKey(to='orm.Layer_Version')),
+            ],
+        ),
+    ]
+
lib/toaster/orm/models.py +31 -0
index 13bd117..5c14727 100644
@@ -321,6 +321,22 @@ class Project(models.Model):
 
         return queryset
 
+    def get_available_distros(self):
+        """ Returns QuerySet of all Distros which are provided by the
+        Layers currently added to the Project """
+        queryset = Distro.objects.filter(
+            layer_version__in=self.get_project_layer_versions())
+
+        return queryset
+
+    def get_all_compatible_distros(self):
+        """ Returns QuerySet of all the compatible Wind River distros available to the
+        project including ones from Layers not currently added """
+        queryset = Distro.objects.filter(
+            layer_version__in=self.get_all_compatible_layer_versions())
+
+        return queryset
+
     def get_available_recipes(self):
         """ Returns QuerySet of all the recipes that are provided by layers
         added to this project """
@@ -1795,6 +1811,21 @@ def signal_runbuilds():
     except FileNotFoundError:
         logger.info("Stopping existing runbuilds: no current process found")
 
+class Distro(models.Model):
+    search_allowed_fields = ["name", "description", "layer_version__layer__name"]
+    up_date = models.DateTimeField(null = True, default = None)
+
+    layer_version = models.ForeignKey('Layer_Version')
+    name = models.CharField(max_length=255)
+    description = models.CharField(max_length=255)
+
+    def get_vcs_distro_file_link_url(self):
+        path = self.name+'.conf'
+        return self.layer_version.get_vcs_file_link_url(path)
+
+    def __unicode__(self):
+        return "Distro " + self.name + "(" + self.description + ")"
+
 django.db.models.signals.post_save.connect(invalidate_cache)
 django.db.models.signals.post_delete.connect(invalidate_cache)
 django.db.models.signals.m2m_changed.connect(invalidate_cache)
lib/toaster/toastergui/api.py +10 -3
index 5b03550..88d6aa7 100644
@@ -874,6 +874,12 @@ class XhrProject(View):
             machinevar.value = request.POST['machineName']
             machinevar.save()
 
+        # Distro name change
+        if 'distroName' in request.POST:
+            distrovar = prj.projectvariable_set.get(name="DISTRO")
+            distrovar.value = request.POST['distroName']
+            distrovar.save()
+
         return JsonResponse({"error": "ok"})
 
     def get(self, request, *args, **kwargs):
@@ -960,10 +966,11 @@ class XhrProject(View):
         except ProjectVariable.DoesNotExist:
             data["machine"] = None
         try:
-            data["distro"] = project.projectvariable_set.get(
-                name="DISTRO").value
+            data["distro"] = {"name":
+                               project.projectvariable_set.get(
+                                   name="DISTRO").value}
         except ProjectVariable.DoesNotExist:
-            data["distro"] = "-- not set yet"
+            data["distro"] = None
 
         data['error'] = "ok"
 
lib/toaster/toastergui/static/js/projectpage.js +73 -0

97 lines not on this page · raw message

lib/toaster/toastergui/tables.py +91 -0

100 lines not on this page · raw message

lib/toaster/toastergui/templates/base.html +1 -0
index 11c6f91..0fbe17b 100644
@@ -49,6 +49,7 @@
         recipesTypeAheadUrl: {% url 'xhr_recipestypeahead' project.id as paturl%}{{paturl|json}},
         layersTypeAheadUrl: {% url 'xhr_layerstypeahead' project.id as paturl%}{{paturl|json}},
         machinesTypeAheadUrl: {% url 'xhr_machinestypeahead' project.id as paturl%}{{paturl|json}},
+        distrosTypeAheadUrl: {% url 'xhr_distrostypeahead' project.id as paturl%}{{paturl|json}},
         projectBuildsUrl: {% url 'projectbuilds' project.id as pburl %}{{pburl|json}},
         xhrCustomRecipeUrl : "{% url 'xhr_customrecipe' %}",
         projectId : {{project.id}},
lib/toaster/toastergui/templates/baseprojectpage.html +1 -0
index 8427d25..f2bb2eb 100644
@@ -32,6 +32,7 @@ $(document).ready(function(){
       <li><a href="{% url 'projectsoftwarerecipes' project.id %}">Software recipes</a></li>
       <li><a href="{% url 'projectmachines' project.id %}">Machines</a></li>
       <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li>
+      <li><a href="{% url 'projectdistros' project.id %}">Distros</a></li>
       <li class="nav-header">Extra configuration</li>
       <li><a href="{% url 'projectconf' project.id %}">BitBake variables</a></li>
 
lib/toaster/toastergui/templates/distro_btn.html new file +20 -0
new file mode 100644
index 0000000..fac7947
@@ -0,0 +1,20 @@
+<a href="{% url 'project' extra.pid %}?setDistro={{data.name}}" class="btn btn-default btn-block layer-exists-{{data.layer_version.id}}"
+    {% if data.layer_version.pk not in extra.current_layers %}
+    style="display:none;"
+    {% endif %}>
+  Set distro</a>
+<a class="btn btn-default btn-block layerbtn layer-add-{{data.layer_version.id}}" data-layer='{
+    "id": {{data.layer_version.id}},
+    "name":  "{{data.layer_version.layer.name}}",
+    "xhrLayerUrl": "{% url "xhr_layer" extra.pid data.pk %}",
+    "layerdetailurl": "{%url 'layerdetails' extra.pid data.layer_version.id %}"
+    }' data-directive="add"
+    {% if data.layer_version.pk in extra.current_layers %}
+    style="display:none;"
+    {% endif %}
+>
+  <span class="glyphicon glyphicon-plus"></span>
+  Add layer
+  <span class="glyphicon glyphicon-question-sign get-help" title="To select this distro, you must first add the {{data.layer_version.layer.name}} layer to your project"></i>
+</a>
+
lib/toaster/toastergui/templates/project.html +16 -0
index ab7e665..11603d1 100644
@@ -77,6 +77,22 @@
       </form>
     </div>
 
+    <div class="well well-transparent" id="distro-section">
+      <h3>Distro</h3>
+
+      <p class="lead"><span id="project-distro-name"></span> <span class="glyphicon glyphicon-edit" id="change-distro-toggle"></span></p>
+
+      <form id="select-distro-form" style="display:none;" class="form-inline">
+        <span class="help-block">Distro suggestions come from the Layer Index</a></span>
+        <div class="form-group">
+          <input class="form-control" id="distro-change-input" autocomplete="off" value="" data-provide="typeahead" data-minlength="1" data-autocomplete="off" type="text">
+        </div>
+        <button id="distro-change-btn" class="btn btn-default" type="button">Save</button>
+        <a href="#" id="cancel-distro-change" class="btn btn-link">Cancel</a>
+        <p class="form-link"><a href="{% url 'projectdistros' project.id %}">View compatible distros</a></p>
+      </form>
+    </div>
+
     <div class="well well-transparent">
       <h3>Most built recipes</h3>
 
lib/toaster/toastergui/typeaheads.py +30 -0
index 58c650f..5aa0f8d 100644
@@ -100,6 +100,36 @@ class MachinesTypeAhead(ToasterTypeAhead):
         return results
 
 
+class DistrosTypeAhead(ToasterTypeAhead):
+    """ Typeahead for all the distros available in the current project's
+    configuration """
+    def __init__(self):
+        super(DistrosTypeAhead, self).__init__()
+
+    def apply_search(self, search_term, prj, request):
+        distros = prj.get_available_distros()
+        distros = distros.order_by("name")
+
+        primary_results = distros.filter(name__istartswith=search_term)
+        secondary_results = distros.filter(name__icontains=search_term).exclude(pk__in=primary_results)
+        tertiary_results = distros.filter(layer_version__layer__name__icontains=search_term).exclude(pk__in=primary_results).exclude(pk__in=secondary_results)
+
+        results = []
+
+        for distro in list(primary_results) + list(secondary_results) + list(tertiary_results):
+
+            detail = "[ %s ]" % (distro.layer_version.layer.name)
+            needed_fields = {
+                'id' : distro.pk,
+                'name' : distro.name,
+                'detail' : detail,
+            }
+
+            results.append(needed_fields)
+
+        return results
+
+
 class RecipesTypeAhead(ToasterTypeAhead):
     """ Typeahead for all the recipes available in the current project's
     configuration """
lib/toaster/toastergui/urls.py +8 -0
index 1ad79a4..6aebc3f 100644
@@ -158,6 +158,11 @@ urlpatterns = [
             name=tables.LayerMachinesTable.__name__.lower()),
 
 
+        url(r'^project/(?P<pid>\d+)/distros/$',
+            tables.DistrosTable.as_view(template_name="generic-toastertable-page.html"),
+            name="projectdistros"),
+
+
         url(r'^project/(?P<pid>\d+)/customrecipe/(?P<custrecipeid>\d+)/selectpackages/$',
             tables.SelectPackagesTable.as_view(), name="recipeselectpackages"),
 
@@ -187,6 +192,9 @@ urlpatterns = [
             typeaheads.GitRevisionTypeAhead.as_view(),
             name='xhr_gitrevtypeahead'),
 
+        url(r'^xhr_typeahead/(?P<pid>\d+)/distros$',
+            typeaheads.DistrosTypeAhead.as_view(), name='xhr_distrostypeahead'),
+
         url(r'^xhr_testreleasechange/(?P<pid>\d+)$', views.xhr_testreleasechange,
             name='xhr_testreleasechange'),
         url(r'^xhr_configvaredit/(?P<pid>\d+)$', views.xhr_configvaredit,
Signature · 1 line
1.9.1
#4 lore raw

PATCH 4/5 toaster: large package set breaks sqlite query

In reply to David Reyna

From: David Reyna <[email protected]>

If you build a project with a large package set, you will get a crash in "views.py" when the dashboard attempts to fetch the package set to calculate the package count and size. This is a sqlite limitation, and it fails with as few as 1220 packages.

[YOCTO #11717]

---
 lib/toaster/toastergui/views.py | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
lib/toaster/toastergui/views.py +9 -4
index 75c5911..5720b9d 100755
@@ -457,10 +457,15 @@ def builddashboard( request, build_id ):
         npkg = 0
         pkgsz = 0
         package = None
-        for package in Package.objects.filter(id__in = [x.package_id for x in t.target_installed_package_set.all()]):
-            pkgsz = pkgsz + package.size
-            if package.installed_name:
-                npkg = npkg + 1
+        # Chunk the query to avoid "too many SQL variables" error
+        package_set = t.target_installed_package_set.all()
+        package_set_len = len(package_set)
+        for ps_start in range(0,package_set_len,500):
+            ps_stop = min(ps_start+500,package_set_len)
+            for package in Package.objects.filter(id__in = [x.package_id for x in package_set[ps_start:ps_stop]]):
+                pkgsz = pkgsz + package.size
+                if package.installed_name:
+                    npkg = npkg + 1
         elem['npkg'] = npkg
         elem['pkgsz'] = pkgsz
         ti = Target_Image_File.objects.filter(target_id = t.id)
Signature · 1 line
1.9.1
#5 lore raw

PATCH 5/5 toaster: test 'commit' first in get_vcs_reference

In reply to David Reyna

From: David Reyna <[email protected]>

The 'commit' value should be tested and used first when resolving the ref for a layer, since that is an explicit override in each layer index 'layeritem' record. If should take precedence over more-global 'branch' and 'release' values, instead of being last.

[YOCTO #11515]

---
 lib/toaster/orm/models.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
lib/toaster/orm/models.py +2 -2
index 5c14727..630e4a0 100644
@@ -1516,12 +1516,12 @@ class Layer_Version(models.Model):
         return self._handle_url_path(self.layer.vcs_web_tree_base_url, '')
 
     def get_vcs_reference(self):
+        if self.commit is not None and len(self.commit) > 0:
+            return self.commit
         if self.branch is not None and len(self.branch) > 0:
             return self.branch
         if self.release is not None:
             return self.release.name
-        if self.commit is not None and len(self.commit) > 0:
-            return self.commit
         return 'N/A'
 
     def get_detailspage_url(self, project_id=None):
Signature · 1 line
1.9.1