blob: 967a45e23b90b9bfda79415cadcec7d232eff15f [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.
Itzik Browne67ebb52016-05-15 05:34:41 +000015
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090016import netaddr
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050017from oslo_log import log
18
Itzik Browne67ebb52016-05-15 05:34:41 +000019from tempest.common import waiters
Itzik Browne67ebb52016-05-15 05:34:41 +000020from tempest.lib.common.utils import data_utils
YAMAMOTO Takashi25935722017-01-23 15:34:11 +090021from tempest.lib.common.utils import test_utils
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -050022from tempest.lib import exceptions as lib_exc
Itzik Browne67ebb52016-05-15 05:34:41 +000023
24from neutron.tests.tempest.api import base as base_api
Jakub Libosvar7c58cb22017-05-03 09:00:14 +000025from neutron.tests.tempest.common import ssh
Itzik Browne67ebb52016-05-15 05:34:41 +000026from neutron.tests.tempest import config
27from neutron.tests.tempest.scenario import constants
28
29CONF = config.CONF
Itzik Browne67ebb52016-05-15 05:34:41 +000030
Alex Stafeyevc4d9c352016-12-12 04:13:33 -050031LOG = log.getLogger(__name__)
32
Itzik Browne67ebb52016-05-15 05:34:41 +000033
34class BaseTempestTestCase(base_api.BaseNetworkTest):
35 @classmethod
36 def resource_setup(cls):
37 super(BaseTempestTestCase, cls).resource_setup()
38
39 cls.servers = []
40 cls.keypairs = []
41
42 @classmethod
43 def resource_cleanup(cls):
44 for server in cls.servers:
45 cls.manager.servers_client.delete_server(server)
46 waiters.wait_for_server_termination(cls.manager.servers_client,
47 server)
48
49 for keypair in cls.keypairs:
50 cls.manager.keypairs_client.delete_keypair(
51 keypair_name=keypair['name'])
52
53 super(BaseTempestTestCase, cls).resource_cleanup()
54
55 @classmethod
56 def create_server(cls, flavor_ref, image_ref, key_name, networks,
Itzik Brownbac51dc2016-10-31 12:25:04 +000057 name=None, security_groups=None):
58 """Create a server using tempest lib
59 All the parameters are the ones used in Compute API
60
61 Args:
62 flavor_ref(str): The flavor of the server to be provisioned.
63 image_ref(str): The image of the server to be provisioned.
64 key_name(str): SSH key to to be used to connect to the
65 provisioned server.
66 networks(list): List of dictionaries where each represent
67 an interface to be attached to the server. For network
68 it should be {'uuid': network_uuid} and for port it should
69 be {'port': port_uuid}
70 name(str): Name of the server to be provisioned.
71 security_groups(list): List of dictionaries where
72 the keys is 'name' and the value is the name of
73 the security group. If it's not passed the default
74 security group will be used.
75 """
76
Itzik Browne67ebb52016-05-15 05:34:41 +000077 name = name or data_utils.rand_name('server-test')
Itzik Brownbac51dc2016-10-31 12:25:04 +000078 if not security_groups:
79 security_groups = [{'name': 'default'}]
80
Itzik Browne67ebb52016-05-15 05:34:41 +000081 server = cls.manager.servers_client.create_server(
Itzik Brownbac51dc2016-10-31 12:25:04 +000082 name=name,
83 flavorRef=flavor_ref,
Itzik Browne67ebb52016-05-15 05:34:41 +000084 imageRef=image_ref,
85 key_name=key_name,
Itzik Brownbac51dc2016-10-31 12:25:04 +000086 networks=networks,
87 security_groups=security_groups)
Itzik Browne67ebb52016-05-15 05:34:41 +000088 cls.servers.append(server['server']['id'])
89 return server
90
91 @classmethod
92 def create_keypair(cls, client=None):
93 client = client or cls.manager.keypairs_client
94 name = data_utils.rand_name('keypair-test')
95 body = client.create_keypair(name=name)
96 cls.keypairs.append(body['keypair'])
97 return body['keypair']
98
99 @classmethod
Itzik Brown1ef813a2016-06-06 12:56:21 +0000100 def create_secgroup_rules(cls, rule_list, secgroup_id=None):
Itzik Browne67ebb52016-05-15 05:34:41 +0000101 client = cls.manager.network_client
102 if not secgroup_id:
103 sgs = client.list_security_groups()['security_groups']
104 for sg in sgs:
105 if sg['name'] == constants.DEFAULT_SECURITY_GROUP:
106 secgroup_id = sg['id']
107 break
108
Itzik Brown1ef813a2016-06-06 12:56:21 +0000109 for rule in rule_list:
110 direction = rule.pop('direction')
111 client.create_security_group_rule(
112 direction=direction,
113 security_group_id=secgroup_id,
114 **rule)
115
116 @classmethod
117 def create_loginable_secgroup_rule(cls, secgroup_id=None):
118 """This rule is intended to permit inbound ssh
119
120 Allowing ssh traffic traffic from all sources, so no group_id is
121 provided.
122 Setting a group_id would only permit traffic from ports
123 belonging to the same security group.
124 """
125
126 rule_list = [{'protocol': 'tcp',
127 'direction': 'ingress',
128 'port_range_min': 22,
129 'port_range_max': 22,
130 'remote_ip_prefix': '0.0.0.0/0'}]
131 cls.create_secgroup_rules(rule_list, secgroup_id=secgroup_id)
Itzik Browne67ebb52016-05-15 05:34:41 +0000132
133 @classmethod
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900134 def create_pingable_secgroup_rule(cls, secgroup_id=None):
135 """This rule is intended to permit inbound ping
136 """
137
138 rule_list = [{'protocol': 'icmp',
139 'direction': 'ingress',
140 'port_range_min': 8, # type
141 'port_range_max': 0, # code
142 'remote_ip_prefix': '0.0.0.0/0'}]
143 cls.create_secgroup_rules(rule_list, secgroup_id=secgroup_id)
144
145 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300146 def create_router_by_client(cls, is_admin=False, **kwargs):
147 kwargs.update({'router_name': data_utils.rand_name('router'),
148 'admin_state_up': True,
149 'external_network_id': CONF.network.public_network_id})
150 if not is_admin:
151 router = cls.create_router(**kwargs)
152 else:
153 router = cls.create_admin_router(**kwargs)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500154 LOG.debug("Created router %s", router['name'])
Itzik Browne67ebb52016-05-15 05:34:41 +0000155 cls.routers.append(router)
156 return router
157
158 @classmethod
159 def create_and_associate_floatingip(cls, port_id):
160 fip = cls.manager.network_client.create_floatingip(
161 CONF.network.public_network_id,
162 port_id=port_id)['floatingip']
163 cls.floating_ips.append(fip)
164 return fip
165
166 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300167 def setup_network_and_server(cls, router=None, **kwargs):
168 """Create network resources and a server.
Itzik Brownbac51dc2016-10-31 12:25:04 +0000169
170 Creating a network, subnet, router, keypair, security group
171 and a server.
172 """
Itzik Brown1ef813a2016-06-06 12:56:21 +0000173 cls.network = cls.create_network()
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500174 LOG.debug("Created network %s", cls.network['name'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000175 cls.subnet = cls.create_subnet(cls.network)
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500176 LOG.debug("Created subnet %s", cls.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000177
Itzik Brownbac51dc2016-10-31 12:25:04 +0000178 secgroup = cls.manager.network_client.create_security_group(
179 name=data_utils.rand_name('secgroup-'))
Alex Stafeyevc4d9c352016-12-12 04:13:33 -0500180 LOG.debug("Created security group %s",
181 secgroup['security_group']['name'])
Itzik Brownbac51dc2016-10-31 12:25:04 +0000182 cls.security_groups.append(secgroup['security_group'])
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300183 if not router:
184 router = cls.create_router_by_client(**kwargs)
185 cls.create_router_interface(router['id'], cls.subnet['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000186 cls.keypair = cls.create_keypair()
Itzik Brownbac51dc2016-10-31 12:25:04 +0000187 cls.create_loginable_secgroup_rule(
188 secgroup_id=secgroup['security_group']['id'])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000189 cls.server = cls.create_server(
190 flavor_ref=CONF.compute.flavor_ref,
191 image_ref=CONF.compute.image_ref,
192 key_name=cls.keypair['name'],
Itzik Brownbac51dc2016-10-31 12:25:04 +0000193 networks=[{'uuid': cls.network['id']}],
194 security_groups=[{'name': secgroup['security_group']['name']}])
Itzik Brown1ef813a2016-06-06 12:56:21 +0000195 waiters.wait_for_server_status(cls.manager.servers_client,
196 cls.server['server']['id'],
197 constants.SERVER_STATUS_ACTIVE)
198 port = cls.client.list_ports(network_id=cls.network['id'],
199 device_id=cls.server[
200 'server']['id'])['ports'][0]
201 cls.fip = cls.create_and_associate_floatingip(port['id'])
Jakub Libosvarc0c2f1d2017-01-31 12:12:21 -0500202
203 def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
204 ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
205 try:
206 ssh_client.test_connection_auth()
207 except lib_exc.SSHTimeout as ssh_e:
208 LOG.debug(ssh_e)
209 self._log_console_output(servers)
210 raise
211
212 def _log_console_output(self, servers=None):
213 if not CONF.compute_feature_enabled.console_output:
214 LOG.debug('Console output not supported, cannot log')
215 return
216 if not servers:
217 servers = self.manager.servers_client.list_servers()
218 servers = servers['servers']
219 for server in servers:
220 try:
221 console_output = (
222 self.manager.servers_client.get_console_output(
223 server['id'])['output'])
224 LOG.debug('Console output for %s\nbody=\n%s',
225 server['id'], console_output)
226 except lib_exc.NotFound:
227 LOG.debug("Server %s disappeared(deleted) while looking "
228 "for the console log", server['id'])
YAMAMOTO Takashi25935722017-01-23 15:34:11 +0900229
230 def _check_remote_connectivity(self, source, dest, should_succeed=True,
231 nic=None):
232 """check ping server via source ssh connection
233
234 :param source: RemoteClient: an ssh connection from which to ping
235 :param dest: and IP to ping against
236 :param should_succeed: boolean should ping succeed or not
237 :param nic: specific network interface to ping from
238 :returns: boolean -- should_succeed == ping
239 :returns: ping is false if ping failed
240 """
241 def ping_host(source, host, count=CONF.validation.ping_count,
242 size=CONF.validation.ping_size, nic=None):
243 addr = netaddr.IPAddress(host)
244 cmd = 'ping6' if addr.version == 6 else 'ping'
245 if nic:
246 cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
247 cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
248 return source.exec_command(cmd)
249
250 def ping_remote():
251 try:
252 result = ping_host(source, dest, nic=nic)
253
254 except lib_exc.SSHExecCommandFailed:
255 LOG.warning('Failed to ping IP: %s via a ssh connection '
256 'from: %s.', dest, source.host)
257 return not should_succeed
258 LOG.debug('ping result: %s', result)
259 # Assert that the return traffic was from the correct
260 # source address.
261 from_source = 'from %s' % dest
262 self.assertIn(from_source, result)
263 return should_succeed
264
265 return test_utils.call_until_true(ping_remote,
266 CONF.validation.ping_timeout,
267 1)
268
269 def check_remote_connectivity(self, source, dest, should_succeed=True,
270 nic=None):
271 self.assertTrue(self._check_remote_connectivity(
272 source, dest, should_succeed, nic))