1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 try:
18 import json
19 except ImportError:
20 import simplejson as json
21 import logging
22 import posixpath
23 import time
24 import socket
25 import urllib2
26
27 LOG = logging.getLogger(__name__)
31 """
32 Encapsulates a resource, and provides actions to invoke on it.
33 """
35 """
36 @param client: A Client object.
37 @param relpath: The relative path of the resource.
38 """
39 self._client = client
40 self._path = relpath.strip('/')
41 self.retries = 3
42 self.retry_sleep = 3
43
44 @property
47
49 if relpath is None:
50 return self._path
51 return self._path + posixpath.normpath('/' + relpath)
52
53 - def invoke(self, method, relpath=None, params=None, data=None, headers=None):
54 """
55 Invoke an API method.
56 @return: Raw body or JSON dictionary (if response content type is JSON).
57 """
58 path = self._join_uri(relpath)
59 resp = self._client.execute(method,
60 path,
61 params=params,
62 data=data,
63 headers=headers)
64 try:
65 body = resp.read()
66 except Exception, ex:
67 raise Exception("Command '%s %s' failed: %s" %
68 (method, path, ex))
69
70 self._client.logger.debug(
71 "%s Got response: %s%s" %
72 (method, body[:32], len(body) > 32 and "..." or ""))
73
74
75 if len(body) != 0 and \
76 resp.info().getmaintype() == "application" and \
77 resp.info().getsubtype() == "json":
78 try:
79 json_dict = json.loads(body)
80 return json_dict
81 except Exception, ex:
82 self._client.logger.exception('JSON decode error: %s' % (body,))
83 raise ex
84 else:
85 return body
86
87
88 - def get(self, relpath=None, params=None):
89 """
90 Invoke the GET method on a resource.
91 @param relpath: Optional. A relative path to this resource's path.
92 @param params: Key-value data.
93
94 @return: A dictionary of the JSON result.
95 """
96 for retry in xrange(self.retries + 1):
97 if retry:
98 time.sleep(self.retry_sleep)
99 try:
100 return self.invoke("GET", relpath, params)
101 except (socket.error, urllib2.URLError) as e:
102 if "timed out" in str(e).lower():
103 log_message = "Timeout issuing GET request for %s." \
104 % (self._join_uri(relpath), )
105 if retry < self.retries:
106 log_message += " Will retry."
107 else:
108 log_message += " No retries left."
109 LOG.warn(log_message, exc_info=True)
110 else:
111 raise
112 else:
113 raise e
114
115
116 - def delete(self, relpath=None, params=None):
117 """
118 Invoke the DELETE method on a resource.
119 @param relpath: Optional. A relative path to this resource's path.
120 @param params: Key-value data.
121
122 @return: A dictionary of the JSON result.
123 """
124 return self.invoke("DELETE", relpath, params)
125
126
127 - def post(self, relpath=None, params=None, data=None, contenttype=None):
128 """
129 Invoke the POST method on a resource.
130 @param relpath: Optional. A relative path to this resource's path.
131 @param params: Key-value data.
132 @param data: Optional. Body of the request.
133 @param contenttype: Optional.
134
135 @return: A dictionary of the JSON result.
136 """
137 return self.invoke("POST", relpath, params, data,
138 self._make_headers(contenttype))
139
140
141 - def put(self, relpath=None, params=None, data=None, contenttype=None):
142 """
143 Invoke the PUT method on a resource.
144 @param relpath: Optional. A relative path to this resource's path.
145 @param params: Key-value data.
146 @param data: Optional. Body of the request.
147 @param contenttype: Optional.
148
149 @return: A dictionary of the JSON result.
150 """
151 return self.invoke("PUT", relpath, params, data,
152 self._make_headers(contenttype))
153
154
156 if contenttype:
157 return { 'Content-Type': contenttype }
158 return None
159