
The driver uses __del__ in which may cause spurious errors. python docs detail the caveats of using __del__. In order to enable the calling application to be able to close existing sessions, a new method has been added: logout. This will be used to terminate and logout of the existing session. Change-Id: I94482b79cc451d027cb853a6d0775e4da189cb7e
477 lines
19 KiB
Python
477 lines
19 KiB
Python
# Copyright (c) 2014 VMware, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""
|
|
Unit tests for session management and API invocation classes.
|
|
"""
|
|
|
|
from eventlet import greenthread
|
|
import mock
|
|
|
|
from oslo.vmware import api
|
|
from oslo.vmware import exceptions
|
|
from oslo.vmware import pbm
|
|
from oslo.vmware import vim_util
|
|
from tests import base
|
|
|
|
|
|
class RetryDecoratorTest(base.TestCase):
|
|
"""Tests for retry decorator class."""
|
|
|
|
def test_retry(self):
|
|
result = "RESULT"
|
|
|
|
@api.RetryDecorator()
|
|
def func(*args, **kwargs):
|
|
return result
|
|
|
|
self.assertEqual(result, func())
|
|
|
|
def func2(*args, **kwargs):
|
|
return result
|
|
|
|
retry = api.RetryDecorator()
|
|
self.assertEqual(result, retry(func2)())
|
|
self.assertTrue(retry._retry_count == 0)
|
|
|
|
def test_retry_with_expected_exceptions(self):
|
|
result = "RESULT"
|
|
responses = [exceptions.VimSessionOverLoadException(None),
|
|
exceptions.VimSessionOverLoadException(None),
|
|
result]
|
|
|
|
def func(*args, **kwargs):
|
|
response = responses.pop(0)
|
|
if isinstance(response, Exception):
|
|
raise response
|
|
return response
|
|
|
|
sleep_time_incr = 0.01
|
|
retry_count = 2
|
|
retry = api.RetryDecorator(10, sleep_time_incr, 10,
|
|
(exceptions.VimSessionOverLoadException,))
|
|
self.assertEqual(result, retry(func)())
|
|
self.assertTrue(retry._retry_count == retry_count)
|
|
self.assertEqual(retry_count * sleep_time_incr, retry._sleep_time)
|
|
|
|
def test_retry_with_max_retries(self):
|
|
responses = [exceptions.VimSessionOverLoadException(None),
|
|
exceptions.VimSessionOverLoadException(None),
|
|
exceptions.VimSessionOverLoadException(None)]
|
|
|
|
def func(*args, **kwargs):
|
|
response = responses.pop(0)
|
|
if isinstance(response, Exception):
|
|
raise response
|
|
return response
|
|
|
|
retry = api.RetryDecorator(2, 0, 0,
|
|
(exceptions.VimSessionOverLoadException,))
|
|
self.assertRaises(exceptions.VimSessionOverLoadException, retry(func))
|
|
self.assertTrue(retry._retry_count == 2)
|
|
|
|
def test_retry_with_unexpected_exception(self):
|
|
|
|
def func(*args, **kwargs):
|
|
raise exceptions.VimException(None)
|
|
|
|
retry = api.RetryDecorator()
|
|
self.assertRaises(exceptions.VimException, retry(func))
|
|
self.assertTrue(retry._retry_count == 0)
|
|
|
|
|
|
class VMwareAPISessionTest(base.TestCase):
|
|
"""Tests for VMwareAPISession."""
|
|
|
|
SERVER_IP = '10.1.2.3'
|
|
USERNAME = 'admin'
|
|
PASSWORD = 'password'
|
|
|
|
def setUp(self):
|
|
super(VMwareAPISessionTest, self).setUp()
|
|
patcher = mock.patch('oslo.vmware.vim.Vim')
|
|
self.addCleanup(patcher.stop)
|
|
self.VimMock = patcher.start()
|
|
self.VimMock.side_effect = lambda *args, **kw: mock.Mock()
|
|
|
|
def _create_api_session(self, _create_session, retry_count=10,
|
|
task_poll_interval=1):
|
|
return api.VMwareAPISession(VMwareAPISessionTest.SERVER_IP,
|
|
VMwareAPISessionTest.USERNAME,
|
|
VMwareAPISessionTest.PASSWORD,
|
|
retry_count,
|
|
task_poll_interval,
|
|
'https',
|
|
_create_session)
|
|
|
|
def test_vim(self):
|
|
api_session = self._create_api_session(False)
|
|
api_session.vim
|
|
self.VimMock.assert_called_with(protocol=api_session._scheme,
|
|
host=VMwareAPISessionTest.SERVER_IP,
|
|
wsdl_loc=api_session._vim_wsdl_loc)
|
|
|
|
@mock.patch.object(pbm, 'PBMClient')
|
|
def test_pbm(self, pbm_client_mock):
|
|
api_session = self._create_api_session(True)
|
|
api_session._pbm_wsdl_loc = mock.Mock()
|
|
pbm = mock.Mock()
|
|
pbm_client_mock.return_value = pbm
|
|
cookie = mock.Mock()
|
|
api_session._get_session_cookie = mock.Mock(return_value=cookie)
|
|
|
|
self.assertEqual(pbm, api_session.pbm)
|
|
pbm.set_cookie.assert_called_once_with(cookie)
|
|
|
|
def test_get_session_cookie(self):
|
|
api_session = self._create_api_session(False)
|
|
vim_obj = api_session.vim
|
|
|
|
cookie_value = 'xyz'
|
|
cookie = mock.Mock()
|
|
cookie.name = 'vmware_soap_session'
|
|
cookie.value = cookie_value
|
|
vim_obj.client.options.transport.cookiejar = [cookie]
|
|
|
|
self.assertEqual(cookie_value, api_session._get_session_cookie())
|
|
|
|
def test_get_session_cookie_with_no_cookie(self):
|
|
api_session = self._create_api_session(False)
|
|
vim_obj = api_session.vim
|
|
|
|
cookie = mock.Mock()
|
|
cookie.name = 'cookie'
|
|
cookie.value = 'xyz'
|
|
vim_obj.client.options.transport.cookiejar = [cookie]
|
|
|
|
self.assertIsNone(api_session._get_session_cookie())
|
|
|
|
def test_create_session(self):
|
|
session = mock.Mock()
|
|
session.key = "12345"
|
|
api_session = self._create_api_session(False)
|
|
vim_obj = api_session.vim
|
|
vim_obj.Login.return_value = session
|
|
|
|
pbm = mock.Mock()
|
|
api_session._pbm = pbm
|
|
cookie = mock.Mock()
|
|
api_session._get_session_cookie = mock.Mock(return_value=cookie)
|
|
|
|
api_session._create_session()
|
|
session_manager = vim_obj.service_content.sessionManager
|
|
vim_obj.Login.assert_called_once_with(
|
|
session_manager, userName=VMwareAPISessionTest.USERNAME,
|
|
password=VMwareAPISessionTest.PASSWORD)
|
|
self.assertFalse(vim_obj.TerminateSession.called)
|
|
self.assertEqual(session.key, api_session._session_id)
|
|
pbm.set_cookie.assert_called_once_with(cookie)
|
|
|
|
def test_create_session_with_existing_session(self):
|
|
old_session_key = '12345'
|
|
new_session_key = '67890'
|
|
session = mock.Mock()
|
|
session.key = new_session_key
|
|
api_session = self._create_api_session(False)
|
|
api_session._session_id = old_session_key
|
|
vim_obj = api_session.vim
|
|
vim_obj.Login.return_value = session
|
|
|
|
api_session._create_session()
|
|
session_manager = vim_obj.service_content.sessionManager
|
|
vim_obj.Login.assert_called_once_with(
|
|
session_manager, userName=VMwareAPISessionTest.USERNAME,
|
|
password=VMwareAPISessionTest.PASSWORD)
|
|
vim_obj.TerminateSession.assert_called_once_with(
|
|
session_manager, sessionId=[old_session_key])
|
|
self.assertEqual(new_session_key, api_session._session_id)
|
|
|
|
def test_invoke_api(self):
|
|
api_session = self._create_api_session(True)
|
|
response = mock.Mock()
|
|
|
|
def api(*args, **kwargs):
|
|
return response
|
|
|
|
module = mock.Mock()
|
|
module.api = api
|
|
ret = api_session.invoke_api(module, 'api')
|
|
self.assertEqual(response, ret)
|
|
|
|
def test_logout_with_exception(self):
|
|
session = mock.Mock()
|
|
session.key = "12345"
|
|
api_session = self._create_api_session(False)
|
|
vim_obj = api_session.vim
|
|
vim_obj.Login.return_value = session
|
|
vim_obj.Logout.side_effect = exceptions.VimFaultException([], None)
|
|
api_session._create_session()
|
|
api_session.logout()
|
|
self.assertEqual("12345", api_session._session_id)
|
|
|
|
def test_logout_no_session(self):
|
|
api_session = self._create_api_session(False)
|
|
vim_obj = api_session.vim
|
|
api_session.logout()
|
|
self.assertEqual(0, vim_obj.Logout.call_count)
|
|
|
|
def test_logout_calls_vim_logout(self):
|
|
session = mock.Mock()
|
|
session.key = "12345"
|
|
api_session = self._create_api_session(False)
|
|
vim_obj = api_session.vim
|
|
vim_obj.Login.return_value = session
|
|
vim_obj.Logout.return_value = None
|
|
|
|
api_session._create_session()
|
|
session_manager = vim_obj.service_content.sessionManager
|
|
vim_obj.Login.assert_called_once_with(
|
|
session_manager, userName=VMwareAPISessionTest.USERNAME,
|
|
password=VMwareAPISessionTest.PASSWORD)
|
|
api_session.logout()
|
|
vim_obj.Logout.assert_called_once_with(
|
|
session_manager)
|
|
self.assertIsNone(api_session._session_id)
|
|
|
|
def test_invoke_api_with_expected_exception(self):
|
|
api_session = self._create_api_session(True)
|
|
ret = mock.Mock()
|
|
responses = [exceptions.VimConnectionException(None), ret]
|
|
|
|
def api(*args, **kwargs):
|
|
response = responses.pop(0)
|
|
if isinstance(response, Exception):
|
|
raise response
|
|
return response
|
|
|
|
module = mock.Mock()
|
|
module.api = api
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
self.assertEqual(ret, api_session.invoke_api(module, 'api'))
|
|
|
|
def test_invoke_api_with_vim_fault_exception(self):
|
|
api_session = self._create_api_session(True)
|
|
|
|
def api(*args, **kwargs):
|
|
raise exceptions.VimFaultException([], None)
|
|
|
|
module = mock.Mock()
|
|
module.api = api
|
|
self.assertRaises(exceptions.VimFaultException,
|
|
lambda: api_session.invoke_api(module, 'api'))
|
|
|
|
def test_invoke_api_with_empty_response(self):
|
|
api_session = self._create_api_session(True)
|
|
vim_obj = api_session.vim
|
|
vim_obj.SessionIsActive.return_value = True
|
|
|
|
def api(*args, **kwargs):
|
|
raise exceptions.VimFaultException(
|
|
[exceptions.NOT_AUTHENTICATED], None)
|
|
|
|
module = mock.Mock()
|
|
module.api = api
|
|
ret = api_session.invoke_api(module, 'api')
|
|
self.assertEqual([], ret)
|
|
vim_obj.SessionIsActive.assert_called_once_with(
|
|
vim_obj.service_content.sessionManager,
|
|
sessionID=api_session._session_id,
|
|
userName=api_session._session_username)
|
|
|
|
def test_invoke_api_with_stale_session(self):
|
|
api_session = self._create_api_session(True)
|
|
api_session._create_session = mock.Mock()
|
|
vim_obj = api_session.vim
|
|
vim_obj.SessionIsActive.return_value = False
|
|
result = mock.Mock()
|
|
responses = [exceptions.VimFaultException(
|
|
[exceptions.NOT_AUTHENTICATED], None), result]
|
|
|
|
def api(*args, **kwargs):
|
|
response = responses.pop(0)
|
|
if isinstance(response, Exception):
|
|
raise response
|
|
return response
|
|
|
|
module = mock.Mock()
|
|
module.api = api
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
ret = api_session.invoke_api(module, 'api')
|
|
self.assertEqual(result, ret)
|
|
vim_obj.SessionIsActive.assert_called_once_with(
|
|
vim_obj.service_content.sessionManager,
|
|
sessionID=api_session._session_id,
|
|
userName=api_session._session_username)
|
|
api_session._create_session.assert_called_once_with()
|
|
|
|
def test_wait_for_task(self):
|
|
api_session = self._create_api_session(True)
|
|
task_info_list = [('queued', 0), ('running', 40), ('success', 100)]
|
|
task_info_list_size = len(task_info_list)
|
|
|
|
def invoke_api_side_effect(module, method, *args, **kwargs):
|
|
(state, progress) = task_info_list.pop(0)
|
|
task_info = mock.Mock()
|
|
task_info.progress = progress
|
|
task_info.state = state
|
|
return task_info
|
|
|
|
api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect)
|
|
task = mock.Mock()
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
ret = api_session.wait_for_task(task)
|
|
self.assertEqual('success', ret.state)
|
|
self.assertEqual(100, ret.progress)
|
|
api_session.invoke_api.assert_called_with(vim_util,
|
|
'get_object_property',
|
|
api_session.vim, task,
|
|
'info')
|
|
self.assertEqual(task_info_list_size,
|
|
api_session.invoke_api.call_count)
|
|
|
|
def test_wait_for_task_with_error_state(self):
|
|
api_session = self._create_api_session(True)
|
|
task_info_list = [('queued', 0), ('running', 40), ('error', -1)]
|
|
task_info_list_size = len(task_info_list)
|
|
|
|
def invoke_api_side_effect(module, method, *args, **kwargs):
|
|
(state, progress) = task_info_list.pop(0)
|
|
task_info = mock.Mock()
|
|
task_info.progress = progress
|
|
task_info.state = state
|
|
return task_info
|
|
|
|
api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect)
|
|
task = mock.Mock()
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
self.assertRaises(exceptions.VMwareDriverException,
|
|
lambda: api_session.wait_for_task(task))
|
|
api_session.invoke_api.assert_called_with(vim_util,
|
|
'get_object_property',
|
|
api_session.vim, task,
|
|
'info')
|
|
self.assertEqual(task_info_list_size,
|
|
api_session.invoke_api.call_count)
|
|
|
|
def test_wait_for_task_with_invoke_api_exception(self):
|
|
api_session = self._create_api_session(True)
|
|
api_session.invoke_api = mock.Mock(
|
|
side_effect=exceptions.VimException(None))
|
|
task = mock.Mock()
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
self.assertRaises(exceptions.VimException,
|
|
lambda: api_session.wait_for_task(task))
|
|
api_session.invoke_api.assert_called_once_with(vim_util,
|
|
'get_object_property',
|
|
api_session.vim, task,
|
|
'info')
|
|
|
|
def test_wait_for_lease_ready(self):
|
|
api_session = self._create_api_session(True)
|
|
lease_states = ['initializing', 'ready']
|
|
num_states = len(lease_states)
|
|
|
|
def invoke_api_side_effect(module, method, *args, **kwargs):
|
|
return lease_states.pop(0)
|
|
|
|
api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect)
|
|
lease = mock.Mock()
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
api_session.wait_for_lease_ready(lease)
|
|
api_session.invoke_api.assert_called_with(vim_util,
|
|
'get_object_property',
|
|
api_session.vim, lease,
|
|
'state')
|
|
self.assertEqual(num_states, api_session.invoke_api.call_count)
|
|
|
|
def test_wait_for_lease_ready_with_error_state(self):
|
|
api_session = self._create_api_session(True)
|
|
responses = ['initializing', 'error', 'error_msg']
|
|
|
|
def invoke_api_side_effect(module, method, *args, **kwargs):
|
|
return responses.pop(0)
|
|
|
|
api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect)
|
|
lease = mock.Mock()
|
|
with mock.patch.object(greenthread, 'sleep'):
|
|
self.assertRaises(exceptions.VimException,
|
|
lambda: api_session.wait_for_lease_ready(lease))
|
|
exp_calls = [mock.call(vim_util, 'get_object_property',
|
|
api_session.vim, lease, 'state')] * 2
|
|
exp_calls.append(mock.call(vim_util, 'get_object_property',
|
|
api_session.vim, lease, 'error'))
|
|
self.assertEqual(exp_calls, api_session.invoke_api.call_args_list)
|
|
|
|
def test_wait_for_lease_ready_with_unknown_state(self):
|
|
api_session = self._create_api_session(True)
|
|
|
|
def invoke_api_side_effect(module, method, *args, **kwargs):
|
|
return 'unknown'
|
|
|
|
api_session.invoke_api = mock.Mock(side_effect=invoke_api_side_effect)
|
|
lease = mock.Mock()
|
|
self.assertRaises(exceptions.VimException,
|
|
lambda: api_session.wait_for_lease_ready(lease))
|
|
api_session.invoke_api.assert_called_once_with(vim_util,
|
|
'get_object_property',
|
|
api_session.vim,
|
|
lease, 'state')
|
|
|
|
def test_wait_for_lease_ready_with_invoke_api_exception(self):
|
|
api_session = self._create_api_session(True)
|
|
api_session.invoke_api = mock.Mock(
|
|
side_effect=exceptions.VimException(None))
|
|
lease = mock.Mock()
|
|
self.assertRaises(exceptions.VimException,
|
|
lambda: api_session.wait_for_lease_ready(lease))
|
|
api_session.invoke_api.assert_called_once_with(
|
|
vim_util, 'get_object_property', api_session.vim, lease,
|
|
'state')
|
|
|
|
def _poll_task_well_known_exceptions(self, fault,
|
|
expected_exception):
|
|
api_session = self._create_api_session(False)
|
|
|
|
def fake_invoke_api(self, module, method, *args, **kwargs):
|
|
task_info = mock.Mock()
|
|
task_info.progress = -1
|
|
task_info.state = 'error'
|
|
error = mock.Mock()
|
|
error.localizedMessage = "Error message"
|
|
error_fault = mock.Mock()
|
|
error_fault.__class__.__name__ = fault
|
|
error.fault = error_fault
|
|
task_info.error = error
|
|
return task_info
|
|
|
|
with (
|
|
mock.patch.object(api_session, 'invoke_api', fake_invoke_api)
|
|
):
|
|
self.assertRaises(expected_exception,
|
|
api_session._poll_task, 'fake-task')
|
|
|
|
def test_poll_task_well_known_exceptions(self):
|
|
for k, v in exceptions._fault_classes_registry.iteritems():
|
|
self._poll_task_well_known_exceptions(k, v)
|
|
|
|
def test_poll_task_unknown_exception(self):
|
|
_unknown_exceptions = {
|
|
'NoDiskSpace': exceptions.VMwareDriverException,
|
|
'RuntimeFault': exceptions.VMwareDriverException
|
|
}
|
|
|
|
for k, v in _unknown_exceptions.iteritems():
|
|
self._poll_task_well_known_exceptions(k, v)
|