blob: b52f213c172d3ae74dbdecdf12422f1266dc85c3 [file] [log] [blame]
jamesren5a13d8d2010-02-12 00:46:09 +00001import logging, pprint, re, urllib, getpass, urlparse
showardf828c772010-01-25 21:49:42 +00002import httplib2
3from django.utils import simplejson
jamesren5a13d8d2010-02-12 00:46:09 +00004from autotest_lib.frontend.afe import rpc_client_lib
showardf828c772010-01-25 21:49:42 +00005
6
showardf828c772010-01-25 21:49:42 +00007_http = httplib2.Http()
jamesren5a13d8d2010-02-12 00:46:09 +00008_request_headers = {}
9
10
11def _get_request_headers(uri):
12 server = urlparse.urlparse(uri)[0:2]
13 if server in _request_headers:
14 return _request_headers[server]
15
16 headers = rpc_client_lib.authorization_headers(getpass.getuser(), uri)
17 headers['Content-Type'] = 'application/json'
18
19 _request_headers[server] = headers
20 return headers
showardf828c772010-01-25 21:49:42 +000021
22
23class RestClientError(Exception):
24 pass
25
26
27class ClientError(Exception):
28 pass
29
30
31class ServerError(Exception):
32 pass
33
34
35class Response(object):
36 def __init__(self, httplib_response, httplib_content):
37 self.status = int(httplib_response['status'])
38 self.headers = httplib_response
39 self.entity_body = httplib_content
40
41
42 def decoded_body(self):
43 return simplejson.loads(self.entity_body)
44
45
46 def __str__(self):
47 return '\n'.join([str(self.status), self.entity_body])
48
49
50class Resource(object):
showardf46ad4c2010-02-03 20:28:59 +000051 def __init__(self, representation_dict):
showardf828c772010-01-25 21:49:42 +000052 assert 'href' in representation_dict
53 for key, value in representation_dict.iteritems():
54 setattr(self, str(key), value)
55
56
57 def __repr__(self):
58 return 'Resource(%r)' % self._representation()
59
60
61 def pprint(self):
62 # pretty-print support for debugging/interactive use
63 pprint.pprint(self._representation())
64
65
66 @classmethod
showardf46ad4c2010-02-03 20:28:59 +000067 def load(cls, uri):
68 directory = cls({'href': uri})
showardf828c772010-01-25 21:49:42 +000069 return directory.get()
70
71
72 def _read_representation(self, value):
73 # recursively convert representation dicts to Resource objects
74 if isinstance(value, list):
75 return [self._read_representation(element) for element in value]
76 if isinstance(value, dict):
77 converted_dict = dict((key, self._read_representation(sub_value))
78 for key, sub_value in value.iteritems())
79 if 'href' in converted_dict:
showardf46ad4c2010-02-03 20:28:59 +000080 return type(self)(converted_dict)
showardf828c772010-01-25 21:49:42 +000081 return converted_dict
82 return value
83
84
85 def _write_representation(self, value):
86 # recursively convert Resource objects to representation dicts
87 if isinstance(value, list):
88 return [self._write_representation(element) for element in value]
89 if isinstance(value, dict):
90 return dict((key, self._write_representation(sub_value))
91 for key, sub_value in value.iteritems())
92 if isinstance(value, Resource):
93 return value._representation()
94 return value
95
96
97 def _representation(self):
98 return dict((key, self._write_representation(value))
99 for key, value in self.__dict__.iteritems()
100 if not key.startswith('_')
101 and not callable(value))
102
103
showardf46ad4c2010-02-03 20:28:59 +0000104 def _do_request(self, method, uri, query_parameters, encoded_body):
showardf828c772010-01-25 21:49:42 +0000105 if query_parameters:
showardf46ad4c2010-02-03 20:28:59 +0000106 query_string = '?' + urllib.urlencode(query_parameters)
107 else:
108 query_string = ''
109 full_uri = uri + query_string
showardf828c772010-01-25 21:49:42 +0000110
111 if encoded_body:
112 entity_body = simplejson.dumps(encoded_body)
113 else:
114 entity_body = None
115
showardf828c772010-01-25 21:49:42 +0000116 logging.debug('%s %s', method, full_uri)
117 if entity_body:
118 logging.debug(entity_body)
119 headers, response_body = _http.request(
showardf46ad4c2010-02-03 20:28:59 +0000120 full_uri, method, body=entity_body,
jamesren5a13d8d2010-02-12 00:46:09 +0000121 headers=_get_request_headers(uri))
showardf828c772010-01-25 21:49:42 +0000122 logging.debug('Response: %s', headers['status'])
123
showardf46ad4c2010-02-03 20:28:59 +0000124 return Response(headers, response_body)
125
126
127 def _request(self, method, query_parameters=None, encoded_body=None):
128 if query_parameters is None:
129 query_parameters = {}
130
131 response = self._do_request(method, self.href, query_parameters,
132 encoded_body)
133
showardf828c772010-01-25 21:49:42 +0000134 if 300 <= response.status < 400: # redirection
135 raise NotImplementedError(str(response)) # TODO
136 if 400 <= response.status < 500:
137 raise ClientError(str(response))
138 if 500 <= response.status < 600:
139 raise ServerError(str(response))
140 return response
141
142
143 def _stringify_query_parameter(self, value):
144 if isinstance(value, (list, tuple)):
145 return ','.join(value)
146 return str(value)
147
148
149 def get(self, **query_parameters):
150 string_parameters = dict((key, self._stringify_query_parameter(value))
151 for key, value in query_parameters.iteritems()
152 if value is not None)
153 response = self._request('GET', query_parameters=string_parameters)
154 assert response.status == 200
155 return self._read_representation(response.decoded_body())
156
157
158 def put(self):
159 response = self._request('PUT', encoded_body=self._representation())
160 assert response.status == 200
161 return self._read_representation(response.decoded_body())
162
163
164 def delete(self):
165 response = self._request('DELETE')
166 assert response.status == 204 # no content
167
168
169 def post(self, request_dict):
170 # request_dict may still have resources in it
171 request_dict = self._write_representation(request_dict)
172 response = self._request('POST', encoded_body=request_dict)
173 assert response.status == 201 # created
174 return self._read_representation({'href': response.headers['location']})