blob: 2bb63445cdcb9b9fcf8dfe846b1dfc70ea71c28b [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
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050018from oslo_log import log
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +030019from tempest.common.utils import net_utils
Itzik Browne67ebb52016-05-15 05:34:41 +000020from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000021from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090022from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050023from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000024
Chandan Kumar667d3d32017-09-22 12:24:06 +053025from neutron_tempest_plugin.api import base as base_api
26from neutron_tempest_plugin.common import ssh
27from neutron_tempest_plugin import config
28from neutron_tempest_plugin.scenario import constants
Itzik Browne67ebb52016-05-15 05:34:41 +000029
30CONF = config.CONF
Itzik Browne67ebb52016-05-15 05:34:41 +000031
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050032LOG = log.getLogger(__name__)
33
Itzik Browne67ebb52016-05-15 05:34:41 +000034
35class BaseTempestTestCase(base_api.BaseNetworkTest):
36 @classmethod
37 def resource_setup(cls):
38 super(BaseTempestTestCase, cls).resource_setup()
39
Itzik Browne67ebb52016-05-15 05:34:41 +000040 cls.keypairs = []
41
42 @classmethod
43 def resource_cleanup(cls):
Itzik Browne67ebb52016-05-15 05:34:41 +000044 for keypair in cls.keypairs:
Roee Agiman6a0a18a2017-11-16 11:51:56 +020045 client = keypair['client']
46 client.delete_keypair(
47 keypair_name=keypair['keypair']['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +000048
49 super(BaseTempestTestCase, cls).resource_cleanup()
50
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000051 def create_server(self, flavor_ref, image_ref, key_name, networks,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020052 **kwargs):
Itzik Brownbac51dc2016-10-31 12:25:04 +000053 """Create a server using tempest lib
54 All the parameters are the ones used in Compute API
Roee Agiman6a0a18a2017-11-16 11:51:56 +020055 * - Kwargs that require admin privileges
Itzik Brownbac51dc2016-10-31 12:25:04 +000056
57 Args:
58 flavor_ref(str): The flavor of the server to be provisioned.
59 image_ref(str): The image of the server to be provisioned.
60 key_name(str): SSH key to to be used to connect to the
61 provisioned server.
62 networks(list): List of dictionaries where each represent
63 an interface to be attached to the server. For network
64 it should be {'uuid': network_uuid} and for port it should
65 be {'port': port_uuid}
Roee Agiman6a0a18a2017-11-16 11:51:56 +020066 kwargs:
Itzik Brownbac51dc2016-10-31 12:25:04 +000067 name(str): Name of the server to be provisioned.
68 security_groups(list): List of dictionaries where
69 the keys is 'name' and the value is the name of
70 the security group. If it's not passed the default
71 security group will be used.
Roee Agiman6a0a18a2017-11-16 11:51:56 +020072 availability_zone(str)*: The availability zone that
73 the instance will be in.
74 You can request a specific az without actually creating one,
75 Just pass 'X:Y' where X is the default availability
76 zone, and Y is the compute host name.
Itzik Brownbac51dc2016-10-31 12:25:04 +000077 """
78
Jakub Libosvarffd9b912017-11-16 09:54:14 +000079 kwargs.setdefault('name', data_utils.rand_name('server-test'))
Itzik Brownbac51dc2016-10-31 12:25:04 +000080
Jakub Libosvarffd9b912017-11-16 09:54:14 +000081 # We cannot use setdefault() here because caller could have passed
82 # security_groups=None and we don't want to pass None to
83 # client.create_server()
84 if not kwargs.get('security_groups'):
85 kwargs['security_groups'] = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +020086
Jakub Libosvarffd9b912017-11-16 09:54:14 +000087 client = self.os_primary.servers_client
88 if kwargs.get('availability_zone'):
Roee Agiman6a0a18a2017-11-16 11:51:56 +020089 client = self.os_admin.servers_client
Roee Agiman6a0a18a2017-11-16 11:51:56 +020090
Jakub Libosvarffd9b912017-11-16 09:54:14 +000091 server = client.create_server(
92 flavorRef=flavor_ref,
93 imageRef=image_ref,
94 key_name=key_name,
95 networks=networks,
96 **kwargs)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +000097
98 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +020099 waiters.wait_for_server_termination,
100 client,
101 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000102 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200103 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000104 server['server']['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000105 return server
106
107 @classmethod
108 def create_keypair(cls, client=None):
rajat294495c042017-06-28 15:37:16 +0530109 client = client or cls.os_primary.keypairs_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000110 name = data_utils.rand_name('keypair-test')
111 body = client.create_keypair(name=name)
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200112 body.update(client=client)
113 if client is cls.os_primary.keypairs_client:
114 cls.keypairs.append(body)
115
Itzik Browne67ebb52016-05-15 05:34:41 +0000116 return body['keypair']
117
118 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +0000119 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
120 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200121 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000122 if not secgroup_id:
123 sgs = client.list_security_groups()['security_groups']
124 for sg in sgs:
125 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
126 secgroup_id = sg['id']
127 break
128
Itzik Brown1ef813a2016-06-06 12:56:21 +0000129 for rule in rule_list:
130 direction = rule.pop('direction')
131 client.create_security_group_rule(
132 direction=direction,
133 security_group_id=secgroup_id,
134 **rule)
135
136 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200137 def create_loginable_secgroup_rule(cls, secgroup_id=None,
138 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000139 """This rule is intended to permit inbound ssh
140
141 Allowing ssh traffic traffic from all sources, so no group_id is
142 provided.
143 Setting a group_id would only permit traffic from ports
144 belonging to the same security group.
145 """
146
147 rule_list = [{'protocol': 'tcp',
148 'direction': 'ingress',
149 'port_range_min': 22,
150 'port_range_max': 22,
151 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200152 client = client or cls.os_primary.network_client
Jens Harbott54357632017-11-21 11:47:06 +0000153 cls.create_secgroup_rules(rule_list, client=client,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200154 secgroup_id=secgroup_id)
Itzik Browne67ebb52016-05-15 05:34:41 +0000155
156 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200157 def create_pingable_secgroup_rule(cls, secgroup_id=None,
158 client=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900159 """This rule is intended to permit inbound ping
160 """
161
162 rule_list = [{'protocol': 'icmp',
163 'direction': 'ingress',
164 'port_range_min': 8, # type
165 'port_range_max': 0, # code
166 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200167 client = client or cls.os_primary.network_client
Jens Harbott54357632017-11-21 11:47:06 +0000168 cls.create_secgroup_rules(rule_list, client=client,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200169 secgroup_id=secgroup_id)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900170
171 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300172 def create_router_by_client(cls, is_admin=False, **kwargs):
173 kwargs.update({'router_name': data_utils.rand_name('router'),
174 'admin_state_up': True,
175 'external_network_id': CONF.network.public_network_id})
176 if not is_admin:
177 router = cls.create_router(**kwargs)
178 else:
179 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500180 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000181 cls.routers.append(router)
182 return router
183
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200184 def create_and_associate_floatingip(self, port_id, client=None):
185 client = client or self.os_primary.network_client
186 fip = client.create_floatingip(
Itzik Browne67ebb52016-05-15 05:34:41 +0000187 CONF.network.public_network_id,
188 port_id=port_id)['floatingip']
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200189 if client is self.os_primary.network_client:
190 self.floating_ips.append(fip)
Itzik Browne67ebb52016-05-15 05:34:41 +0000191 return fip
192
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000193 def setup_network_and_server(self, router=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300194 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000195
196 Creating a network, subnet, router, keypair, security group
197 and a server.
198 """
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000199 self.network = self.create_network()
200 LOG.debug("Created network %s", self.network['name'])
201 self.subnet = self.create_subnet(self.network)
202 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000203
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400204 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530205 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500206 LOG.debug("Created security group %s",
207 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000208 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300209 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000210 router = self.create_router_by_client(**kwargs)
211 self.create_router_interface(router['id'], self.subnet['id'])
212 self.keypair = self.create_keypair()
213 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000214 secgroup_id=secgroup['security_group']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000215 self.server = self.create_server(
Itzik Brown1ef813a2016-06-06 12:56:21 +0000216 flavor_ref=CONF.compute.flavor_ref,
217 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000218 key_name=self.keypair['name'],
219 networks=[{'uuid': self.network['id']}],
Itzik Brownbac51dc2016-10-31 12:25:04 +0000220 security_groups=[{'name': secgroup['security_group']['name']}])
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400221 waiters.wait_for_server_status(self.os_primary.servers_client,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000222 self.server['server']['id'],
Itzik Brown1ef813a2016-06-06 12:56:21 +0000223 constants.SERVER_STATUS_ACTIVE)
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000224 self.port = self.client.list_ports(network_id=self.network['id'],
225 device_id=self.server[
226 'server']['id'])['ports'][0]
227 self.fip = self.create_and_associate_floatingip(self.port['id'])
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500228
229 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
230 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
231 try:
232 ssh_client.test_connection_auth()
233 except lib_exc.SSHTimeout as ssh_e:
234 LOG.debug(ssh_e)
235 self._log_console_output(servers)
236 raise
237
238 def _log_console_output(self, servers=None):
239 if not CONF.compute_feature_enabled.console_output:
240 LOG.debug('Console output not supported, cannot log')
241 return
242 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400243 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500244 servers = servers['servers']
245 for server in servers:
246 try:
247 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400248 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500249 server['id'])['output'])
250 LOG.debug('Console output for %s\nbody=\n%s',
251 server['id'], console_output)
252 except lib_exc.NotFound:
253 LOG.debug("Server %s disappeared(deleted) while looking "
254 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900255
256 def _check_remote_connectivity(self, source, dest, should_succeed=True,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300257 nic=None, mtu=None, fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900258 """check ping server via source ssh connection
259
260 :param source: RemoteClient: an ssh connection from which to ping
261 :param dest: and IP to ping against
262 :param should_succeed: boolean should ping succeed or not
263 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300264 :param mtu: mtu size for the packet to be sent
265 :param fragmentation: Flag for packet fragmentation
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900266 :returns: boolean -- should_succeed == ping
267 :returns: ping is false if ping failed
268 """
269 def ping_host(source, host, count=CONF.validation.ping_count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300270 size=CONF.validation.ping_size, nic=None, mtu=None,
271 fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900272 addr = netaddr.IPAddress(host)
273 cmd = 'ping6' if addr.version == 6 else 'ping'
274 if nic:
275 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300276 if mtu:
277 if not fragmentation:
278 cmd += ' -M do'
279 size = str(net_utils.get_ping_payload_size(
280 mtu=mtu, ip_version=addr.version))
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900281 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
282 return source.exec_command(cmd)
283
284 def ping_remote():
285 try:
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300286 result = ping_host(source, dest, nic=nic, mtu=mtu,
287 fragmentation=fragmentation)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900288
289 except lib_exc.SSHExecCommandFailed:
290 LOG.warning('Failed to ping IP: %s via a ssh connection '
291 'from: %s.', dest, source.host)
292 return not should_succeed
293 LOG.debug('ping result: %s', result)
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)
298 return should_succeed
299
300 return test_utils.call_until_true(ping_remote,
301 CONF.validation.ping_timeout,
302 1)
303
304 def check_remote_connectivity(self, source, dest, should_succeed=True,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300305 nic=None, mtu=None, fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900306 self.assertTrue(self._check_remote_connectivity(
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300307 source, dest, should_succeed, nic, mtu, fragmentation))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530308
309 def ping_ip_address(self, ip_address, should_succeed=True,
310 ping_timeout=None, mtu=None):
311 # the code is taken from tempest/scenario/manager.py in tempest git
312 timeout = ping_timeout or CONF.validation.ping_timeout
313 cmd = ['ping', '-c1', '-w1']
314
315 if mtu:
316 cmd += [
317 # don't fragment
318 '-M', 'do',
319 # ping receives just the size of ICMP payload
320 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
321 ]
322 cmd.append(ip_address)
323
324 def ping():
325 proc = subprocess.Popen(cmd,
326 stdout=subprocess.PIPE,
327 stderr=subprocess.PIPE)
328 proc.communicate()
329
330 return (proc.returncode == 0) == should_succeed
331
332 caller = test_utils.find_test_caller()
333 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
334 ' expected result is %(should_succeed)s', {
335 'caller': caller, 'ip': ip_address, 'timeout': timeout,
336 'should_succeed':
337 'reachable' if should_succeed else 'unreachable'
338 })
339 result = test_utils.call_until_true(ping, timeout, 1)
340 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
341 'ping result is %(result)s', {
342 'caller': caller, 'ip': ip_address, 'timeout': timeout,
343 'result': 'expected' if result else 'unexpected'
344 })
345 return result