1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  import os 
 18  import cookielib 
 19  import logging 
 20  import posixpath 
 21  import types 
 22  import urllib 
 23   
 24  try: 
 25    import socks 
 26    import socket 
 27    socks_server = os.environ.get("SOCKS_SERVER", None) 
 28    if socks_server: 
 29      host, port = socks_server.split(":") 
 30      socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, host, int(port)) 
 31      socket.socket = socks.socksocket 
 32  except ImportError: 
 33    pass 
 34   
 35  import urllib2 
 36   
 37  __docformat__ = "epytext" 
 38   
 39  LOG = logging.getLogger(__name__) 
 42    """ 
 43    Any error result from the Rest API is converted into this exception type. 
 44    """ 
 46      Exception.__init__(self, error) 
 47      self._error = error 
 48      self._code = None 
 49      self._message = str(error) 
 50       
 51      try: 
 52        self._code = error.code 
 53        self._message = error.read() 
 54      except AttributeError: 
 55        pass 
  56   
 58      res = self._message or "" 
 59      if self._code is not None: 
 60        res += " (error %s)" % (self._code,) 
 61      return res 
  62   
 64      if isinstance(self._error, Exception): 
 65        return self._error 
 66      return None 
  67   
 68    @property 
 71   
 72    @property 
  75   
 78    """ 
 79    Basic HTTP client tailored for rest APIs. 
 80    """ 
 81 -  def __init__(self, base_url, exc_class=None, logger=None): 
  82      """ 
 83      @param base_url: The base url to the API. 
 84      @param exc_class: An exception class to handle non-200 results. 
 85   
 86      Creates an HTTP(S) client to connect to the Cloudera Manager API. 
 87      """ 
 88      self._base_url = base_url.rstrip('/') 
 89      self._exc_class = exc_class or RestException 
 90      self._logger = logger or LOG 
 91      self._headers = { } 
 92   
 93       
 94      self._passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() 
 95      authhandler = urllib2.HTTPBasicAuthHandler(self._passmgr) 
 96   
 97       
 98      cookiejar = cookielib.CookieJar() 
 99   
100      self._opener = urllib2.build_opener( 
101          HTTPErrorProcessor(), 
102          urllib2.HTTPCookieProcessor(cookiejar), 
103          authhandler) 
 104   
105   
107      """ 
108      Set up basic auth for the client 
109      @param username: Login name. 
110      @param password: Login password. 
111      @param realm: The authentication realm. 
112      @return: The current object 
113      """ 
114      self._passmgr.add_password(realm, self._base_url, username, password) 
115      return self 
 116   
118      """ 
119      Add headers to the request 
120      @param headers: A dictionary with the key value pairs for the headers 
121      @return: The current object 
122      """ 
123      self._headers = headers 
124      return self 
 125   
126   
127    @property 
129      return self._base_url 
 130   
131    @property 
134   
136      res = self._headers.copy() 
137      if headers: 
138        res.update(headers) 
139      return res 
 140   
141 -  def execute(self, http_method, path, params=None, data=None, headers=None): 
 142      """ 
143      Submit an HTTP request. 
144      @param http_method: GET, POST, PUT, DELETE 
145      @param path: The path of the resource. 
146      @param params: Key-value parameter data. 
147      @param data: The data to attach to the body of the request. 
148      @param headers: The headers to set for this request. 
149   
150      @return: The result of urllib2.urlopen() 
151      """ 
152       
153      url = self._make_url(path, params) 
154      if http_method in ("GET", "DELETE"): 
155        if data is not None: 
156          self.logger.warn( 
157              "GET method does not pass any data. Path '%s'" % (path,)) 
158          data = None 
159   
160       
161      request = urllib2.Request(url, data) 
162       
163      request.get_method = lambda: http_method 
164   
165      headers = self._get_headers(headers) 
166      for k, v in headers.items(): 
167        request.add_header(k, v) 
168   
169       
170      self.logger.debug("%s %s" % (http_method, url)) 
171      try: 
172        return self._opener.open(request) 
173      except urllib2.HTTPError, ex: 
174        raise self._exc_class(ex) 
 175   
177      res = self._base_url 
178      if path: 
179        res += posixpath.normpath('/' + path.lstrip('/')) 
180      if params: 
181        param_str = urllib.urlencode(params, True) 
182        res += '?' + param_str 
183      return iri_to_uri(res) 
  184   
187    """ 
188    Python 2.4 only recognize 200 and 206 as success. It's broken. So we install 
189    the following processor to catch the bug. 
190    """ 
195   
196    https_response = http_response 
 197   
202      """ 
203      Convert an Internationalized Resource Identifier (IRI) portion to a URI 
204      portion that is suitable for inclusion in a URL. 
205   
206      This is the algorithm from section 3.1 of RFC 3987.  However, since we are 
207      assuming input is either UTF-8 or unicode already, we can simplify things a 
208      little from the full method. 
209   
210      Returns an ASCII string containing the encoded result. 
211      """ 
212       
213       
214       
215       
216       
217       
218       
219       
220       
221       
222       
223       
224      if iri is None: 
225          return iri 
226      return urllib.quote(smart_str(iri), safe="/#%[]=:;$&()+,!?*@'~") 
 227   
228   
229   
230   
231 -def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): 
 232      """ 
233      Returns a bytestring version of 's', encoded as specified in 'encoding'. 
234   
235      If strings_only is True, don't convert (some) non-string-like objects. 
236      """ 
237      if strings_only and isinstance(s, (types.NoneType, int)): 
238          return s 
239      elif not isinstance(s, basestring): 
240          try: 
241              return str(s) 
242          except UnicodeEncodeError: 
243              if isinstance(s, Exception): 
244                   
245                   
246                   
247                  return ' '.join([smart_str(arg, encoding, strings_only, 
248                          errors) for arg in s]) 
249              return unicode(s).encode(encoding, errors) 
250      elif isinstance(s, unicode): 
251          return s.encode(encoding, errors) 
252      elif s and encoding != 'utf-8': 
253          return s.decode('utf-8', errors).encode(encoding, errors) 
254      else: 
255          return s 
 256