Updated Branches:
  refs/heads/trunk 741746124 -> 85fcfb1ae

AMBARI-3244. Ambari Client improve errors handling. (Andrew Onischuk via 
mahadev)


Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/85fcfb1a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/85fcfb1a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/85fcfb1a

Branch: refs/heads/trunk
Commit: 85fcfb1aeb2c6ce9cea30f6e2be25fb7858bcd18
Parents: 7417461
Author: Mahadev Konar <[email protected]>
Authored: Tue Sep 17 13:14:14 2013 -0700
Committer: Mahadev Konar <[email protected]>
Committed: Tue Sep 17 13:14:14 2013 -0700

----------------------------------------------------------------------
 .../main/python/ambari_client/core/errors.py    | 88 +++++++++++++-------
 .../python/ambari_client/core/rest_resource.py  | 23 +++--
 .../src/main/python/ambari_client/model/host.py |  2 +-
 .../main/python/ambari_client/model/status.py   |  7 +-
 .../main/python/ambari_client/model/utils.py    | 30 +++++--
 .../src/test/python/TestAmbariClient.py         | 26 +++++-
 6 files changed, 121 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/85fcfb1a/ambari-client/src/main/python/ambari_client/core/errors.py
----------------------------------------------------------------------
diff --git a/ambari-client/src/main/python/ambari_client/core/errors.py 
b/ambari-client/src/main/python/ambari_client/core/errors.py
index 65515ed..c691241 100755
--- a/ambari-client/src/main/python/ambari_client/core/errors.py
+++ b/ambari-client/src/main/python/ambari_client/core/errors.py
@@ -17,36 +17,62 @@
 
 class ResourceError(Exception):
 
-    def __init__(self, msg=None, http_code=None, response=None):
-        self.msg = msg or ''
-        self.status_code = http_code
-        self.response = response
-        Exception.__init__(self)
-        
-    def _get_message(self):
-        return self.msg
-    def _set_message(self, msg):
-        self.msg = msg or ''
-    message = property(_get_message, _set_message)    
-    
-    def __str__(self):
-        if self.msg:
-            return self.msg
-        try:
-            return self._fmt % self.__dict__
-        except (NameError, ValueError, KeyError), e:
-            return 'exception %s: %s' \
-                % (self.__class__.__name__, str(e))
-        
-class ResourceNotFound(ResourceError):
-    """Exception raised when no resource was found. 
+  def __init__(self, response, resource_root=None):
     """
+    Create new exception based on not successful server response
+    @param response: StatusModel response
+    @param resource_root: The resource which sent an error response
+    """
+    self.response = response
+    self.resource_root = resource_root
+    Exception.__init__(self)
+        
+  def get_message(self):
+    """ Get an error message """
+    return self.response.get_message()
+  
+  def get_status_code(self):
+    """ Get a status(error) code from the server response """
+    return self.response.status
+  
+  def get_reponse(self):
+    """ StatusModel object """
+    return self.reponse
+  
+  def get_root_resource(self):
+    """ AmbariClient object """
+    return self.resource_root
+  
+  def __str__(self):
+    if self.get_message():
+      return "exception: %s. %s" % (self.response.status, self.get_message()) 
+    try:
+      return self._fmt % self.__dict__
+    except (NameError, ValueError, KeyError), e:
+      return 'exception %s: %s' \
+             % (self.__class__.__name__, str(e))
 
-class RequestError(Exception):
-    """Exception for incorrect request """
-    
-class Unauthorized(ResourceError):
-    """Exception when an authorization is required """
-
-class RequestFailed(ResourceError):
-    """Exception for unexpected HTTP error  """
\ No newline at end of file
+class ResourceConflict(ResourceError):
+  """ 409 status code """
+class ResourceNotFound(ResourceError):
+  """ 404 status code """
+class BadRequest(ResourceError):
+  """ 400 status code """
+class AuthorizationError(ResourceError):
+  """ 401 status code """
+class ForbiddenError(ResourceError):
+  """ 403 status code """
+class InternalServerError(ResourceError):
+  """ 500 status code """
+class MethodNotAllowed(ResourceError):
+  """ 405 status code """
+class UnknownServerError(ResourceError):
+  """ Received other response code """
+  
+_exceptions_to_codes = { 409:ResourceConflict, 
+                        404:ResourceNotFound,
+                        400:BadRequest,
+                        401:AuthorizationError,
+                        403:ForbiddenError,
+                        500:InternalServerError,
+                        405:MethodNotAllowed }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/85fcfb1a/ambari-client/src/main/python/ambari_client/core/rest_resource.py
----------------------------------------------------------------------
diff --git a/ambari-client/src/main/python/ambari_client/core/rest_resource.py 
b/ambari-client/src/main/python/ambari_client/core/rest_resource.py
index f7e2e23..532fc53 100755
--- a/ambari-client/src/main/python/ambari_client/core/rest_resource.py
+++ b/ambari-client/src/main/python/ambari_client/core/rest_resource.py
@@ -64,22 +64,19 @@ class RestResource(object):
     LOG.debug ("RESPONSE from the REST request >>>>>>> \n" + str(resp))
     LOG.debug ("\n===========================================================")
     #take care of REST calls with no response
-    if not resp and (code != 200 and code != 201):
-        LOG.error("Command '%s %s' failed with error %s" % (http_method, path, 
code))
-        return {"status":code , "message":"Command '%s %s' failed with error 
%s" % (http_method, path, code)}
-        #raise Exception("Command '%s %s' failed with error %s" % 
(http_method, path, code))
-    if resp and (code == 404 or code == 405 or code == 500):
-        LOG.error("Command '%s %s' failed with error %s" % (http_method, path, 
code))
-        return {"status":code , "message":"Command '%s %s' failed with error 
%s" % (http_method, path, code)}
-        #raise Exception("Command '%s %s' failed with error %s" % 
(http_method, path, code))
+
     try:
-        if (code == 200 or code == 201) and not resp:
-          return {"status":code}
+      isOK = (code == 200 or code == 201 or code == 202)
+        
+      if isOK and not resp:
+        json_dict = {"status":code}
+      else:
         json_dict = json.loads(resp)
-        return json_dict
+          
+      return json_dict
     except Exception, ex:
-        LOG.error('JSON decode error: %s' % (resp,))
-        raise ex
+      LOG.error("Command '%s %s' failed with error %s\n%s" % (http_method, 
path, code ,resp))
+      return {"status":code , "message":"Command '%s %s' failed with error %s" 
% (http_method, path, code)}
 
 
   def get(self, path=None):

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/85fcfb1a/ambari-client/src/main/python/ambari_client/model/host.py
----------------------------------------------------------------------
diff --git a/ambari-client/src/main/python/ambari_client/model/host.py 
b/ambari-client/src/main/python/ambari_client/model/host.py
index 2a0e101..c5e94cb 100755
--- a/ambari-client/src/main/python/ambari_client/model/host.py
+++ b/ambari-client/src/main/python/ambari_client/model/host.py
@@ -79,7 +79,7 @@ def _create_host(root_resource, host_name, ip, 
rack_info=None):
   @param rack_info: Rack id. Default None
   @return: An HostModel object
   """
-  host_list = ModelList([HostModel(host_name, ip, rack_info)])
+  host_list = ModelList([HostModel(root_resource, host_name, ip, rack_info)])
   return _create_hosts(root_resource, host_list)
 
 def _add_hosts(root_resource, cluster_name , host_list):

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/85fcfb1a/ambari-client/src/main/python/ambari_client/model/status.py
----------------------------------------------------------------------
diff --git a/ambari-client/src/main/python/ambari_client/model/status.py 
b/ambari-client/src/main/python/ambari_client/model/status.py
index 1eba35d..331a701 100755
--- a/ambari-client/src/main/python/ambari_client/model/status.py
+++ b/ambari-client/src/main/python/ambari_client/model/status.py
@@ -35,7 +35,7 @@ class StatusModel(BaseModel):
     utils.retain_self_helper(BaseModel, **locals())
 
   def __str__(self):
-    return "<<StatusModel>> status = %s ; requestId = %s ;message = %s" % 
(self.status, self._get_id() , self._get_message())
+    return "<<StatusModel>> status = %s ; requestId = %s ;message = %s" % 
(self.status, self._get_id() , self.get_message())
 
   def get_bootstrap_path(self):
     return paths.BOOTSTRAP_PATH + '/' + self.requestId
@@ -43,11 +43,14 @@ class StatusModel(BaseModel):
   def get_request_path(self):
     return paths.REQUEST_PATH % (self._get_id())
 
-  def _get_message(self):
+  def get_message(self):
     if hasattr(self, 'message'):
         return self.message
     else:
         None
+    
+  def is_error(self):
+    return (self.status != 200 and self.status != 201 and self.status != 202)  
         
   def _get_id(self):
     if hasattr(self, 'requestId') and self.requestId:

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/85fcfb1a/ambari-client/src/main/python/ambari_client/model/utils.py
----------------------------------------------------------------------
diff --git a/ambari-client/src/main/python/ambari_client/model/utils.py 
b/ambari-client/src/main/python/ambari_client/model/utils.py
index 4a7fa5e..38add26 100755
--- a/ambari-client/src/main/python/ambari_client/model/utils.py
+++ b/ambari-client/src/main/python/ambari_client/model/utils.py
@@ -17,7 +17,8 @@
 
 import logging
 import sys
-import unicodedata 
+import unicodedata
+from ambari_client.core import errors
 
 LOG = logging.getLogger(__name__)
 
@@ -28,9 +29,22 @@ ref_pkg_dic = 
{"ClusterModelRef":"ambari_client.model.cluster"}
 LIST_KEY = "items"
   
 class ModelUtils(object):
+  
+  @staticmethod
+  def _check_is_error(expected_class, model_dict, resource_root):
+    from ambari_client.model.status import StatusModel
+
+    if model_dict.has_key("status"):
+      resp = ModelUtils.create_model(StatusModel, model_dict.copy(), 
resource_root, "NO_KEY", check_errors=False)
+      
+      if expected_class!=StatusModel or resp.is_error():
+        if resp.status in errors._exceptions_to_codes:
+          raise errors._exceptions_to_codes[resp.status](resp, resource_root)
+        else:
+          raise errors.UnknownServerError(resp, resource_root)
 
   @staticmethod
-  def get_model_list(member_list_clss, member_cls, collection_dict, 
resource_root , RESOURCE_KEY_WORD):
+  def get_model_list(member_list_clss, member_cls, collection_dict, 
resource_root , RESOURCE_KEY_WORD, check_errors=True):
     """
     create a model.
     @param member_list_clss : model_list class.
@@ -40,6 +54,9 @@ class ModelUtils(object):
     @param RESOURCE_KEY_WORD : tsake subset of model_dict based on this key.
     @return: A  ModelList object.
     """
+    if check_errors:
+      ModelUtils._check_is_error(member_list_clss, collection_dict, 
resource_root)
+    
     #print locals()
     json_list = []
     
@@ -67,7 +84,7 @@ class ModelUtils(object):
 
 
   @staticmethod
-  def create_model(model_cls, model_dict, resource_root, RESOURCE_KEY_WORD, 
exception_cls=None):
+  def create_model(model_cls, model_dict, resource_root, RESOURCE_KEY_WORD, 
check_errors=True):
     """
     create a model.
     @param model_cls : model class.
@@ -76,6 +93,9 @@ class ModelUtils(object):
     @param RESOURCE_KEY_WORD : tsake subset of model_dict based on this key.
     @return: A model_cls object.
     """
+    if check_errors:
+      ModelUtils._check_is_error(model_cls, model_dict, resource_root)
+      
     #print locals()
     rw_dict = { }
     LOG.debug ("model_dict =   " + str(model_dict))
@@ -86,10 +106,6 @@ class ModelUtils(object):
     if isinstance(model_dict, dict) and model_dict.has_key("Requests"):
         model_dict = model_dict["Requests"]
         LOG.debug ("model_dict has Requests ;subset = %s" % 
(str(model_dict.items())))
-    if isinstance(model_dict, dict) and model_dict.has_key("status") and 
exception_cls:
-        LOG.debug ("model_dict has status ,might be a exception from Ambari 
;model_cls = %s ;exception_clss = %s" % 
-                   (str(model_cls), str(exception_cls)))
-        return ModelUtils.create_model(exception_cls, model_dict, 
resource_root, "NO_KEY")
      
       
     for k, v in model_dict.items():

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/85fcfb1a/ambari-client/src/test/python/TestAmbariClient.py
----------------------------------------------------------------------
diff --git a/ambari-client/src/test/python/TestAmbariClient.py 
b/ambari-client/src/test/python/TestAmbariClient.py
index 0fba169..2dcdfce 100755
--- a/ambari-client/src/test/python/TestAmbariClient.py
+++ b/ambari-client/src/test/python/TestAmbariClient.py
@@ -21,6 +21,7 @@ limitations under the License.
 
 from mock.mock import MagicMock, patch
 from ambari_client.ambari_api import  AmbariClient 
+from ambari_client.core.errors import BadRequest
 
 import unittest
 
@@ -167,7 +168,27 @@ class TestAmbariClient(unittest.TestCase):
     self.assertEqual(str(ganglia.state), "STARTED", "The ganglia service state 
should be fetched as STARTED")
     self.assertEqual(ganglia.clusterRef.cluster_name, cluster.cluster_name, 
"The clusterRef value for  service  should be fetched ")
     
-
+  @patch("ambari_client.core.http_client.HttpClient")  
+  def test_exceptions(self , http_client):
+    """
+    Test exceptions from ambari.client.core.errors
+    """
+    http_client_mock = MagicMock()
+    http_client.returned_obj = http_client_mock
+    mocked_code = "200" 
+    mocked_content = "text/plain"
+    
+    http_client_mock.invoke.side_effect = http_client_invoke_side_effects
+    client = AmbariClient("localhost", 8080, "admin", "admin", version=1, 
client=http_client_mock)
+    cluster = client.get_cluster('test1')
+    
+    try:
+      cluster.delete_host('deleted_nonexistant_cluster')
+      self.fail('Exception should have been thrown!')
+    except BadRequest, ex:
+      self.assertEquals(str(ex), 'exception: 400. Attempted to add unknown 
hosts to a cluster.  These hosts have not been registered with the server: 
dev05')
+    except Exception, ex:
+      self.fail('Wrong exception thrown!')
 
   
 
@@ -208,3 +229,6 @@ def http_client_invoke_side_effects(*args, **kwargs):
     elif args[1] == "//clusters/test1/services/GANGLIA":
         mocked_response = open('json/get_service.json', 'r').read()
         return mocked_response, mocked_code , mocked_content
+    elif args[1] == "//clusters/test1/hosts/deleted_nonexistant_cluster":
+        mocked_response = open('json/error_adding_host.json', 'r').read()
+        return mocked_response, mocked_code , mocked_content

Reply via email to