[Toaster] [PATCH v2 4/9] toaster: lsupdates Add layerindex fetcher

Michael Wood michael.g.wood at intel.com
Fri Jul 8 03:45:00 PDT 2016


On 07/07/16 18:21, Michael Wood wrote:
> Move and refactor the layerindex layer source update mechanism so that
> we don't have to track the layerindex objects in the toaster database.
> Move this out of the orm and into the management command.
>
> Paves the way for future improvement to allow you to specify a layer
> index server as an argument to the command.
>
> Signed-off-by: Michael Wood <michael.g.wood at intel.com>
> ---
>   .../toaster/orm/management/commands/lsupdates.py   | 250 ++++++++++++++++++++-
>   1 file changed, 244 insertions(+), 6 deletions(-)
>
> diff --git a/bitbake/lib/toaster/orm/management/commands/lsupdates.py b/bitbake/lib/toaster/orm/management/commands/lsupdates.py
> index 75e9513..f4eba81 100644
> --- a/bitbake/lib/toaster/orm/management/commands/lsupdates.py
> +++ b/bitbake/lib/toaster/orm/management/commands/lsupdates.py
> @@ -1,12 +1,250 @@
> -from django.core.management.base import NoArgsCommand, CommandError
> -from orm.models import LayerSource
> +#
> +# ex:ts=4:sw=4:sts=4:et
> +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
> +#
> +# BitBake Toaster Implementation
> +#
> +# Copyright (C) 2016        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
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License along
> +# 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 orm.models import LayerSource, Layer, Release, Layer_Version
> +from orm.models import LayerVersionDependency, Machine, Recipe
> +
>   import os
> +import json
> +import logging
> +logger = logging.getLogger("toaster")
> +
> +DEFAULT_LAYERINDEX_SERVER = "http://layers.openembedded.org/layerindex/api/"
> +
>   
>   class Command(NoArgsCommand):
> -    args    = ""
> -    help    = "Updates locally cached information from all LayerSources"
> +    args = ""
> +    help = "Updates locally cached information from a layerindex server"
> +
> +    def update(self):
> +        """
> +            Fetches layer, recipe and machine information from a layerindex
> +            server
> +        """
> +
> +        self.apiurl = DEFAULT_LAYERINDEX_SERVER
> +
> +        assert self.apiurl is not None
> +        try:
> +            from urllib.request import urlopen, URLError
> +            from urllib.parse import urlparse
> +        except ImportError:
> +            from urllib2 import urlopen, URLError
> +            from urlparse import urlparse
> +
> +        proxy_settings = os.environ.get("http_proxy", None)
> +        oe_core_layer = 'openembedded-core'
> +
> +        def _get_json_response(apiurl=DEFAULT_LAYERINDEX_SERVER):
> +            _parsedurl = urlparse(apiurl)
> +            path = _parsedurl.path
> +
> +            # logger.debug("Fetching %s", apiurl)
> +            try:
> +                res = urlopen(apiurl)
> +            except URLError as e:
> +                raise Exception("Failed to read %s: %s" % (path, e.reason))
> +
> +            return json.loads(res.read().decode('utf-8'))
> +
> +        # verify we can get the basic api
> +        try:
> +            apilinks = _get_json_response()
> +        except Exception as e:
> +            import traceback
> +            if proxy_settings is not None:
> +                logger.info("EE: Using proxy %s" % proxy_settings)
> +            logger.warning("EE: could not connect to %s, skipping update:"
> +                           "%s\n%s" % (self.apiurl, e, traceback.format_exc()))
> +            return
> +
> +        # update branches; only those that we already have names listed in the
> +        # Releases table
> +        whitelist_branch_names = [rel.branch_name
> +                                  for rel in Release.objects.all()]
> +        if len(whitelist_branch_names) == 0:
> +            raise Exception("Failed to make list of branches to fetch")
> +
> +        logger.debug("Fetching branches")
> +
> +        # keep a track of the id mappings so that layer_versions can be created
> +        # for these layers later on
> +        li_layer_id_to_toaster_layer_id = {}
> +
> +        # We may need this? TODO
> +        #branches_info = _get_json_response(apilinks['branches'] +
> +        #                                   "?filter=name:%s"
> +        #                                   % "OR".join(whitelist_branch_names))
> +
> +        # update layers
> +        layers_info = _get_json_response(apilinks['layerItems'])
> +
> +        for li in layers_info:
> +            # Special case for the openembedded-core layer
> +            if li['name'] == oe_core_layer:
> +                try:
> +                    # If we have an existing openembedded-core for example
> +                    # from the toasterconf.json augment the info using the
> +                    # layerindex rather than duplicate it
> +                    oe_core_l = Layer.objects.get(name=oe_core_layer)
> +                    # Take ownership of the layer as now coming from the
> +                    # layerindex
> +                    oe_core_l.summary = li['summary']
> +                    oe_core_l.description = li['description']
> +                    oe_core_l.save()
> +                    li_layer_id_to_toaster_layer_id[li['id']] = oe_core_l.pk
> +                    continue
> +
> +                except Layer.DoesNotExist:
> +                    pass
> +
> +            l, created = Layer.objects.get_or_create(name=li['name'])
> +            l.up_date = li['updated']
> +            l.vcs_url = li['vcs_url']
> +            l.vcs_web_url = li['vcs_web_url']
> +            l.vcs_web_tree_base_url = li['vcs_web_tree_base_url']
> +            l.vcs_web_file_base_url = li['vcs_web_file_base_url']
> +            l.summary = li['summary']
> +            l.description = li['description']
> +            l.save()
> +
> +            li_layer_id_to_toaster_layer_id[li['id']] = l.pk
> +
> +        # update layerbranches/layer_versions
> +        logger.debug("Fetching layer information")
> +        layerbranches_info = _get_json_response(
> +            apilinks['layerBranches'] + "?filter=branch__name:%s" %
> +            "OR".join(whitelist_branch_names))
> +
> +        # Map Layer index layer_branch object id to
> +        # layer_version toaster object id
> +        li_layer_branch_id_to_toaster_lv_id = {}
> +
> +        for lbi in layerbranches_info:
> +
> +            try:
> +                lv, created = Layer_Version.objects.get_or_create(
> +                    layer_source=LayerSource.TYPE_LAYERINDEX,
> +                    layer=Layer.objects.get(
> +                        pk=li_layer_id_to_toaster_layer_id[lbi['layer']])
> +                )
> +            except KeyError:
> +                print("No such layerindex layer referenced by layerbranch %d" %
> +                      lbi['layer'])
> +                continue
> +
> +            lv.up_date = lbi['updated']
> +            lv.commit = lbi['actual_branch']
> +            lv.dirpath = lbi['vcs_subdir']
> +            lv.save()
> +
> +            li_layer_branch_id_to_toaster_lv_id[lbi['id']] =\
> +                lv.pk
> +
> +        # update layer dependencies
> +        layerdependencies_info = _get_json_response(
> +            apilinks['layerDependencies'] +
> +            "?filter=layerbranch__branch__name:%s" %
> +            "OR".join(whitelist_branch_names))
> +
> +        dependlist = {}
> +        for ldi in layerdependencies_info:
> +            try:
> +                lv = Layer_Version.objects.get(
> +                    pk=li_layer_branch_id_to_toaster_lv_id[ldi['layerbranch']])
> +            except Layer_Version.DoesNotExist as e:
> +                continue
> +
> +            if lv not in dependlist:
> +                dependlist[lv] = []
> +            try:
> +                layer_id = li_layer_id_to_toaster_layer_id[ldi['dependency']]
> +
> +                dependlist[lv].append(
> +                    Layer_Version.objects.get(
> +                        layer_source=LayerSource.TYPE_LAYERINDEX,
> +                        layer__pk=layer_id))
> +
> +            except Layer_Version.DoesNotExist:
> +                logger.warning("Cannot find layer version (ls:%s),"
> +                               "up_id:%s lv:%s" %
> +                               (self, ldi['dependency'], lv))
> +
> +        for lv in dependlist:
> +            LayerVersionDependency.objects.filter(layer_version=lv).delete()
> +            for lvd in dependlist[lv]:
> +                LayerVersionDependency.objects.get_or_create(layer_version=lv,
> +                                                             depends_on=lvd)
> +
> +        # update machines
> +        logger.debug("Fetching machine information")
> +        machines_info = _get_json_response(
> +            apilinks['machines'] + "?filter=layerbranch__branch__name:%s" %
> +            "OR".join(whitelist_branch_names))
> +
> +        for mi in machines_info:
> +            mo, created = Machine.objects.get_or_create(
> +                layer_version=Layer_Version.objects.get(
> +                    pk=li_layer_branch_id_to_toaster_lv_id[mi['layerbranch']]))
This was missing the name attribute and should have been:

+            mo, created = Machine.objects.get_or_create(
+                name=mi['name'],
+                layer_version=Layer_Version.objects.get(
+ pk=li_layer_branch_id_to_toaster_lv_id[mi['layerbranch']]))


It has been corrected on the branch

> +            mo.up_date = mi['updated']
> +            mo.name = mi['name']
> +            mo.description = mi['description']
> +            mo.save()
> +
> +        # update recipes; paginate by layer version / layer branch
> +        logger.debug("Fetching target information")
> +        recipes_info = _get_json_response(
> +            apilinks['recipes'] + "?filter=layerbranch__branch__name:%s" %
> +            "OR".join(whitelist_branch_names))
> +
> +        for ri in recipes_info:
> +            try:
> +                lv_id = li_layer_branch_id_to_toaster_lv_id[ri['layerbranch']]
> +                lv = Layer_Version.objects.get(pk=lv_id)
> +
> +                ro, created = Recipe.objects.get_or_create(
> +                    layer_version=lv,
> +                    name=ri['pn']
> +                )
>   
> +                ro.layer_version = lv
> +                ro.up_date = ri['updated']
> +                ro.name = ri['pn']
> +                ro.version = ri['pv']
> +                ro.summary = ri['summary']
> +                ro.description = ri['description']
> +                ro.section = ri['section']
> +                ro.license = ri['license']
> +                ro.homepage = ri['homepage']
> +                ro.bugtracker = ri['bugtracker']
> +                ro.file_path = ri['filepath'] + "/" + ri['filename']
> +                if 'inherits' in ri:
> +                    ro.is_image = 'image' in ri['inherits'].split()
> +                else:  # workaround for old style layer index
> +                    ro.is_image = "-image-" in ri['pn']
> +                ro.save()
> +            except Exception as e:
> +                logger.debug("Failed saving recipe %s", e)
>   
>       def handle_noargs(self, **options):
> -        for ls in LayerSource.objects.all():
> -            ls.update()
> +            self.update()



More information about the toaster mailing list