Nova: test live migration with serial console
Nova offers a "serial console" as an alternative to graphical consoles like VNC/SPICE/RDP. This is useful for platforms which don't have graphical consoles, for example the "IBM system z" platform. This change introduces a test which ensures that the interaction with the serial console is possible before and after a live-migration. As the unified remote console API is available since microversion 2.6, I use this as a base for the tests. This made id necessary to update the schemas. This change introduces a config option to enable new test cases for the serial console. A Nova change (see I7af395a867e0657c26fa064d2b0134345cd96814), which uses the hook for live-migration testing, will use the config option of this change to alter the testing system on the fly to enable the testing of the serial console. Closes-Bug: #1560358 Needed-By: I7af395a867e0657c26fa064d2b0134345cd96814 Change-Id: I020fd94d970ad0cdf7ab65d7656da6ca5766094b
This commit is contained in:
parent
b86de8898f
commit
69d58b8f34
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new boolean config option ``serial_console`` is added to the section
|
||||||
|
``compute-feature-enabled``. If enabled, tests, which validate the
|
||||||
|
behavior of Nova's *serial console* feature (an alternative to VNC,
|
||||||
|
RDP, SPICE) can be executed.
|
@ -13,10 +13,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from tempest.api.compute import base
|
from tempest.api.compute import base
|
||||||
|
from tempest.common import compute
|
||||||
from tempest.common import waiters
|
from tempest.common import waiters
|
||||||
from tempest import config
|
from tempest import config
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
@ -169,6 +172,80 @@ class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest):
|
|||||||
self.assertEqual(target_host, self._get_host_for_server(server_id))
|
self.assertEqual(target_host, self._get_host_for_server(server_id))
|
||||||
|
|
||||||
|
|
||||||
|
class LiveBlockMigrationRemoteConsolesV26TestJson(LiveBlockMigrationTestJSON):
|
||||||
|
min_microversion = '2.6'
|
||||||
|
max_microversion = 'latest'
|
||||||
|
|
||||||
|
@decorators.idempotent_id('6190af80-513e-4f0f-90f2-9714e84955d7')
|
||||||
|
@testtools.skipUnless(CONF.compute_feature_enabled.serial_console,
|
||||||
|
'Serial console not supported.')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
test.is_scheduler_filter_enabled("DifferentHostFilter"),
|
||||||
|
'DifferentHostFilter is not available.')
|
||||||
|
def test_live_migration_serial_console(self):
|
||||||
|
"""Test the live-migration of an instance which has a serial console
|
||||||
|
|
||||||
|
The serial console feature of an instance uses ports on the host.
|
||||||
|
These ports need to be updated when they are already in use by
|
||||||
|
another instance on the target host. This test checks if this
|
||||||
|
update behavior is correctly done, by connecting to the serial
|
||||||
|
consoles of the instances before and after the live migration.
|
||||||
|
"""
|
||||||
|
server01_id = self.create_test_server(wait_until='ACTIVE')['id']
|
||||||
|
hints = {'different_host': server01_id}
|
||||||
|
server02_id = self.create_test_server(scheduler_hints=hints,
|
||||||
|
wait_until='ACTIVE')['id']
|
||||||
|
host01_id = self._get_host_for_server(server01_id)
|
||||||
|
host02_id = self._get_host_for_server(server02_id)
|
||||||
|
self.assertNotEqual(host01_id, host02_id)
|
||||||
|
|
||||||
|
# At this step we have 2 instances on different hosts, both with
|
||||||
|
# serial consoles, both with port 10000 (the default value).
|
||||||
|
# https://bugs.launchpad.net/nova/+bug/1455252 describes the issue
|
||||||
|
# when live-migrating in such a scenario.
|
||||||
|
|
||||||
|
self._verify_console_interaction(server01_id)
|
||||||
|
self._verify_console_interaction(server02_id)
|
||||||
|
|
||||||
|
self._migrate_server_to(server01_id, host02_id)
|
||||||
|
waiters.wait_for_server_status(self.servers_client,
|
||||||
|
server01_id, 'ACTIVE')
|
||||||
|
self.assertEqual(host02_id, self._get_host_for_server(server01_id))
|
||||||
|
self._verify_console_interaction(server01_id)
|
||||||
|
# At this point, both instances have a valid serial console
|
||||||
|
# connection, which means the ports got updated.
|
||||||
|
|
||||||
|
def _verify_console_interaction(self, server_id):
|
||||||
|
body = self.servers_client.get_remote_console(server_id,
|
||||||
|
console_type='serial',
|
||||||
|
protocol='serial')
|
||||||
|
console_url = body['remote_console']['url']
|
||||||
|
data = "test_live_migration_serial_console"
|
||||||
|
console_output = ''
|
||||||
|
t = 0.0
|
||||||
|
interval = 0.1
|
||||||
|
|
||||||
|
ws = compute.create_websocket(console_url)
|
||||||
|
try:
|
||||||
|
# NOTE (markus_z): It can take a long time until the terminal
|
||||||
|
# of the instance is available for interaction. Hence the
|
||||||
|
# long timeout value.
|
||||||
|
while data not in console_output and t <= 120.0:
|
||||||
|
try:
|
||||||
|
ws.send_frame(data)
|
||||||
|
recieved = ws.receive_frame()
|
||||||
|
console_output += recieved
|
||||||
|
except Exception:
|
||||||
|
# In case we had an issue with send/receive on the
|
||||||
|
# websocket connection, we create a new one.
|
||||||
|
ws = compute.create_websocket(console_url)
|
||||||
|
time.sleep(interval)
|
||||||
|
t += interval
|
||||||
|
finally:
|
||||||
|
ws.close()
|
||||||
|
self.assertIn(data, console_output)
|
||||||
|
|
||||||
|
|
||||||
class LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON):
|
class LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON):
|
||||||
min_microversion = '2.25'
|
min_microversion = '2.25'
|
||||||
max_microversion = 'latest'
|
max_microversion = 'latest'
|
||||||
|
@ -399,6 +399,11 @@ ComputeFeaturesGroup = [
|
|||||||
default=False,
|
default=False,
|
||||||
help='Enable RDP console. This configuration value should '
|
help='Enable RDP console. This configuration value should '
|
||||||
'be same as [nova.rdp]->enabled in nova.conf'),
|
'be same as [nova.rdp]->enabled in nova.conf'),
|
||||||
|
cfg.BoolOpt('serial_console',
|
||||||
|
default=False,
|
||||||
|
help='Enable serial console. This configuration value '
|
||||||
|
'should be the same as [nova.serial_console]->enabled '
|
||||||
|
'in nova.conf'),
|
||||||
cfg.BoolOpt('rescue',
|
cfg.BoolOpt('rescue',
|
||||||
default=True,
|
default=True,
|
||||||
help='Does the test environment support instance rescue '
|
help='Does the test environment support instance rescue '
|
||||||
|
48
tempest/lib/api_schema/response/compute/v2_6/servers.py
Normal file
48
tempest/lib/api_schema/response/compute/v2_6/servers.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright 2016 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 copy
|
||||||
|
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_3 import servers
|
||||||
|
|
||||||
|
list_servers = copy.deepcopy(servers.list_servers)
|
||||||
|
get_server = copy.deepcopy(servers.get_server)
|
||||||
|
list_servers_detail = copy.deepcopy(servers.list_servers_detail)
|
||||||
|
|
||||||
|
# NOTE: The consolidated remote console API got introduced with v2.6
|
||||||
|
# with bp/consolidate-console-api. See Nova commit 578bafeda
|
||||||
|
get_remote_consoles = {
|
||||||
|
'status_code': [200],
|
||||||
|
'response_body': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'remote_console': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'protocol': {'enum': ['vnc', 'rdp', 'serial', 'spice']},
|
||||||
|
'type': {'enum': ['novnc', 'xpvnc', 'rdp-html5',
|
||||||
|
'spice-html5', 'serial']},
|
||||||
|
'url': {
|
||||||
|
'type': 'string',
|
||||||
|
'format': 'uri'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['protocol', 'type', 'url']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['remote_console']
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from tempest.lib.api_schema.response.compute.v2_3 import servers
|
from tempest.lib.api_schema.response.compute.v2_6 import servers
|
||||||
|
|
||||||
list_servers = copy.deepcopy(servers.list_servers)
|
list_servers = copy.deepcopy(servers.list_servers)
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ from tempest.lib.api_schema.response.compute.v2_16 import servers as schemav216
|
|||||||
from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
|
from tempest.lib.api_schema.response.compute.v2_19 import servers as schemav219
|
||||||
from tempest.lib.api_schema.response.compute.v2_26 import servers as schemav226
|
from tempest.lib.api_schema.response.compute.v2_26 import servers as schemav226
|
||||||
from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
|
from tempest.lib.api_schema.response.compute.v2_3 import servers as schemav23
|
||||||
|
from tempest.lib.api_schema.response.compute.v2_6 import servers as schemav26
|
||||||
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
|
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
|
||||||
from tempest.lib.common import rest_client
|
from tempest.lib.common import rest_client
|
||||||
from tempest.lib.services.compute import base_compute_client
|
from tempest.lib.services.compute import base_compute_client
|
||||||
@ -37,7 +38,8 @@ class ServersClient(base_compute_client.BaseComputeClient):
|
|||||||
|
|
||||||
schema_versions_info = [
|
schema_versions_info = [
|
||||||
{'min': None, 'max': '2.2', 'schema': schema},
|
{'min': None, 'max': '2.2', 'schema': schema},
|
||||||
{'min': '2.3', 'max': '2.8', 'schema': schemav23},
|
{'min': '2.3', 'max': '2.5', 'schema': schemav23},
|
||||||
|
{'min': '2.6', 'max': '2.8', 'schema': schemav26},
|
||||||
{'min': '2.9', 'max': '2.15', 'schema': schemav29},
|
{'min': '2.9', 'max': '2.15', 'schema': schemav29},
|
||||||
{'min': '2.16', 'max': '2.18', 'schema': schemav216},
|
{'min': '2.16', 'max': '2.18', 'schema': schemav216},
|
||||||
{'min': '2.19', 'max': '2.25', 'schema': schemav219},
|
{'min': '2.19', 'max': '2.25', 'schema': schemav219},
|
||||||
@ -598,6 +600,29 @@ class ServersClient(base_compute_client.BaseComputeClient):
|
|||||||
return self.action(server_id, 'os-getConsoleOutput',
|
return self.action(server_id, 'os-getConsoleOutput',
|
||||||
schema.get_console_output, **kwargs)
|
schema.get_console_output, **kwargs)
|
||||||
|
|
||||||
|
def get_remote_console(self, server_id, console_type, protocol, **kwargs):
|
||||||
|
"""Get a remote console.
|
||||||
|
|
||||||
|
For a full list of available parameters, please refer to the official
|
||||||
|
API reference:
|
||||||
|
TODO (markus_z) The api-ref for that isn't yet available, update this
|
||||||
|
here when the docs in Nova are updated. The old API is at
|
||||||
|
http://developer.openstack.org/api-ref/compute/#get-serial-console-os-getserialconsole-action
|
||||||
|
"""
|
||||||
|
param = {
|
||||||
|
'remote_console': {
|
||||||
|
'type': console_type,
|
||||||
|
'protocol': protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post_body = json.dumps(param)
|
||||||
|
resp, body = self.post("servers/%s/remote-consoles" % server_id,
|
||||||
|
post_body)
|
||||||
|
body = json.loads(body)
|
||||||
|
schema = self.get_schema(self.schema_versions_info)
|
||||||
|
self.validate_response(schema.get_remote_consoles, resp, body)
|
||||||
|
return rest_client.ResponseBody(resp, body)
|
||||||
|
|
||||||
def list_virtual_interfaces(self, server_id):
|
def list_virtual_interfaces(self, server_id):
|
||||||
"""List the virtual interfaces used in an instance."""
|
"""List the virtual interfaces used in an instance."""
|
||||||
resp, body = self.get('/'.join(['servers', server_id,
|
resp, body = self.get('/'.join(['servers', server_id,
|
||||||
|
@ -1168,3 +1168,34 @@ class TestServersClient(base.BaseServiceTest):
|
|||||||
tag=self.FAKE_TAGS[0],
|
tag=self.FAKE_TAGS[0],
|
||||||
status=204,
|
status=204,
|
||||||
to_utf=bytes_body)
|
to_utf=bytes_body)
|
||||||
|
|
||||||
|
|
||||||
|
class TestServersClientMinV26(base.BaseServiceTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestServersClientMinV26, self).setUp()
|
||||||
|
fake_auth = fake_auth_provider.FakeAuthProvider()
|
||||||
|
self.client = servers_client.ServersClient(fake_auth, 'compute',
|
||||||
|
'regionOne')
|
||||||
|
base_compute_client.COMPUTE_MICROVERSION = '2.6'
|
||||||
|
self.server_id = "920eaac8-a284-4fd1-9c2c-b30f0181b125"
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestServersClientMinV26, self).tearDown()
|
||||||
|
base_compute_client.COMPUTE_MICROVERSION = None
|
||||||
|
|
||||||
|
def test_get_remote_consoles(self):
|
||||||
|
self.check_service_client_function(
|
||||||
|
self.client.get_remote_console,
|
||||||
|
'tempest.lib.common.rest_client.RestClient.post',
|
||||||
|
{
|
||||||
|
'remote_console': {
|
||||||
|
'protocol': 'serial',
|
||||||
|
'type': 'serial',
|
||||||
|
'url': 'ws://127.0.0.1:6083/?token=IllAllowIt'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server_id=self.server_id,
|
||||||
|
console_type='serial',
|
||||||
|
protocol='serial',
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user