summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oslo/vmware/api.py11
-rw-r--r--oslo/vmware/exceptions.py101
-rw-r--r--oslo/vmware/vim.py9
-rw-r--r--tests/test_api.py39
-rw-r--r--tests/test_vim.py2
5 files changed, 152 insertions, 10 deletions
diff --git a/oslo/vmware/api.py b/oslo/vmware/api.py
index 7d7397a..8ade849 100644
--- a/oslo/vmware/api.py
+++ b/oslo/vmware/api.py
@@ -251,7 +251,7 @@ class VMwareAPISession(object):
except exceptions.VimFaultException as excep:
# If this is due to an inactive session, we should re-create
# the session and retry.
- if exceptions.NOT_AUTHENTICATED_FAULT in excep.fault_list:
+ if exceptions.NOT_AUTHENTICATED in excep.fault_list:
# The NotAuthenticated fault is set by the fault checker
# due to an empty response. An empty response could be a
# valid response; for e.g., response for the query to
@@ -281,6 +281,10 @@ class VMwareAPISession(object):
else:
# no need to retry for other VIM faults like
# InvalidArgument
+ # Raise specific exceptions here if possible
+ if excep.fault_list:
+ LOG.debug(_("Fault list: %s"), excep.fault_list)
+ raise exceptions.get_fault_class(excep.fault_list[0])
raise
except exceptions.VimConnectionException:
@@ -369,7 +373,10 @@ class VMwareAPISession(object):
"%(error)s.") % {'task': task,
'error': error_msg}
LOG.error(excep_msg)
- raise exceptions.VimException(excep_msg)
+ error = task_info.error
+ name = error.fault.__class__.__name__
+ task_ex = exceptions.get_fault_class(name)(error_msg)
+ raise task_ex
def wait_for_lease_ready(self, lease):
"""Waits for the given lease to be ready.
diff --git a/oslo/vmware/exceptions.py b/oslo/vmware/exceptions.py
index e72dac4..14eb056 100644
--- a/oslo/vmware/exceptions.py
+++ b/oslo/vmware/exceptions.py
@@ -17,7 +17,21 @@
Exception definitions.
"""
-NOT_AUTHENTICATED_FAULT = "NotAuthenticated"
+import six
+
+from oslo.vmware.openstack.common.gettextutils import _
+from oslo.vmware.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+ALREADY_EXISTS = 'AlreadyExists'
+CANNOT_DELETE_FILE = 'CannotDeleteFile'
+FILE_ALREADY_EXISTS = 'FileAlreadyExists'
+FILE_FAULT = 'FileFault'
+FILE_LOCKED = 'FileLocked'
+FILE_NOT_FOUND = 'FileNotFound'
+INVALID_PROPERTY = 'InvalidProperty'
+NOT_AUTHENTICATED = 'NotAuthenticated'
class VimException(Exception):
@@ -66,3 +80,88 @@ class VimFaultException(VimException):
class ImageTransferException(VimException):
"""Thrown when there is an error during image transfer."""
pass
+
+
+class VMwareDriverException(Exception):
+ """Base VMware Driver Exception
+
+ To correctly use this class, inherit from it and define
+ a 'msg_fmt' property. That msg_fmt will get printf'd
+ with the keyword arguments provided to the constructor.
+
+ """
+ msg_fmt = _("An unknown exception occurred.")
+
+ def __init__(self, message=None, **kwargs):
+ self.kwargs = kwargs
+
+ if not message:
+ try:
+ message = self.msg_fmt % kwargs
+
+ except Exception:
+ # kwargs doesn't match a variable in the message
+ # log the issue and the kwargs
+ LOG.exception(_('Exception in string format operation'))
+ for name, value in six.iteritems(kwargs):
+ LOG.error("%s: %s" % (name, value))
+ # at least get the core message out if something happened
+ message = self.msg_fmt
+
+ super(VMwareDriverException, self).__init__(message)
+
+
+class AlreadyExistsException(VMwareDriverException):
+ msg_fmt = _("Resource already exists.")
+
+
+class CannotDeleteFileException(VMwareDriverException):
+ msg_fmt = _("Cannot delete file.")
+
+
+class FileAlreadyExistsException(VMwareDriverException):
+ msg_fmt = _("File already exists.")
+
+
+class FileFaultException(VMwareDriverException):
+ msg_fmt = _("File fault.")
+
+
+class FileLockedException(VMwareDriverException):
+ msg_fmt = _("File locked.")
+
+
+class FileNotFoundException(VMwareDriverException):
+ msg_fmt = _("File not found.")
+
+
+class InvalidPropertyException(VMwareDriverException):
+ msg_fmt = _("Invalid property.")
+
+
+class NotAuthenticatedException(VMwareDriverException):
+ msg_fmt = _("Not Authenticated.")
+
+
+# Populate the fault registry with the exceptions that have
+# special treatment.
+_fault_classes_registry = {
+ ALREADY_EXISTS: AlreadyExistsException,
+ CANNOT_DELETE_FILE: CannotDeleteFileException,
+ FILE_ALREADY_EXISTS: FileAlreadyExistsException,
+ FILE_FAULT: FileFaultException,
+ FILE_LOCKED: FileLockedException,
+ FILE_NOT_FOUND: FileNotFoundException,
+ INVALID_PROPERTY: InvalidPropertyException,
+ NOT_AUTHENTICATED: NotAuthenticatedException,
+}
+
+
+def get_fault_class(name):
+ """Get a named subclass of NovaException."""
+ name = str(name)
+ fault_class = _fault_classes_registry.get(name)
+ if not fault_class:
+ LOG.debug(_('Fault %s not matched.'), name)
+ fault_class = VMwareDriverException
+ return fault_class
diff --git a/oslo/vmware/vim.py b/oslo/vmware/vim.py
index 6f2f2c5..2ed5597 100644
--- a/oslo/vmware/vim.py
+++ b/oslo/vmware/vim.py
@@ -135,8 +135,8 @@ class Vim(object):
# fault.
LOG.debug(_("RetrievePropertiesEx API response is empty; setting "
"fault to %s."),
- exceptions.NOT_AUTHENTICATED_FAULT)
- fault_list = [exceptions.NOT_AUTHENTICATED_FAULT]
+ exceptions.NOT_AUTHENTICATED)
+ fault_list = [exceptions.NOT_AUTHENTICATED]
else:
for obj_cont in response.objects:
if hasattr(obj_cont, 'missingSet'):
@@ -195,8 +195,9 @@ class Vim(object):
doc = excep.document
detail = doc.childAtPath('/Envelope/Body/Fault/detail')
fault_list = []
- for child in detail.getChildren():
- fault_list.append(child.get('type'))
+ if detail:
+ for child in detail.getChildren():
+ fault_list.append(child.get('type'))
raise exceptions.VimFaultException(
fault_list, _("Web fault in %s.") % attr_name, excep)
diff --git a/tests/test_api.py b/tests/test_api.py
index 0f89f42..4182b3f 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -200,7 +200,7 @@ class VMwareAPISessionTest(base.TestCase):
def api(*args, **kwargs):
raise exceptions.VimFaultException(
- [exceptions.NOT_AUTHENTICATED_FAULT], None)
+ [exceptions.NOT_AUTHENTICATED], None)
module = mock.Mock()
module.api = api
@@ -249,7 +249,7 @@ class VMwareAPISessionTest(base.TestCase):
api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect)
task = mock.Mock()
- self.assertRaises(exceptions.VimException,
+ self.assertRaises(exceptions.VMwareDriverException,
lambda: api_session.wait_for_task(task))
api_session.invoke_api.assert_called_with(vim_util,
'get_object_property',
@@ -329,3 +329,38 @@ class VMwareAPISessionTest(base.TestCase):
api_session.invoke_api.assert_called_once_with(
vim_util, 'get_object_property', api_session.vim, lease,
'state')
+
+ def _poll_task_well_known_exceptions(self, fault,
+ expected_exception):
+ api_session = self._create_api_session(False)
+
+ def fake_invoke_api(self, module, method, *args, **kwargs):
+ task_info = mock.Mock()
+ task_info.progress = -1
+ task_info.state = 'error'
+ error = mock.Mock()
+ error.localizedMessage = "Error message"
+ error_fault = mock.Mock()
+ error_fault.__class__.__name__ = fault
+ error.fault = error_fault
+ task_info.error = error
+ return task_info
+
+ with (
+ mock.patch.object(api_session, 'invoke_api', fake_invoke_api)
+ ):
+ self.assertRaises(expected_exception,
+ api_session._poll_task, 'fake-task')
+
+ def test_poll_task_well_known_exceptions(self):
+ for k, v in exceptions._fault_classes_registry.iteritems():
+ self._poll_task_well_known_exceptions(k, v)
+
+ def test_poll_task_unknown_exception(self):
+ _unknown_exceptions = {
+ 'NoDiskSpace': exceptions.VMwareDriverException,
+ 'RuntimeFault': exceptions.VMwareDriverException
+ }
+
+ for k, v in _unknown_exceptions.iteritems():
+ self._poll_task_well_known_exceptions(k, v)
diff --git a/tests/test_vim.py b/tests/test_vim.py
index f13eb66..6dbca70 100644
--- a/tests/test_vim.py
+++ b/tests/test_vim.py
@@ -71,7 +71,7 @@ class VimTest(base.TestCase):
vim.Vim._retrieve_properties_ex_fault_checker(None)
assert False
except exceptions.VimFaultException as ex:
- self.assertEqual([exceptions.NOT_AUTHENTICATED_FAULT],
+ self.assertEqual([exceptions.NOT_AUTHENTICATED],
ex.fault_list)
def test_retrieve_properties_ex_fault_checker(self):