diff options
author | Ed Bartosh <ed.bartosh@linux.intel.com> | 2015-09-28 21:45:22 -0700 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-09-29 14:11:37 +0100 |
commit | a3ff4b28baa9f4cc76ec4993f862f2ed7bd20f65 (patch) | |
tree | d85c61a3e432146f17351c5fc6a3bd1e39588da9 /bitbake | |
parent | 28153acb0aadce9e42ab5eac02f9b68921a9563f (diff) | |
download | poky-a3ff4b28baa9f4cc76ec4993f862f2ed7bd20f65.tar.gz |
bitbake: toaster: Add new ReST API for Image Customisation feature
Implemented xhr_customrecipe API. To create a custom recipe from a
base recipe.
Implemented xhr_customrecipe_packages API to add/remove packages
to/from custom recipe.
co-authored see Signed-off-by
(Bitbake rev: 84be400237173970716616eeab6a37d776aa011b)
Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Elliot Smith <elliot.smith@intel.com>
Signed-off-by: Michael Wood <michael.g.wood@intel.com>
Signed-off-by: brian avery <avery.brian@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake')
-rw-r--r-- | bitbake/lib/toaster/toastergui/urls.py | 10 | ||||
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 152 |
2 files changed, 159 insertions, 3 deletions
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py index 55f325d0d6..b47a161687 100644 --- a/bitbake/lib/toaster/toastergui/urls.py +++ b/bitbake/lib/toaster/toastergui/urls.py | |||
@@ -152,6 +152,14 @@ urlpatterns = patterns('toastergui.views', | |||
152 | # JS Unit tests | 152 | # JS Unit tests |
153 | url(r'^js-unit-tests/$', 'jsunittests', name='js-unit-tests'), | 153 | url(r'^js-unit-tests/$', 'jsunittests', name='js-unit-tests'), |
154 | 154 | ||
155 | # default redirection | 155 | # image customisation functionality |
156 | url(r'^xhr_customrecipe/(?P<recipe_id>\d+)/packages/(?P<package_id>\d+|)$', | ||
157 | 'xhr_customrecipe_packages', name='xhr_customrecipe_packages'), | ||
158 | url(r'^xhr_customrecipe/(?P<recipe_id>\d+)$', 'xhr_customrecipe_id', | ||
159 | name='xhr_customrecipe_id'), | ||
160 | url(r'^xhr_customrecipe/', 'xhr_customrecipe', | ||
161 | name='xhr_customrecipe'), | ||
162 | |||
163 | # default redirection | ||
156 | url(r'^$', RedirectView.as_view( url= 'landing')), | 164 | url(r'^$', RedirectView.as_view( url= 'landing')), |
157 | ) | 165 | ) |
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index 5ea6122a02..392e56da2a 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -26,12 +26,12 @@ | |||
26 | import operator,re | 26 | import operator,re |
27 | 27 | ||
28 | from django.db.models import F, Q, Sum, Count, Max | 28 | from django.db.models import F, Q, Sum, Count, Max |
29 | from django.db import IntegrityError | 29 | from django.db import IntegrityError, Error |
30 | from django.shortcuts import render, redirect | 30 | from django.shortcuts import render, redirect |
31 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable | 31 | from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable |
32 | from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency | 32 | from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency |
33 | from orm.models import Target_Installed_Package, Target_File, Target_Image_File, BuildArtifact | 33 | from orm.models import Target_Installed_Package, Target_File, Target_Image_File, BuildArtifact |
34 | from orm.models import BitbakeVersion | 34 | from orm.models import BitbakeVersion, CustomImageRecipe |
35 | from bldcontrol import bbcontroller | 35 | from bldcontrol import bbcontroller |
36 | from django.views.decorators.cache import cache_control | 36 | from django.views.decorators.cache import cache_control |
37 | from django.core.urlresolvers import reverse, resolve | 37 | from django.core.urlresolvers import reverse, resolve |
@@ -2596,7 +2596,155 @@ if True: | |||
2596 | 2596 | ||
2597 | return HttpResponse(jsonfilter({"error": "ok",}), content_type = "application/json") | 2597 | return HttpResponse(jsonfilter({"error": "ok",}), content_type = "application/json") |
2598 | 2598 | ||
2599 | @xhr_response | ||
2600 | def xhr_customrecipe(request): | ||
2601 | """ | ||
2602 | Custom image recipe REST API | ||
2603 | |||
2604 | Entry point: /xhr_customrecipe/ | ||
2605 | Method: POST | ||
2606 | |||
2607 | Args: | ||
2608 | name: name of custom recipe to create | ||
2609 | project: target project id of orm.models.Project | ||
2610 | base: base recipe id of orm.models.Recipe | ||
2611 | |||
2612 | Returns: | ||
2613 | {"error": "ok", | ||
2614 | "url": <url of the created recipe>} | ||
2615 | or | ||
2616 | {"error": <error message>} | ||
2617 | """ | ||
2618 | # check if request has all required parameters | ||
2619 | for param in ('name', 'project', 'base'): | ||
2620 | if param not in request.POST: | ||
2621 | return {"error": "Missing parameter '%s'" % param} | ||
2622 | |||
2623 | # get project and baserecipe objects | ||
2624 | params = {} | ||
2625 | for name, model in [("project", Project), | ||
2626 | ("base", Recipe)]: | ||
2627 | value = request.POST[name] | ||
2628 | try: | ||
2629 | params[name] = model.objects.get(id=value) | ||
2630 | except model.DoesNotExist: | ||
2631 | return {"error": "Invalid %s id %s" % (name, value)} | ||
2632 | |||
2633 | # create custom recipe | ||
2634 | try: | ||
2635 | recipe = CustomImageRecipe.objects.create( | ||
2636 | name=request.POST["name"], | ||
2637 | base_recipe=params["base"], | ||
2638 | project=params["project"]) | ||
2639 | except Error as err: | ||
2640 | return {"error": "Can't create custom recipe: %s" % err} | ||
2641 | |||
2642 | # Find the package list from the last build of this recipe/target | ||
2643 | build = Build.objects.filter(target__target=params['base'].name, | ||
2644 | project=params['project']).last() | ||
2645 | |||
2646 | if build: | ||
2647 | # Copy in every package | ||
2648 | # We don't want these packages to be linked to anything because | ||
2649 | # that underlying data may change e.g. delete a build | ||
2650 | for package in build.package_set.all(): | ||
2651 | # Create the duplicate | ||
2652 | package.pk = None | ||
2653 | package.save() | ||
2654 | # Disassociate the package from the build | ||
2655 | package.build = None | ||
2656 | package.save() | ||
2657 | recipe.packages.add(package) | ||
2658 | else: | ||
2659 | logger.warn("No packages found for this base recipe") | ||
2660 | |||
2661 | return {"error": "ok", | ||
2662 | "url": reverse('customrecipe', args=(params['project'].pk, | ||
2663 | recipe.id))} | ||
2664 | |||
2665 | @xhr_response | ||
2666 | def xhr_customrecipe_id(request, recipe_id): | ||
2667 | """ | ||
2668 | Set of ReST API processors working with recipe id. | ||
2669 | |||
2670 | Entry point: /xhr_customrecipe/<recipe_id> | ||
2671 | |||
2672 | Methods: | ||
2673 | GET - Get details of custom image recipe | ||
2674 | DELETE - Delete custom image recipe | ||
2675 | |||
2676 | Returns: | ||
2677 | GET: | ||
2678 | {"error": "ok", | ||
2679 | "info": dictionary of field name -> value pairs | ||
2680 | of the CustomImageRecipe model} | ||
2681 | DELETE: | ||
2682 | {"error": "ok"} | ||
2683 | or | ||
2684 | {"error": <error message>} | ||
2685 | """ | ||
2686 | objects = CustomImageRecipe.objects.filter(id=recipe_id) | ||
2687 | if not objects: | ||
2688 | return {"error": "Custom recipe with id=%s " | ||
2689 | "not found" % recipe_id} | ||
2690 | if request.method == 'GET': | ||
2691 | values = CustomImageRecipe.objects.filter(id=recipe_id).values() | ||
2692 | if values: | ||
2693 | return {"error": "ok", "info": values[0]} | ||
2694 | else: | ||
2695 | return {"error": "Custom recipe with id=%s " | ||
2696 | "not found" % recipe_id} | ||
2697 | return {"error": "ok", "info": objects.values()[0]} | ||
2698 | elif request.method == 'DELETE': | ||
2699 | objects.delete() | ||
2700 | return {"error": "ok"} | ||
2701 | else: | ||
2702 | return {"error": "Method %s is not supported" % request.method} | ||
2703 | |||
2704 | @xhr_response | ||
2705 | def xhr_customrecipe_packages(request, recipe_id, package_id): | ||
2706 | """ | ||
2707 | ReST API to add/remove packages to/from custom recipe. | ||
2599 | 2708 | ||
2709 | Entry point: /xhr_customrecipe/<recipe_id>/packages/ | ||
2710 | |||
2711 | Methods: | ||
2712 | PUT - Add package to the recipe | ||
2713 | DELETE - Delete package from the recipe | ||
2714 | |||
2715 | Returns: | ||
2716 | {"error": "ok"} | ||
2717 | or | ||
2718 | {"error": <error message>} | ||
2719 | """ | ||
2720 | try: | ||
2721 | recipe = CustomImageRecipe.objects.get(id=recipe_id) | ||
2722 | except CustomImageRecipe.DoesNotExist: | ||
2723 | return {"error": "Custom recipe with id=%s " | ||
2724 | "not found" % recipe_id} | ||
2725 | |||
2726 | if request.method == 'GET' and not package_id: | ||
2727 | return {"error": "ok", | ||
2728 | "packages": list(recipe.packages.values_list('id'))} | ||
2729 | |||
2730 | try: | ||
2731 | package = Package.objects.get(id=package_id) | ||
2732 | except Package.DoesNotExist: | ||
2733 | return {"error": "Package with id=%s " | ||
2734 | "not found" % package_id} | ||
2735 | |||
2736 | if request.method == 'PUT': | ||
2737 | recipe.packages.add(package) | ||
2738 | return {"error": "ok"} | ||
2739 | elif request.method == 'DELETE': | ||
2740 | if package in recipe.packages.all(): | ||
2741 | recipe.packages.remove(package) | ||
2742 | return {"error": "ok"} | ||
2743 | else: | ||
2744 | return {"error": "Package '%s' is not in the recipe '%s'" % \ | ||
2745 | (package.name, recipe.name)} | ||
2746 | else: | ||
2747 | return {"error": "Method %s is not supported" % request.method} | ||
2600 | 2748 | ||
2601 | def importlayer(request, pid): | 2749 | def importlayer(request, pid): |
2602 | template = "importlayer.html" | 2750 | template = "importlayer.html" |