Minor fix for letfovers
This patch is to fix the faults and leftovers we found during test together with nova 1. Add pool back to session 2. Deal with master/slave hosts when creating session 3. Delete version setting in setup.cfg 4. Minor debug log format fix 5. Move XenAPISessionTestCase from nova to os-xenapi, in file test_session.py Change-Id: I7d4fabb00fc594f768323cd61e3d2cd83b25141c
This commit is contained in:
parent
fcb191ebec
commit
68c5be4b57
@ -29,7 +29,6 @@ from eventlet import timeout
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import versionutils
|
from oslo_utils import versionutils
|
||||||
from six.moves import http_client
|
from six.moves import http_client
|
||||||
from six.moves import range
|
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -57,6 +56,7 @@ def apply_session_helpers(session):
|
|||||||
session.VLAN = cli_objects.VLAN(session)
|
session.VLAN = cli_objects.VLAN(session)
|
||||||
session.host = cli_objects.Host(session)
|
session.host = cli_objects.Host(session)
|
||||||
session.network = cli_objects.Network(session)
|
session.network = cli_objects.Network(session)
|
||||||
|
session.pool = cli_objects.Pool(session)
|
||||||
|
|
||||||
|
|
||||||
class XenAPISession(object):
|
class XenAPISession(object):
|
||||||
@ -86,22 +86,23 @@ class XenAPISession(object):
|
|||||||
self._sessions = queue.Queue()
|
self._sessions = queue.Queue()
|
||||||
self.host_checked = False
|
self.host_checked = False
|
||||||
self.url = self._create_first_session(url, user, pw)
|
self.url = self._create_first_session(url, user, pw)
|
||||||
self._populate_session_pool(url, user, pw)
|
self._populate_session_pool(self.url, user, pw)
|
||||||
self.host_uuid = self._get_host_uuid()
|
self.host_uuid = self._get_host_uuid()
|
||||||
self.host_ref = self._get_host_ref()
|
self.host_ref = self._get_host_ref()
|
||||||
self.product_version, self.product_brand = \
|
self.product_version, self.product_brand = \
|
||||||
self._get_product_version_and_brand()
|
self._get_product_version_and_brand()
|
||||||
# TODO(huanxie) Uncomment _verify_plugin_version() in the future
|
self._verify_plugin_version()
|
||||||
# self._verify_plugin_version()
|
|
||||||
self.platform_version = self._get_platform_version()
|
self.platform_version = self._get_platform_version()
|
||||||
self._cached_xsm_sr_relaxed = None
|
self._cached_xsm_sr_relaxed = None
|
||||||
|
|
||||||
apply_session_helpers(self)
|
apply_session_helpers(self)
|
||||||
|
|
||||||
def _login_with_password(self, user, pw, session):
|
def _login_with_password(self, user, pw, session):
|
||||||
login_exception = exception.SessionLoginTimeout
|
login_exception = XenAPI.Failure(_("Unable to log in to XenAPI "
|
||||||
|
"(is the Dom0 disk full?)"))
|
||||||
with timeout.Timeout(self.timeout, login_exception):
|
with timeout.Timeout(self.timeout, login_exception):
|
||||||
session.login_with_password(user, pw, self.originator)
|
session.login_with_password(user, pw, self.PLUGIN_REQUIRED_VERSION,
|
||||||
|
self.originator)
|
||||||
|
|
||||||
def _verify_plugin_version(self):
|
def _verify_plugin_version(self):
|
||||||
requested_version = self.PLUGIN_REQUIRED_VERSION
|
requested_version = self.PLUGIN_REQUIRED_VERSION
|
||||||
@ -114,18 +115,29 @@ class XenAPISession(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if not versionutils.is_compatible(requested_version, current_version):
|
if not versionutils.is_compatible(requested_version, current_version):
|
||||||
raise exception.OsXenApiException(
|
raise XenAPI.Failure(
|
||||||
_("Plugin version mismatch (Expected %(exp)s, got %(got)s)") %
|
_("Plugin version mismatch (Expected %(exp)s, got %(got)s)") %
|
||||||
{'exp': requested_version, 'got': current_version})
|
{'exp': requested_version, 'got': current_version})
|
||||||
|
|
||||||
def _create_first_session(self, url, user, pw):
|
def _create_first_session(self, url, user, pw):
|
||||||
try:
|
try:
|
||||||
session = self._create_session_and_login(url, user, pw)
|
session = self._create_session_and_login(url, user, pw)
|
||||||
except exception.SessionLoginTimeout:
|
except XenAPI.Failure as e:
|
||||||
raise
|
# if user and pw of the master are different, we're doomed!
|
||||||
|
if e.details[0] == 'HOST_IS_SLAVE':
|
||||||
|
master = e.details[1]
|
||||||
|
url = self.swap_xapi_host(url, master)
|
||||||
|
session = self._create_session_and_login(url, user, pw)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
self._sessions.put(session)
|
self._sessions.put(session)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
def swap_xapi_host(self, url, host_addr):
|
||||||
|
"""Replace the XenServer address present in 'url' with 'host_addr'."""
|
||||||
|
temp_url = urllib.parse.urlparse.urlparse(url)
|
||||||
|
return url.replace(temp_url.hostname, '%s' % host_addr)
|
||||||
|
|
||||||
def _populate_session_pool(self, url, user, pw):
|
def _populate_session_pool(self, url, user, pw):
|
||||||
for i in range(self.concurrent - 1):
|
for i in range(self.concurrent - 1):
|
||||||
session = self._create_session_and_login(url, user, pw)
|
session = self._create_session_and_login(url, user, pw)
|
||||||
@ -325,18 +337,18 @@ class XenAPISession(object):
|
|||||||
name = '%s-%s' % (self.originator, label)
|
name = '%s-%s' % (self.originator, label)
|
||||||
task_ref = self.call_xenapi("task.create", name, desc)
|
task_ref = self.call_xenapi("task.create", name, desc)
|
||||||
try:
|
try:
|
||||||
LOG.debug('Created task %s with ref %s' % (name, task_ref))
|
LOG.debug('Created task %s with ref %s', name, task_ref)
|
||||||
yield task_ref
|
yield task_ref
|
||||||
finally:
|
finally:
|
||||||
self.call_xenapi("task.destroy", task_ref)
|
self.call_xenapi("task.destroy", task_ref)
|
||||||
LOG.debug('Destroyed task ref %s' % task_ref)
|
LOG.debug('Destroyed task ref %s', task_ref)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def http_connection(self, session):
|
def http_connection(self):
|
||||||
conn = None
|
conn = None
|
||||||
|
|
||||||
xs_url = urllib.parse.urlparse(session.url)
|
xs_url = urllib.parse.urlparse(self.url)
|
||||||
LOG.debug("Creating http(s) connection to %s" % session.url)
|
LOG.debug("Creating http(s) connection to %s", self.url)
|
||||||
if xs_url.scheme == 'http':
|
if xs_url.scheme == 'http':
|
||||||
conn = http_client.HTTPConnection(xs_url.netloc)
|
conn = http_client.HTTPConnection(xs_url.netloc)
|
||||||
elif xs_url.scheme == 'https':
|
elif xs_url.scheme == 'https':
|
||||||
|
@ -13,24 +13,29 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from os_xenapi.client import exception
|
from os_xenapi.client import exception
|
||||||
from os_xenapi.client import session
|
from os_xenapi.client import session
|
||||||
|
from os_xenapi.client import XenAPI
|
||||||
from os_xenapi.tests import base
|
from os_xenapi.tests import base
|
||||||
|
|
||||||
|
|
||||||
class SessionTestCase(base.TestCase):
|
class SessionTestCase(base.TestCase):
|
||||||
|
@mock.patch.object(session.XenAPISession, '_verify_plugin_version')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_platform_version')
|
@mock.patch.object(session.XenAPISession, '_get_platform_version')
|
||||||
@mock.patch.object(session.XenAPISession, '_create_session')
|
@mock.patch.object(session.XenAPISession, '_create_session')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_product_version_and_brand')
|
@mock.patch.object(session.XenAPISession, '_get_product_version_and_brand')
|
||||||
def test_session_nova_originator(self, mock_version_and_brand,
|
def test_session_nova_originator(self, mock_version_and_brand,
|
||||||
mock_create_session,
|
mock_create_session,
|
||||||
mock_platform_version):
|
mock_platform_version,
|
||||||
|
mock_verify_plugin_version):
|
||||||
concurrent = 2
|
concurrent = 2
|
||||||
originator = 'os-xenapi-nova'
|
originator = 'os-xenapi-nova'
|
||||||
|
version = '2.0'
|
||||||
timeout = 10
|
timeout = 10
|
||||||
sess = mock.Mock()
|
sess = mock.Mock()
|
||||||
mock_create_session.return_value = sess
|
mock_create_session.return_value = sess
|
||||||
@ -42,15 +47,17 @@ class SessionTestCase(base.TestCase):
|
|||||||
timeout=timeout)
|
timeout=timeout)
|
||||||
|
|
||||||
sess.login_with_password.assert_called_with('username', 'password',
|
sess.login_with_password.assert_called_with('username', 'password',
|
||||||
originator)
|
version, originator)
|
||||||
|
|
||||||
|
@mock.patch.object(session.XenAPISession, '_verify_plugin_version')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_platform_version')
|
@mock.patch.object(session.XenAPISession, '_get_platform_version')
|
||||||
@mock.patch('eventlet.timeout.Timeout')
|
@mock.patch('eventlet.timeout.Timeout')
|
||||||
@mock.patch.object(session.XenAPISession, '_create_session')
|
@mock.patch.object(session.XenAPISession, '_create_session')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_product_version_and_brand')
|
@mock.patch.object(session.XenAPISession, '_get_product_version_and_brand')
|
||||||
def test_session_login_with_timeout(self, mock_version,
|
def test_session_login_with_timeout(self, mock_version,
|
||||||
create_session, mock_timeout,
|
create_session, mock_timeout,
|
||||||
mock_platform_version):
|
mock_platform_version,
|
||||||
|
mock_verify_plugin_version):
|
||||||
concurrent = 2
|
concurrent = 2
|
||||||
originator = 'os-xenapi-nova'
|
originator = 'os-xenapi-nova'
|
||||||
sess = mock.Mock()
|
sess = mock.Mock()
|
||||||
@ -63,12 +70,14 @@ class SessionTestCase(base.TestCase):
|
|||||||
self.assertEqual(concurrent, sess.login_with_password.call_count)
|
self.assertEqual(concurrent, sess.login_with_password.call_count)
|
||||||
self.assertEqual(concurrent, mock_timeout.call_count)
|
self.assertEqual(concurrent, mock_timeout.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(session.XenAPISession, '_verify_plugin_version')
|
||||||
@mock.patch.object(session.XenAPISession, 'call_plugin')
|
@mock.patch.object(session.XenAPISession, 'call_plugin')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_software_version')
|
@mock.patch.object(session.XenAPISession, '_get_software_version')
|
||||||
@mock.patch.object(session.XenAPISession, '_create_session')
|
@mock.patch.object(session.XenAPISession, '_create_session')
|
||||||
def test_relax_xsm_sr_check_true(self, mock_create_session,
|
def test_relax_xsm_sr_check_true(self, mock_create_session,
|
||||||
mock_get_software_version,
|
mock_get_software_version,
|
||||||
mock_call_plugin):
|
mock_call_plugin,
|
||||||
|
mock_verify_plugin_version):
|
||||||
sess = mock.Mock()
|
sess = mock.Mock()
|
||||||
mock_create_session.return_value = sess
|
mock_create_session.return_value = sess
|
||||||
mock_get_software_version.return_value = {'product_version': '6.5.0',
|
mock_get_software_version.return_value = {'product_version': '6.5.0',
|
||||||
@ -80,12 +89,14 @@ class SessionTestCase(base.TestCase):
|
|||||||
'http://someserver', 'username', 'password')
|
'http://someserver', 'username', 'password')
|
||||||
self.assertTrue(xenapi_sess.is_xsm_sr_check_relaxed())
|
self.assertTrue(xenapi_sess.is_xsm_sr_check_relaxed())
|
||||||
|
|
||||||
|
@mock.patch.object(session.XenAPISession, '_verify_plugin_version')
|
||||||
@mock.patch.object(session.XenAPISession, 'call_plugin')
|
@mock.patch.object(session.XenAPISession, 'call_plugin')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_software_version')
|
@mock.patch.object(session.XenAPISession, '_get_software_version')
|
||||||
@mock.patch.object(session.XenAPISession, '_create_session')
|
@mock.patch.object(session.XenAPISession, '_create_session')
|
||||||
def test_relax_xsm_sr_check_XS65_missing(self, mock_create_session,
|
def test_relax_xsm_sr_check_XS65_missing(self, mock_create_session,
|
||||||
mock_get_software_version,
|
mock_get_software_version,
|
||||||
mock_call_plugin):
|
mock_call_plugin,
|
||||||
|
mock_verify_plugin_version):
|
||||||
sess = mock.Mock()
|
sess = mock.Mock()
|
||||||
mock_create_session.return_value = sess
|
mock_create_session.return_value = sess
|
||||||
mock_get_software_version.return_value = {'product_version': '6.5.0',
|
mock_get_software_version.return_value = {'product_version': '6.5.0',
|
||||||
@ -97,12 +108,14 @@ class SessionTestCase(base.TestCase):
|
|||||||
'http://someserver', 'username', 'password')
|
'http://someserver', 'username', 'password')
|
||||||
self.assertFalse(xenapi_sess.is_xsm_sr_check_relaxed())
|
self.assertFalse(xenapi_sess.is_xsm_sr_check_relaxed())
|
||||||
|
|
||||||
|
@mock.patch.object(session.XenAPISession, '_verify_plugin_version')
|
||||||
@mock.patch.object(session.XenAPISession, 'call_plugin')
|
@mock.patch.object(session.XenAPISession, 'call_plugin')
|
||||||
@mock.patch.object(session.XenAPISession, '_get_software_version')
|
@mock.patch.object(session.XenAPISession, '_get_software_version')
|
||||||
@mock.patch.object(session.XenAPISession, '_create_session')
|
@mock.patch.object(session.XenAPISession, '_create_session')
|
||||||
def test_relax_xsm_sr_check_XS7_missing(self, mock_create_session,
|
def test_relax_xsm_sr_check_XS7_missing(self, mock_create_session,
|
||||||
mock_get_software_version,
|
mock_get_software_version,
|
||||||
mock_call_plugin):
|
mock_call_plugin,
|
||||||
|
mock_verify_plugin_version):
|
||||||
sess = mock.Mock()
|
sess = mock.Mock()
|
||||||
mock_create_session.return_value = sess
|
mock_create_session.return_value = sess
|
||||||
mock_get_software_version.return_value = {'product_version': '7.0.0',
|
mock_get_software_version.return_value = {'product_version': '7.0.0',
|
||||||
@ -230,3 +243,120 @@ class CallPluginTestCase(base.TestCase):
|
|||||||
num_retries, callback, retry_cb)
|
num_retries, callback, retry_cb)
|
||||||
call_plugin_serialized.assert_called_with(plugin, fn)
|
call_plugin_serialized.assert_called_with(plugin, fn)
|
||||||
self.assertEqual(2, call_plugin_serialized.call_count)
|
self.assertEqual(2, call_plugin_serialized.call_count)
|
||||||
|
|
||||||
|
|
||||||
|
class XenAPISessionTestCase(base.TestCase):
|
||||||
|
def _get_mock_xapisession(self, software_version):
|
||||||
|
class MockXapiSession(session.XenAPISession):
|
||||||
|
def __init__(_ignore):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_software_version(_ignore):
|
||||||
|
return software_version
|
||||||
|
|
||||||
|
return MockXapiSession()
|
||||||
|
|
||||||
|
@mock.patch.object(XenAPI, 'xapi_local')
|
||||||
|
def test_local_session(self, mock_xapi_local):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.is_local_connection = True
|
||||||
|
mock_xapi_local.return_value = "local_connection"
|
||||||
|
self.assertEqual("local_connection",
|
||||||
|
session._create_session("unix://local"))
|
||||||
|
|
||||||
|
@mock.patch.object(XenAPI, 'Session')
|
||||||
|
def test_remote_session(self, mock_session):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.is_local_connection = False
|
||||||
|
mock_session.return_value = "remote_connection"
|
||||||
|
self.assertEqual("remote_connection", session._create_session("url"))
|
||||||
|
|
||||||
|
def test_get_product_version_product_brand_does_not_fail(self):
|
||||||
|
session = self._get_mock_xapisession(
|
||||||
|
{'build_number': '0',
|
||||||
|
'date': '2012-08-03',
|
||||||
|
'hostname': 'komainu',
|
||||||
|
'linux': '3.2.0-27-generic',
|
||||||
|
'network_backend': 'bridge',
|
||||||
|
'platform_name': 'XCP_Kronos',
|
||||||
|
'platform_version': '1.6.0',
|
||||||
|
'xapi': '1.3',
|
||||||
|
'xen': '4.1.2',
|
||||||
|
'xencenter_max': '1.10',
|
||||||
|
'xencenter_min': '1.10'})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
((1, 6, 0), None),
|
||||||
|
session._get_product_version_and_brand()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_product_version_product_brand_xs_6(self):
|
||||||
|
session = self._get_mock_xapisession(
|
||||||
|
{'product_brand': 'XenServer',
|
||||||
|
'product_version': '6.0.50',
|
||||||
|
'platform_version': '0.0.1'})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
((6, 0, 50), 'XenServer'),
|
||||||
|
session._get_product_version_and_brand()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_verify_plugin_version_same(self):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.PLUGIN_REQUIRED_VERSION = '2.4'
|
||||||
|
with mock.patch.object(session, 'call_plugin_serialized',
|
||||||
|
spec=True) as call_plugin_serialized:
|
||||||
|
call_plugin_serialized.return_value = "2.4"
|
||||||
|
session._verify_plugin_version()
|
||||||
|
|
||||||
|
def test_verify_plugin_version_compatible(self):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.PLUGIN_REQUIRED_VERSION = '2.4'
|
||||||
|
with mock.patch.object(session, 'call_plugin_serialized',
|
||||||
|
spec=True) as call_plugin_serialized:
|
||||||
|
call_plugin_serialized.return_value = "2.5"
|
||||||
|
session._verify_plugin_version()
|
||||||
|
|
||||||
|
def test_verify_plugin_version_python_extensions(self):
|
||||||
|
# Validate that 2.0 is equivalent to 1.8
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.PLUGIN_REQUIRED_VERSION = '2.0'
|
||||||
|
with mock.patch.object(session, 'call_plugin_serialized',
|
||||||
|
spec=True) as call_plugin_serialized:
|
||||||
|
call_plugin_serialized.return_value = "1.8"
|
||||||
|
session._verify_plugin_version()
|
||||||
|
|
||||||
|
def test_verify_plugin_version_bad_maj(self):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.PLUGIN_REQUIRED_VERSION = '2.4'
|
||||||
|
with mock.patch.object(session, 'call_plugin_serialized',
|
||||||
|
spec=True) as call_plugin_serialized:
|
||||||
|
call_plugin_serialized.return_value = "3.0"
|
||||||
|
self.assertRaises(XenAPI.Failure, session._verify_plugin_version)
|
||||||
|
|
||||||
|
def test_verify_plugin_version_bad_min(self):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
session.PLUGIN_REQUIRED_VERSION = '2.4'
|
||||||
|
with mock.patch.object(session, 'call_plugin_serialized',
|
||||||
|
spec=True) as call_plugin_serialized:
|
||||||
|
call_plugin_serialized.return_value = "2.3"
|
||||||
|
self.assertRaises(XenAPI.Failure, session._verify_plugin_version)
|
||||||
|
|
||||||
|
def test_verify_current_version_matches(self):
|
||||||
|
session = self._get_mock_xapisession({})
|
||||||
|
|
||||||
|
# Import the plugin to extract its version
|
||||||
|
path = os.path.dirname(__file__)
|
||||||
|
rel_path_elem = "../../dom0/etc/xapi.d/plugins/dom0_plugin_version.py"
|
||||||
|
for elem in rel_path_elem.split('/'):
|
||||||
|
path = os.path.join(path, elem)
|
||||||
|
path = os.path.realpath(path)
|
||||||
|
|
||||||
|
plugin_version = None
|
||||||
|
with open(path) as plugin_file:
|
||||||
|
for line in plugin_file:
|
||||||
|
if "PLUGIN_VERSION = " in line:
|
||||||
|
plugin_version = line.strip()[17:].strip('"')
|
||||||
|
|
||||||
|
self.assertEqual(session.PLUGIN_REQUIRED_VERSION,
|
||||||
|
plugin_version)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = os-xenapi
|
name = os-xenapi
|
||||||
summary = XenAPI library for OpenStack projects
|
summary = XenAPI library for OpenStack projects
|
||||||
version = 0.0.1
|
|
||||||
description-file =
|
description-file =
|
||||||
README.rst
|
README.rst
|
||||||
author = Citrix
|
author = Citrix
|
||||||
|
1
tox.ini
1
tox.ini
@ -5,7 +5,6 @@ skipsdist = True
|
|||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
|
||||||
setenv =
|
setenv =
|
||||||
VIRTUAL_ENV={envdir}
|
VIRTUAL_ENV={envdir}
|
||||||
PYTHONWARNINGS=default::DeprecationWarning
|
PYTHONWARNINGS=default::DeprecationWarning
|
||||||
|
Loading…
Reference in New Issue
Block a user