blob: 2dcf18af0c8aabf92617129045c641e6b0c6eba7 [file] [log] [blame]
Solio Sarabia60095ff2017-02-28 18:18:26 -06001# Copyright 2012 OpenStack Foundation
2# Copyright 2013 IBM Corp.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17# NOTE(soliosg) Do not edit this file. It will only stay temporarily
18# in ironic, while QA refactors the tempest.scenario interface. This
19# file was copied from openstack/tempest/tempest/scenario/manager.py,
20# openstack/tempest commit: 82a278e88c9e9f9ba49f81c1f8dba0bca7943daf
21
Solio Sarabia60095ff2017-02-28 18:18:26 -060022from oslo_log import log
Solio Sarabia60095ff2017-02-28 18:18:26 -060023from oslo_utils import netutils
Solio Sarabia60095ff2017-02-28 18:18:26 -060024from tempest.common.utils.linux import remote_client
Solio Sarabia60095ff2017-02-28 18:18:26 -060025from tempest import config
26from tempest import exceptions
27from tempest.lib.common.utils import data_utils
28from tempest.lib.common.utils import test_utils
29from tempest.lib import exceptions as lib_exc
30import tempest.test
31
32CONF = config.CONF
33
34LOG = log.getLogger(__name__)
35
36
Roman Popelka7e962d62022-02-28 09:10:31 +010037class ScenarioTest(tempest.scenario.manager.ScenarioTest):
Solio Sarabia60095ff2017-02-28 18:18:26 -060038 """Base class for scenario tests. Uses tempest own clients. """
39
Julia Kreger3a07c4d2021-06-22 10:27:56 -070040 credentials = ['primary', 'admin', 'system_admin']
Solio Sarabia60095ff2017-02-28 18:18:26 -060041
42 @classmethod
43 def setup_clients(cls):
44 super(ScenarioTest, cls).setup_clients()
45 # Clients (in alphabetical order)
Vu Cong Tuanf825d192017-06-21 18:32:15 +070046 cls.flavors_client = cls.os_primary.flavors_client
Solio Sarabia60095ff2017-02-28 18:18:26 -060047 cls.compute_floating_ips_client = (
Vu Cong Tuanf825d192017-06-21 18:32:15 +070048 cls.os_primary.compute_floating_ips_client)
Solio Sarabia60095ff2017-02-28 18:18:26 -060049 if CONF.service_available.glance:
50 # Check if glance v1 is available to determine which client to use.
51 if CONF.image_feature_enabled.api_v1:
Vu Cong Tuanf825d192017-06-21 18:32:15 +070052 cls.image_client = cls.os_primary.image_client
Solio Sarabia60095ff2017-02-28 18:18:26 -060053 elif CONF.image_feature_enabled.api_v2:
Vu Cong Tuanf825d192017-06-21 18:32:15 +070054 cls.image_client = cls.os_primary.image_client_v2
Solio Sarabia60095ff2017-02-28 18:18:26 -060055 else:
56 raise lib_exc.InvalidConfiguration(
57 'Either api_v1 or api_v2 must be True in '
58 '[image-feature-enabled].')
59 # Compute image client
Vu Cong Tuanf825d192017-06-21 18:32:15 +070060 cls.compute_images_client = cls.os_primary.compute_images_client
61 cls.keypairs_client = cls.os_primary.keypairs_client
Solio Sarabia60095ff2017-02-28 18:18:26 -060062 # Nova security groups client
63 cls.compute_security_groups_client = (
Vu Cong Tuanf825d192017-06-21 18:32:15 +070064 cls.os_primary.compute_security_groups_client)
Solio Sarabia60095ff2017-02-28 18:18:26 -060065 cls.compute_security_group_rules_client = (
Vu Cong Tuanf825d192017-06-21 18:32:15 +070066 cls.os_primary.compute_security_group_rules_client)
67 cls.servers_client = cls.os_primary.servers_client
68 cls.interface_client = cls.os_primary.interfaces_client
Solio Sarabia60095ff2017-02-28 18:18:26 -060069 # Neutron network client
Vu Cong Tuanf825d192017-06-21 18:32:15 +070070 cls.networks_client = cls.os_primary.networks_client
71 cls.ports_client = cls.os_primary.ports_client
72 cls.routers_client = cls.os_primary.routers_client
73 cls.subnets_client = cls.os_primary.subnets_client
74 cls.floating_ips_client = cls.os_primary.floating_ips_client
75 cls.security_groups_client = cls.os_primary.security_groups_client
Solio Sarabia60095ff2017-02-28 18:18:26 -060076 cls.security_group_rules_client = (
Vu Cong Tuanf825d192017-06-21 18:32:15 +070077 cls.os_primary.security_group_rules_client)
Solio Sarabia60095ff2017-02-28 18:18:26 -060078
Ghanshyam Mann3b663f62019-12-12 17:01:16 +000079 cls.volumes_client = cls.os_primary.volumes_client_latest
80 cls.snapshots_client = cls.os_primary.snapshots_client_latest
Solio Sarabia60095ff2017-02-28 18:18:26 -060081
82 # ## Test functions library
83 #
84 # The create_[resource] functions only return body and discard the
85 # resp part which is not used in scenario tests
86
Solio Sarabia60095ff2017-02-28 18:18:26 -060087 def create_server(self, name=None, image_id=None, flavor=None,
88 validatable=False, wait_until='ACTIVE',
89 clients=None, **kwargs):
Roman Popelka6fdd3742022-02-28 09:29:45 +010090 return super().create_server(name=name,
91 image_id=image_id,
92 flavor=flavor,
93 validatable=validatable,
94 wait_until=wait_until,
95 clients=clients,
96 **kwargs)
Solio Sarabia60095ff2017-02-28 18:18:26 -060097
Solio Sarabia60095ff2017-02-28 18:18:26 -060098 def get_remote_client(self, ip_address, username=None, private_key=None):
99 """Get a SSH client to a remote server
100
101 @param ip_address the server floating or fixed IP address to use
102 for ssh validation
103 @param username name of the Linux account on the remote server
104 @param private_key the SSH private key to use
105 @return a RemoteClient object
106 """
107
108 if username is None:
109 username = CONF.validation.image_ssh_user
110 # Set this with 'keypair' or others to log in with keypair or
111 # username/password.
112 if CONF.validation.auth_method == 'keypair':
113 password = None
114 if private_key is None:
115 private_key = self.keypair['private_key']
116 else:
117 password = CONF.validation.image_ssh_password
118 private_key = None
119 linux_client = remote_client.RemoteClient(ip_address, username,
120 pkey=private_key,
121 password=password)
122 try:
123 linux_client.validate_authentication()
124 except Exception as e:
125 message = ('Initializing SSH connection to %(ip)s failed. '
126 'Error: %(error)s' % {'ip': ip_address,
127 'error': e})
128 caller = test_utils.find_test_caller()
129 if caller:
130 message = '(%s) %s' % (caller, message)
131 LOG.exception(message)
Roman Popelka31ce1b12022-02-28 10:04:55 +0100132 self.log_console_output()
Solio Sarabia60095ff2017-02-28 18:18:26 -0600133 raise
134
135 return linux_client
136
Solio Sarabia60095ff2017-02-28 18:18:26 -0600137 def check_vm_connectivity(self, ip_address,
138 username=None,
139 private_key=None,
140 should_connect=True,
141 mtu=None):
142 """Check server connectivity
143
144 :param ip_address: server to test against
145 :param username: server's ssh username
146 :param private_key: server's ssh private key to be used
147 :param should_connect: True/False indicates positive/negative test
148 positive - attempt ping and ssh
149 negative - attempt ping and fail if succeed
150 :param mtu: network MTU to use for connectivity validation
151
152 :raises: AssertError if the result of the connectivity check does
153 not match the value of the should_connect param
154 """
155 if should_connect:
156 msg = "Timed out waiting for %s to become reachable" % ip_address
157 else:
158 msg = "ip address %s is reachable" % ip_address
159 self.assertTrue(self.ping_ip_address(ip_address,
160 should_succeed=should_connect,
161 mtu=mtu),
162 msg=msg)
163 if should_connect:
164 # no need to check ssh for negative connectivity
165 self.get_remote_client(ip_address, username, private_key)
166
Solio Sarabia60095ff2017-02-28 18:18:26 -0600167 def create_floating_ip(self, thing, pool_name=None):
168 """Create a floating IP and associates to a server on Nova"""
169
170 if not pool_name:
171 pool_name = CONF.network.floating_network_name
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700172 client = self.os_primary.compute_floating_ips_client
173 floating_ip = (client.
Solio Sarabia60095ff2017-02-28 18:18:26 -0600174 create_floating_ip(pool=pool_name)['floating_ip'])
175 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700176 client.delete_floating_ip,
Solio Sarabia60095ff2017-02-28 18:18:26 -0600177 floating_ip['id'])
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700178 client.associate_floating_ip_to_server(
Solio Sarabia60095ff2017-02-28 18:18:26 -0600179 floating_ip['ip'], thing['id'])
180 return floating_ip
181
182 def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
183 private_key=None):
184 ssh_client = self.get_remote_client(ip_address,
185 private_key=private_key)
186 if dev_name is not None:
187 ssh_client.make_fs(dev_name)
188 ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
189 mount_path))
190 cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
191 ssh_client.exec_command(cmd_timestamp)
192 timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
193 % mount_path)
194 if dev_name is not None:
195 ssh_client.exec_command('sudo umount %s' % mount_path)
196 return timestamp
197
Solio Sarabia60095ff2017-02-28 18:18:26 -0600198 def get_server_ip(self, server):
199 """Get the server fixed or floating IP.
200
201 Based on the configuration we're in, return a correct ip
202 address for validating that a guest is up.
203 """
204 if CONF.validation.connect_method == 'floating':
205 # The tests calling this method don't have a floating IP
206 # and can't make use of the validation resources. So the
207 # method is creating the floating IP there.
208 return self.create_floating_ip(server)['ip']
209 elif CONF.validation.connect_method == 'fixed':
210 # Determine the network name to look for based on config or creds
211 # provider network resources.
212 if CONF.validation.network_for_ssh:
213 addresses = server['addresses'][
214 CONF.validation.network_for_ssh]
215 else:
216 creds_provider = self._get_credentials_provider()
217 net_creds = creds_provider.get_primary_creds()
218 network = getattr(net_creds, 'network', None)
219 addresses = (server['addresses'][network['name']]
220 if network else [])
221 for address in addresses:
222 if (address['version'] == CONF.validation.ip_version_for_ssh
223 and address['OS-EXT-IPS:type'] == 'fixed'):
224 return address['addr']
225 raise exceptions.ServerUnreachable(server_id=server['id'])
226 else:
227 raise lib_exc.InvalidConfiguration()
228
Pavlo Shchelokovskyy40db0332017-03-21 08:00:17 +0000229 def _get_router(self, client=None, tenant_id=None):
230 """Retrieve a router for the given tenant id.
231
232 If a public router has been configured, it will be returned.
233
234 If a public router has not been configured, but a public
235 network has, a tenant router will be created and returned that
236 routes traffic to the public network.
237 """
238 if not client:
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700239 client = self.os_primary.routers_client
Pavlo Shchelokovskyy40db0332017-03-21 08:00:17 +0000240 if not tenant_id:
241 tenant_id = client.tenant_id
242 router_id = CONF.network.public_router_id
243 network_id = CONF.network.public_network_id
244 if router_id:
245 body = client.show_router(router_id)
246 return body['router']
247 elif network_id:
248 router = self._create_router(client, tenant_id)
249 kwargs = {'external_gateway_info': dict(network_id=network_id)}
250 router = client.update_router(router['id'], **kwargs)['router']
251 return router
252 else:
253 raise Exception("Neither of 'public_router_id' or "
254 "'public_network_id' has been defined.")
255
256 def _create_router(self, client=None, tenant_id=None,
257 namestart='router-smoke'):
258 if not client:
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700259 client = self.os_primary.routers_client
Pavlo Shchelokovskyy40db0332017-03-21 08:00:17 +0000260 if not tenant_id:
261 tenant_id = client.tenant_id
262 name = data_utils.rand_name(namestart)
263 result = client.create_router(name=name,
264 admin_state_up=True,
265 tenant_id=tenant_id)
266 router = result['router']
267 self.assertEqual(router['name'], name)
268 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
269 client.delete_router,
270 router['id'])
271 return router
272
Solio Sarabia60095ff2017-02-28 18:18:26 -0600273
274class NetworkScenarioTest(ScenarioTest):
275 """Base class for network scenario tests.
276
277 This class provide helpers for network scenario tests, using the neutron
278 API. Helpers from ancestor which use the nova network API are overridden
279 with the neutron API.
280
281 This Class also enforces using Neutron instead of novanetwork.
282 Subclassed tests will be skipped if Neutron is not enabled
283
284 """
285
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700286 credentials = ['primary', 'admin', 'system_admin']
Solio Sarabia60095ff2017-02-28 18:18:26 -0600287
288 @classmethod
289 def skip_checks(cls):
290 super(NetworkScenarioTest, cls).skip_checks()
291 if not CONF.service_available.neutron:
292 raise cls.skipException('Neutron not available')
293
294 def _create_network(self, networks_client=None,
295 tenant_id=None,
296 namestart='network-smoke-',
297 port_security_enabled=True):
298 if not networks_client:
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700299 networks_client = self.os_primary.networks_client
Solio Sarabia60095ff2017-02-28 18:18:26 -0600300 if not tenant_id:
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700301 tenant_id = self.os_primary.networks_client.tenant_id
Solio Sarabia60095ff2017-02-28 18:18:26 -0600302 name = data_utils.rand_name(namestart)
303 network_kwargs = dict(name=name, tenant_id=tenant_id)
304 # Neutron disables port security by default so we have to check the
305 # config before trying to create the network with port_security_enabled
306 if CONF.network_feature_enabled.port_security:
307 network_kwargs['port_security_enabled'] = port_security_enabled
308 result = networks_client.create_network(**network_kwargs)
309 network = result['network']
310
311 self.assertEqual(network['name'], name)
312 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
313 networks_client.delete_network,
314 network['id'])
315 return network
316
Solio Sarabia60095ff2017-02-28 18:18:26 -0600317 def _get_server_port_id_and_ip4(self, server, ip_addr=None):
Hongbin Lu43015f02018-07-19 15:17:19 +0000318 if ip_addr:
319 ports = self.os_admin.ports_client.list_ports(
320 device_id=server['id'],
321 fixed_ips='ip_address=%s' % ip_addr)['ports']
322 else:
323 ports = self.os_admin.ports_client.list_ports(
324 device_id=server['id'])['ports']
Solio Sarabia60095ff2017-02-28 18:18:26 -0600325 # A port can have more than one IP address in some cases.
326 # If the network is dual-stack (IPv4 + IPv6), this port is associated
327 # with 2 subnets
328 p_status = ['ACTIVE']
329 # NOTE(vsaienko) With Ironic, instances live on separate hardware
330 # servers. Neutron does not bind ports for Ironic instances, as a
331 # result the port remains in the DOWN state.
332 # TODO(vsaienko) remove once bug: #1599836 is resolved.
333 if getattr(CONF.service_available, 'ironic', False):
334 p_status.append('DOWN')
335 port_map = [(p["id"], fxip["ip_address"])
336 for p in ports
337 for fxip in p["fixed_ips"]
338 if netutils.is_valid_ipv4(fxip["ip_address"])
339 and p['status'] in p_status]
340 inactive = [p for p in ports if p['status'] != 'ACTIVE']
341 if inactive:
342 LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
343
344 self.assertNotEqual(0, len(port_map),
345 "No IPv4 addresses found in: %s" % ports)
346 self.assertEqual(len(port_map), 1,
347 "Found multiple IPv4 addresses: %s. "
348 "Unable to determine which port to target."
349 % port_map)
350 return port_map[0]
351
Solio Sarabia60095ff2017-02-28 18:18:26 -0600352 def create_floating_ip(self, thing, external_network_id=None,
353 port_id=None, client=None):
354 """Create a floating IP and associates to a resource/port on Neutron"""
355 if not external_network_id:
356 external_network_id = CONF.network.public_network_id
357 if not client:
Julia Kreger3a07c4d2021-06-22 10:27:56 -0700358 client = self.os_primary.floating_ips_client
Solio Sarabia60095ff2017-02-28 18:18:26 -0600359 if not port_id:
360 port_id, ip4 = self._get_server_port_id_and_ip4(thing)
361 else:
362 ip4 = None
363 result = client.create_floatingip(
364 floating_network_id=external_network_id,
365 port_id=port_id,
366 tenant_id=thing['tenant_id'],
367 fixed_ip_address=ip4
368 )
369 floating_ip = result['floatingip']
370 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
371 client.delete_floatingip,
372 floating_ip['id'])
373 return floating_ip