Merge "vmwareapi oslo.vmware library integration"
This commit is contained in:
commit
1dcdfd1d2b
@ -22,6 +22,8 @@ A fake VMware VI API implementation.
|
||||
import collections
|
||||
import pprint
|
||||
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common import jsonutils
|
||||
@ -30,7 +32,6 @@ from nova.openstack.common import units
|
||||
from nova.openstack.common import uuidutils
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
|
||||
_CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine',
|
||||
'Network', 'HostSystem', 'HostNetworkSystem', 'Task', 'session',
|
||||
@ -40,6 +41,7 @@ _FAKE_FILE_SIZE = 1024
|
||||
|
||||
_db_content = {}
|
||||
_array_types = {}
|
||||
_vim_map = {}
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -296,7 +298,7 @@ class FileAlreadyExists(DataObject):
|
||||
|
||||
def __init__(self):
|
||||
super(FileAlreadyExists, self).__init__()
|
||||
self.__name__ = error_util.FILE_ALREADY_EXISTS
|
||||
self.__name__ = vexc.FILE_ALREADY_EXISTS
|
||||
|
||||
|
||||
class FileNotFound(DataObject):
|
||||
@ -304,7 +306,7 @@ class FileNotFound(DataObject):
|
||||
|
||||
def __init__(self):
|
||||
super(FileNotFound, self).__init__()
|
||||
self.__name__ = error_util.FILE_NOT_FOUND
|
||||
self.__name__ = vexc.FILE_NOT_FOUND
|
||||
|
||||
|
||||
class FileFault(DataObject):
|
||||
@ -312,7 +314,7 @@ class FileFault(DataObject):
|
||||
|
||||
def __init__(self):
|
||||
super(FileFault, self).__init__()
|
||||
self.__name__ = error_util.FILE_FAULT
|
||||
self.__name__ = vexc.FILE_FAULT
|
||||
|
||||
|
||||
class CannotDeleteFile(DataObject):
|
||||
@ -320,7 +322,7 @@ class CannotDeleteFile(DataObject):
|
||||
|
||||
def __init__(self):
|
||||
super(CannotDeleteFile, self).__init__()
|
||||
self.__name__ = error_util.CANNOT_DELETE_FILE
|
||||
self.__name__ = vexc.CANNOT_DELETE_FILE
|
||||
|
||||
|
||||
class FileLocked(DataObject):
|
||||
@ -328,7 +330,7 @@ class FileLocked(DataObject):
|
||||
|
||||
def __init__(self):
|
||||
super(FileLocked, self).__init__()
|
||||
self.__name__ = error_util.FILE_LOCKED
|
||||
self.__name__ = vexc.FILE_LOCKED
|
||||
|
||||
|
||||
class VirtualDisk(DataObject):
|
||||
@ -965,7 +967,7 @@ def _remove_file(file_path):
|
||||
# Check if the remove is for a single file object or for a folder
|
||||
if file_path.find(".vmdk") != -1:
|
||||
if file_path not in _db_content.get("files"):
|
||||
raise error_util.FileNotFoundException(file_path)
|
||||
raise vexc.FileNotFoundException(file_path)
|
||||
_db_content.get("files").remove(file_path)
|
||||
else:
|
||||
# Removes the files in the folder and the folder too from the db
|
||||
@ -999,6 +1001,13 @@ def fake_upload_image(context, image, instance, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def fake_fetch_image(context, instance, host, dc_name, ds_name, file_path,
|
||||
cookies=None):
|
||||
"""Fakes the fetch of an image."""
|
||||
ds_file_path = "[" + ds_name + "] " + file_path
|
||||
_add_file(ds_file_path)
|
||||
|
||||
|
||||
def _get_vm_mdo(vm_ref):
|
||||
"""Gets the Virtual Machine with the ref from the db."""
|
||||
if _db_content.get("VirtualMachine", None) is None:
|
||||
@ -1079,6 +1088,13 @@ class FakeObjectRetrievalSession(FakeSession):
|
||||
return self.ret[self.ind - 1]
|
||||
|
||||
|
||||
def get_fake_vim_object(vmware_api_session):
|
||||
key = vmware_api_session.__repr__()
|
||||
if key not in _vim_map:
|
||||
_vim_map[key] = FakeVim()
|
||||
return _vim_map[key]
|
||||
|
||||
|
||||
class FakeVim(object):
|
||||
"""Fake VIM Class."""
|
||||
|
||||
@ -1112,7 +1128,8 @@ class FakeVim(object):
|
||||
|
||||
self._service_content = service_content
|
||||
|
||||
def get_service_content(self):
|
||||
@property
|
||||
def service_content(self):
|
||||
return self._service_content
|
||||
|
||||
def __repr__(self):
|
||||
@ -1142,8 +1159,8 @@ class FakeVim(object):
|
||||
if (self._session is None or self._session not in
|
||||
_db_content['session']):
|
||||
LOG.debug("Session is faulty")
|
||||
raise error_util.VimFaultException(
|
||||
[error_util.NOT_AUTHENTICATED],
|
||||
raise vexc.VimFaultException(
|
||||
[vexc.NOT_AUTHENTICATED],
|
||||
_("Session Invalid"))
|
||||
|
||||
def _session_is_active(self, *args, **kwargs):
|
||||
@ -1360,7 +1377,7 @@ class FakeVim(object):
|
||||
if _db_content.get("files", None) is None:
|
||||
raise exception.NoFilesFound()
|
||||
if get_file(ds_path):
|
||||
raise error_util.FileAlreadyExistsException()
|
||||
raise vexc.FileAlreadyExistsException()
|
||||
_db_content["files"].append('%s/' % ds_path)
|
||||
|
||||
def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"):
|
||||
|
@ -20,13 +20,14 @@ Stubouts for the test suite
|
||||
import contextlib
|
||||
|
||||
import mock
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import db
|
||||
from nova.tests import test_flavors
|
||||
from nova.tests.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import network_util
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
|
||||
|
||||
def fake_get_vim_object(arg):
|
||||
@ -34,32 +35,39 @@ def fake_get_vim_object(arg):
|
||||
return fake.FakeVim()
|
||||
|
||||
|
||||
@property
|
||||
def fake_vim_prop(arg):
|
||||
"""Stubs out the VMwareAPISession's vim property access method."""
|
||||
return fake.get_fake_vim_object(arg)
|
||||
|
||||
|
||||
def fake_is_vim_object(arg, module):
|
||||
"""Stubs out the VMwareAPISession's is_vim_object method."""
|
||||
return isinstance(module, fake.FakeVim)
|
||||
|
||||
|
||||
def fake_temp_method_exception():
|
||||
raise error_util.VimFaultException(
|
||||
[error_util.NOT_AUTHENTICATED],
|
||||
raise vexc.VimFaultException(
|
||||
[vexc.NOT_AUTHENTICATED],
|
||||
"Session Empty/Not Authenticated")
|
||||
|
||||
|
||||
def fake_temp_session_exception():
|
||||
raise error_util.SessionConnectionException("it's a fake!",
|
||||
raise vexc.VimConnectionException("it's a fake!",
|
||||
"Session Exception")
|
||||
|
||||
|
||||
def fake_session_file_exception():
|
||||
fault_list = [error_util.FILE_ALREADY_EXISTS]
|
||||
raise error_util.VimFaultException(fault_list, 'fake')
|
||||
fault_list = [vexc.FILE_ALREADY_EXISTS]
|
||||
raise vexc.VimFaultException(fault_list,
|
||||
Exception('fake'))
|
||||
|
||||
|
||||
def fake_session_permission_exception():
|
||||
fault_list = [error_util.NO_PERMISSION]
|
||||
fault_list = [vexc.NO_PERMISSION]
|
||||
fault_string = 'Permission to perform this operation was denied.'
|
||||
details = {'privilegeId': 'Resource.AssignVMToPool', 'object': 'domain-c7'}
|
||||
raise error_util.VimFaultException(fault_list, fault_string, details)
|
||||
raise vexc.VimFaultException(fault_list, fault_string, details=details)
|
||||
|
||||
|
||||
def _fake_flavor_get(context, id):
|
||||
@ -78,8 +86,9 @@ def set_stubs(stubs):
|
||||
"""Set the stubs."""
|
||||
stubs.Set(network_util, 'get_network_with_the_name',
|
||||
fake.fake_get_network)
|
||||
stubs.Set(driver.VMwareAPISession, "_get_vim_object",
|
||||
fake_get_vim_object)
|
||||
stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image)
|
||||
stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image)
|
||||
stubs.Set(driver.VMwareAPISession, "vim", fake_vim_prop)
|
||||
stubs.Set(driver.VMwareAPISession, "_is_vim_object",
|
||||
fake_is_vim_object)
|
||||
stubs.Set(db, 'flavor_get', _fake_flavor_get)
|
||||
|
@ -13,10 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import contextlib
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
import mox
|
||||
|
||||
from nova import context
|
||||
@ -29,7 +26,6 @@ from nova.tests.virt.vmwareapi import fake as vmwareapi_fake
|
||||
from nova.tests.virt.vmwareapi import stubs
|
||||
from nova.virt import fake
|
||||
from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import read_write_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
from nova.virt.vmwareapi import vmops
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
@ -123,42 +119,11 @@ class ConfigDriveTestCase(test.NoDBTestCase):
|
||||
block_device_info=None):
|
||||
|
||||
injected_files = injected_files or []
|
||||
read_file_handle = mock.MagicMock()
|
||||
write_file_handle = mock.MagicMock()
|
||||
self.image_ref = self.test_instance.image_ref
|
||||
|
||||
def fake_read_handle(read_iter):
|
||||
return read_file_handle
|
||||
|
||||
def fake_write_handle(host, dc_name, ds_name, cookies,
|
||||
file_path, file_size, scheme="https"):
|
||||
self.assertEqual('dc1', dc_name)
|
||||
self.assertEqual('ds1', ds_name)
|
||||
self.assertEqual('Fake-CookieJar', cookies)
|
||||
split_file_path = file_path.split('/')
|
||||
self.assertEqual('vmware_temp', split_file_path[0])
|
||||
self.assertEqual(self.image_ref, split_file_path[2])
|
||||
self.assertEqual(('%s-flat.vmdk' % self.image_ref),
|
||||
split_file_path[3])
|
||||
self.assertEqual(int(self.image['size']), file_size)
|
||||
|
||||
return write_file_handle
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(read_write_util, 'VMwareHTTPWriteFile',
|
||||
side_effect=fake_write_handle),
|
||||
mock.patch.object(read_write_util, 'GlanceFileRead',
|
||||
side_effect=fake_read_handle),
|
||||
mock.patch.object(vmware_images, 'start_transfer')
|
||||
) as (fake_http_write, fake_glance_read, fake_start_transfer):
|
||||
self.conn.spawn(self.context, self.test_instance, self.image,
|
||||
self.conn.spawn(self.context, self.test_instance, self.image,
|
||||
injected_files=injected_files,
|
||||
admin_password=admin_password,
|
||||
network_info=self.network_info,
|
||||
block_device_info=block_device_info)
|
||||
fake_start_transfer.assert_called_once_with(self.context,
|
||||
read_file_handle, self.image['size'],
|
||||
write_file_handle=write_file_handle)
|
||||
|
||||
def test_create_vm_with_config_drive_verify_method_invocation(self):
|
||||
self.test_instance.config_drive = 'True'
|
||||
|
@ -23,12 +23,13 @@ import collections
|
||||
import contextlib
|
||||
import copy
|
||||
import datetime
|
||||
import time
|
||||
|
||||
from eventlet import greenthread
|
||||
import mock
|
||||
import mox
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
from oslo.vmware import exceptions as vexc
|
||||
from oslo.vmware import vim
|
||||
import suds
|
||||
|
||||
from nova import block_device
|
||||
@ -60,9 +61,7 @@ from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import imagecache
|
||||
from nova.virt.vmwareapi import read_write_util
|
||||
from nova.virt.vmwareapi import vif
|
||||
from nova.virt.vmwareapi import vim
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
from nova.virt.vmwareapi import vmops
|
||||
@ -100,18 +99,17 @@ class VMwareSudsTest(test.NoDBTestCase):
|
||||
self.vim = self._vim_create()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
def _mock_getattr(self, attr_name):
|
||||
self.assertEqual("RetrieveServiceContent", attr_name)
|
||||
return lambda obj, **kwargs: fake_service_content()
|
||||
|
||||
def _vim_create(self):
|
||||
|
||||
def fake_retrieve_service_content(fake):
|
||||
return fake_service_content()
|
||||
|
||||
self.stubs.Set(vim.Vim, 'retrieve_service_content',
|
||||
fake_retrieve_service_content)
|
||||
return vim.Vim()
|
||||
with mock.patch.object(vim.Vim, '__getattr__', self._mock_getattr):
|
||||
return vim.Vim()
|
||||
|
||||
def test_exception_with_deepcopy(self):
|
||||
self.assertIsNotNone(self.vim)
|
||||
self.assertRaises(error_util.VimException,
|
||||
self.assertRaises(vexc.VimException,
|
||||
copy.deepcopy, self.vim)
|
||||
|
||||
|
||||
@ -161,187 +159,38 @@ class VMwareDriverStartupTestCase(test.NoDBTestCase):
|
||||
|
||||
class VMwareSessionTestCase(test.NoDBTestCase):
|
||||
|
||||
def _fake_is_vim_object(self, module):
|
||||
return True
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_call_method_vim_fault(self, mock_sleep):
|
||||
|
||||
def _fake_session_is_active(self):
|
||||
return False
|
||||
|
||||
@mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
return_value=False)
|
||||
def test_call_method(self, mock_is_vim):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
self._fake_is_vim_object),
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
mock.patch.object(driver.VMwareAPISession, '_session_is_active',
|
||||
_fake_session_is_active)
|
||||
) as (_fake_vim, _fake_create, _fake_is_active):
|
||||
api_session = driver.VMwareAPISession()
|
||||
args = ()
|
||||
kwargs = {}
|
||||
self.assertRaises(error_util.VimFaultException,
|
||||
api_session._call_method,
|
||||
stubs, 'fake_temp_method_exception',
|
||||
*args, **kwargs)
|
||||
|
||||
def test_call_method_vim_empty(self):
|
||||
|
||||
def _fake_session_is_active(self):
|
||||
return True
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
mock.patch.object(driver.VMwareAPISession, 'invoke_api'),
|
||||
) as (fake_create, fake_invoke):
|
||||
session = driver.VMwareAPISession()
|
||||
session._vim = mock.Mock()
|
||||
module = mock.Mock()
|
||||
session._call_method(module, 'fira')
|
||||
fake_invoke.assert_called_once_with(module, 'fira', session._vim)
|
||||
|
||||
@mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
return_value=True)
|
||||
def test_call_method_vim(self, mock_is_vim):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
self._fake_is_vim_object),
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
mock.patch.object(driver.VMwareAPISession, '_session_is_active',
|
||||
_fake_session_is_active)
|
||||
) as (_fake_vim, _fake_create, _fake_is_active):
|
||||
api_session = driver.VMwareAPISession()
|
||||
args = ()
|
||||
kwargs = {}
|
||||
res = api_session._call_method(stubs, 'fake_temp_method_exception',
|
||||
*args, **kwargs)
|
||||
self.assertEqual([], res)
|
||||
|
||||
@mock.patch('time.sleep')
|
||||
def test_call_method_session_exception(self, mock_sleep):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
self._fake_is_vim_object),
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
) as (_fake_vim, _fake_create):
|
||||
api_session = driver.VMwareAPISession()
|
||||
args = ()
|
||||
kwargs = {}
|
||||
self.assertRaises(error_util.SessionConnectionException,
|
||||
api_session._call_method,
|
||||
stubs, 'fake_temp_session_exception',
|
||||
*args, **kwargs)
|
||||
|
||||
def test_call_method_session_file_exists_exception(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
self._fake_is_vim_object),
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
) as (_fake_vim, _fake_create):
|
||||
api_session = driver.VMwareAPISession()
|
||||
args = ()
|
||||
kwargs = {}
|
||||
self.assertRaises(error_util.FileAlreadyExistsException,
|
||||
api_session._call_method,
|
||||
stubs, 'fake_session_file_exception',
|
||||
*args, **kwargs)
|
||||
|
||||
def test_call_method_session_no_permission_exception(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(driver.VMwareAPISession, '_is_vim_object',
|
||||
self._fake_is_vim_object),
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
) as (_fake_vim, _fake_create):
|
||||
api_session = driver.VMwareAPISession()
|
||||
args = ()
|
||||
kwargs = {}
|
||||
e = self.assertRaises(error_util.NoPermissionException,
|
||||
api_session._call_method,
|
||||
stubs, 'fake_session_permission_exception',
|
||||
*args, **kwargs)
|
||||
fault_string = 'Permission to perform this operation was denied.'
|
||||
details = {'privilegeId': 'Resource.AssignVMToPool',
|
||||
'object': 'domain-c7'}
|
||||
exception_string = '%s %s' % (fault_string, details)
|
||||
self.assertEqual(exception_string, six.text_type(e))
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
mock.patch.object(driver.VMwareAPISession, 'invoke_api'),
|
||||
) as (fake_create, fake_invoke):
|
||||
session = driver.VMwareAPISession()
|
||||
module = mock.Mock()
|
||||
session._call_method(module, 'fira')
|
||||
fake_invoke.assert_called_once_with(module, 'fira')
|
||||
|
||||
|
||||
class VMwareAPIConfTestCase(test.NoDBTestCase):
|
||||
"""Unit tests for VMWare API configurations."""
|
||||
def setUp(self):
|
||||
super(VMwareAPIConfTestCase, self).setUp()
|
||||
vm_util.vm_refs_cache_reset()
|
||||
|
||||
def tearDown(self):
|
||||
super(VMwareAPIConfTestCase, self).tearDown()
|
||||
|
||||
def test_configure_without_wsdl_loc_override(self):
|
||||
# Test the default configuration behavior. By default,
|
||||
# use the WSDL sitting on the host we are talking to in
|
||||
# order to bind the SOAP client.
|
||||
wsdl_loc = cfg.CONF.vmware.wsdl_location
|
||||
self.assertIsNone(wsdl_loc)
|
||||
wsdl_url = vim.Vim.get_wsdl_url("https", "www.example.com", 443)
|
||||
url = vim.Vim.get_soap_url("https", "www.example.com", 443)
|
||||
self.assertEqual("https://www.example.com:443/sdk/vimService.wsdl",
|
||||
wsdl_url)
|
||||
self.assertEqual("https://www.example.com:443/sdk", url)
|
||||
|
||||
def test_configure_without_wsdl_loc_override_using_ipv6(self):
|
||||
# Same as above but with ipv6 based host ip
|
||||
wsdl_loc = cfg.CONF.vmware.wsdl_location
|
||||
self.assertIsNone(wsdl_loc)
|
||||
wsdl_url = vim.Vim.get_wsdl_url("https", "::1", 443)
|
||||
url = vim.Vim.get_soap_url("https", "::1", 443)
|
||||
self.assertEqual("https://[::1]:443/sdk/vimService.wsdl",
|
||||
wsdl_url)
|
||||
self.assertEqual("https://[::1]:443/sdk", url)
|
||||
|
||||
def test_configure_with_wsdl_loc_override(self):
|
||||
# Use the setting vmwareapi_wsdl_loc to override the
|
||||
# default path to the WSDL.
|
||||
#
|
||||
# This is useful as a work-around for XML parsing issues
|
||||
# found when using some WSDL in combination with some XML
|
||||
# parsers.
|
||||
#
|
||||
# The wsdl_url should point to a different host than the one we
|
||||
# are actually going to send commands to.
|
||||
fake_wsdl = "https://www.test.com:443/sdk/foo.wsdl"
|
||||
self.flags(wsdl_location=fake_wsdl, group='vmware')
|
||||
wsdl_loc = cfg.CONF.vmware.wsdl_location
|
||||
self.assertIsNotNone(wsdl_loc)
|
||||
self.assertEqual(fake_wsdl, wsdl_loc)
|
||||
wsdl_url = vim.Vim.get_wsdl_url("https", "www.example.com", 443)
|
||||
url = vim.Vim.get_soap_url("https", "www.example.com", 443)
|
||||
self.assertEqual(fake_wsdl, wsdl_url)
|
||||
self.assertEqual("https://www.example.com:443/sdk", url)
|
||||
|
||||
def test_configure_non_default_host_port(self):
|
||||
def _fake_create_session(self):
|
||||
pass
|
||||
|
||||
def _fake_retrieve_service_content(self):
|
||||
return None
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(driver.VMwareAPISession, '_create_session',
|
||||
_fake_create_session),
|
||||
mock.patch.object(vim.Vim, 'retrieve_service_content',
|
||||
_fake_retrieve_service_content),
|
||||
mock.patch('suds.client.Client')
|
||||
):
|
||||
self.flags(host_ip='www.test.com',
|
||||
host_port=12345, group='vmware')
|
||||
host_ip = cfg.CONF.vmware.host_ip
|
||||
host_port = cfg.CONF.vmware.host_port
|
||||
self.assertEqual('www.test.com', host_ip)
|
||||
self.assertEqual(12345, host_port)
|
||||
api_session = driver.VMwareAPISession(host_ip=host_ip,
|
||||
host_port=host_port)
|
||||
vim_obj = api_session._get_vim_object()
|
||||
self.assertEqual("https://www.test.com:12345/sdk/vimService.wsdl",
|
||||
vim_obj.wsdl_url)
|
||||
self.assertEqual("https://www.test.com:12345/sdk", vim_obj.url)
|
||||
|
||||
|
||||
@mock.patch.object(driver, 'TIME_BETWEEN_API_CALL_RETRIES', 0)
|
||||
class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
"""Unit tests for Vmware API connection calls."""
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self, create_connection=True):
|
||||
super(VMwareAPIVMTestCase, self).setUp()
|
||||
vm_util.vm_refs_cache_reset()
|
||||
self.context = context.RequestContext('fake', 'fake', is_admin=False)
|
||||
@ -362,13 +211,16 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
stubs.set_stubs(self.stubs)
|
||||
vmwareapi_fake.reset()
|
||||
nova.tests.image.fake.stub_out_image_service(self.stubs)
|
||||
self.conn = driver.VMwareVCDriver(None, False)
|
||||
self.node_name = self.conn._resources.keys()[0]
|
||||
self.node_name2 = self.conn._resources.keys()[1]
|
||||
if cluster_name2 in self.node_name2:
|
||||
self.ds = 'ds1'
|
||||
else:
|
||||
self.ds = 'ds2'
|
||||
if create_connection:
|
||||
self.conn = driver.VMwareVCDriver(None, False)
|
||||
self._set_exception_vars()
|
||||
self.node_name = self.conn._resources.keys()[0]
|
||||
self.node_name2 = self.conn._resources.keys()[1]
|
||||
if cluster_name2 in self.node_name2:
|
||||
self.ds = 'ds1'
|
||||
else:
|
||||
self.ds = 'ds2'
|
||||
|
||||
self.vim = vmwareapi_fake.FakeVim()
|
||||
|
||||
# NOTE(vish): none of the network plugging code is actually
|
||||
@ -386,7 +238,6 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
self.fake_image_uuid = self.image['id']
|
||||
nova.tests.image.fake.stub_out_image_service(self.stubs)
|
||||
self.vnc_host = 'ha-host'
|
||||
self._set_exception_vars()
|
||||
self.instance_without_compute = {'node': None,
|
||||
'vm_state': 'building',
|
||||
'project_id': 'fake',
|
||||
@ -450,9 +301,8 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
vcdriver = driver.VMwareVCDriver(None, False)
|
||||
vcdriver._session = mock.Mock()
|
||||
vcdriver.cleanup_host("foo")
|
||||
vcdriver._session.vim.get_service_content.assert_called_once_with()
|
||||
vcdriver._session.vim.client.service.Logout.assert_called_once_with(
|
||||
vcdriver._session.vim.get_service_content().sessionManager
|
||||
vcdriver._session.vim.service_content.sessionManager
|
||||
)
|
||||
|
||||
@mock.patch('nova.virt.vmwareapi.driver.VMwareVCDriver.__init__')
|
||||
@ -475,45 +325,20 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
def _fake_login(_self):
|
||||
self.attempts += 1
|
||||
if self.attempts == 1:
|
||||
raise exception.NovaException('Here is my fake exception')
|
||||
raise vexc.VimConnectionException('Here is my fake exception')
|
||||
return self.login_session
|
||||
|
||||
def _fake_check_session(_self):
|
||||
return True
|
||||
|
||||
self.stubs.Set(vmwareapi_fake.FakeVim, '_login', _fake_login)
|
||||
self.stubs.Set(time, 'sleep', lambda x: None)
|
||||
self.stubs.Set(vmwareapi_fake.FakeVim, '_check_session',
|
||||
_fake_check_session)
|
||||
self.conn = driver.VMwareAPISession()
|
||||
|
||||
with mock.patch.object(greenthread, 'sleep'):
|
||||
self.conn = driver.VMwareAPISession()
|
||||
self.assertEqual(self.attempts, 2)
|
||||
|
||||
def test_wait_for_task_exception(self):
|
||||
self.flags(task_poll_interval=1, group='vmware')
|
||||
self.login_session = vmwareapi_fake.FakeVim()._login()
|
||||
self.stop_called = 0
|
||||
|
||||
def _fake_login(_self):
|
||||
return self.login_session
|
||||
|
||||
self.stubs.Set(vmwareapi_fake.FakeVim, '_login', _fake_login)
|
||||
|
||||
def fake_poll_task(task_ref, done):
|
||||
done.send_exception(exception.NovaException('fake exception'))
|
||||
|
||||
def fake_stop_loop(loop):
|
||||
self.stop_called += 1
|
||||
return loop.stop()
|
||||
|
||||
self.conn = driver.VMwareAPISession()
|
||||
self.stubs.Set(self.conn, "_poll_task",
|
||||
fake_poll_task)
|
||||
self.stubs.Set(self.conn, "_stop_loop",
|
||||
fake_stop_loop)
|
||||
self.assertRaises(exception.NovaException,
|
||||
self.conn._wait_for_task, 'fake-ref')
|
||||
self.assertEqual(self.stop_called, 1)
|
||||
|
||||
def _get_instance_type_by_name(self, type):
|
||||
for instance_type in test_flavors.DEFAULT_FLAVORS:
|
||||
if instance_type['name'] == type:
|
||||
@ -559,48 +384,17 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
def _create_vm(self, node=None, num_instances=1, uuid=None,
|
||||
instance_type='m1.large', powered_on=True):
|
||||
"""Create and spawn the VM."""
|
||||
|
||||
read_file_handle = mock.MagicMock()
|
||||
write_file_handle = mock.MagicMock()
|
||||
|
||||
def fake_read_handle(read_iter):
|
||||
return read_file_handle
|
||||
|
||||
def _fake_write_mock(host, dc_name, ds_name, cookies,
|
||||
file_path, file_size, scheme="https"):
|
||||
self.vim.fake_transfer_file(ds_name=ds_name,
|
||||
file_path=file_path)
|
||||
image_ref = self.image['id']
|
||||
self.assertIn(dc_name, ['dc1', 'dc2'])
|
||||
self.assertIn(ds_name, ['ds1', 'ds2'])
|
||||
self.assertEqual('Fake-CookieJar', cookies)
|
||||
split_file_path = file_path.split('/')
|
||||
self.assertEqual('vmware_temp', split_file_path[0])
|
||||
self.assertEqual(image_ref, split_file_path[2])
|
||||
self.assertEqual(int(self.image['size']), file_size)
|
||||
return write_file_handle
|
||||
|
||||
if not node:
|
||||
node = self.node_name
|
||||
self._create_instance(node=node, uuid=uuid,
|
||||
instance_type=instance_type)
|
||||
self.assertIsNone(vm_util.vm_ref_cache_get(self.uuid))
|
||||
with contextlib.nested(
|
||||
mock.patch.object(read_write_util, 'VMwareHTTPWriteFile',
|
||||
_fake_write_mock),
|
||||
mock.patch.object(read_write_util, 'GlanceFileRead',
|
||||
fake_read_handle),
|
||||
mock.patch.object(vmware_images, 'start_transfer')
|
||||
) as (fake_http_write, fake_glance_read, fake_start_transfer):
|
||||
self.conn.spawn(self.context, self.instance, self.image,
|
||||
injected_files=[], admin_password=None,
|
||||
network_info=self.network_info,
|
||||
block_device_info=None)
|
||||
fake_start_transfer.assert_called_once_with(self.context,
|
||||
read_file_handle, self.image['size'],
|
||||
write_file_handle=write_file_handle)
|
||||
self._check_vm_record(num_instances=num_instances,
|
||||
powered_on=powered_on)
|
||||
self.conn.spawn(self.context, self.instance, self.image,
|
||||
injected_files=[], admin_password=None,
|
||||
network_info=self.network_info,
|
||||
block_device_info=None)
|
||||
self._check_vm_record(num_instances=num_instances,
|
||||
powered_on=powered_on)
|
||||
self.assertIsNotNone(vm_util.vm_ref_cache_get(self.uuid))
|
||||
|
||||
def _get_vm_record(self):
|
||||
@ -914,8 +708,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
'node': self.instance_node})
|
||||
self._check_vm_info(info, power_state.RUNNING)
|
||||
else:
|
||||
self.assertRaises(error_util.VMwareDriverException,
|
||||
self._create_vm)
|
||||
self.assertRaises(vexc.VMwareDriverException, self._create_vm)
|
||||
self.assertTrue(self.exception)
|
||||
|
||||
def test_spawn_with_delete_exception_not_found(self):
|
||||
@ -994,10 +787,8 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
#
|
||||
# Expect the copy error to be raised
|
||||
self.flags(use_linked_clone=True, group='vmware')
|
||||
self.wait_task = self.conn._session._wait_for_task
|
||||
self.call_method = self.conn._session._call_method
|
||||
|
||||
CopyError = error_util.FileFaultException
|
||||
CopyError = vexc.FileFaultException
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == 'fake-copy-task':
|
||||
@ -1024,14 +815,12 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
# Expect the file to be cleaned up
|
||||
# Expect the copy error to be raised
|
||||
self.flags(use_linked_clone=True, group='vmware')
|
||||
self.wait_task = self.conn._session._wait_for_task
|
||||
self.call_method = self.conn._session._call_method
|
||||
self.task_ref = None
|
||||
uuid = self.fake_image_uuid
|
||||
cached_image = '[%s] vmware_base/%s/%s.80.vmdk' % (self.ds,
|
||||
uuid, uuid)
|
||||
|
||||
CopyError = error_util.FileFaultException
|
||||
CopyError = vexc.FileFaultException
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == self.task_ref:
|
||||
@ -1064,15 +853,13 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
# Expect file to be left behind
|
||||
# Expect file cleanup error to be raised
|
||||
self.flags(use_linked_clone=True, group='vmware')
|
||||
self.wait_task = self.conn._session._wait_for_task
|
||||
self.call_method = self.conn._session._call_method
|
||||
self.task_ref = None
|
||||
uuid = self.fake_image_uuid
|
||||
cached_image = '[%s] vmware_base/%s/%s.80.vmdk' % (self.ds,
|
||||
uuid, uuid)
|
||||
|
||||
CopyError = error_util.FileFaultException
|
||||
DeleteError = error_util.CannotDeleteFileException
|
||||
CopyError = vexc.FileFaultException
|
||||
DeleteError = vexc.CannotDeleteFileException
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == self.task_ref:
|
||||
@ -1136,7 +923,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
'%s.80-flat.vmdk' %
|
||||
self.fake_image_uuid)
|
||||
|
||||
NoDiskSpace = error_util.get_fault_class('NoDiskSpace')
|
||||
NoDiskSpace = vexc.get_fault_class('NoDiskSpace')
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == self.task_ref:
|
||||
@ -1171,7 +958,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
if task_ref == self.task_ref:
|
||||
self.task_ref = None
|
||||
self.exception = True
|
||||
raise error_util.FileAlreadyExistsException()
|
||||
raise vexc.FileAlreadyExistsException()
|
||||
return self.wait_task(task_ref)
|
||||
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
@ -1203,7 +990,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
if task_ref == self.task_ref:
|
||||
self.task_ref = None
|
||||
self.exception = True
|
||||
raise error_util.VMwareDriverException('Exception!')
|
||||
raise vexc.VMwareDriverException('Exception!')
|
||||
return self.wait_task(task_ref)
|
||||
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
@ -1218,7 +1005,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
mock.patch.object(self.conn._session, '_call_method',
|
||||
fake_call_method)
|
||||
) as (_wait_for_task, _call_method):
|
||||
self.assertRaises(error_util.VMwareDriverException,
|
||||
self.assertRaises(vexc.VMwareDriverException,
|
||||
self._create_vm)
|
||||
self.assertTrue(self.exception)
|
||||
|
||||
@ -1236,7 +1023,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
mock.patch.object(self.conn._session, '_call_method',
|
||||
fake_call_method)
|
||||
):
|
||||
self.assertRaises(error_util.VMwareDriverException,
|
||||
self.assertRaises(vexc.VMwareDriverException,
|
||||
self._create_vm)
|
||||
|
||||
def test_spawn_with_move_file_exists_poll_exception(self):
|
||||
@ -1426,7 +1213,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.conn._session, '_wait_for_task',
|
||||
side_effect=exception),
|
||||
mock.patch.object(time, 'sleep')
|
||||
mock.patch.object(vmops, '_time_sleep_wrapper')
|
||||
) as (_fake_wait, _fake_sleep):
|
||||
if exception != error_util.TaskInProgress:
|
||||
self.assertRaises(exception,
|
||||
@ -1737,20 +1524,9 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||
self.stubs.Set(self.conn._volumeops, "attach_disk_to_vm",
|
||||
fake_attach_disk_to_vm)
|
||||
|
||||
def _fake_http_write(host, data_center_name, datastore_name,
|
||||
cookies, file_path, file_size, scheme="https"):
|
||||
self.vim.fake_transfer_file(ds_name=datastore_name,
|
||||
file_path=file_path)
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(read_write_util, 'VMwareHTTPWriteFile',
|
||||
_fake_http_write),
|
||||
mock.patch.object(read_write_util, 'GlanceFileRead'),
|
||||
mock.patch.object(vmware_images, 'start_transfer')
|
||||
|
||||
) as (http_write, glance_read, fake_start_transfer):
|
||||
self.conn.rescue(self.context, self.instance, self.network_info,
|
||||
self.conn.rescue(self.context, self.instance, self.network_info,
|
||||
self.image, 'fake-password')
|
||||
|
||||
info = self.conn.get_info({'name': '1-rescue',
|
||||
'uuid': '%s-rescue' % self.uuid,
|
||||
'node': self.instance_node})
|
||||
@ -2212,8 +1988,8 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase,
|
||||
test_driver.DriverAPITestHelper):
|
||||
|
||||
def setUp(self):
|
||||
super(VMwareAPIVCDriverTestCase, self).setUp()
|
||||
|
||||
super(VMwareAPIVCDriverTestCase, self).setUp(create_connection=False)
|
||||
cluster_name = 'test_cluster'
|
||||
cluster_name2 = 'test_cluster2'
|
||||
self.flags(cluster_name=[cluster_name, cluster_name2],
|
||||
@ -2223,6 +1999,7 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase,
|
||||
image_cache_subdirectory_name='vmware_base')
|
||||
vmwareapi_fake.reset()
|
||||
self.conn = driver.VMwareVCDriver(None, False)
|
||||
self._set_exception_vars()
|
||||
self.node_name = self.conn._resources.keys()[0]
|
||||
self.node_name2 = self.conn._resources.keys()[1]
|
||||
if cluster_name2 in self.node_name2:
|
||||
@ -2579,7 +2356,7 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase,
|
||||
"Original VM name should be with suffix -backup")
|
||||
self.assertEqual(cloned_vm_obj.name, self.instance['uuid'],
|
||||
"VM name does not match instance['uuid']")
|
||||
self.assertRaises(error_util.MissingParameter,
|
||||
self.assertRaises(vexc.MissingParameter,
|
||||
vm_util.clone_vmref_for_instance, self.conn._session,
|
||||
self.instance, None, host_ref, ds_ref,
|
||||
dc_obj.get("vmFolder"))
|
||||
|
@ -16,6 +16,7 @@ import contextlib
|
||||
import re
|
||||
|
||||
import mock
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
@ -23,7 +24,6 @@ from nova.openstack.common import units
|
||||
from nova import test
|
||||
from nova.tests.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
|
||||
|
||||
class DsUtilTestCase(test.NoDBTestCase):
|
||||
@ -146,7 +146,7 @@ class DsUtilTestCase(test.NoDBTestCase):
|
||||
|
||||
def fake_wait_for_task(task_ref):
|
||||
if task_ref == 'fake_exists_task':
|
||||
raise error_util.FileNotFoundException()
|
||||
raise vexc.FileNotFoundException()
|
||||
|
||||
# Should never get here
|
||||
self.fail()
|
||||
|
@ -1,84 +0,0 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
import six
|
||||
|
||||
from nova import test
|
||||
from nova.tests.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import error_util
|
||||
|
||||
|
||||
class ExpectedMethodFault:
|
||||
pass
|
||||
|
||||
|
||||
class ErrorUtilTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(ErrorUtilTestCase, self).setUp()
|
||||
|
||||
def test_fault_checker_empty_response(self):
|
||||
# assertRaises as a Context Manager would have been a good choice to
|
||||
# perform additional checks on the exception raised, instead of
|
||||
# try/catch block in the below tests, but it's available
|
||||
# only from Py 2.7.
|
||||
exp_fault_list = [error_util.NOT_AUTHENTICATED]
|
||||
try:
|
||||
error_util.FaultCheckers.retrievepropertiesex_fault_checker(None)
|
||||
except error_util.VimFaultException as e:
|
||||
self.assertEqual(exp_fault_list, e.fault_list)
|
||||
else:
|
||||
self.fail("VimFaultException was not raised.")
|
||||
|
||||
def test_fault_checker_missing_props(self):
|
||||
fake_objects = fake.FakeRetrieveResult()
|
||||
ml = [fake.MissingProperty(method_fault=ExpectedMethodFault())]
|
||||
fake_objects.add_object(fake.ObjectContent(None, missing_list=ml))
|
||||
|
||||
exp_fault_list = ['ExpectedMethodFault']
|
||||
try:
|
||||
error_util.FaultCheckers.retrievepropertiesex_fault_checker(
|
||||
fake_objects)
|
||||
except error_util.VimFaultException as e:
|
||||
self.assertEqual(exp_fault_list, e.fault_list)
|
||||
else:
|
||||
self.fail("VimFaultException was not raised.")
|
||||
|
||||
def test_fault_checker_no_missing_props(self):
|
||||
fake_objects = fake.FakeRetrieveResult()
|
||||
fake_objects.add_object(fake.ObjectContent(None))
|
||||
self.assertIsNone(
|
||||
error_util.FaultCheckers.retrievepropertiesex_fault_checker(
|
||||
fake_objects))
|
||||
|
||||
def test_exception_summary_exception_as_list(self):
|
||||
# assert that if a list is fed to the VimException object
|
||||
# that it will error.
|
||||
self.assertRaises(ValueError,
|
||||
error_util.VimException,
|
||||
[], ValueError('foo'))
|
||||
|
||||
def test_exception_summary_string(self):
|
||||
e = error_util.VimException("string", ValueError("foo"))
|
||||
string = six.text_type(e)
|
||||
self.assertEqual("string: foo", string)
|
||||
|
||||
def test_vim_fault_exception_string(self):
|
||||
self.assertRaises(ValueError,
|
||||
error_util.VimFaultException,
|
||||
"bad", ValueError("argument"))
|
||||
|
||||
def test_vim_fault_exception(self):
|
||||
vfe = error_util.VimFaultException([ValueError("example")], "cause")
|
||||
string = six.text_type(vfe)
|
||||
self.assertEqual("cause", string)
|
@ -17,6 +17,7 @@ import contextlib
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.network import model as network_model
|
||||
@ -24,7 +25,6 @@ from nova import test
|
||||
from nova.tests import matchers
|
||||
from nova.tests import utils
|
||||
from nova.tests.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import network_util
|
||||
from nova.virt.vmwareapi import vif
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
@ -280,7 +280,7 @@ class VMwareVifTestCase(test.NoDBTestCase):
|
||||
def test_create_port_group_already_exists(self):
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
if method == 'AddPortGroup':
|
||||
raise error_util.AlreadyExistsException()
|
||||
raise vexc.AlreadyExistsException()
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(vm_util, 'get_add_vswitch_port_group_spec'),
|
||||
@ -295,7 +295,7 @@ class VMwareVifTestCase(test.NoDBTestCase):
|
||||
def test_create_port_group_exception(self):
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
if method == 'AddPortGroup':
|
||||
raise error_util.VMwareDriverException()
|
||||
raise vexc.VMwareDriverException()
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(vm_util, 'get_add_vswitch_port_group_spec'),
|
||||
@ -303,7 +303,7 @@ class VMwareVifTestCase(test.NoDBTestCase):
|
||||
mock.patch.object(self.session, '_call_method',
|
||||
fake_call_method)
|
||||
) as (_add_vswitch, _get_host, _call_method):
|
||||
self.assertRaises(error_util.VMwareDriverException,
|
||||
self.assertRaises(vexc.VMwareDriverException,
|
||||
network_util.create_port_group,
|
||||
self.session, 'pg_name',
|
||||
'vswitch_name', vlan_id=0,
|
||||
@ -312,7 +312,7 @@ class VMwareVifTestCase(test.NoDBTestCase):
|
||||
def test_get_neutron_network_invalid_property(self):
|
||||
def fake_call_method(module, method, *args, **kwargs):
|
||||
if method == 'get_dynamic_property':
|
||||
raise error_util.InvalidPropertyException()
|
||||
raise vexc.InvalidPropertyException()
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(vm_util, 'get_host_ref'),
|
||||
|
@ -85,11 +85,10 @@ class VMwareVIMUtilTestCase(test.NoDBTestCase):
|
||||
retrievePropertiesEx.return_value = result
|
||||
|
||||
calls = {'RetrievePropertiesEx': retrievePropertiesEx}
|
||||
|
||||
with stubs.fake_suds_context(calls):
|
||||
session = driver.VMwareAPISession()
|
||||
session = driver.VMwareAPISession(host_ip='localhost')
|
||||
|
||||
service_content = session.vim.get_service_content()
|
||||
service_content = session.vim.service_content
|
||||
props = session._call_method(vim_util, "get_dynamic_properties",
|
||||
service_content.propertyCollector,
|
||||
'fake_type', None)
|
||||
|
@ -19,13 +19,13 @@ import contextlib
|
||||
import re
|
||||
|
||||
import mock
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.network import model as network_model
|
||||
from nova.openstack.common import uuidutils
|
||||
from nova import test
|
||||
from nova.tests.virt.vmwareapi import fake
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
|
||||
|
||||
@ -815,7 +815,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
|
||||
return_value='fake-task'),
|
||||
mock.patch.object(
|
||||
session, "_wait_for_task",
|
||||
side_effect=error_util.InvalidPowerStateException),
|
||||
side_effect=vexc.InvalidPowerStateException),
|
||||
) as (fake_call_method, fake_wait_for_task):
|
||||
vm_util.power_on_instance(session, fake_instance,
|
||||
vm_ref='fake-vm-ref')
|
||||
@ -826,7 +826,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
|
||||
|
||||
def test_create_virtual_disk(self):
|
||||
session = fake.FakeSession()
|
||||
dm = session._get_vim().get_service_content().virtualDiskManager
|
||||
dm = session._get_vim().service_content.virtualDiskManager
|
||||
with contextlib.nested(
|
||||
mock.patch.object(vm_util, "get_vmdk_create_spec",
|
||||
return_value='fake-spec'),
|
||||
@ -852,7 +852,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
|
||||
|
||||
def test_copy_virtual_disk(self):
|
||||
session = fake.FakeSession()
|
||||
dm = session._get_vim().get_service_content().virtualDiskManager
|
||||
dm = session._get_vim().service_content.virtualDiskManager
|
||||
with contextlib.nested(
|
||||
mock.patch.object(session, "_call_method",
|
||||
return_value='fake-task'),
|
||||
@ -1052,7 +1052,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
|
||||
return_value='fake-task'),
|
||||
mock.patch.object(
|
||||
session, '_wait_for_task',
|
||||
side_effect=error_util.InvalidPowerStateException)
|
||||
side_effect=vexc.InvalidPowerStateException)
|
||||
) as (fake_call_method, fake_wait_for_task):
|
||||
vm_util.power_off_instance(session, fake_instance, 'fake-vm-ref')
|
||||
fake_call_method.assert_called_once_with(session._get_vim(),
|
||||
|
@ -16,6 +16,7 @@ import contextlib
|
||||
import copy
|
||||
|
||||
import mock
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova.compute import power_state
|
||||
from nova import context
|
||||
@ -33,7 +34,6 @@ from nova.tests.virt.vmwareapi import stubs
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import driver
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
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 import vmops
|
||||
@ -158,7 +158,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
|
||||
def test_create_folder_if_missing_exception(self):
|
||||
ds_name, ds_ref, ops, path, dc = self._setup_create_folder_mocks()
|
||||
ds_util.mkdir.side_effect = error_util.FileAlreadyExistsException()
|
||||
ds_util.mkdir.side_effect = vexc.FileAlreadyExistsException()
|
||||
ops._create_folder_if_missing(ds_name, ds_ref, 'folder')
|
||||
ds_util.mkdir.assert_called_with(ops._session, path, dc)
|
||||
|
||||
@ -716,7 +716,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
_fetch_image.assert_called_once_with(
|
||||
self._context,
|
||||
self._instance,
|
||||
self._session._host_ip,
|
||||
self._session._host,
|
||||
self._dc_info.name,
|
||||
self._ds.name,
|
||||
upload_file_name,
|
||||
@ -803,17 +803,11 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
|
||||
}
|
||||
self._test_spawn(block_device_info=block_device_info)
|
||||
|
||||
@mock.patch('nova.virt.vmwareapi.driver.VMwareAPISession._get_vim_object')
|
||||
def test_build_virtual_machine(self, mock_get_vim_object):
|
||||
mock_get_vim_object.return_value = vmwareapi_fake.FakeVim()
|
||||
|
||||
fake_session = driver.VMwareAPISession()
|
||||
fake_vmops = vmops.VMwareVCVMOps(fake_session, None, None)
|
||||
|
||||
def test_build_virtual_machine(self):
|
||||
image_id = nova.tests.image.fake.get_valid_image_id()
|
||||
image = vmware_images.VMwareImage(image_id=image_id)
|
||||
|
||||
vm_ref = fake_vmops.build_virtual_machine(self._instance,
|
||||
vm_ref = self._vmops.build_virtual_machine(self._instance,
|
||||
'fake-instance-name',
|
||||
image, self._dc_info,
|
||||
self._ds, self.network_info)
|
||||
|
@ -20,30 +20,24 @@ A connection to the VMware vCenter platform.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
from eventlet import event
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
from oslo.vmware import api
|
||||
from oslo.vmware import vim
|
||||
import suds
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _, _LC, _LW
|
||||
from nova.i18n import _, _LW
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import loopingcall
|
||||
from nova.openstack.common import uuidutils
|
||||
from nova.virt import driver
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import host
|
||||
from nova.virt.vmwareapi import vim
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
from nova.virt.vmwareapi import vmops
|
||||
from nova.virt.vmwareapi import volumeops
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
vmwareapi_opts = [
|
||||
@ -78,6 +72,11 @@ vmwareapi_opts = [
|
||||
cfg.BoolOpt('use_linked_clone',
|
||||
default=True,
|
||||
help='Whether to use linked clone'),
|
||||
cfg.StrOpt('wsdl_location',
|
||||
help='Optional VIM Service WSDL Location '
|
||||
'e.g http://<server>/vimService.wsdl. '
|
||||
'Optional over-ride to default location for bug '
|
||||
'work-arounds')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -181,7 +180,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||
# NOTE(hartsocks): we lean on the init_host to force the vim object
|
||||
# to not be None.
|
||||
vim = self._session.vim
|
||||
service_content = vim.get_service_content()
|
||||
service_content = vim.service_content
|
||||
session_manager = service_content.sessionManager
|
||||
try:
|
||||
vim.client.service.Logout(session_manager)
|
||||
@ -630,227 +629,47 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||
_vmops.detach_interface(instance, vif)
|
||||
|
||||
|
||||
class VMwareAPISession(object):
|
||||
"""Sets up a session with the VC host and handles all
|
||||
class VMwareAPISession(api.VMwareAPISession):
|
||||
"""Sets up a session with the VC/ESX host and handles all
|
||||
the calls made to the host.
|
||||
"""
|
||||
|
||||
def __init__(self, host_ip=CONF.vmware.host_ip,
|
||||
host_port=CONF.vmware.host_port,
|
||||
username=CONF.vmware.host_username,
|
||||
password=CONF.vmware.host_password,
|
||||
retry_count=CONF.vmware.api_retry_count,
|
||||
scheme="https"):
|
||||
self._host_ip = host_ip
|
||||
self._host_port = host_port
|
||||
self._host_username = username
|
||||
self._host_password = password
|
||||
self._api_retry_count = retry_count
|
||||
self._scheme = scheme
|
||||
self._session = 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,
|
||||
port=self._host_port)
|
||||
|
||||
def _create_session(self):
|
||||
"""Creates a session with the VC host."""
|
||||
|
||||
delay = 1
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Login and setup the session with the 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:
|
||||
try:
|
||||
self.vim.TerminateSession(
|
||||
self.vim.get_service_content().sessionManager,
|
||||
sessionId=[self._session.key])
|
||||
except Exception:
|
||||
# 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("TerminateSession failed", exc_info=True)
|
||||
self._session = session
|
||||
return
|
||||
except Exception:
|
||||
LOG.critical(_LC("Unable to connect to server at %(server)s, "
|
||||
"sleeping for %(seconds)s seconds"),
|
||||
{'server': self._host_ip, 'seconds': delay},
|
||||
exc_info=True)
|
||||
# exc_info logs the exception with the message
|
||||
time.sleep(delay)
|
||||
delay = min(2 * delay, 60)
|
||||
super(VMwareAPISession, self).__init__(
|
||||
host=host_ip,
|
||||
port=host_port,
|
||||
server_username=username,
|
||||
server_password=password,
|
||||
api_retry_count=retry_count,
|
||||
task_poll_interval=CONF.vmware.task_poll_interval,
|
||||
scheme=scheme,
|
||||
create_session=True,
|
||||
wsdl_loc=CONF.vmware.wsdl_location
|
||||
)
|
||||
|
||||
def _is_vim_object(self, module):
|
||||
"""Check if the module is a VIM Object instance."""
|
||||
return isinstance(module, vim.Vim)
|
||||
|
||||
def _session_is_active(self):
|
||||
active = False
|
||||
try:
|
||||
active = self.vim.SessionIsActive(
|
||||
self.vim.get_service_content().sessionManager,
|
||||
sessionID=self._session.key,
|
||||
userName=self._session.userName)
|
||||
except Exception:
|
||||
LOG.warning(_("Unable to validate session %s!"),
|
||||
self._session.key)
|
||||
LOG.debug("Unable to validate session %s", self._session.key,
|
||||
exc_info=True)
|
||||
return active
|
||||
|
||||
def _call_method(self, module, method, *args, **kwargs):
|
||||
"""Calls a method within the module specified with
|
||||
args provided.
|
||||
"""
|
||||
args = list(args)
|
||||
retry_count = 0
|
||||
while True:
|
||||
exc_info = False
|
||||
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 as 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_info = sys.exc_info()
|
||||
if error_util.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. We thus check if the session is
|
||||
# active
|
||||
if self._session_is_active():
|
||||
return []
|
||||
LOG.warning(_("Session %s is inactive!"),
|
||||
self._session.key)
|
||||
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.
|
||||
# Raise specific exceptions here if possible
|
||||
if excep.fault_list:
|
||||
fault = excep.fault_list[0]
|
||||
msg_excep = six.text_type(excep)
|
||||
raise error_util.get_fault_class(fault)(msg_excep)
|
||||
break
|
||||
except error_util.SessionOverLoadException:
|
||||
# For exceptions which may come because of session overload,
|
||||
# we retry
|
||||
exc_info = sys.exc_info()
|
||||
except error_util.SessionConnectionException:
|
||||
# For exceptions with connections we create the session
|
||||
exc_info = sys.exc_info()
|
||||
self._create_session()
|
||||
except Exception:
|
||||
# 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_info = sys.exc_info()
|
||||
break
|
||||
|
||||
LOG.debug("_call_method(session=%(key)s) failed. "
|
||||
"Module: %(module)s. "
|
||||
"Method: %(method)s. "
|
||||
"args: %(args)s. "
|
||||
"kwargs: %(kwargs)s. "
|
||||
"Iteration: %(n)s. ",
|
||||
{'key': self._session.key,
|
||||
'module': module,
|
||||
'method': method,
|
||||
'args': args,
|
||||
'kwargs': kwargs,
|
||||
'n': retry_count},
|
||||
exc_info=exc_info)
|
||||
|
||||
# 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(_LC("In vmwareapi: _call_method (session=%s)"),
|
||||
self._session.key, exc_info=True)
|
||||
raise
|
||||
if not self._is_vim_object(module):
|
||||
return self.invoke_api(module, method, self.vim, *args, **kwargs)
|
||||
else:
|
||||
return self.invoke_api(module, method, *args, **kwargs)
|
||||
|
||||
def _get_vim(self):
|
||||
"""Gets the VIM object reference."""
|
||||
if self.vim is None:
|
||||
self._create_session()
|
||||
"""Create the VIM Object instance."""
|
||||
return self.vim
|
||||
|
||||
def _stop_loop(self, loop):
|
||||
loop.stop()
|
||||
|
||||
def _wait_for_task(self, 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 = loopingcall.FixedIntervalLoopingCall(self._poll_task,
|
||||
task_ref, done)
|
||||
loop.start(CONF.vmware.task_poll_interval)
|
||||
try:
|
||||
ret_val = done.wait()
|
||||
finally:
|
||||
self._stop_loop(loop)
|
||||
return ret_val
|
||||
|
||||
def _poll_task(self, 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",
|
||||
{'task_name': task_name, 'task_ref': task_ref})
|
||||
done.send(task_info)
|
||||
else:
|
||||
error_info = six.text_type(task_info.error.localizedMessage)
|
||||
LOG.warn(_("Task [%(task_name)s] %(task_ref)s "
|
||||
"status: error %(error_info)s"),
|
||||
{'task_name': task_name, 'task_ref': task_ref,
|
||||
'error_info': error_info})
|
||||
# Check if we can raise a specific exception
|
||||
error = task_info.error
|
||||
name = error.fault.__class__.__name__
|
||||
task_ex = error_util.get_fault_class(name)(error_info)
|
||||
done.send_exception(task_ex)
|
||||
except Exception as excep:
|
||||
LOG.warn(_("In vmwareapi:_poll_task, Got this error %s") % excep)
|
||||
done.send_exception(excep)
|
||||
return self.wait_for_task(task_ref)
|
||||
|
@ -17,10 +17,11 @@ Datastore utility functions
|
||||
"""
|
||||
import posixpath
|
||||
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common 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
|
||||
|
||||
@ -338,7 +339,7 @@ def file_delete(session, ds_path, dc_ref):
|
||||
file_delete_task = session._call_method(
|
||||
session._get_vim(),
|
||||
"DeleteDatastoreFile_Task",
|
||||
vim.get_service_content().fileManager,
|
||||
vim.service_content.fileManager,
|
||||
name=str(ds_path),
|
||||
datacenter=dc_ref)
|
||||
session._wait_for_task(file_delete_task)
|
||||
@ -375,7 +376,7 @@ def file_move(session, dc_ref, src_file, dst_file):
|
||||
move_task = session._call_method(
|
||||
session._get_vim(),
|
||||
"MoveDatastoreFile_Task",
|
||||
vim.get_service_content().fileManager,
|
||||
vim.service_content.fileManager,
|
||||
sourceName=str(src_file),
|
||||
sourceDatacenter=dc_ref,
|
||||
destinationName=str(dst_file),
|
||||
@ -402,7 +403,7 @@ def file_exists(session, ds_browser, ds_path, file_name):
|
||||
searchSpec=search_spec)
|
||||
try:
|
||||
task_info = session._wait_for_task(search_task)
|
||||
except error_util.FileNotFoundException:
|
||||
except vexc.FileNotFoundException:
|
||||
return False
|
||||
|
||||
file_exists = (getattr(task_info.result, 'file', False) and
|
||||
@ -417,7 +418,7 @@ def mkdir(session, ds_path, dc_ref):
|
||||
"""
|
||||
LOG.debug("Creating directory with path %s", ds_path)
|
||||
session._call_method(session._get_vim(), "MakeDirectory",
|
||||
session._get_vim().get_service_content().fileManager,
|
||||
session._get_vim().service_content.fileManager,
|
||||
name=str(ds_path), datacenter=dc_ref,
|
||||
createParentDirectories=True)
|
||||
LOG.debug("Created directory with path %s", ds_path)
|
||||
@ -435,7 +436,7 @@ def get_sub_folders(session, ds_browser, ds_path):
|
||||
datastorePath=str(ds_path))
|
||||
try:
|
||||
task_info = session._wait_for_task(search_task)
|
||||
except error_util.FileNotFoundException:
|
||||
except vexc.FileNotFoundException:
|
||||
return set()
|
||||
# populate the folder entries
|
||||
if hasattr(task_info.result, 'file'):
|
||||
|
@ -14,132 +14,19 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Exception classes and SOAP response error checking module.
|
||||
Exception classes specific for the VMware driver.
|
||||
"""
|
||||
import six
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.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_POWER_STATE = 'InvalidPowerState'
|
||||
INVALID_PROPERTY = 'InvalidProperty'
|
||||
NO_PERMISSION = 'NoPermission'
|
||||
NOT_AUTHENTICATED = 'NotAuthenticated'
|
||||
TASK_IN_PROGRESS = 'TaskInProgress'
|
||||
# Most VMware-specific exception classes are now centrally defined in
|
||||
# oslo.vmware.
|
||||
# Note(vui):
|
||||
# - map back to NovaException?
|
||||
|
||||
|
||||
class VimException(Exception):
|
||||
"""The VIM Exception class."""
|
||||
|
||||
def __init__(self, exception_summary, excep):
|
||||
Exception.__init__(self)
|
||||
if isinstance(exception_summary, list):
|
||||
# we need this to protect against developers using
|
||||
# this method like VimFaultException
|
||||
raise ValueError(_("exception_summary must not be a list"))
|
||||
|
||||
self.exception_summary = six.text_type(exception_summary)
|
||||
self.exception_obj = excep
|
||||
|
||||
def __str__(self):
|
||||
return (self.exception_summary + ": " +
|
||||
six.text_type(self.exception_obj))
|
||||
|
||||
|
||||
class SessionOverLoadException(VimException):
|
||||
"""Session Overload Exception."""
|
||||
pass
|
||||
|
||||
|
||||
class SessionConnectionException(VimException):
|
||||
"""Session Connection Exception."""
|
||||
pass
|
||||
|
||||
|
||||
class VimAttributeError(VimException):
|
||||
"""VI Attribute Error."""
|
||||
pass
|
||||
|
||||
|
||||
class VimFaultException(Exception):
|
||||
"""The VIM Fault exception class."""
|
||||
|
||||
def __init__(self, fault_list, fault_string, details=None):
|
||||
Exception.__init__(self)
|
||||
if not isinstance(fault_list, list):
|
||||
raise ValueError(_("fault_list must be a list"))
|
||||
self.fault_list = fault_list
|
||||
self.fault_string = fault_string
|
||||
self.details = details
|
||||
|
||||
def __str__(self):
|
||||
if self.details:
|
||||
return '%s %s' % (self.fault_string, self.details)
|
||||
return self.fault_string
|
||||
|
||||
|
||||
class FaultCheckers(object):
|
||||
"""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 messages as properties and not as SOAP faults.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def retrievepropertiesex_fault_checker(resp_obj):
|
||||
"""Checks the RetrievePropertiesEx response for errors. Certain faults
|
||||
are sent as part of the SOAP body as property of missingSet.
|
||||
For example NotAuthenticated fault.
|
||||
"""
|
||||
fault_list = []
|
||||
details = {}
|
||||
if not resp_obj:
|
||||
# This is the case when the session has timed out. ESX SOAP server
|
||||
# sends an empty RetrievePropertiesResponse. Normally missingSet in
|
||||
# the returnval field has the specifics about the error, but that's
|
||||
# not the case with a timed out idle session. It is as bad as a
|
||||
# terminated session for we cannot use the session. So setting
|
||||
# fault to NotAuthenticated fault.
|
||||
fault_list = [NOT_AUTHENTICATED]
|
||||
else:
|
||||
for obj_cont in resp_obj.objects:
|
||||
if hasattr(obj_cont, "missingSet"):
|
||||
for missing_elem in obj_cont.missingSet:
|
||||
fault_type = missing_elem.fault.fault
|
||||
# Fault needs to be added to the type of fault for
|
||||
# uniformity in error checking as SOAP faults define
|
||||
fault_list.append(fault_type.__class__.__name__)
|
||||
if fault_type.__class__.__name__ == NO_PERMISSION:
|
||||
details['object'] = fault_type.object.value
|
||||
details['privilegeId'] = fault_type.privilegeId
|
||||
if fault_list:
|
||||
exc_msg_list = ', '.join(fault_list)
|
||||
fault_string = _("Error(s) %s occurred in the call to "
|
||||
"RetrievePropertiesEx") % exc_msg_list
|
||||
raise VimFaultException(fault_list, fault_string, details)
|
||||
|
||||
|
||||
class VMwareDriverException(exception.NovaException):
|
||||
"""Base class for all exceptions raised by the VMware Driver.
|
||||
|
||||
All exceptions raised by the VMwareAPI drivers should raise
|
||||
an exception descended from this class as a root. This will
|
||||
allow the driver to potentially trap problems related to its
|
||||
own internal configuration before halting the nova-compute
|
||||
node.
|
||||
"""
|
||||
msg_fmt = _("VMware Driver fault.")
|
||||
|
||||
|
||||
class VMwareDriverConfigurationException(VMwareDriverException):
|
||||
class VMwareDriverConfigurationException(vexc.VMwareDriverException):
|
||||
"""Base class for all configuration exceptions.
|
||||
"""
|
||||
msg_fmt = _("VMware Driver configuration fault.")
|
||||
@ -149,90 +36,9 @@ class UseLinkedCloneConfigurationFault(VMwareDriverConfigurationException):
|
||||
msg_fmt = _("No default value for use_linked_clone found.")
|
||||
|
||||
|
||||
class MissingParameter(VMwareDriverException):
|
||||
msg_fmt = _("Missing parameter : %(param)s")
|
||||
|
||||
|
||||
class NoRootDiskDefined(VMwareDriverException):
|
||||
class NoRootDiskDefined(vexc.VMwareDriverException):
|
||||
msg_fmt = _("No root disk defined.")
|
||||
|
||||
|
||||
class AlreadyExistsException(VMwareDriverException):
|
||||
msg_fmt = _("Resource already exists.")
|
||||
code = 409
|
||||
|
||||
|
||||
class CannotDeleteFileException(VMwareDriverException):
|
||||
msg_fmt = _("Cannot delete file.")
|
||||
code = 403
|
||||
|
||||
|
||||
class FileAlreadyExistsException(VMwareDriverException):
|
||||
msg_fmt = _("File already exists.")
|
||||
code = 409
|
||||
|
||||
|
||||
class FileFaultException(VMwareDriverException):
|
||||
msg_fmt = _("File fault.")
|
||||
code = 409
|
||||
|
||||
|
||||
class FileLockedException(VMwareDriverException):
|
||||
msg_fmt = _("File locked.")
|
||||
code = 403
|
||||
|
||||
|
||||
class FileNotFoundException(VMwareDriverException):
|
||||
msg_fmt = _("File not found.")
|
||||
code = 404
|
||||
|
||||
|
||||
class InvalidPropertyException(VMwareDriverException):
|
||||
msg_fmt = _("Invalid property.")
|
||||
code = 400
|
||||
|
||||
|
||||
class NoPermissionException(VMwareDriverException):
|
||||
msg_fmt = _("No Permission.")
|
||||
code = 403
|
||||
|
||||
|
||||
class NotAuthenticatedException(VMwareDriverException):
|
||||
msg_fmt = _("Not Authenticated.")
|
||||
code = 403
|
||||
|
||||
|
||||
class InvalidPowerStateException(VMwareDriverException):
|
||||
msg_fmt = _("Invalid Power State.")
|
||||
code = 409
|
||||
|
||||
|
||||
class TaskInProgress(VMwareDriverException):
|
||||
class TaskInProgress(vexc.VMwareDriverException):
|
||||
msg_fmt = _("Virtual machine is busy.")
|
||||
|
||||
|
||||
# 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_POWER_STATE: InvalidPowerStateException,
|
||||
INVALID_PROPERTY: InvalidPropertyException,
|
||||
NO_PERMISSION: NoPermissionException,
|
||||
NOT_AUTHENTICATED: NotAuthenticatedException,
|
||||
TASK_IN_PROGRESS: TaskInProgress,
|
||||
}
|
||||
|
||||
|
||||
def get_fault_class(name):
|
||||
"""Get a named subclass of VMwareDriverException."""
|
||||
name = str(name)
|
||||
fault_class = _fault_classes_registry.get(name)
|
||||
if not fault_class:
|
||||
LOG.warning(_('Fault %s not matched.'), name)
|
||||
fault_class = VMwareDriverException
|
||||
return fault_class
|
||||
|
@ -36,6 +36,7 @@ This will ensure that a image is not deleted during the spawn.
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common import lockutils
|
||||
@ -43,7 +44,6 @@ from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
from nova.virt import imagecache
|
||||
from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -66,14 +66,14 @@ class ImageCacheManager(imagecache.ImageCacheManager):
|
||||
def _folder_delete(self, ds_path, dc_ref):
|
||||
try:
|
||||
ds_util.file_delete(self._session, ds_path, dc_ref)
|
||||
except (error_util.CannotDeleteFileException,
|
||||
error_util.FileFaultException,
|
||||
error_util.FileLockedException) as e:
|
||||
except (vexc.CannotDeleteFileException,
|
||||
vexc.FileFaultException,
|
||||
vexc.FileLockedException) as e:
|
||||
# There may be more than one process or thread that tries
|
||||
# to delete the file.
|
||||
LOG.warning(_("Unable to delete %(file)s. Exception: %(ex)s"),
|
||||
{'file': ds_path, 'ex': e})
|
||||
except error_util.FileNotFoundException:
|
||||
except vexc.FileNotFoundException:
|
||||
LOG.debug("File not found: %s", ds_path)
|
||||
|
||||
def timestamp_folder_get(self, ds_path, image_id):
|
||||
@ -142,7 +142,7 @@ class ImageCacheManager(imagecache.ImageCacheManager):
|
||||
ts_path = path.join(self._get_timestamp_filename())
|
||||
try:
|
||||
ds_util.mkdir(self._session, ts_path, dc_info.ref)
|
||||
except error_util.FileAlreadyExistsException:
|
||||
except vexc.FileAlreadyExistsException:
|
||||
LOG.debug("Timestamp already exists.")
|
||||
LOG.info(_("Image %s is no longer used by this node. "
|
||||
"Pending deletion!"), image)
|
||||
|
@ -17,11 +17,11 @@
|
||||
"""
|
||||
Utility functions for ESX Networking.
|
||||
"""
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common 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
|
||||
|
||||
@ -162,7 +162,7 @@ def create_port_group(session, pg_name, vswitch_name, vlan_id=0, cluster=None):
|
||||
session._call_method(session._get_vim(),
|
||||
"AddPortGroup", network_system_mor,
|
||||
portgrp=add_prt_grp_spec)
|
||||
except error_util.AlreadyExistsException:
|
||||
except vexc.AlreadyExistsException:
|
||||
# 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
|
||||
|
@ -16,11 +16,11 @@
|
||||
"""VIF drivers for VMware."""
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import network_util
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
@ -126,7 +126,7 @@ def get_neutron_network(session, network_name, cluster, vif):
|
||||
opaque = session._call_method(vim_util, "get_dynamic_property", host,
|
||||
"HostSystem",
|
||||
"config.network.opaqueNetwork")
|
||||
except error_util.InvalidPropertyException:
|
||||
except vexc.InvalidPropertyException:
|
||||
opaque = None
|
||||
if opaque:
|
||||
bridge = vif['network']['id']
|
||||
|
@ -1,269 +0,0 @@
|
||||
# Copyright (c) 2012 VMware, Inc.
|
||||
# Copyright (c) 2011 Citrix Systems, Inc.
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Classes for making VMware VI SOAP calls.
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
import decorator
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
import suds
|
||||
|
||||
from nova.i18n import _
|
||||
from nova import utils
|
||||
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'
|
||||
ADDRESS_IN_USE_ERROR = 'Address already in use'
|
||||
|
||||
vmwareapi_wsdl_loc_opt = cfg.StrOpt('wsdl_location',
|
||||
help='Optional VIM Service WSDL Location '
|
||||
'e.g http://<server>/vimService.wsdl. '
|
||||
'Optional over-ride to default location for bug work-arounds')
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opt(vmwareapi_wsdl_loc_opt, 'vmware')
|
||||
|
||||
|
||||
@decorator.decorator
|
||||
def retry_if_task_in_progress(f, *args, **kwargs):
|
||||
retries = max(CONF.vmware.api_retry_count, 1)
|
||||
delay = 1
|
||||
for attempt in range(1, retries + 1):
|
||||
if attempt != 1:
|
||||
time.sleep(delay)
|
||||
delay = min(2 * delay, 60)
|
||||
try:
|
||||
f(*args, **kwargs)
|
||||
return
|
||||
except error_util.TaskInProgress:
|
||||
pass
|
||||
|
||||
|
||||
def get_moref(value, type):
|
||||
"""Get managed object reference."""
|
||||
moref = suds.sudsobject.Property(value)
|
||||
moref._type = type
|
||||
return moref
|
||||
|
||||
|
||||
def object_to_dict(obj, list_depth=1):
|
||||
"""Convert Suds object into serializable format.
|
||||
|
||||
The calling function can limit the amount of list entries that
|
||||
are converted.
|
||||
"""
|
||||
d = {}
|
||||
for k, v in suds.sudsobject.asdict(obj).iteritems():
|
||||
if hasattr(v, '__keylist__'):
|
||||
d[k] = object_to_dict(v, list_depth=list_depth)
|
||||
elif isinstance(v, list):
|
||||
d[k] = []
|
||||
used = 0
|
||||
for item in v:
|
||||
used = used + 1
|
||||
if used > list_depth:
|
||||
break
|
||||
if hasattr(item, '__keylist__'):
|
||||
d[k].append(object_to_dict(item, list_depth=list_depth))
|
||||
else:
|
||||
d[k].append(item)
|
||||
else:
|
||||
d[k] = v
|
||||
return d
|
||||
|
||||
|
||||
class VIMMessagePlugin(suds.plugin.MessagePlugin):
|
||||
def addAttributeForValue(self, node):
|
||||
# suds does not handle AnyType properly.
|
||||
# VI SDK requires type attribute to be set when AnyType is used
|
||||
if node.name == 'value':
|
||||
node.set('xsi:type', 'xsd:string')
|
||||
|
||||
def marshalled(self, context):
|
||||
"""suds will send the specified soap envelope.
|
||||
Provides the plugin with the opportunity to prune empty
|
||||
nodes and fixup nodes before sending it to the server.
|
||||
"""
|
||||
# suds builds the entire request object based on the wsdl schema.
|
||||
# VI SDK throws server errors if optional SOAP nodes are sent
|
||||
# without values, e.g. <test/> as opposed to <test>test</test>
|
||||
context.envelope.prune()
|
||||
context.envelope.walk(self.addAttributeForValue)
|
||||
|
||||
|
||||
class Vim:
|
||||
"""The VIM Object."""
|
||||
|
||||
def __init__(self,
|
||||
protocol="https",
|
||||
host="localhost",
|
||||
port=443):
|
||||
"""Creates the necessary Communication interfaces and gets the
|
||||
ServiceContent for initiating SOAP transactions.
|
||||
|
||||
protocol: http or https
|
||||
host : ESX IPAddress or Hostname
|
||||
port : port for connection
|
||||
"""
|
||||
if not suds:
|
||||
raise Exception(_("Unable to import suds."))
|
||||
|
||||
self._protocol = protocol
|
||||
self._host_name = host
|
||||
self.wsdl_url = Vim.get_wsdl_url(protocol, host, port)
|
||||
self.url = Vim.get_soap_url(protocol, host, port)
|
||||
self.client = suds.client.Client(self.wsdl_url, location=self.url,
|
||||
plugins=[VIMMessagePlugin()])
|
||||
self._service_content = self.retrieve_service_content()
|
||||
|
||||
def retrieve_service_content(self):
|
||||
return self.RetrieveServiceContent("ServiceInstance")
|
||||
|
||||
@staticmethod
|
||||
def get_wsdl_url(protocol, host_name, port):
|
||||
"""Allows override of the wsdl location, making this static
|
||||
means we can test the logic outside of the constructor
|
||||
without forcing the test environment to have multiple valid
|
||||
wsdl locations to test against.
|
||||
|
||||
:param protocol: https or http
|
||||
:param host_name: localhost or other server name
|
||||
:param port: port for connection
|
||||
:return: string to WSDL location for vSphere WS Management API
|
||||
"""
|
||||
# optional WSDL location over-ride for work-arounds
|
||||
if CONF.vmware.wsdl_location:
|
||||
return CONF.vmware.wsdl_location
|
||||
|
||||
# calculate default WSDL location if no override supplied
|
||||
return Vim.get_soap_url(protocol, host_name, port) + "/vimService.wsdl"
|
||||
|
||||
@staticmethod
|
||||
def get_soap_url(protocol, host_name, port):
|
||||
"""Calculates the location of the SOAP services
|
||||
for a particular server. Created as a static
|
||||
method for testing.
|
||||
|
||||
:param protocol: https or http
|
||||
:param host_name: localhost or other vSphere server name
|
||||
:param port: port for connection
|
||||
:return: the url to the active vSphere WS Management API
|
||||
"""
|
||||
if utils.is_valid_ipv6(host_name):
|
||||
return '%s://[%s]:%d/sdk' % (protocol, host_name, port)
|
||||
return '%s://%s:%d/sdk' % (protocol, host_name, port)
|
||||
|
||||
def get_service_content(self):
|
||||
"""Gets the service content object."""
|
||||
return self._service_content
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
"""Makes the API calls and gets the result."""
|
||||
def vim_request_handler(managed_object, **kwargs):
|
||||
"""Builds the SOAP message and parses the response for fault
|
||||
checking and other errors.
|
||||
|
||||
managed_object : Managed Object Reference or Managed
|
||||
Object Name
|
||||
**kwargs : Keyword arguments of the call
|
||||
"""
|
||||
# Dynamic handler for VI SDK Calls
|
||||
try:
|
||||
request_mo = self._request_managed_object_builder(
|
||||
managed_object)
|
||||
request = getattr(self.client.service, attr_name)
|
||||
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:
|
||||
raise
|
||||
except suds.MethodNotFound:
|
||||
raise
|
||||
except suds.WebFault as excep:
|
||||
doc = excep.document
|
||||
fault_string = doc.childAtPath("/Envelope/Body/Fault/"
|
||||
"faultstring").getText()
|
||||
detail = doc.childAtPath("/Envelope/Body/Fault/detail")
|
||||
fault_list = []
|
||||
details = {}
|
||||
if detail:
|
||||
for fault in detail.getChildren():
|
||||
fault_list.append(fault.get("type"))
|
||||
for child in fault.getChildren():
|
||||
details[child.name] = child.getText()
|
||||
raise error_util.VimFaultException(fault_list, fault_string,
|
||||
details)
|
||||
except AttributeError as excep:
|
||||
raise error_util.VimAttributeError(_("No such SOAP method "
|
||||
"'%s' provided by VI SDK") % (attr_name), excep)
|
||||
except (httplib.CannotSendRequest,
|
||||
httplib.ResponseNotReady,
|
||||
httplib.CannotSendHeader) as excep:
|
||||
raise error_util.SessionOverLoadException(_("httplib "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
except (urllib2.URLError,
|
||||
urllib2.HTTPError) as excep:
|
||||
raise error_util.SessionConnectionException(_("urllib2 "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
except Exception as excep:
|
||||
# Socket errors which need special handling for they
|
||||
# might be caused by ESX API call overload
|
||||
msg_excep = six.text_type(excep)
|
||||
if (msg_excep.find(ADDRESS_IN_USE_ERROR) != -1 or
|
||||
msg_excep.find(CONN_ABORT_ERROR)) != -1:
|
||||
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 six.text_type(excep).find(RESP_NOT_XML_ERROR) != -1:
|
||||
raise error_util.SessionOverLoadException(_("Type "
|
||||
"error in %s: ") % (attr_name), excep)
|
||||
else:
|
||||
raise error_util.VimException(
|
||||
_("Exception in %s ") % (attr_name), excep)
|
||||
return vim_request_handler
|
||||
|
||||
def _request_managed_object_builder(self, managed_object):
|
||||
"""Builds the request managed object."""
|
||||
# Request Managed Object Builder
|
||||
if isinstance(managed_object, str):
|
||||
mo = suds.sudsobject.Property(managed_object)
|
||||
mo._type = managed_object
|
||||
else:
|
||||
mo = managed_object
|
||||
return mo
|
||||
|
||||
def __repr__(self):
|
||||
return "VIM Object"
|
||||
|
||||
def __str__(self):
|
||||
return "VIM Object"
|
@ -18,6 +18,8 @@ The VMware API utility module.
|
||||
"""
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.vmware import vim_util as vutil
|
||||
import suds
|
||||
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common import log as logging
|
||||
@ -37,108 +39,34 @@ CONF.register_opt(vmware_opts, 'vmware')
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def build_selection_spec(client_factory, name):
|
||||
"""Builds the selection spec."""
|
||||
sel_spec = client_factory.create('ns0:SelectionSpec')
|
||||
sel_spec.name = name
|
||||
return sel_spec
|
||||
def object_to_dict(obj, list_depth=1):
|
||||
"""Convert Suds object into serializable format.
|
||||
|
||||
|
||||
def build_traversal_spec(client_factory, name, spec_type, path, skip,
|
||||
select_set):
|
||||
"""Builds the traversal spec object."""
|
||||
traversal_spec = client_factory.create('ns0:TraversalSpec')
|
||||
traversal_spec.name = name
|
||||
traversal_spec.type = spec_type
|
||||
traversal_spec.path = path
|
||||
traversal_spec.skip = skip
|
||||
traversal_spec.selectSet = select_set
|
||||
return traversal_spec
|
||||
|
||||
|
||||
def build_recursive_traversal_spec(client_factory):
|
||||
"""Builds the Recursive Traversal Spec to traverse the object managed
|
||||
object hierarchy.
|
||||
The calling function can limit the amount of list entries that
|
||||
are converted.
|
||||
"""
|
||||
visit_folders_select_spec = build_selection_spec(client_factory,
|
||||
"visitFolders")
|
||||
# For getting to hostFolder from datacenter
|
||||
dc_to_hf = build_traversal_spec(client_factory, "dc_to_hf", "Datacenter",
|
||||
"hostFolder", False,
|
||||
[visit_folders_select_spec])
|
||||
# For getting to vmFolder from datacenter
|
||||
dc_to_vmf = build_traversal_spec(client_factory, "dc_to_vmf", "Datacenter",
|
||||
"vmFolder", False,
|
||||
[visit_folders_select_spec])
|
||||
# For getting Host System to virtual machine
|
||||
h_to_vm = build_traversal_spec(client_factory, "h_to_vm", "HostSystem",
|
||||
"vm", False,
|
||||
[visit_folders_select_spec])
|
||||
|
||||
# For getting to Host System from Compute Resource
|
||||
cr_to_h = build_traversal_spec(client_factory, "cr_to_h",
|
||||
"ComputeResource", "host", False, [])
|
||||
|
||||
# For getting to datastore from Compute Resource
|
||||
cr_to_ds = build_traversal_spec(client_factory, "cr_to_ds",
|
||||
"ComputeResource", "datastore", False, [])
|
||||
|
||||
rp_to_rp_select_spec = build_selection_spec(client_factory, "rp_to_rp")
|
||||
rp_to_vm_select_spec = build_selection_spec(client_factory, "rp_to_vm")
|
||||
# For getting to resource pool from Compute Resource
|
||||
cr_to_rp = build_traversal_spec(client_factory, "cr_to_rp",
|
||||
"ComputeResource", "resourcePool", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# For getting to child res pool from the parent res pool
|
||||
rp_to_rp = build_traversal_spec(client_factory, "rp_to_rp", "ResourcePool",
|
||||
"resourcePool", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# For getting to Virtual Machine from the Resource Pool
|
||||
rp_to_vm = build_traversal_spec(client_factory, "rp_to_vm", "ResourcePool",
|
||||
"vm", False,
|
||||
[rp_to_rp_select_spec, rp_to_vm_select_spec])
|
||||
|
||||
# Get the assorted traversal spec which takes care of the objects to
|
||||
# be searched for from the root folder
|
||||
traversal_spec = build_traversal_spec(client_factory, "visitFolders",
|
||||
"Folder", "childEntity", False,
|
||||
[visit_folders_select_spec, dc_to_hf,
|
||||
dc_to_vmf, cr_to_ds, cr_to_h, cr_to_rp,
|
||||
rp_to_rp, h_to_vm, rp_to_vm])
|
||||
return traversal_spec
|
||||
d = {}
|
||||
for k, v in suds.sudsobject.asdict(obj).iteritems():
|
||||
if hasattr(v, '__keylist__'):
|
||||
d[k] = object_to_dict(v, list_depth=list_depth)
|
||||
elif isinstance(v, list):
|
||||
d[k] = []
|
||||
used = 0
|
||||
for item in v:
|
||||
used = used + 1
|
||||
if used > list_depth:
|
||||
break
|
||||
if hasattr(item, '__keylist__'):
|
||||
d[k].append(object_to_dict(item, list_depth=list_depth))
|
||||
else:
|
||||
d[k].append(item)
|
||||
else:
|
||||
d[k] = v
|
||||
return d
|
||||
|
||||
|
||||
def build_property_spec(client_factory, type="VirtualMachine",
|
||||
properties_to_collect=None,
|
||||
all_properties=False):
|
||||
"""Builds the Property Spec."""
|
||||
if not properties_to_collect:
|
||||
properties_to_collect = ["name"]
|
||||
|
||||
property_spec = client_factory.create('ns0:PropertySpec')
|
||||
property_spec.all = all_properties
|
||||
property_spec.pathSet = properties_to_collect
|
||||
property_spec.type = type
|
||||
return property_spec
|
||||
|
||||
|
||||
def build_object_spec(client_factory, root_folder, traversal_specs):
|
||||
"""Builds the object Spec."""
|
||||
object_spec = client_factory.create('ns0:ObjectSpec')
|
||||
object_spec.obj = root_folder
|
||||
object_spec.skip = False
|
||||
object_spec.selectSet = traversal_specs
|
||||
return object_spec
|
||||
|
||||
|
||||
def build_property_filter_spec(client_factory, property_specs, object_specs):
|
||||
"""Builds the Property Filter Spec."""
|
||||
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
|
||||
property_filter_spec.propSet = property_specs
|
||||
property_filter_spec.objectSet = object_specs
|
||||
return property_filter_spec
|
||||
def get_moref(value, type):
|
||||
return vutil.get_moref(value, type)
|
||||
|
||||
|
||||
def get_object_properties(vim, collector, mobj, type, properties):
|
||||
@ -148,7 +76,7 @@ def get_object_properties(vim, collector, mobj, type, properties):
|
||||
return None
|
||||
usecoll = collector
|
||||
if usecoll is None:
|
||||
usecoll = vim.get_service_content().propertyCollector
|
||||
usecoll = vim.service_content.propertyCollector
|
||||
property_filter_spec = client_factory.create('ns0:PropertyFilterSpec')
|
||||
property_spec = client_factory.create('ns0:PropertySpec')
|
||||
property_spec.all = (properties is None or len(properties) == 0)
|
||||
@ -197,24 +125,8 @@ def get_dynamic_properties(vim, mobj, type, property_names):
|
||||
|
||||
def get_objects(vim, type, properties_to_collect=None, all=False):
|
||||
"""Gets the list of objects of the type specified."""
|
||||
if not properties_to_collect:
|
||||
properties_to_collect = ["name"]
|
||||
|
||||
client_factory = vim.client.factory
|
||||
object_spec = build_object_spec(client_factory,
|
||||
vim.get_service_content().rootFolder,
|
||||
[build_recursive_traversal_spec(client_factory)])
|
||||
property_spec = build_property_spec(client_factory, type=type,
|
||||
properties_to_collect=properties_to_collect,
|
||||
all_properties=all)
|
||||
property_filter_spec = build_property_filter_spec(client_factory,
|
||||
[property_spec],
|
||||
[object_spec])
|
||||
options = client_factory.create('ns0:RetrieveOptions')
|
||||
options.maxObjects = CONF.vmware.maximum_objects
|
||||
return vim.RetrievePropertiesEx(
|
||||
vim.get_service_content().propertyCollector,
|
||||
specSet=[property_filter_spec], options=options)
|
||||
return vutil.get_objects(vim, type, CONF.vmware.maximum_objects,
|
||||
properties_to_collect, all)
|
||||
|
||||
|
||||
def get_inner_objects(vim, base_obj, path, inner_type,
|
||||
@ -222,32 +134,34 @@ def get_inner_objects(vim, base_obj, path, inner_type,
|
||||
"""Gets the list of inner objects of the type specified."""
|
||||
client_factory = vim.client.factory
|
||||
base_type = base_obj._type
|
||||
traversal_spec = build_traversal_spec(client_factory, 'inner', base_type,
|
||||
path, False, [])
|
||||
object_spec = build_object_spec(client_factory, base_obj, [traversal_spec])
|
||||
property_spec = build_property_spec(client_factory, type=inner_type,
|
||||
traversal_spec = vutil.build_traversal_spec(client_factory, 'inner',
|
||||
base_type, path, False, [])
|
||||
object_spec = vutil.build_object_spec(client_factory,
|
||||
base_obj,
|
||||
[traversal_spec])
|
||||
property_spec = vutil.build_property_spec(client_factory, type_=inner_type,
|
||||
properties_to_collect=properties_to_collect,
|
||||
all_properties=all)
|
||||
property_filter_spec = build_property_filter_spec(client_factory,
|
||||
property_filter_spec = vutil.build_property_filter_spec(client_factory,
|
||||
[property_spec], [object_spec])
|
||||
options = client_factory.create('ns0:RetrieveOptions')
|
||||
options.maxObjects = CONF.vmware.maximum_objects
|
||||
return vim.RetrievePropertiesEx(
|
||||
vim.get_service_content().propertyCollector,
|
||||
vim.service_content.propertyCollector,
|
||||
specSet=[property_filter_spec], options=options)
|
||||
|
||||
|
||||
def cancel_retrieve(vim, token):
|
||||
"""Cancels the retrieve operation."""
|
||||
return vim.CancelRetrievePropertiesEx(
|
||||
vim.get_service_content().propertyCollector,
|
||||
vim.service_content.propertyCollector,
|
||||
token=token)
|
||||
|
||||
|
||||
def continue_to_get_objects(vim, token):
|
||||
"""Continues to get the list of objects of the type specified."""
|
||||
return vim.ContinueRetrievePropertiesEx(
|
||||
vim.get_service_content().propertyCollector,
|
||||
vim.service_content.propertyCollector,
|
||||
token=token)
|
||||
|
||||
|
||||
@ -294,10 +208,10 @@ def get_properties_for_a_collection_of_objects(vim, type,
|
||||
options = client_factory.create('ns0:RetrieveOptions')
|
||||
options.maxObjects = CONF.vmware.maximum_objects
|
||||
return vim.RetrievePropertiesEx(
|
||||
vim.get_service_content().propertyCollector,
|
||||
vim.service_content.propertyCollector,
|
||||
specSet=[prop_filter_spec], options=options)
|
||||
|
||||
|
||||
def get_about_info(vim):
|
||||
"""Get the About Info from the service content."""
|
||||
return vim.get_service_content().about
|
||||
return vim.service_content.about
|
||||
|
@ -22,6 +22,7 @@ import copy
|
||||
import functools
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
@ -30,7 +31,6 @@ from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import units
|
||||
from nova import utils
|
||||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -854,7 +854,7 @@ def _get_vm_ref_from_vm_uuid(session, instance_uuid):
|
||||
vm_refs = session._call_method(
|
||||
session._get_vim(),
|
||||
"FindAllByUuid",
|
||||
session._get_vim().get_service_content().searchIndex,
|
||||
session._get_vim().service_content.searchIndex,
|
||||
uuid=instance_uuid,
|
||||
vmSearch=True,
|
||||
instanceUuid=True)
|
||||
@ -1298,7 +1298,7 @@ def create_virtual_disk(session, dc_ref, adapter_type, disk_type,
|
||||
vmdk_create_task = session._call_method(
|
||||
session._get_vim(),
|
||||
"CreateVirtualDisk_Task",
|
||||
session._get_vim().get_service_content().virtualDiskManager,
|
||||
session._get_vim().service_content.virtualDiskManager,
|
||||
name=virtual_disk_path,
|
||||
datacenter=dc_ref,
|
||||
spec=vmdk_create_spec)
|
||||
@ -1328,7 +1328,7 @@ def copy_virtual_disk(session, dc_ref, source, dest, copy_spec=None):
|
||||
vmdk_copy_task = session._call_method(
|
||||
vim,
|
||||
"CopyVirtualDisk_Task",
|
||||
vim.get_service_content().virtualDiskManager,
|
||||
vim.service_content.virtualDiskManager,
|
||||
sourceName=source,
|
||||
sourceDatacenter=dc_ref,
|
||||
destName=dest,
|
||||
@ -1356,7 +1356,7 @@ def clone_vmref_for_instance(session, instance, vm_ref, host_ref, ds_ref,
|
||||
if vm_ref is None:
|
||||
LOG.warn(_("vmwareapi:vm_util:clone_vmref_for_instance, called "
|
||||
"with vm_ref=None"))
|
||||
raise error_util.MissingParameter(param="vm_ref")
|
||||
raise vexc.MissingParameter(param="vm_ref")
|
||||
# Get the clone vm spec
|
||||
client_factory = session._get_vim().client.factory
|
||||
rel_spec = relocate_vm_spec(client_factory, ds_ref, host_ref,
|
||||
@ -1449,7 +1449,7 @@ def power_on_instance(session, instance, vm_ref=None):
|
||||
"PowerOnVM_Task", vm_ref)
|
||||
session._wait_for_task(poweron_task)
|
||||
LOG.debug("Powered on the VM", instance=instance)
|
||||
except error_util.InvalidPowerStateException:
|
||||
except vexc.InvalidPowerStateException:
|
||||
LOG.debug("VM already powered on", instance=instance)
|
||||
|
||||
|
||||
@ -1528,5 +1528,5 @@ def power_off_instance(session, instance, vm_ref=None):
|
||||
"PowerOffVM_Task", vm_ref)
|
||||
session._wait_for_task(poweroff_task)
|
||||
LOG.debug("Powered off the VM", instance=instance)
|
||||
except error_util.InvalidPowerStateException:
|
||||
except vexc.InvalidPowerStateException:
|
||||
LOG.debug("VM already powered off", instance=instance)
|
||||
|
@ -22,8 +22,11 @@ Class for VM tasks like spawn, snapshot, suspend, resume etc.
|
||||
import collections
|
||||
import copy
|
||||
import os
|
||||
import time
|
||||
|
||||
import decorator
|
||||
from oslo.config import cfg
|
||||
from oslo.vmware import exceptions as vexc
|
||||
|
||||
from nova.api.metadata import base as instance_metadata
|
||||
from nova import compute
|
||||
@ -49,7 +52,6 @@ from nova.virt.vmwareapi import ds_util
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import imagecache
|
||||
from nova.virt.vmwareapi import vif as vmwarevif
|
||||
from nova.virt.vmwareapi import vim
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
from nova.virt.vmwareapi import vmware_images
|
||||
@ -111,6 +113,29 @@ class VirtualMachineInstanceConfigInfo(object):
|
||||
return self.cache_image_folder.join(cached_image_file_name)
|
||||
|
||||
|
||||
# Note(vui): See https://bugs.launchpad.net/nova/+bug/1363349
|
||||
# for cases where mocking time.sleep() can have unintended effects on code
|
||||
# not under test. For now, unblock the affected test cases by providing
|
||||
# a wrapper function to work around needing to mock time.sleep()
|
||||
def _time_sleep_wrapper(delay):
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
@decorator.decorator
|
||||
def retry_if_task_in_progress(f, *args, **kwargs):
|
||||
retries = max(CONF.vmware.api_retry_count, 1)
|
||||
delay = 1
|
||||
for attempt in range(1, retries + 1):
|
||||
if attempt != 1:
|
||||
_time_sleep_wrapper(delay)
|
||||
delay = min(2 * delay, 60)
|
||||
try:
|
||||
f(*args, **kwargs)
|
||||
return
|
||||
except error_util.TaskInProgress:
|
||||
pass
|
||||
|
||||
|
||||
class VMwareVMOps(object):
|
||||
"""Management class for VM-related tasks."""
|
||||
|
||||
@ -151,7 +176,7 @@ class VMwareVMOps(object):
|
||||
return lst_vm_names
|
||||
|
||||
def _extend_virtual_disk(self, instance, requested_size, name, dc_ref):
|
||||
service_content = self._session._get_vim().get_service_content()
|
||||
service_content = self._session._get_vim().service_content
|
||||
LOG.debug("Extending root virtual disk to %s", requested_size)
|
||||
vmdk_extend_task = self._session._call_method(
|
||||
self._session._get_vim(),
|
||||
@ -178,10 +203,10 @@ class VMwareVMOps(object):
|
||||
def _delete_datastore_file(self, instance, datastore_path, dc_ref):
|
||||
try:
|
||||
ds_util.file_delete(self._session, datastore_path, dc_ref)
|
||||
except (error_util.CannotDeleteFileException,
|
||||
error_util.FileFaultException,
|
||||
error_util.FileLockedException,
|
||||
error_util.FileNotFoundException):
|
||||
except (vexc.CannotDeleteFileException,
|
||||
vexc.FileFaultException,
|
||||
vexc.FileLockedException,
|
||||
vexc.FileNotFoundException):
|
||||
LOG.debug("Unable to delete %(ds)s. There may be more than "
|
||||
"one process or thread trying to delete the file",
|
||||
{'ds': datastore_path},
|
||||
@ -428,7 +453,7 @@ class VMwareVMOps(object):
|
||||
|
||||
vmware_images.fetch_image(context,
|
||||
instance,
|
||||
self._session._host_ip,
|
||||
self._session._host,
|
||||
dc_info.name,
|
||||
datastore.name,
|
||||
upload_rel_path,
|
||||
@ -454,7 +479,7 @@ class VMwareVMOps(object):
|
||||
try:
|
||||
ds_util.file_move(self._session, dc_info.ref,
|
||||
src_folder, dest_folder)
|
||||
except error_util.FileAlreadyExistsException:
|
||||
except vexc.FileAlreadyExistsException:
|
||||
# File move has failed. This may be due to the fact that a
|
||||
# process or thread has already completed the opertaion.
|
||||
# In the event of a FileAlreadyExists we continue,
|
||||
@ -564,7 +589,7 @@ class VMwareVMOps(object):
|
||||
ds_util.file_delete(self._session,
|
||||
root_vmdk_path,
|
||||
dc_info.ref)
|
||||
except error_util.FileNotFoundException:
|
||||
except vexc.FileNotFoundException:
|
||||
# File was never created: cleanup not
|
||||
# required
|
||||
pass
|
||||
@ -624,7 +649,7 @@ class VMwareVMOps(object):
|
||||
upload_folder)
|
||||
vmware_images.upload_iso_to_datastore(
|
||||
tmp_file, instance,
|
||||
host=self._session._host_ip,
|
||||
host=self._session._host,
|
||||
data_center_name=dc_name,
|
||||
datastore_name=data_store_name,
|
||||
cookies=cookies,
|
||||
@ -682,7 +707,7 @@ class VMwareVMOps(object):
|
||||
snapshot = task_info.result
|
||||
return snapshot
|
||||
|
||||
@vim.retry_if_task_in_progress
|
||||
@retry_if_task_in_progress
|
||||
def _delete_vm_snapshot(self, instance, vm_ref, snapshot):
|
||||
LOG.debug("Deleting Snapshot of the VM instance", instance=instance)
|
||||
delete_snapshot_task = self._session._call_method(
|
||||
@ -709,7 +734,7 @@ class VMwareVMOps(object):
|
||||
"""
|
||||
vm_ref = vm_util.get_vm_ref(self._session, instance)
|
||||
client_factory = self._session._get_vim().client.factory
|
||||
service_content = self._session._get_vim().get_service_content()
|
||||
service_content = self._session._get_vim().service_content
|
||||
|
||||
def _get_vm_and_vmdk_attribs():
|
||||
# Get the vmdk file name that the VM is pointing to
|
||||
@ -801,7 +826,7 @@ class VMwareVMOps(object):
|
||||
disk_type=constants.DEFAULT_DISK_TYPE,
|
||||
adapter_type=adapter_type,
|
||||
image_version=1,
|
||||
host=self._session._host_ip,
|
||||
host=self._session._host,
|
||||
data_center_name=dc_info.name,
|
||||
datastore_name=datastore_name,
|
||||
cookies=cookies,
|
||||
@ -1269,7 +1294,7 @@ class VMwareVMOps(object):
|
||||
data = {}
|
||||
# All of values received are objects. Convert them to dictionaries
|
||||
for value in query.values():
|
||||
prop_dict = vim.object_to_dict(value, list_depth=1)
|
||||
prop_dict = vim_util.object_to_dict(value, list_depth=1)
|
||||
data.update(prop_dict)
|
||||
return data
|
||||
|
||||
@ -1437,7 +1462,7 @@ class VMwareVMOps(object):
|
||||
try:
|
||||
ds_util.mkdir(self._session, path, dc_info.ref)
|
||||
LOG.debug("Folder %s created.", path)
|
||||
except error_util.FileAlreadyExistsException:
|
||||
except vexc.FileAlreadyExistsException:
|
||||
# NOTE(hartsocks): if the folder already exists, that
|
||||
# just means the folder was prepped by another process.
|
||||
pass
|
||||
|
@ -22,7 +22,6 @@ from oslo.config import cfg
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.virt.vmwareapi import vim
|
||||
from nova.virt.vmwareapi import vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
|
||||
@ -300,7 +299,7 @@ class VMwareVolumeOps(object):
|
||||
|
||||
def _get_volume_ref(self, volume_ref_name):
|
||||
"""Get the volume moref from the ref name."""
|
||||
return vim.get_moref(volume_ref_name, 'VirtualMachine')
|
||||
return vim_util.get_moref(volume_ref_name, 'VirtualMachine')
|
||||
|
||||
def _get_vmdk_base_volume_device(self, volume_ref):
|
||||
# Get the vmdk file name that the VM is pointing to
|
||||
|
@ -38,3 +38,4 @@ oslo.messaging>=1.4.0.0a3
|
||||
oslo.i18n>=0.2.0 # Apache-2.0
|
||||
lockfile>=0.8
|
||||
rfc3986>=0.2.0 # Apache-2.0
|
||||
oslo.vmware>=0.5.0
|
||||
|
Loading…
Reference in New Issue
Block a user