blob: 1cb8004f57f5449bc44755c1174f97b80ae8d618 [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
Ghanshyam Manne2183ca2023-02-10 19:31:52 -0600143 @decorators.attr(type='multinode')
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800144 @decorators.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
Paras Babbare18335e2020-06-23 17:09:19 -0400145 @testtools.skipUnless(CONF.compute_feature_enabled.
146 block_migration_for_live_migration,
147 'Block Live migration not available')
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800148 def test_live_block_migration(self):
zhufl1b3b03d2020-04-16 08:38:16 +0800149 """Test live migrating an active server"""
Joe Gordon8843f0f2015-03-17 15:07:34 -0700150 self._test_live_migration()
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800151
Ghanshyam Manne2183ca2023-02-10 19:31:52 -0600152 @decorators.attr(type='multinode')
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800153 @decorators.idempotent_id('1e107f21-61b2-4988-8f22-b196e938ab88')
Paras Babbare18335e2020-06-23 17:09:19 -0400154 @testtools.skipUnless(CONF.compute_feature_enabled.
155 block_migration_for_live_migration,
156 'Block Live migration not available')
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800157 @testtools.skipUnless(CONF.compute_feature_enabled.pause,
158 'Pause is not available.')
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800159 def test_live_block_migration_paused(self):
zhufl1b3b03d2020-04-16 08:38:16 +0800160 """Test live migrating a paused server"""
Joe Gordon8843f0f2015-03-17 15:07:34 -0700161 self._test_live_migration(state='PAUSED')
162
Ghanshyam Manne2183ca2023-02-10 19:31:52 -0600163 @decorators.attr(type='multinode')
melanie witt334f3132017-12-14 21:49:55 +0000164 @testtools.skipUnless(CONF.compute_feature_enabled.
165 volume_backed_live_migration,
166 'Volume-backed live migration not available')
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800167 @decorators.idempotent_id('5071cf17-3004-4257-ae61-73a84e28badd')
Andrea Frittolicd368412017-08-14 21:37:56 +0100168 @utils.services('volume')
Joe Gordon8843f0f2015-03-17 15:07:34 -0700169 def test_volume_backed_live_migration(self):
zhufl1b3b03d2020-04-16 08:38:16 +0800170 """Test live migrating an active server booted from volume"""
Joe Gordon8843f0f2015-03-17 15:07:34 -0700171 self._test_live_migration(volume_backed=True)
Matt Riedemannbb9f7042015-03-03 08:53:11 -0800172
Ghanshyam Manne2183ca2023-02-10 19:31:52 -0600173 @decorators.attr(type='multinode')
Ken'ichi Ohmichiebbfd1c2017-01-27 16:37:00 -0800174 @decorators.idempotent_id('e19c0cc6-6720-4ed8-be83-b6603ed5c812')
Timofey Durakovad7aea52015-10-12 13:37:44 +0300175 @testtools.skipIf(not CONF.compute_feature_enabled.
Matthew Treinishd5c96022013-10-17 21:51:23 +0000176 block_migration_for_live_migration,
Bob Ballc078be92013-04-09 14:25:00 +0100177 'Block Live migration not available')
Matthew Treinishd5c96022013-10-17 21:51:23 +0000178 @testtools.skipIf(not CONF.compute_feature_enabled.
179 block_migrate_cinder_iscsi,
Bob Ballc078be92013-04-09 14:25:00 +0100180 'Block Live migration not configured for iSCSI')
lkuchlanf4413c42018-05-03 07:49:35 +0300181 @utils.services('volume')
Lee Yarwood249b30e2020-02-26 12:38:14 +0000182 def test_live_block_migration_with_attached_volume(self):
183 """Test the live-migration of an instance with an attached volume.
184
185 This tests the live-migration of an instance with both a local disk and
186 attach volume. This differs from test_volume_backed_live_migration
187 above that tests live-migration with only an attached volume.
188 """
Lee Yarwoodc8526392022-03-02 13:06:11 -0600189 validation_resources = self.get_class_validation_resources(
Lee Yarwood00bdb222022-03-02 13:06:11 -0600190 self.os_primary)
191 server = self.create_test_server(
192 validatable=True,
193 validation_resources=validation_resources,
194 wait_until="SSHABLE")
Matt Riedemanncb16a662016-10-01 18:30:05 -0400195 server_id = server['id']
zhuflbe052d62019-11-04 10:56:02 +0800196 if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
197 # not to specify a host so that the scheduler will pick one
198 target_host = None
199 else:
200 target_host = self.get_host_other_than(server_id)
Bob Ballc078be92013-04-09 14:25:00 +0100201
Matt Riedemanncb16a662016-10-01 18:30:05 -0400202 volume = self.create_volume()
Bob Ballc078be92013-04-09 14:25:00 +0100203
204 # Attach the volume to the server
Matt Riedemanncb16a662016-10-01 18:30:05 -0400205 self.attach_volume(server, volume, device='/dev/xvdb')
lianghao39d86992017-04-11 16:01:26 +0800206 server = self.admin_servers_client.show_server(server_id)['server']
207 volume_id1 = server["os-extended-volumes:volumes_attached"][0]["id"]
lianghaod5818442018-01-11 19:07:26 +0800208 self._live_migrate(server_id, target_host, 'ACTIVE')
lianghao39d86992017-04-11 16:01:26 +0800209
210 server = self.admin_servers_client.show_server(server_id)['server']
211 volume_id2 = server["os-extended-volumes:volumes_attached"][0]["id"]
212
lianghao39d86992017-04-11 16:01:26 +0800213 self.assertEqual(volume_id1, volume_id2)
Eli Qiaoe07eacc2016-03-03 13:49:37 +0800214
elajkat0b14db22021-02-08 16:43:59 +0100215 def _create_net_subnet(self, name, cidr):
216 net_name = data_utils.rand_name(name=name)
217 net = self.networks_client.create_network(name=net_name)['network']
218 self.addClassResourceCleanup(
219 self.networks_client.delete_network, net['id'])
220
221 subnet = self.subnets_client.create_subnet(
222 network_id=net['id'],
223 cidr=cidr,
224 ip_version=4)
225 self.addClassResourceCleanup(self.subnets_client.delete_subnet,
226 subnet['subnet']['id'])
227 return net
228
229 def _create_port(self, network_id, name):
230 name = data_utils.rand_name(name=name)
231 port = self.ports_client.create_port(name=name,
232 network_id=network_id)['port']
233 self.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
234 self.ports_client.delete_port,
235 port_id=port['id'])
236 return port
237
238 def _create_trunk_with_subport(self):
239 tenant_network = self.get_tenant_network()
240 parent = self._create_port(network_id=tenant_network['id'],
241 name='parent')
242 net = self._create_net_subnet(name='subport_net', cidr='19.80.0.0/24')
243 subport = self._create_port(network_id=net['id'], name='subport')
244
245 trunk = self.trunks_client.create_trunk(
246 name=data_utils.rand_name('trunk'),
247 port_id=parent['id'],
248 sub_ports=[{"segmentation_id": 42, "port_id": subport['id'],
249 "segmentation_type": "vlan"}]
250 )['trunk']
251 self.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
252 self.trunks_client.delete_trunk,
253 trunk['id'])
254 return trunk, parent, subport
255
256 def _is_port_status_active(self, port_id):
257 port = self.ports_client.show_port(port_id)['port']
258 return port['status'] == 'ACTIVE'
259
Ghanshyam Manne2183ca2023-02-10 19:31:52 -0600260 @decorators.attr(type='multinode')
elajkat0b14db22021-02-08 16:43:59 +0100261 @decorators.idempotent_id('0022c12e-a482-42b0-be2d-396b5f0cffe3')
262 @utils.requires_ext(service='network', extension='trunk')
263 @utils.services('network')
264 def test_live_migration_with_trunk(self):
265 """Test live migration with trunk and subport"""
266 trunk, parent, subport = self._create_trunk_with_subport()
267
268 server = self.create_test_server(
269 wait_until="ACTIVE", networks=[{'port': parent['id']}])
270
271 # Wait till subport status is ACTIVE
272 self.assertTrue(
273 test_utils.call_until_true(
274 self._is_port_status_active, CONF.validation.connect_timeout,
275 5, subport['id']))
elajkat5c106842021-04-15 17:45:34 +0200276 self.assertTrue(
277 test_utils.call_until_true(
278 self._is_port_status_active, CONF.validation.connect_timeout,
279 5, parent['id']))
elajkat0b14db22021-02-08 16:43:59 +0100280 subport = self.ports_client.show_port(subport['id'])['port']
281
282 if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
283 # not to specify a host so that the scheduler will pick one
284 target_host = None
285 else:
286 target_host = self.get_host_other_than(server['id'])
287
288 self._live_migrate(server['id'], target_host, 'ACTIVE')
289
290 # Wait till subport status is ACTIVE
291 self.assertTrue(
292 test_utils.call_until_true(
293 self._is_port_status_active, CONF.validation.connect_timeout,
294 5, subport['id']))
elajkat5c106842021-04-15 17:45:34 +0200295 self.assertTrue(
296 test_utils.call_until_true(
297 self._is_port_status_active, CONF.validation.connect_timeout,
298 5, parent['id']))
elajkat0b14db22021-02-08 16:43:59 +0100299
Eli Qiaoe07eacc2016-03-03 13:49:37 +0800300
zhufl0d784eb2018-08-23 11:19:16 +0800301class LiveMigrationRemoteConsolesV26Test(LiveMigrationTestBase):
Markus Zoeller69d58b82017-02-17 10:09:22 +0100302 min_microversion = '2.6'
303 max_microversion = 'latest'
304
Ghanshyam Manne2183ca2023-02-10 19:31:52 -0600305 @decorators.attr(type='multinode')
Markus Zoeller69d58b82017-02-17 10:09:22 +0100306 @decorators.idempotent_id('6190af80-513e-4f0f-90f2-9714e84955d7')
307 @testtools.skipUnless(CONF.compute_feature_enabled.serial_console,
308 'Serial console not supported.')
309 @testtools.skipUnless(
Andrea Frittoli88eb6772017-08-07 21:06:27 +0100310 compute.is_scheduler_filter_enabled("DifferentHostFilter"),
Markus Zoeller69d58b82017-02-17 10:09:22 +0100311 'DifferentHostFilter is not available.')
312 def test_live_migration_serial_console(self):
313 """Test the live-migration of an instance which has a serial console
314
315 The serial console feature of an instance uses ports on the host.
316 These ports need to be updated when they are already in use by
317 another instance on the target host. This test checks if this
318 update behavior is correctly done, by connecting to the serial
319 consoles of the instances before and after the live migration.
320 """
321 server01_id = self.create_test_server(wait_until='ACTIVE')['id']
322 hints = {'different_host': server01_id}
323 server02_id = self.create_test_server(scheduler_hints=hints,
324 wait_until='ACTIVE')['id']
Duc Truong09941202017-06-07 10:15:20 -0700325 host01_id = self.get_host_for_server(server01_id)
326 host02_id = self.get_host_for_server(server02_id)
Markus Zoeller69d58b82017-02-17 10:09:22 +0100327 self.assertNotEqual(host01_id, host02_id)
328
329 # At this step we have 2 instances on different hosts, both with
330 # serial consoles, both with port 10000 (the default value).
331 # https://bugs.launchpad.net/nova/+bug/1455252 describes the issue
332 # when live-migrating in such a scenario.
333
334 self._verify_console_interaction(server01_id)
335 self._verify_console_interaction(server02_id)
336
lianghaod5818442018-01-11 19:07:26 +0800337 self._live_migrate(server01_id, host02_id, 'ACTIVE')
Markus Zoeller69d58b82017-02-17 10:09:22 +0100338 self._verify_console_interaction(server01_id)
339 # At this point, both instances have a valid serial console
340 # connection, which means the ports got updated.
341
342 def _verify_console_interaction(self, server_id):
343 body = self.servers_client.get_remote_console(server_id,
344 console_type='serial',
345 protocol='serial')
346 console_url = body['remote_console']['url']
347 data = "test_live_migration_serial_console"
348 console_output = ''
349 t = 0.0
350 interval = 0.1
351
352 ws = compute.create_websocket(console_url)
353 try:
354 # NOTE (markus_z): It can take a long time until the terminal
355 # of the instance is available for interaction. Hence the
356 # long timeout value.
357 while data not in console_output and t <= 120.0:
358 try:
359 ws.send_frame(data)
Pengfei Zhang20f41542018-01-19 11:48:18 +0800360 received = ws.receive_frame()
361 console_output += received
Markus Zoeller69d58b82017-02-17 10:09:22 +0100362 except Exception:
363 # In case we had an issue with send/receive on the
364 # websocket connection, we create a new one.
365 ws = compute.create_websocket(console_url)
366 time.sleep(interval)
367 t += interval
368 finally:
369 ws.close()
370 self.assertIn(data, console_output)
371
372
Duc Truong09941202017-06-07 10:15:20 -0700373class LiveAutoBlockMigrationV225Test(LiveMigrationTest):
Eli Qiaoe07eacc2016-03-03 13:49:37 +0800374 min_microversion = '2.25'
375 max_microversion = 'latest'
376 block_migration = 'auto'