blob: 2826f56bce4ef5bdc4b326be6a03b5a4d9a7c10f [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Mate Lakat99ee9142012-09-14 12:34:46 +01002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Markus Zoeller69d58b82017-02-17 10:09:22 +010016import time
17
Sarafraj Singh61e40452016-09-29 13:06:59 -050018from oslo_log import log as logging
ivan-zhu1feeb382013-01-24 10:14:39 +080019import testtools
Matthew Treinisha83a16e2012-12-07 13:44:02 -050020
Sean Dague1937d092013-05-17 16:36:38 -040021from tempest.api.compute import base
Markus Zoeller69d58b82017-02-17 10:09:22 +010022from tempest.common import compute
Andrea Frittolicd368412017-08-14 21:37:56 +010023from tempest.common import utils
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +000024from tempest.common import waiters
Mate Lakat99ee9142012-09-14 12:34:46 +010025from tempest import config
elajkat0b14db22021-02-08 16:43:59 +010026from tempest.lib.common.utils import data_utils
27from tempest.lib.common.utils import test_utils
Brian Haleya3879b52016-02-24 16:53:47 -060028from tempest.lib import decorators
Mate Lakat99ee9142012-09-14 12:34:46 +010029
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000030CONF = config.CONF
Sarafraj Singh61e40452016-09-29 13:06:59 -050031LOG = logging.getLogger(__name__)
Matthew Treinishb0a78fc2014-01-29 16:49:12 +000032
Mate Lakat99ee9142012-09-14 12:34:46 +010033
zhufl0d784eb2018-08-23 11:19:16 +080034class LiveMigrationTestBase(base.BaseV2ComputeAdminTest):
zhufl1b3b03d2020-04-16 08:38:16 +080035 """Test live migration operations supported by admin user"""
Mate Lakat99ee9142012-09-14 12:34:46 +010036
Eric Friedbfaa50f2020-01-09 12:04:54 -060037 create_default_network = True
38
Mate Lakat99ee9142012-09-14 12:34:46 +010039 @classmethod
Timofey Durakovad7aea52015-10-12 13:37:44 +030040 def skip_checks(cls):
zhufl0d784eb2018-08-23 11:19:16 +080041 super(LiveMigrationTestBase, cls).skip_checks()
Timofey Durakovad7aea52015-10-12 13:37:44 +030042
43 if not CONF.compute_feature_enabled.live_migration:
44 skip_msg = ("%s skipped as live-migration is "
45 "not available" % cls.__name__)
46 raise cls.skipException(skip_msg)
Jordan Pittierf60c1002015-11-16 11:24:09 +010047 if CONF.compute.min_compute_nodes < 2:
Timofey Durakov7a04e912015-11-17 17:37:53 +030048 raise cls.skipException(
Timofey Durakovad7aea52015-10-12 13:37:44 +030049 "Less than 2 compute nodes, skipping migration test.")
50
51 @classmethod
Rohan Kanade60b73092015-02-04 17:58:19 +053052 def setup_clients(cls):
zhufl0d784eb2018-08-23 11:19:16 +080053 super(LiveMigrationTestBase, cls).setup_clients()
Jordan Pittier8160d312017-04-18 11:52:23 +020054 cls.admin_migration_client = cls.os_admin.migrations_client
elajkat0b14db22021-02-08 16:43:59 +010055 cls.networks_client = cls.os_primary.networks_client
56 cls.subnets_client = cls.os_primary.subnets_client
57 cls.ports_client = cls.os_primary.ports_client
58 cls.trunks_client = cls.os_primary.trunks_client
Mate Lakat99ee9142012-09-14 12:34:46 +010059
Claudiu Belu516b7a42016-03-03 06:28:43 -080060 def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
Eli Qiaoe07eacc2016-03-03 13:49:37 +080061 kwargs = dict()
62 block_migration = getattr(self, 'block_migration', None)
63 if self.block_migration is None:
zhufl40891d22018-03-30 10:45:05 +080064 if self.is_requested_microversion_compatible('2.24'):
65 kwargs['disk_over_commit'] = False
Eli Qiaoe07eacc2016-03-03 13:49:37 +080066 block_migration = (CONF.compute_feature_enabled.
67 block_migration_for_live_migration and
68 not volume_backed)
zhufl4a2cfff2017-01-03 15:45:02 +080069 self.admin_servers_client.live_migrate_server(
Timofey Durakovf358a7e2015-10-05 13:06:51 +030070 server_id, host=dest_host, block_migration=block_migration,
Eli Qiaoe07eacc2016-03-03 13:49:37 +080071 **kwargs)
Mate Lakat99ee9142012-09-14 12:34:46 +010072
Sarafraj Singh61e40452016-09-29 13:06:59 -050073 def _live_migrate(self, server_id, target_host, state,
74 volume_backed=False):
zhuflbe052d62019-11-04 10:56:02 +080075 # If target_host is None, check whether source host is different with
76 # the new host after migration.
77 if target_host is None:
78 source_host = self.get_host_for_server(server_id)
Sarafraj Singh61e40452016-09-29 13:06:59 -050079 self._migrate_server_to(server_id, target_host, volume_backed)
80 waiters.wait_for_server_status(self.servers_client, server_id, state)
81 migration_list = (self.admin_migration_client.list_migrations()
82 ['migrations'])
83
84 msg = ("Live Migration failed. Migrations list for Instance "
85 "%s: [" % server_id)
86 for live_migration in migration_list:
87 if (live_migration['instance_uuid'] == server_id):
88 msg += "\n%s" % live_migration
89 msg += "]"
zhuflbe052d62019-11-04 10:56:02 +080090 if target_host is None:
91 self.assertNotEqual(source_host,
92 self.get_host_for_server(server_id), msg)
93 else:
94 self.assertEqual(target_host, self.get_host_for_server(server_id),
95 msg)
Sarafraj Singh61e40452016-09-29 13:06:59 -050096
zhufl0d784eb2018-08-23 11:19:16 +080097
98class LiveMigrationTest(LiveMigrationTestBase):
99 max_microversion = '2.24'
100 block_migration = None
101
Lee Yarwood00bdb222022-03-02 13:06:11 -0600102 @classmethod
103 def setup_credentials(cls):
104 cls.prepare_instance_network()
105 super(LiveMigrationTest, cls).setup_credentials()
106
Joe Gordon8843f0f2015-03-17 15:07:34 -0700107 def _test_live_migration(self, state='ACTIVE', volume_backed=False):
108 """Tests live migration between two hosts.
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800109
110 Requires CONF.compute_feature_enabled.live_migration to be True.
111
112 :param state: The vm_state the migrated server should be in before and
113 after the live migration. Supported values are 'ACTIVE'
114 and 'PAUSED'.
Joe Gordon8843f0f2015-03-17 15:07:34 -0700115 :param volume_backed: If the instance is volume backed or not. If
116 volume_backed, *block* migration is not used.
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800117 """
Timofey Durakovf358a7e2015-10-05 13:06:51 +0300118 # Live migrate an instance to another host
Jordan Pittier599a3562016-01-08 17:38:14 +0100119 server_id = self.create_test_server(wait_until="ACTIVE",
120 volume_backed=volume_backed)['id']
Duc Truong09941202017-06-07 10:15:20 -0700121 source_host = self.get_host_for_server(server_id)
zhuflbe052d62019-11-04 10:56:02 +0800122 if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
123 # not to specify a host so that the scheduler will pick one
124 destination_host = None
125 else:
126 destination_host = self.get_host_other_than(server_id)
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800127
128 if state == 'PAUSED':
129 self.admin_servers_client.pause_server(server_id)
Ken'ichi Ohmichi0eb153c2015-07-13 02:18:25 +0000130 waiters.wait_for_server_status(self.admin_servers_client,
131 server_id, state)
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800132
Sarafraj Singh61e40452016-09-29 13:06:59 -0500133 LOG.info("Live migrate from source %s to destination %s",
134 source_host, destination_host)
135 self._live_migrate(server_id, destination_host, state, volume_backed)
136 if CONF.compute_feature_enabled.live_migrate_back_and_forth:
137 # If live_migrate_back_and_forth is enabled it is a grenade job.
138 # Therefore test should validate whether LM is compatible in both
139 # ways, so live migrate VM back to the source host
140 LOG.info("Live migrate back to source %s", source_host)
141 self._live_migrate(server_id, source_host, state, volume_backed)
Mate Lakat99ee9142012-09-14 12:34:46 +0100142
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800143 @decorators.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
Paras Babbare18335e2020-06-23 17:09:19 -0400144 @testtools.skipUnless(CONF.compute_feature_enabled.
145 block_migration_for_live_migration,
146 'Block Live migration not available')
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800147 def test_live_block_migration(self):
zhufl1b3b03d2020-04-16 08:38:16 +0800148 """Test live migrating an active server"""
Joe Gordon8843f0f2015-03-17 15:07:34 -0700149 self._test_live_migration()
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800150
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800151 @decorators.idempotent_id('1e107f21-61b2-4988-8f22-b196e938ab88')
Paras Babbare18335e2020-06-23 17:09:19 -0400152 @testtools.skipUnless(CONF.compute_feature_enabled.
153 block_migration_for_live_migration,
154 'Block Live migration not available')
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800155 @testtools.skipUnless(CONF.compute_feature_enabled.pause,
156 'Pause is not available.')
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800157 def test_live_block_migration_paused(self):
zhufl1b3b03d2020-04-16 08:38:16 +0800158 """Test live migrating a paused server"""
Joe Gordon8843f0f2015-03-17 15:07:34 -0700159 self._test_live_migration(state='PAUSED')
160
melanie witt334f3132017-12-14 21:49:55 +0000161 @testtools.skipUnless(CONF.compute_feature_enabled.
162 volume_backed_live_migration,
163 'Volume-backed live migration not available')
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800164 @decorators.idempotent_id('5071cf17-3004-4257-ae61-73a84e28badd')
Andrea Frittolicd368412017-08-14 21:37:56 +0100165 @utils.services('volume')
Joe Gordon8843f0f2015-03-17 15:07:34 -0700166 def test_volume_backed_live_migration(self):
zhufl1b3b03d2020-04-16 08:38:16 +0800167 """Test live migrating an active server booted from volume"""
Joe Gordon8843f0f2015-03-17 15:07:34 -0700168 self._test_live_migration(volume_backed=True)
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800169
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800170 @decorators.idempotent_id('e19c0cc6-6720-4ed8-be83-b6603ed5c812')
Timofey Durakovad7aea52015-10-12 13:37:44 +0300171 @testtools.skipIf(not CONF.compute_feature_enabled.
Matthew Treinishd5c96022013-10-17 21:51:23 +0000172 block_migration_for_live_migration,
Bob Ballc078be92013-04-09 14:25:00 +0100173 'Block Live migration not available')
Matthew Treinishd5c96022013-10-17 21:51:23 +0000174 @testtools.skipIf(not CONF.compute_feature_enabled.
175 block_migrate_cinder_iscsi,
Bob Ballc078be92013-04-09 14:25:00 +0100176 'Block Live migration not configured for iSCSI')
lkuchlanf4413c42018-05-03 07:49:35 +0300177 @utils.services('volume')
Lee Yarwood249b30e2020-02-26 12:38:14 +0000178 def test_live_block_migration_with_attached_volume(self):
179 """Test the live-migration of an instance with an attached volume.
180
181 This tests the live-migration of an instance with both a local disk and
182 attach volume. This differs from test_volume_backed_live_migration
183 above that tests live-migration with only an attached volume.
184 """
Lee Yarwoodc8526392022-03-02 13:06:11 -0600185 validation_resources = self.get_class_validation_resources(
Lee Yarwood00bdb222022-03-02 13:06:11 -0600186 self.os_primary)
187 server = self.create_test_server(
188 validatable=True,
189 validation_resources=validation_resources,
190 wait_until="SSHABLE")
Matt Riedemanncb16a662016-10-01 18:30:05 -0400191 server_id = server['id']
zhuflbe052d62019-11-04 10:56:02 +0800192 if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
193 # not to specify a host so that the scheduler will pick one
194 target_host = None
195 else:
196 target_host = self.get_host_other_than(server_id)
Bob Ballc078be92013-04-09 14:25:00 +0100197
Matt Riedemanncb16a662016-10-01 18:30:05 -0400198 volume = self.create_volume()
Bob Ballc078be92013-04-09 14:25:00 +0100199
200 # Attach the volume to the server
Matt Riedemanncb16a662016-10-01 18:30:05 -0400201 self.attach_volume(server, volume, device='/dev/xvdb')
lianghao39d86992017-04-11 16:01:26 +0800202 server = self.admin_servers_client.show_server(server_id)['server']
203 volume_id1 = server["os-extended-volumes:volumes_attached"][0]["id"]
lianghaod5818442018-01-11 19:07:26 +0800204 self._live_migrate(server_id, target_host, 'ACTIVE')
lianghao39d86992017-04-11 16:01:26 +0800205
206 server = self.admin_servers_client.show_server(server_id)['server']
207 volume_id2 = server["os-extended-volumes:volumes_attached"][0]["id"]
208
lianghao39d86992017-04-11 16:01:26 +0800209 self.assertEqual(volume_id1, volume_id2)
Eli Qiaoe07eacc2016-03-03 13:49:37 +0800210
elajkat0b14db22021-02-08 16:43:59 +0100211 def _create_net_subnet(self, name, cidr):
212 net_name = data_utils.rand_name(name=name)
213 net = self.networks_client.create_network(name=net_name)['network']
214 self.addClassResourceCleanup(
215 self.networks_client.delete_network, net['id'])
216
217 subnet = self.subnets_client.create_subnet(
218 network_id=net['id'],
219 cidr=cidr,
220 ip_version=4)
221 self.addClassResourceCleanup(self.subnets_client.delete_subnet,
222 subnet['subnet']['id'])
223 return net
224
225 def _create_port(self, network_id, name):
226 name = data_utils.rand_name(name=name)
227 port = self.ports_client.create_port(name=name,
228 network_id=network_id)['port']
229 self.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
230 self.ports_client.delete_port,
231 port_id=port['id'])
232 return port
233
234 def _create_trunk_with_subport(self):
235 tenant_network = self.get_tenant_network()
236 parent = self._create_port(network_id=tenant_network['id'],
237 name='parent')
238 net = self._create_net_subnet(name='subport_net', cidr='19.80.0.0/24')
239 subport = self._create_port(network_id=net['id'], name='subport')
240
241 trunk = self.trunks_client.create_trunk(
242 name=data_utils.rand_name('trunk'),
243 port_id=parent['id'],
244 sub_ports=[{"segmentation_id": 42, "port_id": subport['id'],
245 "segmentation_type": "vlan"}]
246 )['trunk']
247 self.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
248 self.trunks_client.delete_trunk,
249 trunk['id'])
250 return trunk, parent, subport
251
252 def _is_port_status_active(self, port_id):
253 port = self.ports_client.show_port(port_id)['port']
254 return port['status'] == 'ACTIVE'
255
256 @decorators.idempotent_id('0022c12e-a482-42b0-be2d-396b5f0cffe3')
257 @utils.requires_ext(service='network', extension='trunk')
258 @utils.services('network')
259 def test_live_migration_with_trunk(self):
260 """Test live migration with trunk and subport"""
261 trunk, parent, subport = self._create_trunk_with_subport()
262
263 server = self.create_test_server(
264 wait_until="ACTIVE", networks=[{'port': parent['id']}])
265
266 # Wait till subport status is ACTIVE
267 self.assertTrue(
268 test_utils.call_until_true(
269 self._is_port_status_active, CONF.validation.connect_timeout,
270 5, subport['id']))
elajkat5c106842021-04-15 17:45:34 +0200271 self.assertTrue(
272 test_utils.call_until_true(
273 self._is_port_status_active, CONF.validation.connect_timeout,
274 5, parent['id']))
elajkat0b14db22021-02-08 16:43:59 +0100275 subport = self.ports_client.show_port(subport['id'])['port']
276
277 if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
278 # not to specify a host so that the scheduler will pick one
279 target_host = None
280 else:
281 target_host = self.get_host_other_than(server['id'])
282
283 self._live_migrate(server['id'], target_host, 'ACTIVE')
284
285 # Wait till subport status is ACTIVE
286 self.assertTrue(
287 test_utils.call_until_true(
288 self._is_port_status_active, CONF.validation.connect_timeout,
289 5, subport['id']))
elajkat5c106842021-04-15 17:45:34 +0200290 self.assertTrue(
291 test_utils.call_until_true(
292 self._is_port_status_active, CONF.validation.connect_timeout,
293 5, parent['id']))
elajkat0b14db22021-02-08 16:43:59 +0100294
Eli Qiaoe07eacc2016-03-03 13:49:37 +0800295
zhufl0d784eb2018-08-23 11:19:16 +0800296class LiveMigrationRemoteConsolesV26Test(LiveMigrationTestBase):
Markus Zoeller69d58b82017-02-17 10:09:22 +0100297 min_microversion = '2.6'
298 max_microversion = 'latest'
299
300 @decorators.idempotent_id('6190af80-513e-4f0f-90f2-9714e84955d7')
301 @testtools.skipUnless(CONF.compute_feature_enabled.serial_console,
302 'Serial console not supported.')
303 @testtools.skipUnless(
Andrea Frittoli88eb6772017-08-07 21:06:27 +0100304 compute.is_scheduler_filter_enabled("DifferentHostFilter"),
Markus Zoeller69d58b82017-02-17 10:09:22 +0100305 'DifferentHostFilter is not available.')
306 def test_live_migration_serial_console(self):
307 """Test the live-migration of an instance which has a serial console
308
309 The serial console feature of an instance uses ports on the host.
310 These ports need to be updated when they are already in use by
311 another instance on the target host. This test checks if this
312 update behavior is correctly done, by connecting to the serial
313 consoles of the instances before and after the live migration.
314 """
315 server01_id = self.create_test_server(wait_until='ACTIVE')['id']
316 hints = {'different_host': server01_id}
317 server02_id = self.create_test_server(scheduler_hints=hints,
318 wait_until='ACTIVE')['id']
Duc Truong09941202017-06-07 10:15:20 -0700319 host01_id = self.get_host_for_server(server01_id)
320 host02_id = self.get_host_for_server(server02_id)
Markus Zoeller69d58b82017-02-17 10:09:22 +0100321 self.assertNotEqual(host01_id, host02_id)
322
323 # At this step we have 2 instances on different hosts, both with
324 # serial consoles, both with port 10000 (the default value).
325 # https://bugs.launchpad.net/nova/+bug/1455252 describes the issue
326 # when live-migrating in such a scenario.
327
328 self._verify_console_interaction(server01_id)
329 self._verify_console_interaction(server02_id)
330
lianghaod5818442018-01-11 19:07:26 +0800331 self._live_migrate(server01_id, host02_id, 'ACTIVE')
Markus Zoeller69d58b82017-02-17 10:09:22 +0100332 self._verify_console_interaction(server01_id)
333 # At this point, both instances have a valid serial console
334 # connection, which means the ports got updated.
335
336 def _verify_console_interaction(self, server_id):
337 body = self.servers_client.get_remote_console(server_id,
338 console_type='serial',
339 protocol='serial')
340 console_url = body['remote_console']['url']
341 data = "test_live_migration_serial_console"
342 console_output = ''
343 t = 0.0
344 interval = 0.1
345
346 ws = compute.create_websocket(console_url)
347 try:
348 # NOTE (markus_z): It can take a long time until the terminal
349 # of the instance is available for interaction. Hence the
350 # long timeout value.
351 while data not in console_output and t <= 120.0:
352 try:
353 ws.send_frame(data)
Pengfei Zhang20f41542018-01-19 11:48:18 +0800354 received = ws.receive_frame()
355 console_output += received
Markus Zoeller69d58b82017-02-17 10:09:22 +0100356 except Exception:
357 # In case we had an issue with send/receive on the
358 # websocket connection, we create a new one.
359 ws = compute.create_websocket(console_url)
360 time.sleep(interval)
361 t += interval
362 finally:
363 ws.close()
364 self.assertIn(data, console_output)
365
366
Duc Truong09941202017-06-07 10:15:20 -0700367class LiveAutoBlockMigrationV225Test(LiveMigrationTest):
Eli Qiaoe07eacc2016-03-03 13:49:37 +0800368 min_microversion = '2.25'
369 max_microversion = 'latest'
370 block_migration = 'auto'