Move performance dashboard upload logic to devtoolslib. This patch moves the logic that uploads chart_data to performance dashboard, as well as the command line args spec that configures such upload to devtoolslib, so that it can be shared between `mojo_benchmark` and `perf_test_runner` (in a follow-up). R=etiennej@chromium.org Review URL: https://codereview.chromium.org/1421823002 . Cr-Mirrored-From: https://github.com/domokit/mojo Cr-Mirrored-Commit: aa9b52c175231c8677ac279a0630fda1b121ce50
diff --git a/devtoolslib/perf_dashboard.py b/devtoolslib/perf_dashboard.py index 74eb8ca..71fd143 100644 --- a/devtoolslib/perf_dashboard.py +++ b/devtoolslib/perf_dashboard.py
@@ -10,8 +10,15 @@ See http://www.chromium.org/developers/speed-infra/performance-dashboard/sending-data-to-the-performance-dashboard. """ -import json from collections import defaultdict +import httplib +import json +import pprint +import urllib +import urllib2 + + +_LOCAL_SERVER = "http://127.0.0.1:8080" class ChartDataRecorder(object): @@ -41,3 +48,121 @@ 'charts': self.charts } return json.dumps(chart_data) + + +def add_argparse_server_arguments(parser): + """Adds argparse arguments needed to upload the chart data to a performance + dashboard to the given parser. + """ + dashboard_group = parser.add_argument_group('Performance dashboard server', + 'These arguments allow to specify the performance dashboard server ' + 'to upload the results to.') + + dashboard_group.add_argument( + '--server-url', + help='Url of the server instance to upload the results to. By default a ' + 'local instance is assumed to be running on port 8080.') + dashboard_group.add_argument( + '--master-name', + help='Buildbot master name, used to construct link to buildbot log by ' + 'the dashboard, and also as the top-level category for the data.') + dashboard_group.add_argument( + '--perf-id', + help='Used as the second-level category for the data, usually the ' + 'platform type.') + dashboard_group.add_argument( + '--test-name', + help='Name of the test that the perf data was generated from.') + dashboard_group.add_argument( + '--builder-name', + help='Buildbot builder name, used to construct link to buildbot log by ' + 'the dashboard.') + dashboard_group.add_argument( + '--build-number', type=int, + help='Build number, used to construct link to buildbot log by the ' + 'dashboard.') + dashboard_group.add_argument( + '--dry-run', action='store_true', default=False, + help='Display the server URL and the data to upload, but do not actually ' + 'upload the data.') + + +def upload_chart_data(master_name, perf_id, test_name, builder_name, + build_number, revision, chart_data, point_id, + server_url=None, dry_run=False): + """Uploads the provided chart data to an instance of performance dashboard. + See the argparse help above for description of the arguments. + + + Returns: + A boolean value indicating whether the operation succeeded or not. + """ + class _UploadException(Exception): + pass + + def _upload(server_url, json_data): + """Make an HTTP POST with the given data to the performance dashboard. + + Args: + server_url: URL of the performance dashboard instance. + json_data: JSON string that contains the data to be sent. + + Raises: + _UploadException: An error occurred during uploading. + """ + # When data is provided to urllib2.Request, a POST is sent instead of GET. + # The data must be in the application/x-www-form-urlencoded format. + data = urllib.urlencode({"data": json_data}) + req = urllib2.Request("%s/add_point" % server_url, data) + try: + urllib2.urlopen(req) + except urllib2.HTTPError as e: + raise _UploadException("HTTPError: %d. Response: %s\n" + "JSON: %s\n" % (e.code, e.read(), json_data)) + except urllib2.URLError as e: + raise _UploadException("URLError: %s for JSON %s\n" % + (str(e.reason), json_data)) + except httplib.HTTPException as e: + raise _UploadException("HTTPException for JSON %s\n" % json_data) + + # Wrap the |chart_data| with meta data as required by the spec. + formatted_data = { + "master": master_name, + "bot": perf_id, + "masterid": master_name, + "buildername": builder_name, + "buildnumber": build_number, + "versions": { + "mojo": revision + }, + "point_id": point_id, + "supplemental": {}, + "chart_data": chart_data + } + + upload_url = server_url if server_url else _LOCAL_SERVER + + if dry_run: + print "Won't upload because --dry-run is specified." + print "Server: %s" % upload_url + print "Data:" + pprint.pprint(formatted_data) + else: + print "Uploading data to %s ..." % upload_url + try: + _upload(upload_url, json.dumps(formatted_data)) + except _UploadException as e: + print e + return False + + print "Done." + + dashboard_params = urllib.urlencode({ + "masters": master_name, + "bots": perf_id, + "tests": test_name, + "rev": point_id + }) + print "Results Dashboard: %s/report?%s" % (upload_url, dashboard_params) + + return True