summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsateesh <sateesh.chodapuneedi@citrix.com>2011-03-10 21:07:44 +0530
committersateesh <sateesh.chodapuneedi@citrix.com>2011-03-10 21:07:44 +0530
commit3e97dc47221d19b39aba99f6d389d2ec326e72be (patch)
treeb85f7c1040133e103ebf03cc6c4a064fa95dffbb
parentf53357d32304cd721185704fa0d48454b5627199 (diff)
downloadnova-3e97dc47221d19b39aba99f6d389d2ec326e72be.tar.gz
nova-3e97dc47221d19b39aba99f6d389d2ec326e72be.tar.xz
nova-3e97dc47221d19b39aba99f6d389d2ec326e72be.zip
Updated the code to detect the exception by fault type.
SOAP faults are embedded in the SOAP response as a property. Certain faults are sent as a part of the SOAP body as property of missingSet. E.g. NotAuthenticated fault. So we examine the response object for missingSet and try to check the property for fault type.
-rw-r--r--nova/console/vmrc.py2
-rw-r--r--nova/virt/vmwareapi/error_util.py82
-rw-r--r--nova/virt/vmwareapi/fake.py13
-rw-r--r--nova/virt/vmwareapi/network_utils.py44
-rw-r--r--nova/virt/vmwareapi/vim.py69
-rw-r--r--nova/virt/vmwareapi/vmops.py13
-rw-r--r--nova/virt/vmwareapi_conn.py23
7 files changed, 178 insertions, 68 deletions
diff --git a/nova/console/vmrc.py b/nova/console/vmrc.py
index 09f8067e5..9c5e3b444 100644
--- a/nova/console/vmrc.py
+++ b/nova/console/vmrc.py
@@ -119,7 +119,7 @@ class VMRCSessionConsole(VMRCConsole):
vim_session._get_vim(),
"AcquireCloneTicket",
vim_session._get_vim().get_service_content().sessionManager)
- return str(vm_ref) + ":" + virtual_machine_ticket
+ return str(vm_ref.value) + ":" + virtual_machine_ticket
def is_otp(self):
"""Is one time password."""
diff --git a/nova/virt/vmwareapi/error_util.py b/nova/virt/vmwareapi/error_util.py
new file mode 100644
index 000000000..3196b5038
--- /dev/null
+++ b/nova/virt/vmwareapi/error_util.py
@@ -0,0 +1,82 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Citrix Systems, Inc.
+# Copyright 2011 OpenStack LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Exception classes and SOAP response error checking module
+"""
+
+FAULT_NOT_AUTHENTICATED = "NotAuthenticated"
+FAULT_ALREADY_EXISTS = "AlreadyExists"
+
+
+class VimException(Exception):
+ """The VIM Exception class"""
+
+ def __init__(self, exception_summary, excep):
+ Exception.__init__(self)
+ self.exception_summary = exception_summary
+ self.exception_obj = excep
+
+ def __str__(self):
+ return self.exception_summary + str(self.exception_obj)
+
+
+class SessionOverLoadException(VimException):
+ """Session Overload Exception"""
+ pass
+
+
+class VimAttributeError(VimException):
+ """VI Attribute Error"""
+ pass
+
+
+class VimFaultException(Exception):
+ """The VIM Fault exception class"""
+
+ def __init__(self, fault_list, excep):
+ Exception.__init__(self)
+ self.fault_list = fault_list
+ self.exception_obj = excep
+
+ def __str__(self):
+ return str(self.exception_obj)
+
+
+class FaultCheckers:
+ """Methods for fault checking of SOAP response. Per Method error handlers
+ for which we desire error checking are defined. SOAP faults are
+ embedded in the SOAP as a property and not as a SOAP fault."""
+
+ @classmethod
+ def retrieveproperties_fault_checker(self, resp_obj):
+ """Checks the RetrieveProperties response for errors. Certain faults
+ are sent as a part of the SOAP body as property of missingSet.
+ For example NotAuthenticated fault"""
+ fault_list = []
+ for obj_cont in resp_obj:
+ if hasattr(obj_cont, "missingSet"):
+ for missing_elem in obj_cont.missingSet:
+ fault_type = missing_elem.fault.fault.__class__.__name__
+ #Fault needs to be added to the type of fault for
+ #uniformity in error checking as SOAP faults define
+ fault_list.append(fault_type)
+ if fault_list:
+ exc_msg_list = ', '.join(fault_list)
+ raise VimFaultException(fault_list, Exception(_("Error(s) %s "
+ "occurred in the call to RetrieveProperties") %
+ exc_msg_list))
diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py
index 40ed18340..909d1a6cf 100644
--- a/nova/virt/vmwareapi/fake.py
+++ b/nova/virt/vmwareapi/fake.py
@@ -25,7 +25,7 @@ import uuid
from nova import exception
from nova import log as logging
from nova.virt.vmwareapi import vim
-from nova.virt.vmwareapi.vim import SessionFaultyException
+from nova.virt.vmwareapi import error_util
_CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine',
'Network', 'HostSystem', 'HostNetworkSystem', 'Task', 'session',
@@ -500,7 +500,7 @@ class FakeVim(object):
"out: %s") % s)
del _db_content['session'][s]
- def _terminate(self, *args, **kwargs):
+ def _terminate_session(self, *args, **kwargs):
""" Terminates a session """
s = kwargs.get("sessionId")[0]
if s not in _db_content['session']:
@@ -512,7 +512,9 @@ class FakeVim(object):
if (self._session is None or self._session not in
_db_content['session']):
LOG.debug(_("Session is faulty"))
- raise SessionFaultyException(_("Session Invalid"))
+ raise error_util.VimFaultException(
+ [error_util.FAULT_NOT_AUTHENTICATED],
+ _("Session Invalid"))
def _create_vm(self, method, *args, **kwargs):
""" Creates and registers a VM object with the Host System """
@@ -656,8 +658,9 @@ class FakeVim(object):
return lambda *args, **kwargs: self._login()
elif attr_name == "Logout":
self._logout()
- elif attr_name == "Terminate":
- return lambda *args, **kwargs: self._terminate(*args, **kwargs)
+ elif attr_name == "TerminateSession":
+ return lambda *args, **kwargs: self._terminate_session(
+ *args, **kwargs)
elif attr_name == "CreateVM_Task":
return lambda *args, **kwargs: self._create_vm(attr_name,
*args, **kwargs)
diff --git a/nova/virt/vmwareapi/network_utils.py b/nova/virt/vmwareapi/network_utils.py
index 59a3c234f..f27121071 100644
--- a/nova/virt/vmwareapi/network_utils.py
+++ b/nova/virt/vmwareapi/network_utils.py
@@ -20,15 +20,12 @@ Utility functions for ESX Networking
"""
from nova import log as logging
+from nova.virt.vmwareapi import error_util
from nova.virt.vmwareapi import vim_util
from nova.virt.vmwareapi import vm_util
-from nova.virt.vmwareapi.vim import VimException
LOG = logging.getLogger("nova.virt.vmwareapi.network_utils")
-PORT_GROUP_EXISTS_EXCEPTION = \
- 'The specified key, name, or identifier already exists.'
-
class NetworkHelper:
@@ -38,7 +35,13 @@ class NetworkHelper:
argument. """
datacenters = session._call_method(vim_util, "get_objects",
"Datacenter", ["network"])
- vm_networks = datacenters[0].propSet[0].val.ManagedObjectReference
+ vm_networks_ret = datacenters[0].propSet[0].val
+ #Meaning there are no networks on the host. suds responds with a ""
+ #in the parent property field rather than a [] in the
+ #ManagedObjectRefernce property field of the parent
+ if not vm_networks_ret:
+ return None
+ vm_networks = vm_networks_ret.ManagedObjectReference
networks = session._call_method(vim_util,
"get_properites_for_a_collection_of_objects",
"Network", vm_networks, ["summary.name"])
@@ -54,9 +57,14 @@ class NetworkHelper:
#Get the list of vSwicthes on the Host System
host_mor = session._call_method(vim_util, "get_objects",
"HostSystem")[0].obj
- vswitches = session._call_method(vim_util,
+ vswitches_ret = session._call_method(vim_util,
"get_dynamic_property", host_mor,
- "HostSystem", "config.network.vswitch").HostVirtualSwitch
+ "HostSystem", "config.network.vswitch")
+ #Meaning there are no vSwitches on the host. Shouldn't be the case,
+ #but just doing code check
+ if not vswitches_ret:
+ return
+ vswitches = vswitches_ret.HostVirtualSwitch
#Get the vSwitch associated with the network adapter
for elem in vswitches:
try:
@@ -71,9 +79,13 @@ class NetworkHelper:
""" Checks if the vlan_inteface exists on the esx host """
host_net_system_mor = session._call_method(vim_util, "get_objects",
"HostSystem", ["configManager.networkSystem"])[0].propSet[0].val
- physical_nics = session._call_method(vim_util,
+ physical_nics_ret = session._call_method(vim_util,
"get_dynamic_property", host_net_system_mor,
- "HostNetworkSystem", "networkInfo.pnic").PhysicalNic
+ "HostNetworkSystem", "networkInfo.pnic")
+ #Meaning there are no physical nics on the host
+ if not physical_nics_ret:
+ return False
+ physical_nics = physical_nics_ret.PhysicalNic
for pnic in physical_nics:
if vlan_interface == pnic.device:
return True
@@ -84,9 +96,15 @@ class NetworkHelper:
""" Get the vlan id and vswicth associated with the port group """
host_mor = session._call_method(vim_util, "get_objects",
"HostSystem")[0].obj
- port_grps_on_host = session._call_method(vim_util,
+ port_grps_on_host_ret = session._call_method(vim_util,
"get_dynamic_property", host_mor,
- "HostSystem", "config.network.portgroup").HostPortGroup
+ "HostSystem", "config.network.portgroup")
+ if not port_grps_on_host_ret:
+ excep = ("ESX SOAP server returned an empty port group "
+ "for the host system in its response")
+ LOG.exception(excep)
+ raise Exception(_(excep))
+ port_grps_on_host = port_grps_on_host_ret.HostPortGroup
for p_gp in port_grps_on_host:
if p_gp.spec.name == pg_name:
p_grp_vswitch_name = p_gp.vswitch.split("-")[-1]
@@ -113,13 +131,13 @@ class NetworkHelper:
session._call_method(session._get_vim(),
"AddPortGroup", network_system_mor,
portgrp=add_prt_grp_spec)
- except VimException, exc:
+ except error_util.VimFaultException, exc:
#There can be a race condition when two instances try
#adding port groups at the same time. One succeeds, then
#the other one will get an exception. Since we are
#concerned with the port group being created, which is done
#by the other call, we can ignore the exception.
- if str(exc).find(PORT_GROUP_EXISTS_EXCEPTION) == -1:
+ if error_util.FAULT_ALREADY_EXISTS not in exc.fault_list:
raise Exception(exc)
LOG.debug(_("Created Port Group with name %s on "
"the ESX host") % pg_name)
diff --git a/nova/virt/vmwareapi/vim.py b/nova/virt/vmwareapi/vim.py
index 6a3e4b376..cea65e198 100644
--- a/nova/virt/vmwareapi/vim.py
+++ b/nova/virt/vmwareapi/vim.py
@@ -21,11 +21,13 @@ Classes for making VMware VI SOAP calls
import httplib
+from suds import WebFault
from suds.client import Client
from suds.plugin import MessagePlugin
from suds.sudsobject import Property
from nova import flags
+from nova.virt.vmwareapi import error_util
RESP_NOT_XML_ERROR = 'Response is "text/html", not "text/xml'
CONN_ABORT_ERROR = 'Software caused connection abort'
@@ -40,33 +42,6 @@ flags.DEFINE_string('vmwareapi_wsdl_loc',
'Read the readme for vmware to setup')
-class VimException(Exception):
- """The VIM Exception class"""
-
- def __init__(self, exception_summary, excep):
- Exception.__init__(self)
- self.exception_summary = exception_summary
- self.exception_obj = excep
-
- def __str__(self):
- return self.exception_summary + str(self.exception_obj)
-
-
-class SessionOverLoadException(VimException):
- """Session Overload Exception"""
- pass
-
-
-class SessionFaultyException(VimException):
- """Session Faulty Exception"""
- pass
-
-
-class VimAttributeError(VimException):
- """VI Attribute Error"""
- pass
-
-
class VIMMessagePlugin(MessagePlugin):
def addAttributeForValue(self, node):
@@ -133,29 +108,49 @@ class Vim:
request_mo = \
self._request_managed_object_builder(managed_object)
request = getattr(self.client.service, attr_name)
- return request(request_mo, **kwargs)
+ response = request(request_mo, **kwargs)
+ #To check for the faults that are part of the message body
+ #and not returned as Fault object response from the ESX
+ #SOAP server
+ if hasattr(error_util.FaultCheckers,
+ attr_name.lower() + "_fault_checker"):
+ fault_checker = getattr(error_util.FaultCheckers,
+ attr_name.lower() + "_fault_checker")
+ fault_checker(response)
+ return response
+ #Catch the VimFaultException that is raised by the fault
+ #check of the SOAP response
+ except error_util.VimFaultException, excep:
+ raise
+ except WebFault, excep:
+ doc = excep.document
+ detail = doc.childAtPath("/Envelope/Body/Fault/detail")
+ fault_list = []
+ for child in detail.getChildren():
+ fault_list.append(child.get("type"))
+ raise error_util.VimFaultException(fault_list, excep)
except AttributeError, excep:
- raise VimAttributeError(_("No such SOAP method '%s'"
- " provided by VI SDK") % (attr_name), excep)
+ raise error_util.VimAttributeError(_("No such SOAP method "
+ "'%s' provided by VI SDK") % (attr_name), excep)
except (httplib.CannotSendRequest,
httplib.ResponseNotReady,
httplib.CannotSendHeader), excep:
- raise SessionOverLoadException(_("httplib error in"
- " %s: ") % (attr_name), excep)
+ raise error_util.SessionOverLoadException(_("httplib "
+ "error in %s: ") % (attr_name), excep)
except Exception, excep:
# Socket errors which need special handling for they
# might be caused by ESX API call overload
if (str(excep).find(ADDRESS_IN_USE_ERROR) != -1 or
str(excep).find(CONN_ABORT_ERROR)) != -1:
- raise SessionOverLoadException(_("Socket error in"
- " %s: ") % (attr_name), excep)
+ raise error_util.SessionOverLoadException(_("Socket "
+ "error in %s: ") % (attr_name), excep)
# Type error that needs special handling for it might be
# caused by ESX host API call overload
elif str(excep).find(RESP_NOT_XML_ERROR) != -1:
- raise SessionOverLoadException(_("Type error in "
- " %s: ") % (attr_name), excep)
+ raise error_util.SessionOverLoadException(_("Type "
+ "error in %s: ") % (attr_name), excep)
else:
- raise VimException(
+ raise error_util.VimException(
_("Exception in %s ") % (attr_name), excep)
return vim_request_handler
diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py
index 524c35af5..344c4518b 100644
--- a/nova/virt/vmwareapi/vmops.py
+++ b/nova/virt/vmwareapi/vmops.py
@@ -373,10 +373,15 @@ class VMWareVMOps(object):
def _check_if_tmp_folder_exists():
#Copy the contents of the VM that were there just before the
#snapshot was taken
- ds_ref = vim_util.get_dynamic_property(self._session._get_vim(),
- vm_ref,
- "VirtualMachine",
- "datastore").ManagedObjectReference[0]
+ ds_ref_ret = vim_util.get_dynamic_property(
+ self._session._get_vim(),
+ vm_ref,
+ "VirtualMachine",
+ "datastore")
+ if not ds_ref_ret:
+ raise Exception(_("Failed to get the datastore reference(s) "
+ "which the VM uses"))
+ ds_ref = ds_ref_ret.ManagedObjectReference[0]
ds_browser = vim_util.get_dynamic_property(
self._session._get_vim(),
ds_ref,
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py
index bd3ab4320..a2609278d 100644
--- a/nova/virt/vmwareapi_conn.py
+++ b/nova/virt/vmwareapi_conn.py
@@ -40,6 +40,7 @@ from nova import db
from nova import flags
from nova import log as logging
from nova import utils
+from nova.virt.vmwareapi import error_util
from nova.virt.vmwareapi import vim
from nova.virt.vmwareapi import vim_util
from nova.virt.vmwareapi.vmops import VMWareVMOps
@@ -60,7 +61,7 @@ flags.DEFINE_string('vmwareapi_host_password',
'Password for connection to VMWare ESX host.'
'Used only if connection_type is vmwareapi.')
flags.DEFINE_float('vmwareapi_task_poll_interval',
- 1.0,
+ 5.0,
'The interval used for polling of remote tasks '
'Used only if connection_type is vmwareapi')
flags.DEFINE_float('vmwareapi_api_retry_count',
@@ -264,13 +265,19 @@ class VMWareAPISession(object):
ret_val = temp_module(*args, **kwargs)
return ret_val
- except vim.SessionFaultyException, excep:
+ except error_util.VimFaultException, excep:
# If it is a Session Fault Exception, it may point
# to a session gone bad. So we try re-creating a session
# and then proceeding ahead with the call.
exc = excep
- self._create_session()
- except vim.SessionOverLoadException, excep:
+ if error_util.FAULT_NOT_AUTHENTICATED in excep.fault_list:
+ self._create_session()
+ else:
+ #No re-trying for errors for API call has gone through
+ #and is the caller's fault. Caller should handle these
+ #errors. e.g, InvalidArgument fault.
+ break
+ except error_util.SessionOverLoadException, excep:
# For exceptions which may come because of session overload,
# we retry
exc = excep
@@ -288,7 +295,7 @@ class VMWareAPISession(object):
LOG.critical(_("In vmwareapi:_call_method, "
"got this exception: %s") % exc)
- raise Exception(exc)
+ raise
def _get_vim(self):
"""Gets the VIM object reference"""
@@ -301,11 +308,11 @@ class VMWareAPISession(object):
The task is polled until it completes.
"""
done = event.Event()
- self.loop = utils.LoopingCall(self._poll_task, instance_id, task_ref,
+ loop = utils.LoopingCall(self._poll_task, instance_id, task_ref,
done)
- self.loop.start(FLAGS.vmwareapi_task_poll_interval, now=True)
+ loop.start(FLAGS.vmwareapi_task_poll_interval, now=True)
ret_val = done.wait()
- self.loop.stop()
+ loop.stop()
return ret_val
def _poll_task(self, instance_id, task_ref, done):