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