Merge lp:~codehelp/lava-dashboard/multinode into lp:lava-dashboard/multinode

Proposed by Neil Williams
Status: Merged
Approved by: Neil Williams
Approved revision: 410
Merged at revision: 409
Proposed branch: lp:~codehelp/lava-dashboard/multinode
Merge into: lp:lava-dashboard/multinode
Diff against target: 176 lines (+150/-2)
1 file modified
dashboard_app/xmlrpc.py (+150/-2)
To merge this branch: bzr merge lp:~codehelp/lava-dashboard/multinode
Reviewer Review Type Date Requested Status
Fu Wei Pending
Linaro Automation & Validation Pending
Review via email: mp+180140@code.launchpad.net

This proposal supersedes a proposal from 2013-08-05.

Description of the change

MultiNode support to put_pending result bundles for jobs with a sub_id ending with .0. The MultiNode job with sub_id ending with .0 will be required by LAVA Coordinator to wait until all other results for that group have been posted as pending. Once the wait is cleared, the job aggregates its own results with the group pending results as a single bundle using put_group.

Updated to change to storing the pending bundles in a file and explicitly check for exceptions which would cause a failure to submit the aggregated result bundle.

To post a comment you must log in.
Revision history for this message
Neil Williams (codehelp) wrote : Posted in a previous version of this proposal

Fix up description:

MultiNode support to put_pending result bundles for jobs with a sub_id ending not with .0 into a stack inside the dashboard. The MultiNode job with sub_id ending with .0 will be required by LAVA Coordinator to wait until all other results for that group have been posted as pending. Once the wait is cleared, the job aggregates its own results with the group pending results as a single bundle using put_group.

Revision history for this message
Fu Wei (fu-wei) wrote : Posted in a previous version of this proposal

I have tested it, please merge it.

review: Approve
Revision history for this message
Fu Wei (fu-wei) wrote : Posted in a previous version of this proposal

yes, I can get the result bundle now, I will do more test.....

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dashboard_app/xmlrpc.py'
2--- dashboard_app/xmlrpc.py 2013-05-02 10:35:29 +0000
3+++ dashboard_app/xmlrpc.py 2013-08-14 13:55:48 +0000
4@@ -2,7 +2,7 @@
5 #
6 # Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
7 #
8-# This file is part of Launch Control.
9+# This file is part of LAVA Dashboard
10 #
11 # Launch Control is free software: you can redistribute it and/or modify
12 # it under the terms of the GNU Affero General Public License version 3
13@@ -25,7 +25,9 @@
14 import logging
15 import re
16 import xmlrpclib
17-
18+import hashlib
19+import json
20+import os
21 from django.contrib.auth.models import User, Group
22 from django.core.urlresolvers import reverse
23 from django.db import IntegrityError, DatabaseError
24@@ -243,6 +245,152 @@
25 'dashboard_app.views.redirect_to_bundle',
26 kwargs={'content_sha1':bundle.content_sha1}))
27
28+ def put_pending(self, content, group_name):
29+ """
30+ Name
31+ ----
32+ `put_pending` (`content`, `group_name`)
33+
34+ Description
35+ -----------
36+ MultiNode internal call.
37+
38+ Stores the bundle until the coordinator allows the complete
39+ bundle list to be aggregated from the list and submitted by put_group
40+
41+ Arguments
42+ ---------
43+ `content`: string
44+ Full text of the bundle. This *MUST* be a valid JSON
45+ document and it *SHOULD* match the "Dashboard Bundle Format
46+ 1.0" schema. The SHA1 of the content *MUST* be unique or a
47+ ``Fault(409, "...")`` is raised. This is used to protect
48+ from simple duplicate submissions.
49+ `group_name`: string
50+ Unique ID of the MultiNode group. Other pending bundles will
51+ be aggregated into a single result bundle for this group.
52+
53+ Return value
54+ ------------
55+ If all goes well this function returns the SHA1 of the content.
56+
57+ """
58+ try:
59+ # add this to a list which put_group can use.
60+ sha1 = hashlib.sha1()
61+ sha1.update(content)
62+ hexdigest = sha1.hexdigest()
63+ groupfile = "/tmp/%s" % group_name
64+ with open(groupfile, "a+") as grp_file:
65+ grp_file.write("%s\n" % content)
66+ return hexdigest
67+ except Exception as e:
68+ logging.debug("Dashboard pending submission caused an exception: %s" % e)
69+
70+ def put_group(self, content, content_filename, pathname, group_name):
71+ """
72+ Name
73+ ----
74+ `put_group` (`content`, `content_filename`, `pathname`, `group_name`)
75+
76+ Description
77+ -----------
78+ MultiNode internal call.
79+
80+ Adds the final bundle to the list, aggregates the list
81+ into a single group bundle and submits the group bundle.
82+
83+ Arguments
84+ ---------
85+ `content`: string
86+ Full text of the bundle. This *MUST* be a valid JSON
87+ document and it *SHOULD* match the "Dashboard Bundle Format
88+ 1.0" schema. The SHA1 of the content *MUST* be unique or a
89+ ``Fault(409, "...")`` is raised. This is used to protect
90+ from simple duplicate submissions.
91+ `content_filename`: string
92+ Name of the file that contained the text of the bundle. The
93+ `content_filename` can be an arbitrary string and will be
94+ stored along with the content for reference.
95+ `pathname`: string
96+ Pathname of the bundle stream where a new bundle should
97+ be created and stored. This argument *MUST* designate a
98+ pre-existing bundle stream or a ``Fault(404, "...")`` exception
99+ is raised. In addition the user *MUST* have access
100+ permission to upload bundles there or a ``Fault(403, "...")``
101+ exception is raised. See below for access rules.
102+ `group_name`: string
103+ Unique ID of the MultiNode group. Other pending bundles will
104+ be aggregated into a single result bundle for this group. At
105+ least one other bundle must have already been submitted as
106+ pending for the specified MultiNode group. LAVA Coordinator
107+ causes the parent job to wait until all nodes have been marked
108+ as having pending bundles, even if some bundles are empty.
109+
110+ Return value
111+ ------------
112+ If all goes well this function returns the full URL of the bundle.
113+
114+ Exceptions raised
115+ -----------------
116+ ValueError:
117+ One or more bundles could not be converted to JSON prior
118+ to aggregation.
119+ 404
120+ Either:
121+
122+ - Bundle stream not found
123+ - Uploading to specified stream is not permitted
124+ 409
125+ Duplicate bundle content
126+
127+ Rules for bundle stream access
128+ ------------------------------
129+ The following rules govern bundle stream upload access rights:
130+ - all anonymous streams are accessible
131+ - personal streams are accessible to owners
132+ - team streams are accessible to team members
133+
134+ """
135+ grp_file = "/tmp/%s" % group_name
136+ bundle_set = {}
137+ bundle_set[group_name] = []
138+ if os.path.isfile(grp_file):
139+ with open(grp_file, "r") as grp_data:
140+ grp_list = grp_data.readlines()
141+ for testrun in grp_list:
142+ bundle_set[group_name].append(json.loads(testrun))
143+ # Note: now that we have the data from the group, the group data file could be re-used
144+ # as an error log which is simpler than debugging through XMLRPC.
145+ else:
146+ raise ValueError("Aggregation failure for %s - check coordinator rpc_delay?" % group_name)
147+ group_tests = []
148+ try:
149+ json_data = json.loads(content)
150+ except ValueError:
151+ logging.debug("Invalid JSON content within the sub_id zero bundle")
152+ json_data = None
153+ try:
154+ bundle_set[group_name].append(json_data)
155+ except Exception as e:
156+ logging.debug("appending JSON caused exception %s" % e)
157+ try:
158+ for bundle_list in bundle_set[group_name]:
159+ for test_run in bundle_list['test_runs']:
160+ group_tests.append(test_run)
161+ except Exception as e:
162+ logging.debug("aggregating bundles caused exception %s" % e)
163+ group_content = json.dumps({"test_runs": group_tests, "format": json_data['format']})
164+ bundle = self._put(group_content, content_filename, pathname)
165+ logging.debug("Returning permalink to aggregated bundle for %s" % group_name)
166+ permalink = self._context.request.build_absolute_uri(
167+ reverse('dashboard_app.views.redirect_to_bundle',
168+ kwargs={'content_sha1': bundle.content_sha1}))
169+ # only delete the group file when things go well.
170+ if os.path.isfile(grp_file):
171+ os.remove(grp_file)
172+ return permalink
173+
174 def get(self, content_sha1):
175 """
176 Name

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: