From 89bfdc2715b5192a3ad06da8ea113605626c227b Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Thu, 5 Jul 2012 16:05:16 -0400 Subject: refactor vmwareapi_conn => vmwareapi/driver Part of bp:virt-driver-cleanup Make the vmwareapi driver consistent in naming as a driver instead of a connection. Get all the vmware driver parts into the same directory Change-Id: I7c7e7c391086cf2fc32e8cb85d557005d417042f --- nova/tests/test_vmwareapi.py | 4 +- nova/tests/vmwareapi/stubs.py | 6 +- nova/virt/connection.py | 2 +- nova/virt/vmwareapi/__init__.py | 2 + nova/virt/vmwareapi/driver.py | 398 ++++++++++++++++++++++++++++++++++++++++ nova/virt/vmwareapi_conn.py | 398 ---------------------------------------- 6 files changed, 406 insertions(+), 404 deletions(-) create mode 100644 nova/virt/vmwareapi/driver.py delete mode 100644 nova/virt/vmwareapi_conn.py diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index 647a9d0a0..7b9ad38e5 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -28,8 +28,8 @@ from nova import test import nova.tests.image.fake from nova.tests.vmwareapi import db_fakes from nova.tests.vmwareapi import stubs +from nova.virt.vmwareapi import driver from nova.virt.vmwareapi import fake as vmwareapi_fake -from nova.virt import vmwareapi_conn FLAGS = flags.FLAGS @@ -50,7 +50,7 @@ class VMWareAPIVMTestCase(test.TestCase): vmwareapi_fake.reset() db_fakes.stub_out_db_instance_api(self.stubs) stubs.set_stubs(self.stubs) - self.conn = vmwareapi_conn.VMWareESXDriver(False) + self.conn = driver.VMWareESXDriver(False) # NOTE(vish): none of the network plugging code is actually # being tested self.network_info = [({'bridge': 'fa0', diff --git a/nova/tests/vmwareapi/stubs.py b/nova/tests/vmwareapi/stubs.py index e4d7a25d5..494b201d0 100644 --- a/nova/tests/vmwareapi/stubs.py +++ b/nova/tests/vmwareapi/stubs.py @@ -19,11 +19,11 @@ Stubouts for the test suite """ +from nova.virt.vmwareapi import driver from nova.virt.vmwareapi import fake from nova.virt.vmwareapi import network_utils from nova.virt.vmwareapi import vmops from nova.virt.vmwareapi import vmware_images -from nova.virt import vmwareapi_conn def fake_get_vim_object(arg): @@ -45,7 +45,7 @@ def set_stubs(stubs): stubs.Set(vmware_images, 'get_vmdk_size_and_properties', fake.fake_get_vmdk_size_and_properties) stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image) - stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", + stubs.Set(driver.VMWareAPISession, "_get_vim_object", fake_get_vim_object) - stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object", + stubs.Set(driver.VMWareAPISession, "_is_vim_object", fake_is_vim_object) diff --git a/nova/virt/connection.py b/nova/virt/connection.py index d7a0f56de..c96c7d92c 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -36,7 +36,7 @@ known_drivers = { 'baremetal': 'baremetal.proxy.ProxyConnection', 'fake': 'fake.FakeDriver', 'libvirt': 'libvirt.LibvirtDriver', - 'vmwareapi': 'vmwareapi_conn.VMWareESXDriver', + 'vmwareapi': 'vmwareapi.VMWareESXDriver', 'xenapi': 'xenapi.connection.XenAPIDriver' } diff --git a/nova/virt/vmwareapi/__init__.py b/nova/virt/vmwareapi/__init__.py index d9b27de08..fa6f6ceb5 100644 --- a/nova/virt/vmwareapi/__init__.py +++ b/nova/virt/vmwareapi/__init__.py @@ -17,3 +17,5 @@ """ :mod:`vmwareapi` -- Nova support for VMware ESX/ESXi Server through VMware API. """ +# NOTE(sdague) for nicer compute_driver specification +from nova.virt.vmwareapi.driver import VMWareESXDriver diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py new file mode 100644 index 000000000..2b1d4cedf --- /dev/null +++ b/nova/virt/vmwareapi/driver.py @@ -0,0 +1,398 @@ +# 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. + +""" +A connection to the VMware ESX platform. + +**Related Flags** + +:vmwareapi_host_ip: IPAddress of VMware ESX server. +:vmwareapi_host_username: Username for connection to VMware ESX Server. +:vmwareapi_host_password: Password for connection to VMware ESX Server. +:vmwareapi_task_poll_interval: The interval (seconds) used for polling of + remote tasks + (default: 1.0). +:vmwareapi_api_retry_count: The API retry count in case of failure such as + network failures (socket errors etc.) + (default: 10). + +""" + +import time + +from eventlet import event + +from nova import exception +from nova import flags +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova import utils +from nova.virt import driver +from nova.virt.vmwareapi import error_util +from nova.virt.vmwareapi import vim +from nova.virt.vmwareapi import vim_util +from nova.virt.vmwareapi import vmops + + +LOG = logging.getLogger(__name__) + +vmwareapi_opts = [ + cfg.StrOpt('vmwareapi_host_ip', + default=None, + help='URL for connection to VMWare ESX host.Required if ' + 'connection_type is vmwareapi.'), + cfg.StrOpt('vmwareapi_host_username', + default=None, + help='Username for connection to VMWare ESX host. ' + 'Used only if connection_type is vmwareapi.'), + cfg.StrOpt('vmwareapi_host_password', + default=None, + help='Password for connection to VMWare ESX host. ' + 'Used only if connection_type is vmwareapi.'), + cfg.FloatOpt('vmwareapi_task_poll_interval', + default=5.0, + help='The interval used for polling of remote tasks. ' + 'Used only if connection_type is vmwareapi'), + cfg.IntOpt('vmwareapi_api_retry_count', + default=10, + help='The number of times we retry on failures, e.g., ' + 'socket error, etc. ' + 'Used only if connection_type is vmwareapi'), + cfg.StrOpt('vmwareapi_vlan_interface', + default='vmnic0', + help='Physical ethernet adapter name for vlan networking'), + ] + +FLAGS = flags.FLAGS +FLAGS.register_opts(vmwareapi_opts) + +TIME_BETWEEN_API_CALL_RETRIES = 2.0 + + +class Failure(Exception): + """Base Exception class for handling task failures.""" + + def __init__(self, details): + self.details = details + + def __str__(self): + return str(self.details) + + +class VMWareESXDriver(driver.ComputeDriver): + """The ESX host connection object.""" + + def __init__(self, read_only=False, scheme="https"): + super(VMWareESXDriver, self).__init__() + + host_ip = FLAGS.vmwareapi_host_ip + host_username = FLAGS.vmwareapi_host_username + host_password = FLAGS.vmwareapi_host_password + api_retry_count = FLAGS.vmwareapi_api_retry_count + if not host_ip or host_username is None or host_password is None: + raise Exception(_("Must specify vmwareapi_host_ip," + "vmwareapi_host_username " + "and vmwareapi_host_password to use" + "connection_type=vmwareapi")) + + session = VMWareAPISession(host_ip, host_username, host_password, + api_retry_count, scheme=scheme) + self._vmops = vmops.VMWareVMOps(session) + + def init_host(self, host): + """Do the initialization that needs to be done.""" + # FIXME(sateesh): implement this + pass + + def list_instances(self): + """List VM instances.""" + return self._vmops.list_instances() + + def spawn(self, context, instance, image_meta, network_info, + block_device_mapping=None): + """Create VM instance.""" + self._vmops.spawn(context, instance, image_meta, network_info) + + def snapshot(self, context, instance, name): + """Create snapshot from a running VM instance.""" + self._vmops.snapshot(context, instance, name) + + def reboot(self, instance, network_info, reboot_type): + """Reboot VM instance.""" + self._vmops.reboot(instance, network_info) + + def destroy(self, instance, network_info, block_device_info=None): + """Destroy VM instance.""" + self._vmops.destroy(instance, network_info) + + def pause(self, instance): + """Pause VM instance.""" + self._vmops.pause(instance) + + def unpause(self, instance): + """Unpause paused VM instance.""" + self._vmops.unpause(instance) + + def suspend(self, instance): + """Suspend the specified instance.""" + self._vmops.suspend(instance) + + def resume(self, instance): + """Resume the suspended VM instance.""" + self._vmops.resume(instance) + + def get_info(self, instance): + """Return info about the VM instance.""" + return self._vmops.get_info(instance) + + def get_diagnostics(self, instance): + """Return data about VM diagnostics.""" + return self._vmops.get_info(instance) + + def get_console_output(self, instance): + """Return snapshot of console.""" + return self._vmops.get_console_output(instance) + + def get_volume_connector(self, _instance): + """Return volume connector information""" + # TODO(vish): When volume attaching is supported, return the + # proper initiator iqn and host. + return { + 'ip': FLAGS.vmwareapi_host_ip, + 'initiator': None, + 'host': None + } + + def attach_volume(self, connection_info, instance_name, mountpoint): + """Attach volume storage to VM instance.""" + pass + + def detach_volume(self, connection_info, instance_name, mountpoint): + """Detach volume storage to VM instance.""" + pass + + def get_console_pool_info(self, console_type): + """Get info about the host on which the VM resides.""" + return {'address': FLAGS.vmwareapi_host_ip, + 'username': FLAGS.vmwareapi_host_username, + 'password': FLAGS.vmwareapi_host_password} + + def update_available_resource(self, ctxt, host): + """This method is supported only by libvirt.""" + return + + def host_power_action(self, host, action): + """Reboots, shuts down or powers up the host.""" + raise NotImplementedError() + + def host_maintenance_mode(self, host, mode): + """Start/Stop host maintenance window. On start, it triggers + guest VMs evacuation.""" + raise NotImplementedError() + + def set_host_enabled(self, host, enabled): + """Sets the specified host's ability to accept new instances.""" + raise NotImplementedError() + + def plug_vifs(self, instance, network_info): + """Plug VIFs into networks.""" + self._vmops.plug_vifs(instance, network_info) + + def unplug_vifs(self, instance, network_info): + """Unplug VIFs from networks.""" + self._vmops.unplug_vifs(instance, network_info) + + +class VMWareAPISession(object): + """ + Sets up a session with the ESX host and handles all + the calls made to the host. + """ + + def __init__(self, host_ip, host_username, host_password, + api_retry_count, scheme="https"): + self._host_ip = host_ip + self._host_username = host_username + self._host_password = host_password + self.api_retry_count = api_retry_count + self._scheme = scheme + self._session_id = None + self.vim = None + self._create_session() + + def _get_vim_object(self): + """Create the VIM Object instance.""" + return vim.Vim(protocol=self._scheme, host=self._host_ip) + + def _create_session(self): + """Creates a session with the ESX host.""" + while True: + try: + # Login and setup the session with the ESX host for making + # API calls + self.vim = self._get_vim_object() + session = self.vim.Login( + self.vim.get_service_content().sessionManager, + userName=self._host_username, + password=self._host_password) + # Terminate the earlier session, if possible ( For the sake of + # preserving sessions as there is a limit to the number of + # sessions we can have ) + if self._session_id: + try: + self.vim.TerminateSession( + self.vim.get_service_content().sessionManager, + sessionId=[self._session_id]) + except Exception, excep: + # This exception is something we can live with. It is + # just an extra caution on our side. The session may + # have been cleared. We could have made a call to + # SessionIsActive, but that is an overhead because we + # anyway would have to call TerminateSession. + LOG.debug(excep) + self._session_id = session.key + return + except Exception, excep: + LOG.critical(_("In vmwareapi:_create_session, " + "got this exception: %s") % excep) + raise exception.NovaException(excep) + + def __del__(self): + """Logs-out the session.""" + # Logout to avoid un-necessary increase in session count at the + # ESX host + try: + self.vim.Logout(self.vim.get_service_content().sessionManager) + except Exception, excep: + # It is just cautionary on our part to do a logout in del just + # to ensure that the session is not left active. + LOG.debug(excep) + + def _is_vim_object(self, module): + """Check if the module is a VIM Object instance.""" + return isinstance(module, vim.Vim) + + def _call_method(self, module, method, *args, **kwargs): + """ + Calls a method within the module specified with + args provided. + """ + args = list(args) + retry_count = 0 + exc = None + last_fault_list = [] + while True: + try: + if not self._is_vim_object(module): + # If it is not the first try, then get the latest + # vim object + if retry_count > 0: + args = args[1:] + args = [self.vim] + args + retry_count += 1 + temp_module = module + + for method_elem in method.split("."): + temp_module = getattr(temp_module, method_elem) + + return temp_module(*args, **kwargs) + 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 + if error_util.FAULT_NOT_AUTHENTICATED in excep.fault_list: + # Because of the idle session returning an empty + # RetrievePropertiesResponse and also the same is returned + # when there is say empty answer to the query for + # VMs on the host ( as in no VMs on the host), we have no + # way to differentiate. + # So if the previous response was also am empty response + # and after creating a new session, we get the same empty + # response, then we are sure of the response being supposed + # to be empty. + if error_util.FAULT_NOT_AUTHENTICATED in last_fault_list: + return [] + last_fault_list = 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 + except Exception, excep: + # If it is a proper exception, say not having furnished + # proper data in the SOAP call or the retry limit having + # exceeded, we raise the exception + exc = excep + break + # If retry count has been reached then break and + # raise the exception + if retry_count > self.api_retry_count: + break + time.sleep(TIME_BETWEEN_API_CALL_RETRIES) + + LOG.critical(_("In vmwareapi:_call_method, " + "got this exception: %s") % exc) + raise + + def _get_vim(self): + """Gets the VIM object reference.""" + if self.vim is None: + self._create_session() + return self.vim + + def _wait_for_task(self, instance_uuid, task_ref): + """ + Return a Deferred that will give the result of the given task. + The task is polled until it completes. + """ + done = event.Event() + loop = utils.LoopingCall(self._poll_task, instance_uuid, task_ref, + done) + loop.start(FLAGS.vmwareapi_task_poll_interval) + ret_val = done.wait() + loop.stop() + return ret_val + + def _poll_task(self, instance_uuid, task_ref, done): + """ + Poll the given task, and fires the given Deferred if we + get a result. + """ + try: + task_info = self._call_method(vim_util, "get_dynamic_property", + task_ref, "Task", "info") + task_name = task_info.name + if task_info.state in ['queued', 'running']: + return + elif task_info.state == 'success': + LOG.debug(_("Task [%(task_name)s] %(task_ref)s " + "status: success") % locals()) + done.send("success") + else: + error_info = str(task_info.error.localizedMessage) + LOG.warn(_("Task [%(task_name)s] %(task_ref)s " + "status: error %(error_info)s") % locals()) + done.send_exception(exception.NovaException(error_info)) + except Exception, excep: + LOG.warn(_("In vmwareapi:_poll_task, Got this error %s") % excep) + done.send_exception(excep) diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py deleted file mode 100644 index 2b1d4cedf..000000000 --- a/nova/virt/vmwareapi_conn.py +++ /dev/null @@ -1,398 +0,0 @@ -# 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. - -""" -A connection to the VMware ESX platform. - -**Related Flags** - -:vmwareapi_host_ip: IPAddress of VMware ESX server. -:vmwareapi_host_username: Username for connection to VMware ESX Server. -:vmwareapi_host_password: Password for connection to VMware ESX Server. -:vmwareapi_task_poll_interval: The interval (seconds) used for polling of - remote tasks - (default: 1.0). -:vmwareapi_api_retry_count: The API retry count in case of failure such as - network failures (socket errors etc.) - (default: 10). - -""" - -import time - -from eventlet import event - -from nova import exception -from nova import flags -from nova.openstack.common import cfg -from nova.openstack.common import log as logging -from nova import utils -from nova.virt import driver -from nova.virt.vmwareapi import error_util -from nova.virt.vmwareapi import vim -from nova.virt.vmwareapi import vim_util -from nova.virt.vmwareapi import vmops - - -LOG = logging.getLogger(__name__) - -vmwareapi_opts = [ - cfg.StrOpt('vmwareapi_host_ip', - default=None, - help='URL for connection to VMWare ESX host.Required if ' - 'connection_type is vmwareapi.'), - cfg.StrOpt('vmwareapi_host_username', - default=None, - help='Username for connection to VMWare ESX host. ' - 'Used only if connection_type is vmwareapi.'), - cfg.StrOpt('vmwareapi_host_password', - default=None, - help='Password for connection to VMWare ESX host. ' - 'Used only if connection_type is vmwareapi.'), - cfg.FloatOpt('vmwareapi_task_poll_interval', - default=5.0, - help='The interval used for polling of remote tasks. ' - 'Used only if connection_type is vmwareapi'), - cfg.IntOpt('vmwareapi_api_retry_count', - default=10, - help='The number of times we retry on failures, e.g., ' - 'socket error, etc. ' - 'Used only if connection_type is vmwareapi'), - cfg.StrOpt('vmwareapi_vlan_interface', - default='vmnic0', - help='Physical ethernet adapter name for vlan networking'), - ] - -FLAGS = flags.FLAGS -FLAGS.register_opts(vmwareapi_opts) - -TIME_BETWEEN_API_CALL_RETRIES = 2.0 - - -class Failure(Exception): - """Base Exception class for handling task failures.""" - - def __init__(self, details): - self.details = details - - def __str__(self): - return str(self.details) - - -class VMWareESXDriver(driver.ComputeDriver): - """The ESX host connection object.""" - - def __init__(self, read_only=False, scheme="https"): - super(VMWareESXDriver, self).__init__() - - host_ip = FLAGS.vmwareapi_host_ip - host_username = FLAGS.vmwareapi_host_username - host_password = FLAGS.vmwareapi_host_password - api_retry_count = FLAGS.vmwareapi_api_retry_count - if not host_ip or host_username is None or host_password is None: - raise Exception(_("Must specify vmwareapi_host_ip," - "vmwareapi_host_username " - "and vmwareapi_host_password to use" - "connection_type=vmwareapi")) - - session = VMWareAPISession(host_ip, host_username, host_password, - api_retry_count, scheme=scheme) - self._vmops = vmops.VMWareVMOps(session) - - def init_host(self, host): - """Do the initialization that needs to be done.""" - # FIXME(sateesh): implement this - pass - - def list_instances(self): - """List VM instances.""" - return self._vmops.list_instances() - - def spawn(self, context, instance, image_meta, network_info, - block_device_mapping=None): - """Create VM instance.""" - self._vmops.spawn(context, instance, image_meta, network_info) - - def snapshot(self, context, instance, name): - """Create snapshot from a running VM instance.""" - self._vmops.snapshot(context, instance, name) - - def reboot(self, instance, network_info, reboot_type): - """Reboot VM instance.""" - self._vmops.reboot(instance, network_info) - - def destroy(self, instance, network_info, block_device_info=None): - """Destroy VM instance.""" - self._vmops.destroy(instance, network_info) - - def pause(self, instance): - """Pause VM instance.""" - self._vmops.pause(instance) - - def unpause(self, instance): - """Unpause paused VM instance.""" - self._vmops.unpause(instance) - - def suspend(self, instance): - """Suspend the specified instance.""" - self._vmops.suspend(instance) - - def resume(self, instance): - """Resume the suspended VM instance.""" - self._vmops.resume(instance) - - def get_info(self, instance): - """Return info about the VM instance.""" - return self._vmops.get_info(instance) - - def get_diagnostics(self, instance): - """Return data about VM diagnostics.""" - return self._vmops.get_info(instance) - - def get_console_output(self, instance): - """Return snapshot of console.""" - return self._vmops.get_console_output(instance) - - def get_volume_connector(self, _instance): - """Return volume connector information""" - # TODO(vish): When volume attaching is supported, return the - # proper initiator iqn and host. - return { - 'ip': FLAGS.vmwareapi_host_ip, - 'initiator': None, - 'host': None - } - - def attach_volume(self, connection_info, instance_name, mountpoint): - """Attach volume storage to VM instance.""" - pass - - def detach_volume(self, connection_info, instance_name, mountpoint): - """Detach volume storage to VM instance.""" - pass - - def get_console_pool_info(self, console_type): - """Get info about the host on which the VM resides.""" - return {'address': FLAGS.vmwareapi_host_ip, - 'username': FLAGS.vmwareapi_host_username, - 'password': FLAGS.vmwareapi_host_password} - - def update_available_resource(self, ctxt, host): - """This method is supported only by libvirt.""" - return - - def host_power_action(self, host, action): - """Reboots, shuts down or powers up the host.""" - raise NotImplementedError() - - def host_maintenance_mode(self, host, mode): - """Start/Stop host maintenance window. On start, it triggers - guest VMs evacuation.""" - raise NotImplementedError() - - def set_host_enabled(self, host, enabled): - """Sets the specified host's ability to accept new instances.""" - raise NotImplementedError() - - def plug_vifs(self, instance, network_info): - """Plug VIFs into networks.""" - self._vmops.plug_vifs(instance, network_info) - - def unplug_vifs(self, instance, network_info): - """Unplug VIFs from networks.""" - self._vmops.unplug_vifs(instance, network_info) - - -class VMWareAPISession(object): - """ - Sets up a session with the ESX host and handles all - the calls made to the host. - """ - - def __init__(self, host_ip, host_username, host_password, - api_retry_count, scheme="https"): - self._host_ip = host_ip - self._host_username = host_username - self._host_password = host_password - self.api_retry_count = api_retry_count - self._scheme = scheme - self._session_id = None - self.vim = None - self._create_session() - - def _get_vim_object(self): - """Create the VIM Object instance.""" - return vim.Vim(protocol=self._scheme, host=self._host_ip) - - def _create_session(self): - """Creates a session with the ESX host.""" - while True: - try: - # Login and setup the session with the ESX host for making - # API calls - self.vim = self._get_vim_object() - session = self.vim.Login( - self.vim.get_service_content().sessionManager, - userName=self._host_username, - password=self._host_password) - # Terminate the earlier session, if possible ( For the sake of - # preserving sessions as there is a limit to the number of - # sessions we can have ) - if self._session_id: - try: - self.vim.TerminateSession( - self.vim.get_service_content().sessionManager, - sessionId=[self._session_id]) - except Exception, excep: - # This exception is something we can live with. It is - # just an extra caution on our side. The session may - # have been cleared. We could have made a call to - # SessionIsActive, but that is an overhead because we - # anyway would have to call TerminateSession. - LOG.debug(excep) - self._session_id = session.key - return - except Exception, excep: - LOG.critical(_("In vmwareapi:_create_session, " - "got this exception: %s") % excep) - raise exception.NovaException(excep) - - def __del__(self): - """Logs-out the session.""" - # Logout to avoid un-necessary increase in session count at the - # ESX host - try: - self.vim.Logout(self.vim.get_service_content().sessionManager) - except Exception, excep: - # It is just cautionary on our part to do a logout in del just - # to ensure that the session is not left active. - LOG.debug(excep) - - def _is_vim_object(self, module): - """Check if the module is a VIM Object instance.""" - return isinstance(module, vim.Vim) - - def _call_method(self, module, method, *args, **kwargs): - """ - Calls a method within the module specified with - args provided. - """ - args = list(args) - retry_count = 0 - exc = None - last_fault_list = [] - while True: - try: - if not self._is_vim_object(module): - # If it is not the first try, then get the latest - # vim object - if retry_count > 0: - args = args[1:] - args = [self.vim] + args - retry_count += 1 - temp_module = module - - for method_elem in method.split("."): - temp_module = getattr(temp_module, method_elem) - - return temp_module(*args, **kwargs) - 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 - if error_util.FAULT_NOT_AUTHENTICATED in excep.fault_list: - # Because of the idle session returning an empty - # RetrievePropertiesResponse and also the same is returned - # when there is say empty answer to the query for - # VMs on the host ( as in no VMs on the host), we have no - # way to differentiate. - # So if the previous response was also am empty response - # and after creating a new session, we get the same empty - # response, then we are sure of the response being supposed - # to be empty. - if error_util.FAULT_NOT_AUTHENTICATED in last_fault_list: - return [] - last_fault_list = 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 - except Exception, excep: - # If it is a proper exception, say not having furnished - # proper data in the SOAP call or the retry limit having - # exceeded, we raise the exception - exc = excep - break - # If retry count has been reached then break and - # raise the exception - if retry_count > self.api_retry_count: - break - time.sleep(TIME_BETWEEN_API_CALL_RETRIES) - - LOG.critical(_("In vmwareapi:_call_method, " - "got this exception: %s") % exc) - raise - - def _get_vim(self): - """Gets the VIM object reference.""" - if self.vim is None: - self._create_session() - return self.vim - - def _wait_for_task(self, instance_uuid, task_ref): - """ - Return a Deferred that will give the result of the given task. - The task is polled until it completes. - """ - done = event.Event() - loop = utils.LoopingCall(self._poll_task, instance_uuid, task_ref, - done) - loop.start(FLAGS.vmwareapi_task_poll_interval) - ret_val = done.wait() - loop.stop() - return ret_val - - def _poll_task(self, instance_uuid, task_ref, done): - """ - Poll the given task, and fires the given Deferred if we - get a result. - """ - try: - task_info = self._call_method(vim_util, "get_dynamic_property", - task_ref, "Task", "info") - task_name = task_info.name - if task_info.state in ['queued', 'running']: - return - elif task_info.state == 'success': - LOG.debug(_("Task [%(task_name)s] %(task_ref)s " - "status: success") % locals()) - done.send("success") - else: - error_info = str(task_info.error.localizedMessage) - LOG.warn(_("Task [%(task_name)s] %(task_ref)s " - "status: error %(error_info)s") % locals()) - done.send_exception(exception.NovaException(error_info)) - except Exception, excep: - LOG.warn(_("In vmwareapi:_poll_task, Got this error %s") % excep) - done.send_exception(excep) -- cgit