blob: 50f6b54b01c93ef1cbe2bf303a3a0d518b5cb622 [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'))
Jens Harbott54357632017-11-21 11:47:06 +000080 security_groups = kwargs.get('security_groups')
81 if not security_groups:
82 security_groups = [{'name': 'default'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +020083 availability_zone = kwargs.get('availability_zone')
Itzik Brownbac51dc2016-10-31 12:25:04 +000084
Roee Agiman6a0a18a2017-11-16 11:51:56 +020085 server_args = {
86 'name': name,
87 'flavorRef': flavor_ref,
88 'imageRef': image_ref,
89 'key_name': key_name,
90 'networks': networks,
91 'security_groups': security_groups
92 }
93
94 if availability_zone:
95 server_args['availability_zone'] = availability_zone
96 client = self.os_admin.servers_client
97 else:
98 client = self.os_primary.servers_client
99
100 server = client.create_server(**server_args)
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000101
102 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200103 waiters.wait_for_server_termination,
104 client,
105 server['server']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000106 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200107 client.delete_server,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000108 server['server']['id'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000109 return server
110
111 @classmethod
112 def create_keypair(cls, client=None):
rajat294495c042017-06-28 15:37:16 +0530113 client = client or cls.os_primary.keypairs_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000114 name = data_utils.rand_name('keypair-test')
115 body = client.create_keypair(name=name)
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200116 body.update(client=client)
117 if client is cls.os_primary.keypairs_client:
118 cls.keypairs.append(body)
119
Itzik Browne67ebb52016-05-15 05:34:41 +0000120 return body['keypair']
121
122 @classmethod
Jens Harbott54357632017-11-21 11:47:06 +0000123 def create_secgroup_rules(cls, rule_list, secgroup_id=None,
124 client=None):
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200125 client = client or cls.os_primary.network_client
Itzik Browne67ebb52016-05-15 05:34:41 +0000126 if not secgroup_id:
127 sgs = client.list_security_groups()['security_groups']
128 for sg in sgs:
129 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
130 secgroup_id = sg['id']
131 break
132
Itzik Brown1ef813a2016-06-06 12:56:21 +0000133 for rule in rule_list:
134 direction = rule.pop('direction')
135 client.create_security_group_rule(
136 direction=direction,
137 security_group_id=secgroup_id,
138 **rule)
139
140 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200141 def create_loginable_secgroup_rule(cls, secgroup_id=None,
142 client=None):
Itzik Brown1ef813a2016-06-06 12:56:21 +0000143 """This rule is intended to permit inbound ssh
144
145 Allowing ssh traffic traffic from all sources, so no group_id is
146 provided.
147 Setting a group_id would only permit traffic from ports
148 belonging to the same security group.
149 """
150
151 rule_list = [{'protocol': 'tcp',
152 'direction': 'ingress',
153 'port_range_min': 22,
154 'port_range_max': 22,
155 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200156 client = client or cls.os_primary.network_client
Jens Harbott54357632017-11-21 11:47:06 +0000157 cls.create_secgroup_rules(rule_list, client=client,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200158 secgroup_id=secgroup_id)
Itzik Browne67ebb52016-05-15 05:34:41 +0000159
160 @classmethod
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200161 def create_pingable_secgroup_rule(cls, secgroup_id=None,
162 client=None):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900163 """This rule is intended to permit inbound ping
164 """
165
166 rule_list = [{'protocol': 'icmp',
167 'direction': 'ingress',
168 'port_range_min': 8, # type
169 'port_range_max': 0, # code
170 'remote_ip_prefix': '0.0.0.0/0'}]
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200171 client = client or cls.os_primary.network_client
Jens Harbott54357632017-11-21 11:47:06 +0000172 cls.create_secgroup_rules(rule_list, client=client,
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200173 secgroup_id=secgroup_id)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900174
175 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300176 def create_router_by_client(cls, is_admin=False, **kwargs):
177 kwargs.update({'router_name': data_utils.rand_name('router'),
178 'admin_state_up': True,
179 'external_network_id': CONF.network.public_network_id})
180 if not is_admin:
181 router = cls.create_router(**kwargs)
182 else:
183 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500184 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000185 cls.routers.append(router)
186 return router
187
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200188 def create_and_associate_floatingip(self, port_id, client=None):
189 client = client or self.os_primary.network_client
190 fip = client.create_floatingip(
Itzik Browne67ebb52016-05-15 05:34:41 +0000191 CONF.network.public_network_id,
192 port_id=port_id)['floatingip']
Roee Agiman6a0a18a2017-11-16 11:51:56 +0200193 if client is self.os_primary.network_client:
194 self.floating_ips.append(fip)
Itzik Browne67ebb52016-05-15 05:34:41 +0000195 return fip
196
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000197 def setup_network_and_server(self, router=None, **kwargs):
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300198 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000199
200 Creating a network, subnet, router, keypair, security group
201 and a server.
202 """
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000203 self.network = self.create_network()
204 LOG.debug("Created network %s", self.network['name'])
205 self.subnet = self.create_subnet(self.network)
206 LOG.debug("Created subnet %s", self.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000207
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400208 secgroup = self.os_primary.network_client.create_security_group(
Chandan Kumarc125fd12017-11-15 19:41:01 +0530209 name=data_utils.rand_name('secgroup'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500210 LOG.debug("Created security group %s",
211 secgroup['security_group']['name'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000212 self.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300213 if not router:
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000214 router = self.create_router_by_client(**kwargs)
215 self.create_router_interface(router['id'], self.subnet['id'])
216 self.keypair = self.create_keypair()
217 self.create_loginable_secgroup_rule(
Itzik Brownbac51dc2016-10-31 12:25:04 +0000218 secgroup_id=secgroup['security_group']['id'])
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000219 self.server = self.create_server(
Itzik Brown1ef813a2016-06-06 12:56:21 +0000220 flavor_ref=CONF.compute.flavor_ref,
221 image_ref=CONF.compute.image_ref,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000222 key_name=self.keypair['name'],
223 networks=[{'uuid': self.network['id']}],
Itzik Brownbac51dc2016-10-31 12:25:04 +0000224 security_groups=[{'name': secgroup['security_group']['name']}])
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400225 waiters.wait_for_server_status(self.os_primary.servers_client,
Genadi Chereshnya918dd0b2017-05-17 13:02:20 +0000226 self.server['server']['id'],
Itzik Brown1ef813a2016-06-06 12:56:21 +0000227 constants.SERVER_STATUS_ACTIVE)
Jakub Libosvar1345d9d2017-06-09 13:59:05 +0000228 self.port = self.client.list_ports(network_id=self.network['id'],
229 device_id=self.server[
230 'server']['id'])['ports'][0]
231 self.fip = self.create_and_associate_floatingip(self.port['id'])
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500232
233 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
234 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
235 try:
236 ssh_client.test_connection_auth()
237 except lib_exc.SSHTimeout as ssh_e:
238 LOG.debug(ssh_e)
239 self._log_console_output(servers)
240 raise
241
242 def _log_console_output(self, servers=None):
243 if not CONF.compute_feature_enabled.console_output:
244 LOG.debug('Console output not supported, cannot log')
245 return
246 if not servers:
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400247 servers = self.os_primary.servers_client.list_servers()
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500248 servers = servers['servers']
249 for server in servers:
250 try:
251 console_output = (
Brian Haleyf86ac2e2017-06-21 10:43:50 -0400252 self.os_primary.servers_client.get_console_output(
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500253 server['id'])['output'])
254 LOG.debug('Console output for %s\nbody=\n%s',
255 server['id'], console_output)
256 except lib_exc.NotFound:
257 LOG.debug("Server %s disappeared(deleted) while looking "
258 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900259
260 def _check_remote_connectivity(self, source, dest, should_succeed=True,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300261 nic=None, mtu=None, fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900262 """check ping server via source ssh connection
263
264 :param source: RemoteClient: an ssh connection from which to ping
265 :param dest: and IP to ping against
266 :param should_succeed: boolean should ping succeed or not
267 :param nic: specific network interface to ping from
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300268 :param mtu: mtu size for the packet to be sent
269 :param fragmentation: Flag for packet fragmentation
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900270 :returns: boolean -- should_succeed == ping
271 :returns: ping is false if ping failed
272 """
273 def ping_host(source, host, count=CONF.validation.ping_count,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300274 size=CONF.validation.ping_size, nic=None, mtu=None,
275 fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900276 addr = netaddr.IPAddress(host)
277 cmd = 'ping6' if addr.version == 6 else 'ping'
278 if nic:
279 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300280 if mtu:
281 if not fragmentation:
282 cmd += ' -M do'
283 size = str(net_utils.get_ping_payload_size(
284 mtu=mtu, ip_version=addr.version))
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900285 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
286 return source.exec_command(cmd)
287
288 def ping_remote():
289 try:
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300290 result = ping_host(source, dest, nic=nic, mtu=mtu,
291 fragmentation=fragmentation)
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900292
293 except lib_exc.SSHExecCommandFailed:
294 LOG.warning('Failed to ping IP: %s via a ssh connection '
295 'from: %s.', dest, source.host)
296 return not should_succeed
297 LOG.debug('ping result: %s', result)
298 # Assert that the return traffic was from the correct
299 # source address.
300 from_source = 'from %s' % dest
301 self.assertIn(from_source, result)
302 return should_succeed
303
304 return test_utils.call_until_true(ping_remote,
305 CONF.validation.ping_timeout,
306 1)
307
308 def check_remote_connectivity(self, source, dest, should_succeed=True,
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300309 nic=None, mtu=None, fragmentation=True):
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900310 self.assertTrue(self._check_remote_connectivity(
Genadi Chereshnya6d10c6e2017-07-05 11:34:20 +0300311 source, dest, should_succeed, nic, mtu, fragmentation))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530312
313 def ping_ip_address(self, ip_address, should_succeed=True,
314 ping_timeout=None, mtu=None):
315 # the code is taken from tempest/scenario/manager.py in tempest git
316 timeout = ping_timeout or CONF.validation.ping_timeout
317 cmd = ['ping', '-c1', '-w1']
318
319 if mtu:
320 cmd += [
321 # don't fragment
322 '-M', 'do',
323 # ping receives just the size of ICMP payload
324 '-s', str(net_utils.get_ping_payload_size(mtu, 4))
325 ]
326 cmd.append(ip_address)
327
328 def ping():
329 proc = subprocess.Popen(cmd,
330 stdout=subprocess.PIPE,
331 stderr=subprocess.PIPE)
332 proc.communicate()
333
334 return (proc.returncode == 0) == should_succeed
335
336 caller = test_utils.find_test_caller()
337 LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
338 ' expected result is %(should_succeed)s', {
339 'caller': caller, 'ip': ip_address, 'timeout': timeout,
340 'should_succeed':
341 'reachable' if should_succeed else 'unreachable'
342 })
343 result = test_utils.call_until_true(ping, timeout, 1)
344 LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
345 'ping result is %(result)s', {
346 'caller': caller, 'ip': ip_address, 'timeout': timeout,
347 'result': 'expected' if result else 'unexpected'
348 })
349 return result