blob: e20904218c38b79e18b10c0b2d121752d9983bf0 [file] [log] [blame]
phoglund@webrtc.orgd4f0a0e2012-02-01 10:59:23 +00001#!/usr/bin/env python
2#-*- coding: utf-8 -*-
3# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
4#
5# Use of this source code is governed by a BSD-style license
6# that can be found in the LICENSE file in the root of the source
7# tree. An additional intellectual property rights grant can be found
8# in the file PATENTS. All contributing project authors may
9# be found in the AUTHORS file in the root of the source tree.
10
11"""Contains utilities for communicating with the dashboard."""
12
13__author__ = 'phoglund@webrtc.org (Patrik Höglund)'
14
15import httplib
16import shelve
17import oauth.oauth as oauth
18
19import constants
20
21
22class FailedToReadRequiredInputFile(Exception):
23 pass
24
25
26class FailedToReportToDashboard(Exception):
27 pass
28
29
30class DashboardConnection:
31 """Helper class for pushing data to the dashboard.
32
33 This class deals with most of details for accessing protected resources
34 (i.e. data-writing operations) on the dashboard. Such operations are
35 authenticated using OAuth. This class requires a consumer secret and a
36 access token.
37
38 The consumer secret is created manually on the machine running the script
39 (only authorized users should be able to log into the machine and see the
40 secret though). The access token is generated by the
41 request_oauth_permission.py script. Both these values are stored as files
42 on disk, in the scripts' working directory, according to the formats
43 prescribed in the read_required_files method.
44 """
45
46 def __init__(self, consumer_key):
47 self.consumer_key_ = consumer_key
48
49 def read_required_files(self, consumer_secret_file, access_token_file):
50 """Reads required data for making OAuth requests.
51
52 Args:
53 consumer_secret_file: A plain text file with a single line containing
54 the consumer secret string.
55 access_token_file: A shelve file with an entry access_token
56 containing the access token in string form.
57 """
58 self.access_token_ = self._read_access_token(access_token_file)
59 self.consumer_secret_ = self._read_consumer_secret(consumer_secret_file)
60
61 def send_post_request(self, sub_url, parameters):
62 """Sends an OAuth request for a protected resource in the dashboard.
63
64 Use this when you want to report new data to the dashboard. You must have
65 called the read_required_files method prior to calling this method, since
66 that method will read in the consumer secret and access token we need to
67 make the OAuth request. These concepts are described in the class
68 description.
69
phoglund@webrtc.org86ce46d2012-02-06 10:55:12 +000070 The server is expected to respond with HTTP status 200 and a completely
71 empty response if the call failed. The server may put diagnostic
72 information in the response.
73
phoglund@webrtc.orgd4f0a0e2012-02-01 10:59:23 +000074 Args:
75 sub_url: A relative url within the dashboard domain, for example
76 /add_coverage_data.
77 parameters: A dict which maps from POST parameter names to values.
78
phoglund@webrtc.orgd4f0a0e2012-02-01 10:59:23 +000079 Raises:
80 FailedToReportToDashboard: If the dashboard didn't respond
phoglund@webrtc.org86ce46d2012-02-06 10:55:12 +000081 with HTTP 200 to our request or if the response is non-empty.
phoglund@webrtc.orgd4f0a0e2012-02-01 10:59:23 +000082 """
83 consumer = oauth.OAuthConsumer(self.consumer_key_, self.consumer_secret_)
84 create_oauth_request = oauth.OAuthRequest.from_consumer_and_token
85 oauth_request = create_oauth_request(consumer,
86 token=self.access_token_,
87 http_method='POST',
88 http_url=sub_url,
89 parameters=parameters)
90
91 signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
92 oauth_request.sign_request(signature_method_hmac_sha1, consumer,
93 self.access_token_)
94
95 connection = httplib.HTTPConnection(constants.DASHBOARD_SERVER)
96
97 headers = {'Content-Type': 'application/x-www-form-urlencoded'}
98 connection.request('POST', sub_url, body=oauth_request.to_postdata(),
99 headers=headers)
100
101 response = connection.getresponse()
102 connection.close()
103
104 if response.status != 200:
phoglund@webrtc.org86ce46d2012-02-06 10:55:12 +0000105 message = ('Failed to report to %s%s: got response %d (%s)' %
phoglund@webrtc.orgd4f0a0e2012-02-01 10:59:23 +0000106 (constants.DASHBOARD_SERVER, sub_url, response.status,
107 response.reason))
108 raise FailedToReportToDashboard(message)
109
phoglund@webrtc.org86ce46d2012-02-06 10:55:12 +0000110 # The response content should be empty on success, so check that:
111 response_content = response.read()
112 if response_content:
113 message = ('Dashboard reported the following error: %s.' %
114 response_content)
115 raise FailedToReportToDashboard(message)
phoglund@webrtc.orgd4f0a0e2012-02-01 10:59:23 +0000116
117 def _read_access_token(self, filename):
118 input_file = shelve.open(filename)
119
120 if not input_file.has_key('access_token'):
121 raise FailedToReadRequiredInputFile('Missing correct %s file in current '
122 'directory. You may have to run '
123 'request_oauth_permission.py.' %
124 filename)
125
126 token = input_file['access_token']
127 input_file.close()
128
129 return oauth.OAuthToken.from_string(token)
130
131 def _read_consumer_secret(self, filename):
132 try:
133 input_file = open(filename, 'r')
134 except IOError as error:
135 raise FailedToReadRequiredInputFile(error)
136
137 whole_file = input_file.read()
138 if whole_file.count('\n') > 1 or not whole_file.strip():
139 raise FailedToReadRequiredInputFile('Expected a single line with the '
140 'consumer secret in file %s.' %
141 filename)
142 return whole_file
143