blob: 3adaa1e4fb67fdd3c06bd80a80db205ee1adb99e [file] [log] [blame]
Itzik Browne67ebb52016-05-15 05:34:41 +00001# Copyright 2016 Red Hat, Inc.
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.
Chandan Kumarc125fd12017-11-15 19:41:01 +053015import subprocess
Itzik Browne67ebb52016-05-15 05:34:41 +000016
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090017import netaddr
Assaf Muller92fdc782018-05-31 10:32:47 -040018from neutron_lib.api import validators
19from neutron_lib import constants as neutron_lib_constants
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050020from oslo_log import log
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +030021from tempest.common.utils import net_utils
Itzik Browne67ebb52016-05-15 05:34:41 +000022from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000023from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090024from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050025from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000026
Chandan Kumar667d3d32017-09-22 12:24:06 +053027from neutron_tempest_plugin.api import base as base_api
28from neutron_tempest_plugin.common import ssh
29from neutron_tempest_plugin import config
30from neutron_tempest_plugin.scenario import constants
Itzik Browne67ebb52016-05-15 05:34:41 +000031
32CONF = config.CONF
Itzik Browne67ebb52016-05-15 05:34:41 +000033
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050034LOG = log.getLogger(__name__)
35
Itzik Browne67ebb52016-05-15 05:34:41 +000036
37class BaseTempestTestCase(base_api.BaseNetworkTest):
Itzik Browne67ebb52016-05-15 05:34:41 +000038
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000039 def create_server(self, flavor_ref, image_ref, key_name, networks,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020040 **kwargs):
Itzik Brownbac51dc2016-10-31 12:25:04 +000041 """Create a server using tempest lib
42 All the parameters are the ones used in Compute API
Roee Agiman6a0a18a2017-11-16 11:51:56 +020043 * - Kwargs that require admin privileges
Itzik Brownbac51dc2016-10-31 12:25:04 +000044
45 Args:
46 flavor_ref(str): The flavor of the server to be provisioned.
47 image_ref(str): The image of the server to be provisioned.
48 key_name(str): SSH key to to be used to connect to the
49 provisioned server.
50 networks(list): List of dictionaries where each represent
51 an interface to be attached to the server. For network
52 it should be {'uuid': network_uuid} and for port it should
53 be {'port': port_uuid}
Roee Agiman6a0a18a2017-11-16 11:51:56 +020054 kwargs:
Itzik Brownbac51dc2016-10-31 12:25:04 +000055 name(str): Name of the server to be provisioned.
56 security_groups(list): List of dictionaries where
57 the keys is 'name' and the value is the name of
58 the security group. If it's not passed the default
59 security group will be used.
Roee Agiman6a0a18a2017-11-16 11:51:56 +020060 availability_zone(str)*: The availability zone that
61 the instance will be in.
62 You can request a specific az without actually creating one,
63 Just pass 'X:Y' where X is the default availability
64 zone, and Y is the compute host name.
Itzik Brownbac51dc2016-10-31 12:25:04 +000065 """
66
Jakub Libosvarffd9b912017-11-16 09:54:14 +000067 kwargs.setdefault('name', data_utils.rand_name('server-test'))
Itzik Brownbac51dc2016-10-31 12:25:04 +000068
Jakub Libosvarffd9b912017-11-16 09:54:14 +000069 # We cannot use setdefault() here because caller could have passed
70 # security_groups=None and we don't want to pass None to
71 # client.create_server()
72 if not kwargs.get('security_groups'):
73 kwargs['security_groups'] = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +020074
Jakub Libosvarffd9b912017-11-16 09:54:14 +000075 client = self.os_primary.servers_client
76 if kwargs.get('availability_zone'):
Roee Agiman6a0a18a2017-11-16 11:51:56 +020077 client = self.os_admin.servers_client
Roee Agiman6a0a18a2017-11-16 11:51:56 +020078
Jakub Libosvarffd9b912017-11-16 09:54:14 +000079 server = client.create_server(
80 flavorRef=flavor_ref,
81 imageRef=image_ref,
82 key_name=key_name,
83 networks=networks,
84 **kwargs)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000085
86 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020087 waiters.wait_for_server_termination,
88 client,
89 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000090 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020091 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000092 server['server']['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +000093 return server
94
95 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +000096 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
97 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +020098 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +000099 if not secgroup_id:
100 sgs = client.list_security_groups()['security_groups']
101 for sg in sgs:
102 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
103 secgroup_id = sg['id']
104 break
105
Itzik Brown1ef813a2016-06-06 12:56:21 +0000106 for rule in rule_list:
107 direction = rule.pop('direction')
108 client.create_security_group_rule(
109 direction=direction,
110 security_group_id=secgroup_id,
111 **rule)
112
113 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200114 def create_loginable_secgroup_rule(cls, secgroup_id=None,
115 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000116 """This rule is intended to permit inbound ssh
117
118 Allowing ssh traffic traffic from all sources, so no group_id is
119 provided.
120 Setting a group_id would only permit traffic from ports
121 belonging to the same security group.
122 """
123
124 rule_list = [{'protocol': 'tcp',
125 'direction': 'ingress',
126 'port_range_min': 22,
127 'port_range_max': 22,
128 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200129 client = client or cls.os_primary.network_client
Jens Harbott54357632017-11-21 11:47:06 +0000130 cls.create_secgroup_rules(rule_list, client=client,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200131 secgroup_id=secgroup_id)
Itzik Browne67ebb52016-05-15 05:34:41 +0000132
133 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200134 def create_pingable_secgroup_rule(cls, secgroup_id=None,
135 client=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900136 """This rule is intended to permit inbound ping
137 """
138
139 rule_list = [{'protocol': 'icmp',
140 'direction': 'ingress',
141 'port_range_min': 8, # type
142 'port_range_max': 0, # code
143 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200144 client = client or cls.os_primary.network_client
Jens Harbott54357632017-11-21 11:47:06 +0000145 cls.create_secgroup_rules(rule_list, client=client,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200146 secgroup_id=secgroup_id)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900147
148 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300149 def create_router_by_client(cls, is_admin=False, **kwargs):
150 kwargs.update({'router_name': data_utils.rand_name('router'),
151 'admin_state_up': True,
152 'external_network_id': CONF.network.public_network_id})
153 if not is_admin:
154 router = cls.create_router(**kwargs)
155 else:
156 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500157 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000158 cls.routers.append(router)
159 return router
160
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200161 def create_and_associate_floatingip(self, port_id, client=None):
162 client = client or self.os_primary.network_client
163 fip = client.create_floatingip(
Itzik Browne67ebb52016-05-15 05:34:41 +0000164 CONF.network.public_network_id,
165 port_id=port_id)['floatingip']
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200166 if client is self.os_primary.network_client:
167 self.floating_ips.append(fip)
Itzik Browne67ebb52016-05-15 05:34:41 +0000168 return fip
169
Hongbin Lu965b03d2018-04-25 22:32:30 +0000170 def create_interface(cls, server_id, port_id, client=None):
171 client = client or cls.os_primary.interfaces_client
172 body = client.create_interface(server_id, port_id=port_id)
173 return body['interfaceAttachment']
174
175 def delete_interface(cls, server_id, port_id, client=None):
176 client = client or cls.os_primary.interfaces_client
177 client.delete_interface(server_id, port_id=port_id)
178
Assaf Muller92fdc782018-05-31 10:32:47 -0400179 def setup_network_and_server(
180 self, router=None, server_name=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300181 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000182
183 Creating a network, subnet, router, keypair, security group
184 and a server.
185 """
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000186 self.network = self.create_network()
187 LOG.debug("Created network %s", self.network['name'])
188 self.subnet = self.create_subnet(self.network)
189 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000190
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400191 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530192 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500193 LOG.debug("Created security group %s",
194 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000195 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300196 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000197 router = self.create_router_by_client(**kwargs)
198 self.create_router_interface(router['id'], self.subnet['id'])
199 self.keypair = self.create_keypair()
200 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000201 secgroup_id=secgroup['security_group']['id'])
Assaf Muller92fdc782018-05-31 10:32:47 -0400202
203 server_kwargs = {
204 'flavor_ref': CONF.compute.flavor_ref,
205 'image_ref': CONF.compute.image_ref,
206 'key_name': self.keypair['name'],
207 'networks': [{'uuid': self.network['id']}],
208 'security_groups': [{'name': secgroup['security_group']['name']}],
209 }
210 if server_name is not None:
211 server_kwargs['name'] = server_name
212
213 self.server = self.create_server(**server_kwargs)
Federico Ressie7417b72018-05-30 05:50:58 +0200214 self.wait_for_server_active(self.server['server'])
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000215 self.port = self.client.list_ports(network_id=self.network['id'],
216 device_id=self.server[
217 'server']['id'])['ports'][0]
218 self.fip = self.create_and_associate_floatingip(self.port['id'])
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500219
220 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
221 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
222 try:
223 ssh_client.test_connection_auth()
224 except lib_exc.SSHTimeout as ssh_e:
225 LOG.debug(ssh_e)
226 self._log_console_output(servers)
227 raise
228
229 def _log_console_output(self, servers=None):
230 if not CONF.compute_feature_enabled.console_output:
231 LOG.debug('Console output not supported, cannot log')
232 return
233 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400234 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500235 servers = servers['servers']
236 for server in servers:
237 try:
238 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400239 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500240 server['id'])['output'])
241 LOG.debug('Console output for %s\nbody=\n%s',
242 server['id'], console_output)
243 except lib_exc.NotFound:
244 LOG.debug("Server %s disappeared(deleted) while looking "
245 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900246
247 def _check_remote_connectivity(self, source, dest, should_succeed=True,
Assaf Muller92fdc782018-05-31 10:32:47 -0400248 nic=None, mtu=None, fragmentation=True,
249 timeout=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900250 """check ping server via source ssh connection
251
252 :param source: RemoteClient: an ssh connection from which to ping
253 :param dest: and IP to ping against
254 :param should_succeed: boolean should ping succeed or not
255 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300256 :param mtu: mtu size for the packet to be sent
257 :param fragmentation: Flag for packet fragmentation
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900258 :returns: boolean -- should_succeed == ping
259 :returns: ping is false if ping failed
260 """
261 def ping_host(source, host, count=CONF.validation.ping_count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300262 size=CONF.validation.ping_size, nic=None, mtu=None,
263 fragmentation=True):
Assaf Muller92fdc782018-05-31 10:32:47 -0400264 IP_VERSION_4 = neutron_lib_constants.IP_VERSION_4
265 IP_VERSION_6 = neutron_lib_constants.IP_VERSION_6
266
267 # Use 'ping6' for IPv6 addresses, 'ping' for IPv4 and hostnames
268 ip_version = (
269 IP_VERSION_6 if netaddr.valid_ipv6(host) else IP_VERSION_4)
270 cmd = (
271 'ping6' if ip_version == IP_VERSION_6 else 'ping')
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900272 if nic:
273 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300274 if mtu:
275 if not fragmentation:
276 cmd += ' -M do'
277 size = str(net_utils.get_ping_payload_size(
Assaf Muller92fdc782018-05-31 10:32:47 -0400278 mtu=mtu, ip_version=ip_version))
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900279 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
280 return source.exec_command(cmd)
281
282 def ping_remote():
283 try:
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300284 result = ping_host(source, dest, nic=nic, mtu=mtu,
285 fragmentation=fragmentation)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900286
287 except lib_exc.SSHExecCommandFailed:
288 LOG.warning('Failed to ping IP: %s via a ssh connection '
289 'from: %s.', dest, source.host)
290 return not should_succeed
291 LOG.debug('ping result: %s', result)
Assaf Muller92fdc782018-05-31 10:32:47 -0400292
293 if validators.validate_ip_address(dest) is None:
294 # Assert that the return traffic was from the correct
295 # source address.
296 from_source = 'from %s' % dest
297 self.assertIn(from_source, result)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900298 return should_succeed
299
Assaf Muller92fdc782018-05-31 10:32:47 -0400300 return test_utils.call_until_true(
301 ping_remote, timeout or CONF.validation.ping_timeout, 1)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900302
303 def check_remote_connectivity(self, source, dest, should_succeed=True,
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200304 nic=None, mtu=None, fragmentation=True,
Assaf Muller92fdc782018-05-31 10:32:47 -0400305 servers=None, timeout=None):
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200306 try:
307 self.assertTrue(self._check_remote_connectivity(
Assaf Muller92fdc782018-05-31 10:32:47 -0400308 source, dest, should_succeed, nic, mtu, fragmentation,
309 timeout=timeout))
Slawek Kaplonskib07251f2018-05-16 12:21:50 +0200310 except lib_exc.SSHTimeout as ssh_e:
311 LOG.debug(ssh_e)
312 self._log_console_output(servers)
313 raise
314 except AssertionError:
315 self._log_console_output(servers)
316 raise
Chandan Kumarc125fd12017-11-15 19:41:01 +0530317
318 def ping_ip_address(self, ip_address, should_succeed=True,
319 ping_timeout=None, mtu=None):
320 # the code is taken from tempest/scenario/manager.py in tempest git
321 timeout = ping_timeout or CONF.validation.ping_timeout
322 cmd = ['ping', '-c1', '-w1']
323
324 if mtu:
325 cmd += [
326 # don't fragment
327 '-M', 'do',
328 # ping receives just the size of ICMP payload
329 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
330 ]
331 cmd.append(ip_address)
332
333 def ping():
334 proc = subprocess.Popen(cmd,
335 stdout=subprocess.PIPE,
336 stderr=subprocess.PIPE)
337 proc.communicate()
338
339 return (proc.returncode == 0) == should_succeed
340
341 caller = test_utils.find_test_caller()
342 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
343 ' expected result is %(should_succeed)s', {
344 'caller': caller, 'ip': ip_address, 'timeout': timeout,
345 'should_succeed':
346 'reachable' if should_succeed else 'unreachable'
347 })
348 result = test_utils.call_until_true(ping, timeout, 1)
349 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
350 'ping result is %(result)s', {
351 'caller': caller, 'ip': ip_address, 'timeout': timeout,
352 'result': 'expected' if result else 'unexpected'
353 })
354 return result
Federico Ressie7417b72018-05-30 05:50:58 +0200355
356 def wait_for_server_status(self, server, status, client=None, **kwargs):
357 """Waits for a server to reach a given status.
358
359 :param server: mapping having schema {'id': <server_id>}
360 :param status: string status to wait for (es: 'ACTIVE')
361 :param clien: servers client (self.os_primary.servers_client as
362 default value)
363 """
364
365 client = client or self.os_primary.servers_client
366 waiters.wait_for_server_status(client, server['id'], status, **kwargs)
367
368 def wait_for_server_active(self, server, client=None):
369 """Waits for a server to reach active status.
370
371 :param server: mapping having schema {'id': <server_id>}
372 :param clien: servers client (self.os_primary.servers_client as
373 default value)
374 """
375 self.wait_for_server_status(
376 server, constants.SERVER_STATUS_ACTIVE, client)