Draft implementation of grouping
authorEd Bartosh <eduard.bartosh@intel.com>
Wed, 14 Aug 2013 15:53:40 +0000 (18:53 +0300)
committerEd Bartosh <eduard.bartosh@intel.com>
Thu, 15 Aug 2013 14:09:59 +0000 (17:09 +0300)
Grouping is done by creating group project and aggregating binaries
from submission projects into it.

Various checks for submissions have been implemented. Submissions
should have been published, they should not have common packages and
all grouped projects should have the same target project.

Note, that backend should be adjusted to build images for group
projects the same way as for submission projects.

Fixes: #1143
Change-Id: I5bdff34c951453707579579620bf3c28a0417d21
Signed-off-by: Ed Bartosh <eduard.bartosh@intel.com>
Reviewed-on: https://otctools.jf.intel.com/review/5900
Tested-by: OTC Tools Tester <ed.bartosh@linux.intel.com>
repa/group.py

index 0c3089af7435416fe83d0041812ae900ceacad94..95b71e8a2aba2a2a5d57aff3f9d8ece8c64159f2 100755 (executable)
@@ -12,8 +12,124 @@ Group submissions.
 """
 
 import sys
+import time
+import json
+
+from collections import defaultdict
+from StringIO import StringIO
 
 from repa.main import sub_main
+from repa.obs import OBS, OBS_PREFIX
+from repa.common import RepaException
+
+
+def check_target_prj(submissions):
+    """Check if target projects are the same for all submissions"""
+    result = defaultdict(list)
+    for submission, data in submissions.iteritems():
+        result[data['meta']['obs_target_prj']].append(submission)
+    if len(result) > 1:
+        message = '\n'.join('%s: %s' % (project, ' '.join(subms)) \
+                                for project, subms in result.iteritems())
+        raise RepaException('Target projects differ:\n%s\n' % message)
+
+
+def check_build_results(bresults):
+    """Check if build targets are published."""
+    for subm, _prj, results in bresults:
+        for target, res in results.iteritems():
+            if res['state'] != 'published' or res['code'] != 'published':
+                if res['packages']:
+                    raise RepaException("%s: target %s is not published yet" %
+                                        (subm, target))
+            # check if status of package builds is not worse than in
+            # target project: for pkg, status in res['packages'] ...
+
+
+def check_common_pkgs(obs, submissions):
+    """Check if submissions have common packages"""
+    binaries = {}
+    for submission, data in submissions.iteritems():
+        pkgs = list(obs.get_binary_packages(data['project']))
+        # check if submission has binary packages
+        for repo, bins in pkgs:
+            #if not bins:
+            #    raise RepaException('No binary packages found in %s %s/%s' % \
+            #                            (submission, repo[0], repo[1]))
+            # check if submissions have common packages
+            for subm, info in binaries.iteritems():
+                if repo == info['repo']:
+                    common = set(info['bins']).intersection(bins)
+                    if common:
+                        msg = '%s and %s have common packages: %s' % \
+                              (subm, submission, ','.join(common))
+                        raise RepaException(msg)
+
+
+def create_group_project(obs, submissions, meta, comment):
+    """Create group project."""
+    target_prj = str(meta['obs_target_prj'])
+    timestamp = time.strftime('%Y%m%d.%H%M%S', time.gmtime())
+    name = "submitgroup/%s/%s" % ('/'.join(meta['git_tag'].split('/')[1:-1]),
+                                  timestamp)
+    gmeta = {'name': name, 'obs_target_prj': target_prj,
+             'submissions': submissions, 'comment': comment}
+    project = '%s%s' % (OBS_PREFIX, name.replace('/', ':'))
+
+    saved = sys.stdout
+    sys.stdout = StringIO()
+    try:
+        obs.create_project(project, target_prj, description=json.dumps(gmeta))
+    finally:
+        sys.stdout = saved
+    return name, str(project)
+
+
+def aggregate(obs, bresults, gproject):
+    """Aggregate packages into group project."""
+    aggregated = set()
+    for subm, prj, results in bresults:
+        for res in results.itervalues():
+            for pkg, state in res['packages']:
+                if state == 'succeeded' and pkg not in aggregated:
+                    print 'aggregating %s/%s' % (subm, pkg)
+                    obs.aggregate_package(prj, pkg, gproject, pkg)
+                    aggregated.add(pkg)
+
+    return aggregated
+
+
+def group_submissions(obs, submissions, comment):
+    """Group multiple submissions into one group."""
+    # find correspondent prerelease projects
+    info = {}
+    for submission in submissions:
+        suffix = submission.replace('/', ':')
+        found = list(obs.get_projects('^%s.*:%s$' % (OBS_PREFIX, suffix)))
+        if not found:
+            raise RepaException('OBS project not found for %s' % submission)
+        project, description = found[0]
+        info[submission] = {'project': project, 'meta': json.loads(description)}
+
+    # Validate submissions
+    check_target_prj(info)
+
+    bresults = [(subm, data['project'], obs.get_build_results(data['project']))\
+                 for subm, data in info.iteritems()]
+    check_build_results(bresults)
+
+    check_common_pkgs(obs, info)
+
+    # create group project
+    name, gproject = create_group_project(obs, info.keys(),
+                                          info.itervalues().next()['meta'],
+                                          comment)
+    print 'Created submit group %s\n' % name
+
+    aggregated = aggregate(obs, bresults, gproject)
+
+    print '\n%d submissions (%d packages) have been merged into %s' % \
+          (len(info), len(aggregated), name)
 
 class Group(object):
     """Subcommand: Manage group  submissions."""
@@ -25,12 +141,15 @@ class Group(object):
     @staticmethod
     def add_arguments(parser, _config):
         """Adds arguments to the parser. Called from [sub_]main."""
-        parser.add_argument('--comment', help='comment')
+        parser.add_argument('submission', nargs='+',
+                           help='space separated list of submissions')
+        parser.add_argument('-comment', help='comment', default='')
 
-    def run(self, argv):
+    @staticmethod
+    def run(argv):
         """Command line entry point. Called from [sub_]main."""
-        print '%s: Not implemented yet' % self.help
-        print 'comment=%s' % argv.comment
+        obs = OBS(argv.apiurl, argv.apiuser, argv.apipasswd)
+        return group_submissions(obs, argv.submission, argv.comment)
 
 
 if __name__ == '__main__':