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