Adds cold resize Mixin
The mixin contains code and tests necessary in order to test different scenarios / features with cold resize. The mixins will ensure that the Feature classes they are mixed into will still behave properly after cold resize.
This commit is contained in:
parent
136ef0ac9d
commit
696da58fde
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2017 Cloudbase Solutions SRL
|
||||
# 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.
|
||||
|
||||
from tempest.lib import exceptions
|
||||
|
||||
|
||||
class ResizeException(exceptions.TempestException):
|
||||
message = ("Server %(server_id)s failed to resize to the given "
|
||||
"flavor %(flavor)s")
|
|
@ -0,0 +1,108 @@
|
|||
# Copyright 2017 Cloudbase Solutions SRL
|
||||
# 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.
|
||||
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
import testtools
|
||||
|
||||
from oswin_tempest_plugin import config
|
||||
from oswin_tempest_plugin import exceptions
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _ResizeUtils(object):
|
||||
|
||||
def _get_server_migration(self, server_id):
|
||||
final_states = ['error', 'confirmed']
|
||||
for i in range(10):
|
||||
migrations = (
|
||||
self.admin_migrations_client.list_migrations()['migrations'])
|
||||
server_migration = [m for m in migrations
|
||||
if m['instance_uuid'] == server_id]
|
||||
if server_migration:
|
||||
migration_status = server_migration[0]['status']
|
||||
LOG.debug("Server's %s migration status: %s",
|
||||
server_id, migration_status)
|
||||
if migration_status in final_states:
|
||||
return server_migration[0]
|
||||
else:
|
||||
# NOTE(claudiub): the migration might not appear *immediately*
|
||||
# after the cold resize was requested.
|
||||
LOG.info("Server's %s migration was not found.", server_id)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
return server_migration[0] if server_migration else None
|
||||
|
||||
def _resize_server(self, server_tuple, new_flavor):
|
||||
server = server_tuple.server
|
||||
self.servers_client.resize_server(server['id'],
|
||||
flavor_ref=new_flavor['id'])
|
||||
|
||||
migration = self._get_server_migration(server['id'])
|
||||
if migration and migration['status'] == 'error':
|
||||
# the migration ended up in an error state. Raise an exception.
|
||||
raise exceptions.ResizeException(server_id=server['id'],
|
||||
flavor=new_flavor)
|
||||
|
||||
self._wait_for_server_status(server, 'VERIFY_RESIZE')
|
||||
self.servers_client.confirm_resize_server(server['id'])
|
||||
|
||||
|
||||
class _ResizeMixin(_ResizeUtils):
|
||||
"""Cold resize mixin.
|
||||
|
||||
This mixin will add cold resize tests. The tests will create a new
|
||||
instance, resize it to a new flavor, and check its network connectivity.
|
||||
|
||||
The new flavor is based on the configured compute.flavor_ref, with some
|
||||
updates. For example, if the vNUMA configuration is to be tested, the new
|
||||
flavor would contain the flavor extra_spec 'hw:numa_nodes=1'.
|
||||
"""
|
||||
|
||||
# NOTE(claudiub): These flavor dicts are updates to the base flavor
|
||||
# tempest is configured with. For example, _BIGGER_FLAVOR can be:
|
||||
# _BIGGER_FLAVOR = {'disk': 1}
|
||||
# which means a flavor having +1 GB disk size will be created, and
|
||||
# a created instance will be resized to it.
|
||||
|
||||
_SMALLER_FLAVOR = {}
|
||||
_BIGGER_FLAVOR = {}
|
||||
_BAD_FLAVOR = {}
|
||||
|
||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
||||
'Resize is not available.')
|
||||
def test_resize(self):
|
||||
new_flavor = self._create_new_flavor(self._get_flavor_ref(),
|
||||
self._BIGGER_FLAVOR)
|
||||
server_tuple = self._create_server()
|
||||
self._resize_server(server_tuple, new_flavor)
|
||||
self._check_server_connectivity(server_tuple)
|
||||
|
||||
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
|
||||
'Resize is not available.')
|
||||
def test_resize_negative(self):
|
||||
new_flavor = self._create_new_flavor(self._get_flavor_ref(),
|
||||
self._BAD_FLAVOR)
|
||||
server_tuple = self._create_server()
|
||||
|
||||
self.assertRaises(exceptions.ResizeException, self._resize_server,
|
||||
server_tuple, new_flavor)
|
||||
# assert that the server is still reachable, even if the resize
|
||||
# failed.
|
||||
self._check_server_connectivity(server_tuple)
|
|
@ -16,11 +16,13 @@
|
|||
from oswin_tempest_plugin import config
|
||||
from oswin_tempest_plugin.tests import test_base
|
||||
from oswin_tempest_plugin.tests._mixins import migrate
|
||||
from oswin_tempest_plugin.tests._mixins import resize
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class _BaseDiskTestMixin(migrate._MigrateMixin):
|
||||
class _BaseDiskTestMixin(migrate._MigrateMixin,
|
||||
resize._ResizeMixin):
|
||||
"""Image types / formats test suite.
|
||||
|
||||
This test suite will spawn instances with a configured image and will
|
||||
|
@ -30,6 +32,9 @@ class _BaseDiskTestMixin(migrate._MigrateMixin):
|
|||
|
||||
_CONF_OPTION_NAME = ''
|
||||
|
||||
_BIGGER_FLAVOR = {'disk': 1}
|
||||
_BAD_FLAVOR = {'disk': -1}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(_BaseDiskTestMixin, cls).skip_checks()
|
||||
|
@ -48,6 +53,7 @@ class VhdDiskTest(test_base.TestBase, _BaseDiskTestMixin):
|
|||
|
||||
_IMAGE_REF = CONF.hyperv.vhd_image_ref
|
||||
_CONF_OPTION_NAME = 'hyperv.vhd_image_ref'
|
||||
_FLAVOR_SUFFIX = 'vhd'
|
||||
|
||||
# TODO(claudiub): validate that the images really are VHD / VHDX.
|
||||
|
||||
|
@ -56,6 +62,7 @@ class VhdxDiskTest(test_base.TestBase, _BaseDiskTestMixin):
|
|||
|
||||
_IMAGE_REF = CONF.hyperv.vhdx_image_ref
|
||||
_CONF_OPTION_NAME = 'hyperv.vhdx_image_ref'
|
||||
_FLAVOR_SUFFIX = 'vhdx'
|
||||
|
||||
|
||||
class Generation2DiskTest(test_base.TestBase, _BaseDiskTestMixin):
|
||||
|
@ -65,6 +72,7 @@ class Generation2DiskTest(test_base.TestBase, _BaseDiskTestMixin):
|
|||
|
||||
_IMAGE_REF = CONF.hyperv.gen2_image_ref
|
||||
_CONF_OPTION_NAME = 'hyperv.gen2_image_ref'
|
||||
_FLAVOR_SUFFIX = 'gen2'
|
||||
|
||||
# TODO(claudiub): Add validation that the given gen2_image_ref really has
|
||||
# the 'hw_machine_type=hyperv-gen2' property.
|
||||
|
|
|
@ -45,6 +45,9 @@ class TestBase(tempest.test.BaseTestCase):
|
|||
# Inheriting TestCases should change this image ref if needed.
|
||||
_IMAGE_REF = CONF.compute.image_ref
|
||||
|
||||
# suffix to use for the newly created flavors.
|
||||
_FLAVOR_SUFFIX = ''
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestBase, cls).skip_checks()
|
||||
|
@ -71,6 +74,8 @@ class TestBase(tempest.test.BaseTestCase):
|
|||
cls.keypairs_client = cls.os_primary.keypairs_client
|
||||
cls.servers_client = cls.os_primary.servers_client
|
||||
cls.admin_servers_client = cls.os_admin.servers_client
|
||||
cls.admin_flavors_client = cls.os_admin.flavors_client
|
||||
cls.admin_migrations_client = cls.os_admin.migrations_client
|
||||
|
||||
# Neutron network client
|
||||
cls.security_groups_client = (
|
||||
|
@ -101,6 +106,34 @@ class TestBase(tempest.test.BaseTestCase):
|
|||
def _get_image_ref(self):
|
||||
return self._IMAGE_REF
|
||||
|
||||
def _flavor_cleanup(self, flavor_id):
|
||||
try:
|
||||
self.admin_flavors_client.delete_flavor(flavor_id)
|
||||
self.admin_flavors_client.wait_for_resource_deletion(flavor_id)
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
|
||||
def _create_new_flavor(self, flavor_ref, flavor_updates):
|
||||
"""Creates a new flavor based on the given flavor and flavor updates.
|
||||
|
||||
:returns: the newly created flavor's ID.
|
||||
"""
|
||||
flavor = self.admin_flavors_client.show_flavor(flavor_ref)['flavor']
|
||||
|
||||
flavor_name = 'test_resize'
|
||||
if self._FLAVOR_SUFFIX:
|
||||
flavor_name += '_%s' % self._FLAVOR_SUFFIX
|
||||
|
||||
new_flavor = self.admin_flavors_client.create_flavor(
|
||||
name=data_utils.rand_name(flavor_name),
|
||||
ram=flavor['ram'] + flavor_updates.get('ram', 0),
|
||||
disk=flavor['disk'] + flavor_updates.get('disk', 0),
|
||||
vcpus=flavor['vcpus'] + flavor_updates.get('vcpus', 0),
|
||||
)['flavor']
|
||||
|
||||
self.addCleanup(self._flavor_cleanup, new_flavor['id'])
|
||||
return new_flavor
|
||||
|
||||
def _get_flavor_ref(self):
|
||||
return CONF.compute.flavor_ref
|
||||
|
||||
|
|
Loading…
Reference in New Issue