blob: 9fe6a9462ae0290a15ece8f3202d63268a3b0985 [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
Roee Agiman6a0a18a2017-11-16 11:51:56 +020079 name = kwargs.get('name', data_utils.rand_name('server-test'))
80 security_groups = kwargs.get(
81 'security_groups', [{'name': 'default'}])
82 availability_zone = kwargs.get('availability_zone')
Itzik Brownbac51dc2016-10-31 12:25:04 +000083
Roee Agiman6a0a18a2017-11-16 11:51:56 +020084 server_args = {
85 'name': name,
86 'flavorRef': flavor_ref,
87 'imageRef': image_ref,
88 'key_name': key_name,
89 'networks': networks,
90 'security_groups': security_groups
91 }
92
93 if availability_zone:
94 server_args['availability_zone'] = availability_zone
95 client = self.os_admin.servers_client
96 else:
97 client = self.os_primary.servers_client
98
99 server = client.create_server(**server_args)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000100
101 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200102 waiters.wait_for_server_termination,
103 client,
104 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000105 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200106 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000107 server['server']['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000108 return server
109
110 @classmethod
111 def create_keypair(cls, client=None):
rajat294495c042017-06-28 15:37:16 +0530112 client = client or cls.os_primary.keypairs_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000113 name = data_utils.rand_name('keypair-test')
114 body = client.create_keypair(name=name)
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200115 body.update(client=client)
116 if client is cls.os_primary.keypairs_client:
117 cls.keypairs.append(body)
118
Itzik Browne67ebb52016-05-15 05:34:41 +0000119 return body['keypair']
120
121 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200122 def create_secgroup_rules(cls, rule_list, client=None,
123 secgroup_id=None):
124 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000125 if not secgroup_id:
126 sgs = client.list_security_groups()['security_groups']
127 for sg in sgs:
128 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
129 secgroup_id = sg['id']
130 break
131
Itzik Brown1ef813a2016-06-06 12:56:21 +0000132 for rule in rule_list:
133 direction = rule.pop('direction')
134 client.create_security_group_rule(
135 direction=direction,
136 security_group_id=secgroup_id,
137 **rule)
138
139 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200140 def create_loginable_secgroup_rule(cls, secgroup_id=None,
141 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000142 """This rule is intended to permit inbound ssh
143
144 Allowing ssh traffic traffic from all sources, so no group_id is
145 provided.
146 Setting a group_id would only permit traffic from ports
147 belonging to the same security group.
148 """
149
150 rule_list = [{'protocol': 'tcp',
151 'direction': 'ingress',
152 'port_range_min': 22,
153 'port_range_max': 22,
154 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200155 client = client or cls.os_primary.network_client
156 cls.create_secgroup_rules(rule_list, client,
157 secgroup_id=secgroup_id)
Itzik Browne67ebb52016-05-15 05:34:41 +0000158
159 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200160 def create_pingable_secgroup_rule(cls, secgroup_id=None,
161 client=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900162 """This rule is intended to permit inbound ping
163 """
164
165 rule_list = [{'protocol': 'icmp',
166 'direction': 'ingress',
167 'port_range_min': 8, # type
168 'port_range_max': 0, # code
169 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200170 client = client or cls.os_primary.network_client
171 cls.create_secgroup_rules(rule_list, client,
172 secgroup_id=secgroup_id)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900173
174 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300175 def create_router_by_client(cls, is_admin=False, **kwargs):
176 kwargs.update({'router_name': data_utils.rand_name('router'),
177 'admin_state_up': True,
178 'external_network_id': CONF.network.public_network_id})
179 if not is_admin:
180 router = cls.create_router(**kwargs)
181 else:
182 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500183 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000184 cls.routers.append(router)
185 return router
186
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200187 def create_and_associate_floatingip(self, port_id, client=None):
188 client = client or self.os_primary.network_client
189 fip = client.create_floatingip(
Itzik Browne67ebb52016-05-15 05:34:41 +0000190 CONF.network.public_network_id,
191 port_id=port_id)['floatingip']
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200192 if client is self.os_primary.network_client:
193 self.floating_ips.append(fip)
Itzik Browne67ebb52016-05-15 05:34:41 +0000194 return fip
195
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000196 def setup_network_and_server(self, router=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300197 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000198
199 Creating a network, subnet, router, keypair, security group
200 and a server.
201 """
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000202 self.network = self.create_network()
203 LOG.debug("Created network %s", self.network['name'])
204 self.subnet = self.create_subnet(self.network)
205 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000206
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400207 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530208 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500209 LOG.debug("Created security group %s",
210 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000211 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300212 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000213 router = self.create_router_by_client(**kwargs)
214 self.create_router_interface(router['id'], self.subnet['id'])
215 self.keypair = self.create_keypair()
216 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000217 secgroup_id=secgroup['security_group']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000218 self.server = self.create_server(
Itzik Brown1ef813a2016-06-06 12:56:21 +0000219 flavor_ref=CONF.compute.flavor_ref,
220 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000221 key_name=self.keypair['name'],
222 networks=[{'uuid': self.network['id']}],
Itzik Brownbac51dc2016-10-31 12:25:04 +0000223 security_groups=[{'name': secgroup['security_group']['name']}])
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400224 waiters.wait_for_server_status(self.os_primary.servers_client,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000225 self.server['server']['id'],
Itzik Brown1ef813a2016-06-06 12:56:21 +0000226 constants.SERVER_STATUS_ACTIVE)
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000227 self.port = self.client.list_ports(network_id=self.network['id'],
228 device_id=self.server[
229 'server']['id'])['ports'][0]
230 self.fip = self.create_and_associate_floatingip(self.port['id'])
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500231
232 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
233 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
234 try:
235 ssh_client.test_connection_auth()
236 except lib_exc.SSHTimeout as ssh_e:
237 LOG.debug(ssh_e)
238 self._log_console_output(servers)
239 raise
240
241 def _log_console_output(self, servers=None):
242 if not CONF.compute_feature_enabled.console_output:
243 LOG.debug('Console output not supported, cannot log')
244 return
245 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400246 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500247 servers = servers['servers']
248 for server in servers:
249 try:
250 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400251 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500252 server['id'])['output'])
253 LOG.debug('Console output for %s\nbody=\n%s',
254 server['id'], console_output)
255 except lib_exc.NotFound:
256 LOG.debug("Server %s disappeared(deleted) while looking "
257 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900258
259 def _check_remote_connectivity(self, source, dest, should_succeed=True,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300260 nic=None, mtu=None, fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900261 """check ping server via source ssh connection
262
263 :param source: RemoteClient: an ssh connection from which to ping
264 :param dest: and IP to ping against
265 :param should_succeed: boolean should ping succeed or not
266 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300267 :param mtu: mtu size for the packet to be sent
268 :param fragmentation: Flag for packet fragmentation
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900269 :returns: boolean -- should_succeed == ping
270 :returns: ping is false if ping failed
271 """
272 def ping_host(source, host, count=CONF.validation.ping_count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300273 size=CONF.validation.ping_size, nic=None, mtu=None,
274 fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900275 addr = netaddr.IPAddress(host)
276 cmd = 'ping6' if addr.version == 6 else 'ping'
277 if nic:
278 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300279 if mtu:
280 if not fragmentation:
281 cmd += ' -M do'
282 size = str(net_utils.get_ping_payload_size(
283 mtu=mtu, ip_version=addr.version))
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900284 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
285 return source.exec_command(cmd)
286
287 def ping_remote():
288 try:
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300289 result = ping_host(source, dest, nic=nic, mtu=mtu,
290 fragmentation=fragmentation)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900291
292 except lib_exc.SSHExecCommandFailed:
293 LOG.warning('Failed to ping IP: %s via a ssh connection '
294 'from: %s.', dest, source.host)
295 return not should_succeed
296 LOG.debug('ping result: %s', result)
297 # Assert that the return traffic was from the correct
298 # source address.
299 from_source = 'from %s' % dest
300 self.assertIn(from_source, result)
301 return should_succeed
302
303 return test_utils.call_until_true(ping_remote,
304 CONF.validation.ping_timeout,
305 1)
306
307 def check_remote_connectivity(self, source, dest, should_succeed=True,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300308 nic=None, mtu=None, fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900309 self.assertTrue(self._check_remote_connectivity(
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300310 source, dest, should_succeed, nic, mtu, fragmentation))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530311
312 def ping_ip_address(self, ip_address, should_succeed=True,
313 ping_timeout=None, mtu=None):
314 # the code is taken from tempest/scenario/manager.py in tempest git
315 timeout = ping_timeout or CONF.validation.ping_timeout
316 cmd = ['ping', '-c1', '-w1']
317
318 if mtu:
319 cmd += [
320 # don't fragment
321 '-M', 'do',
322 # ping receives just the size of ICMP payload
323 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
324 ]
325 cmd.append(ip_address)
326
327 def ping():
328 proc = subprocess.Popen(cmd,
329 stdout=subprocess.PIPE,
330 stderr=subprocess.PIPE)
331 proc.communicate()
332
333 return (proc.returncode == 0) == should_succeed
334
335 caller = test_utils.find_test_caller()
336 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
337 ' expected result is %(should_succeed)s', {
338 'caller': caller, 'ip': ip_address, 'timeout': timeout,
339 'should_succeed':
340 'reachable' if should_succeed else 'unreachable'
341 })
342 result = test_utils.call_until_true(ping, timeout, 1)
343 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
344 'ping result is %(result)s', {
345 'caller': caller, 'ip': ip_address, 'timeout': timeout,
346 'result': 'expected' if result else 'unexpected'
347 })
348 return result