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
|
||||
# under the License.
|
||||
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
import testtools
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import compute
|
||||
from tempest.common import waiters
|
||||
from tempest import config
|
||||
from tempest.lib import decorators
|
||||
@ -169,6 +172,80 @@ class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest):
|
||||
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):
|
||||
min_microversion = '2.25'
|
||||
max_microversion = 'latest'
|
||||
|
@ -399,6 +399,11 @@ ComputeFeaturesGroup = [
|
||||
default=False,
|
||||
help='Enable RDP console. This configuration value should '
|
||||
'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',
|
||||
default=True,
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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_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_6 import servers as schemav26
|
||||
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
|
||||
from tempest.lib.common import rest_client
|
||||
from tempest.lib.services.compute import base_compute_client
|
||||
@ -37,7 +38,8 @@ class ServersClient(base_compute_client.BaseComputeClient):
|
||||
|
||||
schema_versions_info = [
|
||||
{'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.16', 'max': '2.18', 'schema': schemav216},
|
||||
{'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',
|
||||
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):
|
||||
"""List the virtual interfaces used in an instance."""
|
||||
resp, body = self.get('/'.join(['servers', server_id,
|
||||
|
@ -1168,3 +1168,34 @@ class TestServersClient(base.BaseServiceTest):
|
||||
tag=self.FAKE_TAGS[0],
|
||||
status=204,
|
||||
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…
x
Reference in New Issue
Block a user