Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 1 | # Copyright 2013 IBM Corp. |
| 2 | # 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 | |
Rohan Kanade | 9ce97df | 2013-12-10 18:59:35 +0530 | [diff] [blame] | 16 | import time |
| 17 | |
Slawek Kaplonski | b3daeb4 | 2019-06-29 23:49:59 +0200 | [diff] [blame] | 18 | from oslo_log import log |
junboli | bc2ae86 | 2017-07-29 15:46:48 +0800 | [diff] [blame] | 19 | |
Sean Dague | 1937d09 | 2013-05-17 16:36:38 -0400 | [diff] [blame] | 20 | from tempest.api.compute import base |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 21 | from tempest.common import compute |
Andrea Frittoli | cd36841 | 2017-08-14 21:37:56 +0100 | [diff] [blame] | 22 | from tempest.common import utils |
Ryan Tidwell | 1964a26 | 2016-05-04 15:13:23 -0700 | [diff] [blame] | 23 | from tempest.common.utils import net_utils |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 24 | from tempest.common import waiters |
Matthew Treinish | b0a78fc | 2014-01-29 16:49:12 +0000 | [diff] [blame] | 25 | from tempest import config |
Doug Schveninger | 24675aa | 2019-08-16 22:28:39 -0500 | [diff] [blame] | 26 | from tempest.lib.common.utils import data_utils |
Lucas Alvares Gomes | 692422b | 2018-10-10 10:33:45 +0100 | [diff] [blame] | 27 | from tempest.lib.common.utils.linux import remote_client |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 28 | from tempest.lib.common.utils import test_utils |
Matt Riedemann | 3570c1e | 2016-07-29 12:15:38 -0400 | [diff] [blame] | 29 | from tempest.lib import decorators |
Andrea Frittoli (andreaf) | db9672e | 2016-02-23 14:07:24 -0500 | [diff] [blame] | 30 | from tempest.lib import exceptions as lib_exc |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 31 | |
Matthew Treinish | b0a78fc | 2014-01-29 16:49:12 +0000 | [diff] [blame] | 32 | CONF = config.CONF |
| 33 | |
Slawek Kaplonski | b3daeb4 | 2019-06-29 23:49:59 +0200 | [diff] [blame] | 34 | LOG = log.getLogger(__name__) |
| 35 | |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 36 | |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 37 | class AttachInterfacesTestBase(base.BaseV2ComputeTest): |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 38 | |
| 39 | @classmethod |
Emily Hugenbruch | e7991d9 | 2014-12-12 16:53:36 +0000 | [diff] [blame] | 40 | def skip_checks(cls): |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 41 | super(AttachInterfacesTestBase, cls).skip_checks() |
Matthew Treinish | b0a78fc | 2014-01-29 16:49:12 +0000 | [diff] [blame] | 42 | if not CONF.service_available.neutron: |
Mark McClain | f2982e8 | 2013-07-06 17:48:03 -0400 | [diff] [blame] | 43 | raise cls.skipException("Neutron is required") |
Adam Gandelman | 7186f7a | 2014-07-23 09:28:56 -0400 | [diff] [blame] | 44 | if not CONF.compute_feature_enabled.interface_attach: |
| 45 | raise cls.skipException("Interface attachment is not available.") |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 46 | if not CONF.validation.run_validation: |
| 47 | raise cls.skipException('Validation should be enabled to ensure ' |
| 48 | 'guest OS is running and capable of ' |
| 49 | 'processing ACPI events.') |
Emily Hugenbruch | e7991d9 | 2014-12-12 16:53:36 +0000 | [diff] [blame] | 50 | |
| 51 | @classmethod |
| 52 | def setup_credentials(cls): |
Salvatore Orlando | 5a33724 | 2014-01-15 22:49:22 +0000 | [diff] [blame] | 53 | # This test class requires network and subnet |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 54 | cls.set_network_resources(network=True, subnet=True, router=True, |
| 55 | dhcp=True) |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 56 | super(AttachInterfacesTestBase, cls).setup_credentials() |
Emily Hugenbruch | e7991d9 | 2014-12-12 16:53:36 +0000 | [diff] [blame] | 57 | |
| 58 | @classmethod |
| 59 | def setup_clients(cls): |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 60 | super(AttachInterfacesTestBase, cls).setup_clients() |
Jordan Pittier | 8160d31 | 2017-04-18 11:52:23 +0200 | [diff] [blame] | 61 | cls.subnets_client = cls.os_primary.subnets_client |
| 62 | cls.ports_client = cls.os_primary.ports_client |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 63 | |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 64 | def _wait_for_validation(self, server, validation_resources): |
| 65 | linux_client = remote_client.RemoteClient( |
| 66 | self.get_server_ip(server, validation_resources), |
| 67 | self.image_ssh_user, |
| 68 | self.image_ssh_password, |
| 69 | validation_resources['keypair']['private_key'], |
| 70 | server=server, |
Ade Lee | 6ded070 | 2021-09-04 15:56:34 -0400 | [diff] [blame] | 71 | servers_client=self.servers_client, |
| 72 | ssh_key_type=CONF.validation.ssh_key_type) |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 73 | linux_client.validate_authentication() |
| 74 | |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 75 | def _create_server_get_interfaces(self): |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 76 | validation_resources = self.get_test_validation_resources( |
| 77 | self.os_primary) |
| 78 | server = self.create_test_server( |
| 79 | validatable=True, |
| 80 | validation_resources=validation_resources, |
| 81 | wait_until='ACTIVE') |
Mark Goddard | fa30d2f | 2019-09-02 14:41:02 +0100 | [diff] [blame] | 82 | # NOTE(mgoddard): Get detailed server to ensure addresses are present |
| 83 | # in fixed IP case. |
| 84 | server = self.servers_client.show_server(server['id'])['server'] |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 85 | # NOTE(artom) self.create_test_server adds cleanups, but this is |
| 86 | # apparently not enough? Add cleanup here. |
| 87 | self.addCleanup(self.delete_server, server['id']) |
| 88 | self._wait_for_validation(server, validation_resources) |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 89 | try: |
| 90 | fip = set([validation_resources['floating_ip']['ip']]) |
| 91 | except KeyError: |
| 92 | fip = () |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 93 | ifs = (self.interfaces_client.list_interfaces(server['id']) |
| 94 | ['interfaceAttachments']) |
| 95 | body = waiters.wait_for_interface_status( |
| 96 | self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE') |
| 97 | ifs[0]['port_state'] = body['port_state'] |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 98 | return server, ifs, fip |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 99 | |
| 100 | |
| 101 | class AttachInterfacesTestJSON(AttachInterfacesTestBase): |
zhufl | 735e169 | 2020-08-13 16:46:50 +0800 | [diff] [blame] | 102 | """Test attaching interfaces""" |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 103 | |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 104 | def wait_for_port_detach(self, port_id): |
| 105 | """Waits for the port's device_id to be unset. |
| 106 | |
| 107 | :param port_id: The id of the port being detached. |
| 108 | :returns: The final port dict from the show_port response. |
| 109 | """ |
| 110 | port = self.ports_client.show_port(port_id)['port'] |
| 111 | device_id = port['device_id'] |
| 112 | start = int(time.time()) |
| 113 | |
| 114 | # NOTE(mriedem): Nova updates the port's device_id to '' rather than |
| 115 | # None, but it's not contractual so handle Falsey either way. |
| 116 | while device_id: |
| 117 | time.sleep(self.build_interval) |
| 118 | port = self.ports_client.show_port(port_id)['port'] |
| 119 | device_id = port['device_id'] |
| 120 | |
| 121 | timed_out = int(time.time()) - start >= self.build_timeout |
| 122 | |
| 123 | if device_id and timed_out: |
| 124 | message = ('Port %s failed to detach (device_id %s) within ' |
| 125 | 'the required time (%s s).' % |
| 126 | (port_id, device_id, self.build_timeout)) |
guo yunxian | ebb15f2 | 2016-11-01 21:03:35 +0800 | [diff] [blame] | 127 | raise lib_exc.TimeoutException(message) |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 128 | |
| 129 | return port |
| 130 | |
lianghao | 1635334 | 2017-11-28 21:08:12 +0800 | [diff] [blame] | 131 | def _check_interface(self, iface, server_id=None, port_id=None, |
| 132 | network_id=None, fixed_ip=None, mac_addr=None): |
| 133 | if server_id: |
| 134 | iface = waiters.wait_for_interface_status( |
| 135 | self.interfaces_client, server_id, iface['port_id'], 'ACTIVE') |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 136 | if port_id: |
| 137 | self.assertEqual(iface['port_id'], port_id) |
| 138 | if network_id: |
| 139 | self.assertEqual(iface['net_id'], network_id) |
| 140 | if fixed_ip: |
| 141 | self.assertEqual(iface['fixed_ips'][0]['ip_address'], fixed_ip) |
venkata anil | 4537530 | 2014-12-30 10:41:43 +0000 | [diff] [blame] | 142 | if mac_addr: |
| 143 | self.assertEqual(iface['mac_addr'], mac_addr) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 144 | |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 145 | def _test_create_interface(self, server): |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 146 | iface = (self.interfaces_client.create_interface(server['id']) |
ghanshyam | a2364f1 | 2015-08-24 15:45:37 +0900 | [diff] [blame] | 147 | ['interfaceAttachment']) |
zhufl | 7b63833 | 2016-11-14 10:23:30 +0800 | [diff] [blame] | 148 | iface = waiters.wait_for_interface_status( |
| 149 | self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE') |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 150 | return iface |
| 151 | |
| 152 | def _test_create_interface_by_network_id(self, server, ifs): |
| 153 | network_id = ifs[0]['net_id'] |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 154 | iface = self.interfaces_client.create_interface( |
ghanshyam | a2364f1 | 2015-08-24 15:45:37 +0900 | [diff] [blame] | 155 | server['id'], net_id=network_id)['interfaceAttachment'] |
lianghao | 1635334 | 2017-11-28 21:08:12 +0800 | [diff] [blame] | 156 | self._check_interface(iface, server_id=server['id'], |
| 157 | network_id=network_id) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 158 | return iface |
| 159 | |
Maho Koshiya | 3fc1246 | 2015-12-14 19:03:12 +0900 | [diff] [blame] | 160 | def _test_create_interface_by_port_id(self, server, ifs): |
| 161 | network_id = ifs[0]['net_id'] |
Doug Schveninger | 24675aa | 2019-08-16 22:28:39 -0500 | [diff] [blame] | 162 | port = self.ports_client.create_port( |
| 163 | network_id=network_id, |
| 164 | name=data_utils.rand_name(self.__class__.__name__)) |
Maho Koshiya | 3fc1246 | 2015-12-14 19:03:12 +0900 | [diff] [blame] | 165 | port_id = port['port']['id'] |
| 166 | self.addCleanup(self.ports_client.delete_port, port_id) |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 167 | iface = self.interfaces_client.create_interface( |
Maho Koshiya | 3fc1246 | 2015-12-14 19:03:12 +0900 | [diff] [blame] | 168 | server['id'], port_id=port_id)['interfaceAttachment'] |
lianghao | 1635334 | 2017-11-28 21:08:12 +0800 | [diff] [blame] | 169 | self._check_interface(iface, server_id=server['id'], port_id=port_id, |
| 170 | network_id=network_id) |
Maho Koshiya | 3fc1246 | 2015-12-14 19:03:12 +0900 | [diff] [blame] | 171 | return iface |
| 172 | |
Maho Koshiya | 7b62958 | 2016-02-22 10:59:01 +0900 | [diff] [blame] | 173 | def _test_create_interface_by_fixed_ips(self, server, ifs): |
| 174 | network_id = ifs[0]['net_id'] |
Ryan Tidwell | 1964a26 | 2016-05-04 15:13:23 -0700 | [diff] [blame] | 175 | subnet_id = ifs[0]['fixed_ips'][0]['subnet_id'] |
| 176 | ip_list = net_utils.get_unused_ip_addresses(self.ports_client, |
| 177 | self.subnets_client, |
| 178 | network_id, |
| 179 | subnet_id, |
| 180 | 1) |
Maho Koshiya | 7b62958 | 2016-02-22 10:59:01 +0900 | [diff] [blame] | 181 | |
Ryan Tidwell | 1964a26 | 2016-05-04 15:13:23 -0700 | [diff] [blame] | 182 | fixed_ips = [{'ip_address': ip_list[0]}] |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 183 | iface = self.interfaces_client.create_interface( |
Maho Koshiya | 7b62958 | 2016-02-22 10:59:01 +0900 | [diff] [blame] | 184 | server['id'], net_id=network_id, |
| 185 | fixed_ips=fixed_ips)['interfaceAttachment'] |
Attila Fazekas | 3588bb3 | 2018-11-04 12:40:27 +0100 | [diff] [blame] | 186 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, |
| 187 | self.ports_client.delete_port, |
| 188 | iface['port_id']) |
lianghao | 1635334 | 2017-11-28 21:08:12 +0800 | [diff] [blame] | 189 | self._check_interface(iface, server_id=server['id'], |
| 190 | fixed_ip=ip_list[0]) |
Maho Koshiya | 7b62958 | 2016-02-22 10:59:01 +0900 | [diff] [blame] | 191 | return iface |
| 192 | |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 193 | def _test_show_interface(self, server, ifs): |
| 194 | iface = ifs[0] |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 195 | _iface = self.interfaces_client.show_interface( |
ghanshyam | a2364f1 | 2015-08-24 15:45:37 +0900 | [diff] [blame] | 196 | server['id'], iface['port_id'])['interfaceAttachment'] |
venkata anil | 4537530 | 2014-12-30 10:41:43 +0000 | [diff] [blame] | 197 | self._check_interface(iface, port_id=_iface['port_id'], |
| 198 | network_id=_iface['net_id'], |
| 199 | fixed_ip=_iface['fixed_ips'][0]['ip_address'], |
| 200 | mac_addr=_iface['mac_addr']) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 201 | |
| 202 | def _test_delete_interface(self, server, ifs): |
| 203 | # NOTE(danms): delete not the first or last, but one in the middle |
| 204 | iface = ifs[1] |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 205 | self.interfaces_client.delete_interface(server['id'], iface['port_id']) |
| 206 | _ifs = (self.interfaces_client.list_interfaces(server['id']) |
ghanshyam | a2364f1 | 2015-08-24 15:45:37 +0900 | [diff] [blame] | 207 | ['interfaceAttachments']) |
Oleg Bondarev | ee50bb1 | 2014-01-16 00:01:34 +0400 | [diff] [blame] | 208 | start = int(time.time()) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 209 | |
Oleg Bondarev | ee50bb1 | 2014-01-16 00:01:34 +0400 | [diff] [blame] | 210 | while len(ifs) == len(_ifs): |
| 211 | time.sleep(self.build_interval) |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 212 | _ifs = (self.interfaces_client.list_interfaces(server['id']) |
ghanshyam | a2364f1 | 2015-08-24 15:45:37 +0900 | [diff] [blame] | 213 | ['interfaceAttachments']) |
Oleg Bondarev | ee50bb1 | 2014-01-16 00:01:34 +0400 | [diff] [blame] | 214 | timed_out = int(time.time()) - start >= self.build_timeout |
| 215 | if len(ifs) == len(_ifs) and timed_out: |
| 216 | message = ('Failed to delete interface within ' |
| 217 | 'the required time: %s sec.' % self.build_timeout) |
guo yunxian | ebb15f2 | 2016-11-01 21:03:35 +0800 | [diff] [blame] | 218 | raise lib_exc.TimeoutException(message) |
Oleg Bondarev | ee50bb1 | 2014-01-16 00:01:34 +0400 | [diff] [blame] | 219 | |
| 220 | self.assertNotIn(iface['port_id'], [i['port_id'] for i in _ifs]) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 221 | return _ifs |
| 222 | |
| 223 | def _compare_iface_list(self, list1, list2): |
| 224 | # NOTE(danms): port_state will likely have changed, so just |
| 225 | # confirm the port_ids are the same at least |
| 226 | list1 = [x['port_id'] for x in list1] |
| 227 | list2 = [x['port_id'] for x in list2] |
| 228 | |
| 229 | self.assertEqual(sorted(list1), sorted(list2)) |
| 230 | |
Ken'ichi Ohmichi | 14b0ae1 | 2017-01-27 17:18:52 -0800 | [diff] [blame] | 231 | @decorators.idempotent_id('73fe8f02-590d-4bf1-b184-e9ca81065051') |
Andrea Frittoli | cd36841 | 2017-08-14 21:37:56 +0100 | [diff] [blame] | 232 | @utils.services('network') |
zhufl | 607cfbf | 2017-12-28 14:55:08 +0800 | [diff] [blame] | 233 | def test_create_list_show_delete_interfaces_by_network_port(self): |
zhufl | 735e169 | 2020-08-13 16:46:50 +0800 | [diff] [blame] | 234 | """Test create/list/show/delete interfaces by network port""" |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 235 | server, ifs, _ = self._create_server_get_interfaces() |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 236 | interface_count = len(ifs) |
zhufl | 080dcfb | 2016-10-21 17:45:38 +0800 | [diff] [blame] | 237 | self.assertGreater(interface_count, 0) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 238 | |
Rohan Kanade | 9ce97df | 2013-12-10 18:59:35 +0530 | [diff] [blame] | 239 | try: |
| 240 | iface = self._test_create_interface(server) |
| 241 | except lib_exc.BadRequest as e: |
| 242 | msg = ('Multiple possible networks found, use a Network ID to be ' |
| 243 | 'more specific.') |
likui | 19b70a3 | 2020-12-02 13:25:18 +0800 | [diff] [blame] | 244 | if not CONF.compute.fixed_network_name and str(e) == msg: |
Rohan Kanade | 9ce97df | 2013-12-10 18:59:35 +0530 | [diff] [blame] | 245 | raise |
| 246 | else: |
| 247 | ifs.append(iface) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 248 | |
| 249 | iface = self._test_create_interface_by_network_id(server, ifs) |
| 250 | ifs.append(iface) |
| 251 | |
Maho Koshiya | 3fc1246 | 2015-12-14 19:03:12 +0900 | [diff] [blame] | 252 | iface = self._test_create_interface_by_port_id(server, ifs) |
| 253 | ifs.append(iface) |
| 254 | |
zhufl | 607cfbf | 2017-12-28 14:55:08 +0800 | [diff] [blame] | 255 | _ifs = (self.interfaces_client.list_interfaces(server['id']) |
| 256 | ['interfaceAttachments']) |
| 257 | self._compare_iface_list(ifs, _ifs) |
| 258 | |
| 259 | self._test_show_interface(server, ifs) |
| 260 | |
| 261 | _ifs = self._test_delete_interface(server, ifs) |
| 262 | self.assertEqual(len(ifs) - 1, len(_ifs)) |
| 263 | |
| 264 | @decorators.idempotent_id('d290c06c-f5b3-11e7-8ec8-002293781009') |
| 265 | @utils.services('network') |
| 266 | def test_create_list_show_delete_interfaces_by_fixed_ip(self): |
zhufl | 735e169 | 2020-08-13 16:46:50 +0800 | [diff] [blame] | 267 | """Test create/list/show/delete interfaces by fixed ip""" |
zhufl | 607cfbf | 2017-12-28 14:55:08 +0800 | [diff] [blame] | 268 | # NOTE(zhufl) By default only project that is admin or network owner |
| 269 | # or project with role advsvc is authorised to create interfaces with |
| 270 | # fixed-ip, so if we don't create network for each project, do not |
| 271 | # test _test_create_interface_by_fixed_ips. |
| 272 | if not (CONF.auth.use_dynamic_credentials and |
| 273 | CONF.auth.create_isolated_networks and |
| 274 | not CONF.network.shared_physical_network): |
Matt Riedemann | 91d9242 | 2019-01-29 16:19:49 -0500 | [diff] [blame] | 275 | raise self.skipException("Only owner network supports " |
| 276 | "creating interface by fixed ip.") |
zhufl | 607cfbf | 2017-12-28 14:55:08 +0800 | [diff] [blame] | 277 | |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 278 | server, ifs, _ = self._create_server_get_interfaces() |
zhufl | 607cfbf | 2017-12-28 14:55:08 +0800 | [diff] [blame] | 279 | interface_count = len(ifs) |
| 280 | self.assertGreater(interface_count, 0) |
| 281 | |
Maho Koshiya | 7b62958 | 2016-02-22 10:59:01 +0900 | [diff] [blame] | 282 | iface = self._test_create_interface_by_fixed_ips(server, ifs) |
| 283 | ifs.append(iface) |
| 284 | |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 285 | _ifs = (self.interfaces_client.list_interfaces(server['id']) |
ghanshyam | a2364f1 | 2015-08-24 15:45:37 +0900 | [diff] [blame] | 286 | ['interfaceAttachments']) |
Dan Smith | 8ad1c47 | 2013-02-26 13:03:16 -0500 | [diff] [blame] | 287 | self._compare_iface_list(ifs, _ifs) |
| 288 | |
| 289 | self._test_show_interface(server, ifs) |
| 290 | |
| 291 | _ifs = self._test_delete_interface(server, ifs) |
| 292 | self.assertEqual(len(ifs) - 1, len(_ifs)) |
| 293 | |
Ken'ichi Ohmichi | 14b0ae1 | 2017-01-27 17:18:52 -0800 | [diff] [blame] | 294 | @decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4') |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 295 | def test_reassign_port_between_servers(self): |
zhufl | 735e169 | 2020-08-13 16:46:50 +0800 | [diff] [blame] | 296 | """Tests reassigning port between servers |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 297 | |
Dan Smith | 7249e2a | 2023-07-21 09:53:06 -0700 | [diff] [blame] | 298 | 1. Create two servers in Nova. |
| 299 | 2. Create a port in Neutron. |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 300 | 3. Attach the port to the first server. |
| 301 | 4. Detach the port from the first server. |
| 302 | 5. Attach the port to the second server. |
| 303 | 6. Detach the port from the second server. |
| 304 | """ |
| 305 | network = self.get_tenant_network() |
| 306 | network_id = network['id'] |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 307 | |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 308 | # NOTE(artom) We create two servers one at a time because |
| 309 | # create_test_server doesn't support multiple validatable servers. |
| 310 | validation_resources = self.get_test_validation_resources( |
| 311 | self.os_primary) |
| 312 | |
| 313 | def _create_validatable_server(): |
| 314 | _, servers = compute.create_test_server( |
| 315 | self.os_primary, tenant_network=network, |
Dan Smith | 7249e2a | 2023-07-21 09:53:06 -0700 | [diff] [blame] | 316 | validatable=True, |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 317 | validation_resources=validation_resources) |
| 318 | return servers[0] |
| 319 | |
Dan Smith | 7249e2a | 2023-07-21 09:53:06 -0700 | [diff] [blame] | 320 | # NOTE(danms): We create these with no waiters because we will wait |
| 321 | # for them to be validatable (i.e. SSHABLE) below. That way some of |
| 322 | # the server creation overlap each other and with create_port. |
Artom Lifshitz | b477594 | 2018-09-05 16:24:01 +0300 | [diff] [blame] | 323 | servers = [_create_validatable_server(), _create_validatable_server()] |
| 324 | |
Dan Smith | 7249e2a | 2023-07-21 09:53:06 -0700 | [diff] [blame] | 325 | port = self.ports_client.create_port( |
| 326 | network_id=network_id, |
| 327 | name=data_utils.rand_name(self.__class__.__name__)) |
| 328 | port_id = port['port']['id'] |
| 329 | self.addCleanup(self.ports_client.delete_port, port_id) |
| 330 | |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 331 | # add our cleanups for the servers since we bypassed the base class |
| 332 | for server in servers: |
zhufl | 1355d98 | 2017-01-05 12:06:20 +0800 | [diff] [blame] | 333 | self.addCleanup(self.delete_server, server['id']) |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 334 | |
| 335 | for server in servers: |
Mark Goddard | fa30d2f | 2019-09-02 14:41:02 +0100 | [diff] [blame] | 336 | # NOTE(mgoddard): Get detailed server to ensure addresses are |
| 337 | # present in fixed IP case. |
| 338 | server = self.servers_client.show_server(server['id'])['server'] |
Dan Smith | 7249e2a | 2023-07-21 09:53:06 -0700 | [diff] [blame] | 339 | compute.wait_for_ssh_or_ping(server, self.os_primary, network, |
| 340 | True, validation_resources, |
| 341 | 'SSHABLE', True) |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 342 | # attach the port to the server |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 343 | iface = self.interfaces_client.create_interface( |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 344 | server['id'], port_id=port_id)['interfaceAttachment'] |
lianghao | 1635334 | 2017-11-28 21:08:12 +0800 | [diff] [blame] | 345 | self._check_interface(iface, server_id=server['id'], |
| 346 | port_id=port_id) |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 347 | |
| 348 | # detach the port from the server; this is a cast in the compute |
| 349 | # API so we have to poll the port until the device_id is unset. |
lkuchlan | 87b5a2d | 2016-09-27 15:46:16 +0300 | [diff] [blame] | 350 | self.interfaces_client.delete_interface(server['id'], port_id) |
Matt Riedemann | a8c641a | 2016-07-12 17:07:33 -0400 | [diff] [blame] | 351 | self.wait_for_port_detach(port_id) |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 352 | |
| 353 | |
| 354 | class AttachInterfacesUnderV243Test(AttachInterfacesTestBase): |
zhufl | 735e169 | 2020-08-13 16:46:50 +0800 | [diff] [blame] | 355 | """Test attaching interfaces with compute microversion less than 2.44""" |
| 356 | |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 357 | max_microversion = '2.43' |
| 358 | |
| 359 | @decorators.attr(type='smoke') |
| 360 | @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9') |
| 361 | @utils.services('network') |
| 362 | def test_add_remove_fixed_ip(self): |
zhufl | 735e169 | 2020-08-13 16:46:50 +0800 | [diff] [blame] | 363 | """Test adding and removing fixed ip from server""" |
Matthew Treinish | c68546f | 2018-10-12 10:53:45 -0400 | [diff] [blame] | 364 | # NOTE(zhufl) By default only project that is admin or network owner |
| 365 | # or project with role advsvc is authorised to add interfaces with |
| 366 | # fixed-ip, so if we don't create network for each project, do not |
| 367 | # test |
| 368 | if not (CONF.auth.use_dynamic_credentials and |
| 369 | CONF.auth.create_isolated_networks and |
| 370 | not CONF.network.shared_physical_network): |
| 371 | raise self.skipException("Only owner network supports " |
| 372 | "creating interface by fixed ip.") |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 373 | # Add and Remove the fixed IP to server. |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 374 | server, ifs, fip = self._create_server_get_interfaces() |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 375 | original_interface_count = len(ifs) # This is the number of ports. |
| 376 | self.assertGreater(original_interface_count, 0) |
| 377 | # Get the starting list of IPs on the server. |
| 378 | addresses = self.os_primary.servers_client.list_addresses( |
| 379 | server['id'])['addresses'] |
| 380 | # There should be one entry for the single network mapped to a list of |
| 381 | # addresses, which at this point should have at least one entry. |
| 382 | # Note that we could start with two addresses depending on how tempest |
| 383 | # is configured for using floating IPs. |
| 384 | self.assertEqual(1, len(addresses), addresses) # number of networks |
| 385 | # Keep track of the original addresses so we can know which IP is new. |
| 386 | original_ips = [addr['addr'] for addr in list(addresses.values())[0]] |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 387 | # Make sure the floating IP possibly assigned during |
| 388 | # server creation is always present in the set of original ips. |
| 389 | original_ips = set(original_ips).union(fip) |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 390 | original_ip_count = len(original_ips) |
| 391 | self.assertGreater(original_ip_count, 0, addresses) # at least 1 |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 392 | network_id = ifs[0]['net_id'] |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 393 | # Add another fixed IP to the server. This should result in another |
| 394 | # fixed IP on the same network (and same port since we only have one |
| 395 | # port). |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 396 | self.servers_client.add_fixed_ip(server['id'], networkId=network_id) |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 397 | |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 398 | def _wait_for_ip_change(expected_count): |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 399 | _addresses = self.os_primary.servers_client.list_addresses( |
| 400 | server['id'])['addresses'] |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 401 | _ips = set([addr['addr'] for addr in list(_addresses.values())[0]]) |
| 402 | # Make sure possible floating ip is always present in the set. |
| 403 | _ips = _ips.union(fip) |
| 404 | LOG.debug("Wait for change of IPs. All IPs still associated to " |
Slawek Kaplonski | b3daeb4 | 2019-06-29 23:49:59 +0200 | [diff] [blame] | 405 | "the server %(id)s: %(ips)s", |
| 406 | {'id': server['id'], 'ips': _ips}) |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 407 | return len(_ips) == expected_count |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 408 | |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 409 | # Wait for the ips count to increase by one. |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 410 | if not test_utils.call_until_true( |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 411 | _wait_for_ip_change, CONF.compute.build_timeout, |
| 412 | CONF.compute.build_interval, original_ip_count + 1): |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 413 | raise lib_exc.TimeoutException( |
| 414 | 'Timed out while waiting for IP count to increase.') |
| 415 | |
| 416 | # Remove the fixed IP that we just added. |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 417 | server_detail = self.os_primary.servers_client.show_server( |
| 418 | server['id'])['server'] |
| 419 | # Get the Fixed IP from server. |
| 420 | fixed_ip = None |
| 421 | for ip_set in server_detail['addresses']: |
| 422 | for ip in server_detail['addresses'][ip_set]: |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 423 | if (ip['OS-EXT-IPS:type'] == 'fixed' and |
| 424 | ip['addr'] not in original_ips): |
zhufl | 615e63b | 2018-08-01 17:23:38 +0800 | [diff] [blame] | 425 | fixed_ip = ip['addr'] |
| 426 | break |
| 427 | if fixed_ip is not None: |
| 428 | break |
| 429 | self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip) |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 430 | # Wait for the interface count to decrease by one. |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 431 | if not test_utils.call_until_true( |
David Sedlák | 61a3c8e | 2019-10-30 15:38:21 +0100 | [diff] [blame] | 432 | _wait_for_ip_change, CONF.compute.build_timeout, |
| 433 | CONF.compute.build_interval, original_ip_count): |
Matt Riedemann | d4cb10f | 2018-09-26 13:03:08 -0400 | [diff] [blame] | 434 | raise lib_exc.TimeoutException( |
| 435 | 'Timed out while waiting for IP count to decrease.') |
zhufl | 7761859 | 2020-11-17 15:41:20 +0800 | [diff] [blame] | 436 | |
| 437 | |
| 438 | class AttachInterfacesV270Test(AttachInterfacesTestBase): |
| 439 | """Test interface API with microversion greater than 2.69""" |
| 440 | min_microversion = '2.70' |
| 441 | |
| 442 | @decorators.idempotent_id('2853f095-8277-4067-92bd-9f10bd4f8e0c') |
| 443 | @utils.services('network') |
| 444 | def test_create_get_list_interfaces(self): |
| 445 | """Test interface API with microversion greater than 2.69 |
| 446 | |
| 447 | Checking create, get, list interface APIs response schema. |
| 448 | """ |
| 449 | server = self.create_test_server(wait_until='ACTIVE') |
| 450 | try: |
| 451 | iface = self.interfaces_client.create_interface(server['id'])[ |
| 452 | 'interfaceAttachment'] |
| 453 | iface = waiters.wait_for_interface_status( |
| 454 | self.interfaces_client, server['id'], iface['port_id'], |
| 455 | 'ACTIVE') |
| 456 | except lib_exc.BadRequest as e: |
| 457 | msg = ('Multiple possible networks found, use a Network ID to be ' |
| 458 | 'more specific.') |
likui | 19b70a3 | 2020-12-02 13:25:18 +0800 | [diff] [blame] | 459 | if not CONF.compute.fixed_network_name and str(e) == msg: |
zhufl | 7761859 | 2020-11-17 15:41:20 +0800 | [diff] [blame] | 460 | raise |
| 461 | else: |
| 462 | # just to check the response schema |
| 463 | self.interfaces_client.show_interface( |
| 464 | server['id'], iface['port_id']) |
| 465 | self.interfaces_client.list_interfaces(server['id']) |