summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/toastergui/api.py
diff options
context:
space:
mode:
authorDavid Reyna <David.Reyna@windriver.com>2018-08-15 18:04:09 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2018-08-20 10:20:51 +0100
commit08fbdc02e32d715ae94e3b9603fb3ec8351c8fd3 (patch)
tree8b1eb6531f782ba531903cc7248fbd5705e5adf3 /bitbake/lib/toaster/toastergui/api.py
parent9af0f1a46bbb6ad9ee8b35957251f4aa826b023f (diff)
downloadpoky-08fbdc02e32d715ae94e3b9603fb3ec8351c8fd3.tar.gz
bitbake: Toaster: Implement the project-specific feature and releated enhancements and defects.
Here is the primary driving enhancement: * Bug 12785 - Support Project Specific configuration for external tools (e.g. ISS, Eclipse) - Isolated project-specific configuration page (full Toaster context hidden) - Support for new project, reconfigure existing project, and import existing command line project - Ability to define variables (e.g. image recipe) and pass them back to external GUI - Ability to execute the cloning phase, so that external GUI receive a buildable project - Ability to call back to the external GUI when updates are completed and ready - Compatibility of above projects with the normal full Toaster interface - Ability to pass to a 'complete' or 'cancel' web page so that the external GUI can immediately stop that Toaster instance, and not leave dangling servers nor edit sessions open Here are the supporting enhancements, where at least the back end is implemented: * Bug 12821 - Make Toaster conf changes compatible with command line usage * Bug 12822 - Support importing user changes to conf files into Toaster * Bug 12823 - Support importing user build directories into Toaster * Bug 12824 - Scan imported layers for content so that they are immediately available * Bug 12825 - show layer clone item in progress bar Here are defects fixed: * Bug 12817 - builddelete.py requires explicit 'add_arguments' * Bug 12818 - Remove orphaned imported layers when project is deleted * Bug 12826 - fix imported layer management * Bug 12819 - build using selected bitbake env, not Toaster's env * Bug 12820 - Toaster randomizes the layer order in toaster_bblayers.conf [YOCTO #12785] (Bitbake rev: 985d6cec290bdd80998a63483561a73c75d82d65) Signed-off-by: David Reyna <David.Reyna@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/api.py')
-rw-r--r--bitbake/lib/toaster/toastergui/api.py168
1 files changed, 166 insertions, 2 deletions
diff --git a/bitbake/lib/toaster/toastergui/api.py b/bitbake/lib/toaster/toastergui/api.py
index ab6ba69e0e..1bec56d468 100644
--- a/bitbake/lib/toaster/toastergui/api.py
+++ b/bitbake/lib/toaster/toastergui/api.py
@@ -22,7 +22,9 @@ import os
22import re 22import re
23import logging 23import logging
24import json 24import json
25import subprocess
25from collections import Counter 26from collections import Counter
27from shutil import copyfile
26 28
27from orm.models import Project, ProjectTarget, Build, Layer_Version 29from orm.models import Project, ProjectTarget, Build, Layer_Version
28from orm.models import LayerVersionDependency, LayerSource, ProjectLayer 30from orm.models import LayerVersionDependency, LayerSource, ProjectLayer
@@ -38,6 +40,18 @@ from django.core.urlresolvers import reverse
38from django.db.models import Q, F 40from django.db.models import Q, F
39from django.db import Error 41from django.db import Error
40from toastergui.templatetags.projecttags import filtered_filesizeformat 42from toastergui.templatetags.projecttags import filtered_filesizeformat
43from django.utils import timezone
44import pytz
45
46# development/debugging support
47verbose = 2
48def _log(msg):
49 if 1 == verbose:
50 print(msg)
51 elif 2 == verbose:
52 f1=open('/tmp/toaster.log', 'a')
53 f1.write("|" + msg + "|\n" )
54 f1.close()
41 55
42logger = logging.getLogger("toaster") 56logger = logging.getLogger("toaster")
43 57
@@ -137,6 +151,130 @@ class XhrBuildRequest(View):
137 return response 151 return response
138 152
139 153
154class XhrProjectUpdate(View):
155
156 def get(self, request, *args, **kwargs):
157 return HttpResponse()
158
159 def post(self, request, *args, **kwargs):
160 """
161 Project Update
162
163 Entry point: /xhr_projectupdate/<project_id>
164 Method: POST
165
166 Args:
167 pid: pid of project to update
168
169 Returns:
170 {"error": "ok"}
171 or
172 {"error": <error message>}
173 """
174
175 project = Project.objects.get(pk=kwargs['pid'])
176 logger.debug("ProjectUpdateCallback:project.pk=%d,project.builddir=%s" % (project.pk,project.builddir))
177
178 if 'do_update' in request.POST:
179
180 # Extract any default image recipe
181 if 'default_image' in request.POST:
182 project.set_variable(Project.PROJECT_SPECIFIC_DEFAULTIMAGE,str(request.POST['default_image']))
183 else:
184 project.set_variable(Project.PROJECT_SPECIFIC_DEFAULTIMAGE,'')
185
186 logger.debug("ProjectUpdateCallback:Chain to the build request")
187
188 # Chain to the build request
189 xhrBuildRequest = XhrBuildRequest()
190 return xhrBuildRequest.post(request, *args, **kwargs)
191
192 logger.warning("ERROR:XhrProjectUpdate")
193 response = HttpResponse()
194 response.status_code = 500
195 return response
196
197class XhrSetDefaultImageUrl(View):
198
199 def get(self, request, *args, **kwargs):
200 return HttpResponse()
201
202 def post(self, request, *args, **kwargs):
203 """
204 Project Update
205
206 Entry point: /xhr_setdefaultimage/<project_id>
207 Method: POST
208
209 Args:
210 pid: pid of project to update default image
211
212 Returns:
213 {"error": "ok"}
214 or
215 {"error": <error message>}
216 """
217
218 project = Project.objects.get(pk=kwargs['pid'])
219 logger.debug("XhrSetDefaultImageUrl:project.pk=%d" % (project.pk))
220
221 # set any default image recipe
222 if 'targets' in request.POST:
223 default_target = str(request.POST['targets'])
224 project.set_variable(Project.PROJECT_SPECIFIC_DEFAULTIMAGE,default_target)
225 logger.debug("XhrSetDefaultImageUrl,project.pk=%d,project.builddir=%s" % (project.pk,project.builddir))
226 return error_response('ok')
227
228 logger.warning("ERROR:XhrSetDefaultImageUrl")
229 response = HttpResponse()
230 response.status_code = 500
231 return response
232
233
234#
235# Layer Management
236#
237# Rules for 'local_source_dir' layers
238# * Layers must have a unique name in the Layers table
239# * A 'local_source_dir' layer is supposed to be shared
240# by all projects that use it, so that it can have the
241# same logical name
242# * Each project that uses a layer will have its own
243# LayerVersion and Project Layer for it
244# * During the Paroject delete process, when the last
245# LayerVersion for a 'local_source_dir' layer is deleted
246# then the Layer record is deleted to remove orphans
247#
248
249def scan_layer_content(layer,layer_version):
250 # if this is a local layer directory, we can immediately scan its content
251 if layer.local_source_dir:
252 try:
253 # recipes-*/*/*.bb
254 cmd = '%s %s' % ('ls', os.path.join(layer.local_source_dir,'recipes-*/*/*.bb'))
255 recipes_list = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
256 recipes_list = recipes_list.decode("utf-8").strip()
257 if recipes_list and 'No such' not in recipes_list:
258 for recipe in recipes_list.split('\n'):
259 recipe_path = recipe[recipe.rfind('recipes-'):]
260 recipe_name = recipe[recipe.rfind('/')+1:].replace('.bb','')
261 recipe_ver = recipe_name.rfind('_')
262 if recipe_ver > 0:
263 recipe_name = recipe_name[0:recipe_ver]
264 if recipe_name:
265 ro, created = Recipe.objects.get_or_create(
266 layer_version=layer_version,
267 name=recipe_name
268 )
269 if created:
270 ro.file_path = recipe_path
271 ro.summary = 'Recipe %s from layer %s' % (recipe_name,layer.name)
272 ro.description = ro.summary
273 ro.save()
274
275 except Exception as e:
276 logger.warning("ERROR:scan_layer_content: %s" % e)
277
140class XhrLayer(View): 278class XhrLayer(View):
141 """ Delete, Get, Add and Update Layer information 279 """ Delete, Get, Add and Update Layer information
142 280
@@ -265,6 +403,7 @@ class XhrLayer(View):
265 (csv)] 403 (csv)]
266 404
267 """ 405 """
406
268 try: 407 try:
269 project = Project.objects.get(pk=kwargs['pid']) 408 project = Project.objects.get(pk=kwargs['pid'])
270 409
@@ -285,7 +424,13 @@ class XhrLayer(View):
285 if layer_data['name'] in existing_layers: 424 if layer_data['name'] in existing_layers:
286 return JsonResponse({"error": "layer-name-exists"}) 425 return JsonResponse({"error": "layer-name-exists"})
287 426
288 layer = Layer.objects.create(name=layer_data['name']) 427 if ('local_source_dir' in layer_data):
428 # Local layer can be shared across projects. They have no 'release'
429 # and are not included in get_all_compatible_layer_versions() above
430 layer,created = Layer.objects.get_or_create(name=layer_data['name'])
431 _log("Local Layer created=%s" % created)
432 else:
433 layer = Layer.objects.create(name=layer_data['name'])
289 434
290 layer_version = Layer_Version.objects.create( 435 layer_version = Layer_Version.objects.create(
291 layer=layer, 436 layer=layer,
@@ -293,7 +438,7 @@ class XhrLayer(View):
293 layer_source=LayerSource.TYPE_IMPORTED) 438 layer_source=LayerSource.TYPE_IMPORTED)
294 439
295 # Local layer 440 # Local layer
296 if ('local_source_dir' in layer_data) and layer.local_source_dir: 441 if ('local_source_dir' in layer_data): ### and layer.local_source_dir:
297 layer.local_source_dir = layer_data['local_source_dir'] 442 layer.local_source_dir = layer_data['local_source_dir']
298 # git layer 443 # git layer
299 elif 'vcs_url' in layer_data: 444 elif 'vcs_url' in layer_data:
@@ -325,6 +470,9 @@ class XhrLayer(View):
325 'layerdetailurl': 470 'layerdetailurl':
326 layer_dep.get_detailspage_url(project.pk)}) 471 layer_dep.get_detailspage_url(project.pk)})
327 472
473 # Scan the layer's content and update components
474 scan_layer_content(layer,layer_version)
475
328 except Layer_Version.DoesNotExist: 476 except Layer_Version.DoesNotExist:
329 return error_response("layer-dep-not-found") 477 return error_response("layer-dep-not-found")
330 except Project.DoesNotExist: 478 except Project.DoesNotExist:
@@ -1014,8 +1162,24 @@ class XhrProject(View):
1014 state=BuildRequest.REQ_INPROGRESS): 1162 state=BuildRequest.REQ_INPROGRESS):
1015 XhrBuildRequest.cancel_build(br) 1163 XhrBuildRequest.cancel_build(br)
1016 1164
1165 # gather potential orphaned local layers attached to this project
1166 project_local_layer_list = []
1167 for pl in ProjectLayer.objects.filter(project=project):
1168 if pl.layercommit.layer_source == LayerSource.TYPE_IMPORTED:
1169 project_local_layer_list.append(pl.layercommit.layer)
1170
1171 # deep delete the project and its dependencies
1017 project.delete() 1172 project.delete()
1018 1173
1174 # delete any local layers now orphaned
1175 _log("LAYER_ORPHAN_CHECK:Check for orphaned layers")
1176 for layer in project_local_layer_list:
1177 layer_refs = Layer_Version.objects.filter(layer=layer)
1178 _log("LAYER_ORPHAN_CHECK:Ref Count for '%s' = %d" % (layer.name,len(layer_refs)))
1179 if 0 == len(layer_refs):
1180 _log("LAYER_ORPHAN_CHECK:DELETE orpahned '%s'" % (layer.name))
1181 Layer.objects.filter(pk=layer.id).delete()
1182
1019 except Project.DoesNotExist: 1183 except Project.DoesNotExist:
1020 return error_response("Project %s does not exist" % 1184 return error_response("Project %s does not exist" %
1021 kwargs['project_id']) 1185 kwargs['project_id'])