86ce46d4ff
Left to do: - Directory reorganization. BUG= TEST= Review URL: https://webrtc-codereview.appspot.com/384003 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1605 4adac7df-926f-26a2-2b94-8c16560cd09d
144 lines
5.5 KiB
Python
144 lines
5.5 KiB
Python
#!/usr/bin/env python
|
|
#-*- coding: utf-8 -*-
|
|
# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
#
|
|
# Use of this source code is governed by a BSD-style license
|
|
# that can be found in the LICENSE file in the root of the source
|
|
# tree. An additional intellectual property rights grant can be found
|
|
# in the file PATENTS. All contributing project authors may
|
|
# be found in the AUTHORS file in the root of the source tree.
|
|
|
|
"""Contains utilities for communicating with the dashboard."""
|
|
|
|
__author__ = 'phoglund@webrtc.org (Patrik Höglund)'
|
|
|
|
import httplib
|
|
import shelve
|
|
import oauth.oauth as oauth
|
|
|
|
import constants
|
|
|
|
|
|
class FailedToReadRequiredInputFile(Exception):
|
|
pass
|
|
|
|
|
|
class FailedToReportToDashboard(Exception):
|
|
pass
|
|
|
|
|
|
class DashboardConnection:
|
|
"""Helper class for pushing data to the dashboard.
|
|
|
|
This class deals with most of details for accessing protected resources
|
|
(i.e. data-writing operations) on the dashboard. Such operations are
|
|
authenticated using OAuth. This class requires a consumer secret and a
|
|
access token.
|
|
|
|
The consumer secret is created manually on the machine running the script
|
|
(only authorized users should be able to log into the machine and see the
|
|
secret though). The access token is generated by the
|
|
request_oauth_permission.py script. Both these values are stored as files
|
|
on disk, in the scripts' working directory, according to the formats
|
|
prescribed in the read_required_files method.
|
|
"""
|
|
|
|
def __init__(self, consumer_key):
|
|
self.consumer_key_ = consumer_key
|
|
|
|
def read_required_files(self, consumer_secret_file, access_token_file):
|
|
"""Reads required data for making OAuth requests.
|
|
|
|
Args:
|
|
consumer_secret_file: A plain text file with a single line containing
|
|
the consumer secret string.
|
|
access_token_file: A shelve file with an entry access_token
|
|
containing the access token in string form.
|
|
"""
|
|
self.access_token_ = self._read_access_token(access_token_file)
|
|
self.consumer_secret_ = self._read_consumer_secret(consumer_secret_file)
|
|
|
|
def send_post_request(self, sub_url, parameters):
|
|
"""Sends an OAuth request for a protected resource in the dashboard.
|
|
|
|
Use this when you want to report new data to the dashboard. You must have
|
|
called the read_required_files method prior to calling this method, since
|
|
that method will read in the consumer secret and access token we need to
|
|
make the OAuth request. These concepts are described in the class
|
|
description.
|
|
|
|
The server is expected to respond with HTTP status 200 and a completely
|
|
empty response if the call failed. The server may put diagnostic
|
|
information in the response.
|
|
|
|
Args:
|
|
sub_url: A relative url within the dashboard domain, for example
|
|
/add_coverage_data.
|
|
parameters: A dict which maps from POST parameter names to values.
|
|
|
|
Raises:
|
|
FailedToReportToDashboard: If the dashboard didn't respond
|
|
with HTTP 200 to our request or if the response is non-empty.
|
|
"""
|
|
consumer = oauth.OAuthConsumer(self.consumer_key_, self.consumer_secret_)
|
|
create_oauth_request = oauth.OAuthRequest.from_consumer_and_token
|
|
oauth_request = create_oauth_request(consumer,
|
|
token=self.access_token_,
|
|
http_method='POST',
|
|
http_url=sub_url,
|
|
parameters=parameters)
|
|
|
|
signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
|
|
oauth_request.sign_request(signature_method_hmac_sha1, consumer,
|
|
self.access_token_)
|
|
|
|
connection = httplib.HTTPConnection(constants.DASHBOARD_SERVER)
|
|
|
|
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
connection.request('POST', sub_url, body=oauth_request.to_postdata(),
|
|
headers=headers)
|
|
|
|
response = connection.getresponse()
|
|
connection.close()
|
|
|
|
if response.status != 200:
|
|
message = ('Failed to report to %s%s: got response %d (%s)' %
|
|
(constants.DASHBOARD_SERVER, sub_url, response.status,
|
|
response.reason))
|
|
raise FailedToReportToDashboard(message)
|
|
|
|
# The response content should be empty on success, so check that:
|
|
response_content = response.read()
|
|
if response_content:
|
|
message = ('Dashboard reported the following error: %s.' %
|
|
response_content)
|
|
raise FailedToReportToDashboard(message)
|
|
|
|
def _read_access_token(self, filename):
|
|
input_file = shelve.open(filename)
|
|
|
|
if not input_file.has_key('access_token'):
|
|
raise FailedToReadRequiredInputFile('Missing correct %s file in current '
|
|
'directory. You may have to run '
|
|
'request_oauth_permission.py.' %
|
|
filename)
|
|
|
|
token = input_file['access_token']
|
|
input_file.close()
|
|
|
|
return oauth.OAuthToken.from_string(token)
|
|
|
|
def _read_consumer_secret(self, filename):
|
|
try:
|
|
input_file = open(filename, 'r')
|
|
except IOError as error:
|
|
raise FailedToReadRequiredInputFile(error)
|
|
|
|
whole_file = input_file.read()
|
|
if whole_file.count('\n') > 1 or not whole_file.strip():
|
|
raise FailedToReadRequiredInputFile('Expected a single line with the '
|
|
'consumer secret in file %s.' %
|
|
filename)
|
|
return whole_file
|
|
|