diff options
Diffstat (limited to 'bitbake/lib/toaster/orm/models.py')
| -rw-r--r-- | bitbake/lib/toaster/orm/models.py | 123 |
1 files changed, 121 insertions, 2 deletions
diff --git a/bitbake/lib/toaster/orm/models.py b/bitbake/lib/toaster/orm/models.py index 40cdb9e5c5..9edbef3396 100644 --- a/bitbake/lib/toaster/orm/models.py +++ b/bitbake/lib/toaster/orm/models.py | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | from __future__ import unicode_literals | 22 | from __future__ import unicode_literals |
| 23 | 23 | ||
| 24 | from django.db import models, IntegrityError | 24 | from django.db import models, IntegrityError |
| 25 | from django.db.models import F, Q, Avg, Max, Sum | 25 | from django.db.models import F, Q, Avg, Max, Sum, Count |
| 26 | from django.utils import timezone | 26 | from django.utils import timezone |
| 27 | from django.utils.encoding import force_bytes | 27 | from django.utils.encoding import force_bytes |
| 28 | 28 | ||
| @@ -438,7 +438,9 @@ class Build(models.Model): | |||
| 438 | 438 | ||
| 439 | def get_image_file_extensions(self): | 439 | def get_image_file_extensions(self): |
| 440 | """ | 440 | """ |
| 441 | Get list of file name extensions for images produced by this build | 441 | Get list of file name extensions for images produced by this build; |
| 442 | note that this is the actual list of extensions stored on Target objects | ||
| 443 | for this build, and not the value of IMAGE_FSTYPES. | ||
| 442 | """ | 444 | """ |
| 443 | extensions = [] | 445 | extensions = [] |
| 444 | 446 | ||
| @@ -458,6 +460,15 @@ class Build(models.Model): | |||
| 458 | 460 | ||
| 459 | return ', '.join(extensions) | 461 | return ', '.join(extensions) |
| 460 | 462 | ||
| 463 | def get_image_fstypes(self): | ||
| 464 | """ | ||
| 465 | Get the IMAGE_FSTYPES variable value for this build as a de-duplicated | ||
| 466 | list of image file suffixes. | ||
| 467 | """ | ||
| 468 | image_fstypes = Variable.objects.get( | ||
| 469 | build=self, variable_name='IMAGE_FSTYPES').variable_value | ||
| 470 | return list(set(re.split(r' {1,}', image_fstypes))) | ||
| 471 | |||
| 461 | def get_sorted_target_list(self): | 472 | def get_sorted_target_list(self): |
| 462 | tgts = Target.objects.filter(build_id = self.id).order_by( 'target' ); | 473 | tgts = Target.objects.filter(build_id = self.id).order_by( 'target' ); |
| 463 | return( tgts ); | 474 | return( tgts ); |
| @@ -612,6 +623,114 @@ class Target(models.Model): | |||
| 612 | def __unicode__(self): | 623 | def __unicode__(self): |
| 613 | return self.target | 624 | return self.target |
| 614 | 625 | ||
| 626 | def get_similar_targets(self): | ||
| 627 | """ | ||
| 628 | Get targets for the same machine, task and target name | ||
| 629 | (e.g. 'core-image-minimal') from a successful build for this project | ||
| 630 | (but excluding this target). | ||
| 631 | |||
| 632 | Note that we look for targets built by this project because projects | ||
| 633 | can have different configurations from each other, and put their | ||
| 634 | artifacts in different directories. | ||
| 635 | """ | ||
| 636 | query = ~Q(pk=self.pk) & \ | ||
| 637 | Q(target=self.target) & \ | ||
| 638 | Q(build__machine=self.build.machine) & \ | ||
| 639 | Q(build__outcome=Build.SUCCEEDED) & \ | ||
| 640 | Q(build__project=self.build.project) | ||
| 641 | |||
| 642 | return Target.objects.filter(query) | ||
| 643 | |||
| 644 | def get_similar_target_with_image_files(self): | ||
| 645 | """ | ||
| 646 | Get the most recent similar target with Target_Image_Files associated | ||
| 647 | with it, for the purpose of cloning those files onto this target. | ||
| 648 | """ | ||
| 649 | similar_target = None | ||
| 650 | |||
| 651 | candidates = self.get_similar_targets() | ||
| 652 | if candidates.count() < 1: | ||
| 653 | return similar_target | ||
| 654 | |||
| 655 | task_subquery = Q(task=self.task) | ||
| 656 | |||
| 657 | # we can look for a 'build' task if this task is a 'populate_sdk_ext' | ||
| 658 | # task, as it will have created images; and vice versa; note that | ||
| 659 | # 'build' targets can have their task set to ''; | ||
| 660 | # also note that 'populate_sdk' does not produce image files | ||
| 661 | image_tasks = [ | ||
| 662 | '', # aka 'build' | ||
| 663 | 'build', | ||
| 664 | 'populate_sdk_ext' | ||
| 665 | ] | ||
| 666 | if self.task in image_tasks: | ||
| 667 | task_subquery = Q(task__in=image_tasks) | ||
| 668 | |||
| 669 | query = task_subquery & Q(num_files__gt=0) | ||
| 670 | |||
| 671 | # annotate with the count of files, to exclude any targets which | ||
| 672 | # don't have associated files | ||
| 673 | candidates = candidates.annotate( | ||
| 674 | num_files=Count('target_image_file')) | ||
| 675 | |||
| 676 | candidates = candidates.filter(query) | ||
| 677 | |||
| 678 | if candidates.count() > 0: | ||
| 679 | candidates.order_by('build__completed_on') | ||
| 680 | similar_target = candidates.last() | ||
| 681 | |||
| 682 | return similar_target | ||
| 683 | |||
| 684 | def clone_artifacts_from(self, target): | ||
| 685 | """ | ||
| 686 | Make clones of the BuildArtifacts, Target_Image_Files and | ||
| 687 | TargetArtifactFile objects associated with Target target, then | ||
| 688 | associate them with this target. | ||
| 689 | |||
| 690 | Note that for Target_Image_Files, we only want files from the previous | ||
| 691 | build whose suffix matches one of the suffixes defined in this | ||
| 692 | target's build's IMAGE_FSTYPES configuration variable. This prevents the | ||
| 693 | Target_Image_File object for an ext4 image being associated with a | ||
| 694 | target for a project which didn't produce an ext4 image (for example). | ||
| 695 | |||
| 696 | Also sets the license_manifest_path of this target to the same path | ||
| 697 | as that of target being cloned from, as the license manifest path is | ||
| 698 | also a build artifact but is treated differently. | ||
| 699 | """ | ||
| 700 | |||
| 701 | image_fstypes = self.build.get_image_fstypes() | ||
| 702 | |||
| 703 | # filter out any image files whose suffixes aren't in the | ||
| 704 | # IMAGE_FSTYPES suffixes variable for this target's build | ||
| 705 | image_files = [target_image_file \ | ||
| 706 | for target_image_file in target.target_image_file_set.all() \ | ||
| 707 | if target_image_file.suffix in image_fstypes] | ||
| 708 | |||
| 709 | for image_file in image_files: | ||
| 710 | image_file.pk = None | ||
| 711 | image_file.target = self | ||
| 712 | image_file.save() | ||
| 713 | |||
| 714 | artifact_files = target.targetartifactfile_set.all() | ||
| 715 | for artifact_file in artifact_files: | ||
| 716 | artifact_file.pk = None | ||
| 717 | artifact_file.target = self | ||
| 718 | artifact_file.save() | ||
| 719 | |||
| 720 | self.license_manifest_path = target.license_manifest_path | ||
| 721 | self.save() | ||
| 722 | |||
| 723 | # an Artifact is anything that results from a target being built, and may | ||
| 724 | # be of interest to the user, and is not an image file | ||
| 725 | class TargetArtifactFile(models.Model): | ||
| 726 | target = models.ForeignKey(Target) | ||
| 727 | file_name = models.FilePathField() | ||
| 728 | file_size = models.IntegerField() | ||
| 729 | |||
| 730 | @property | ||
| 731 | def basename(self): | ||
| 732 | return os.path.basename(self.file_name) | ||
| 733 | |||
| 615 | class Target_Image_File(models.Model): | 734 | class Target_Image_File(models.Model): |
| 616 | # valid suffixes for image files produced by a build | 735 | # valid suffixes for image files produced by a build |
| 617 | SUFFIXES = { | 736 | SUFFIXES = { |
