diff options
Diffstat (limited to 'bitbake/lib/toaster/toastergui/api.py')
-rw-r--r-- | bitbake/lib/toaster/toastergui/api.py | 168 |
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 | |||
22 | import re | 22 | import re |
23 | import logging | 23 | import logging |
24 | import json | 24 | import json |
25 | import subprocess | ||
25 | from collections import Counter | 26 | from collections import Counter |
27 | from shutil import copyfile | ||
26 | 28 | ||
27 | from orm.models import Project, ProjectTarget, Build, Layer_Version | 29 | from orm.models import Project, ProjectTarget, Build, Layer_Version |
28 | from orm.models import LayerVersionDependency, LayerSource, ProjectLayer | 30 | from orm.models import LayerVersionDependency, LayerSource, ProjectLayer |
@@ -38,6 +40,18 @@ from django.core.urlresolvers import reverse | |||
38 | from django.db.models import Q, F | 40 | from django.db.models import Q, F |
39 | from django.db import Error | 41 | from django.db import Error |
40 | from toastergui.templatetags.projecttags import filtered_filesizeformat | 42 | from toastergui.templatetags.projecttags import filtered_filesizeformat |
43 | from django.utils import timezone | ||
44 | import pytz | ||
45 | |||
46 | # development/debugging support | ||
47 | verbose = 2 | ||
48 | def _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 | ||
42 | logger = logging.getLogger("toaster") | 56 | logger = logging.getLogger("toaster") |
43 | 57 | ||
@@ -137,6 +151,130 @@ class XhrBuildRequest(View): | |||
137 | return response | 151 | return response |
138 | 152 | ||
139 | 153 | ||
154 | class 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 | |||
197 | class 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 | |||
249 | def 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 | |||
140 | class XhrLayer(View): | 278 | class 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']) |