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