blob: d605dfff542e208ebeffe4cc7e17002930ca1fc0 [file] [log] [blame]
Maru Newby81f07a02012-09-05 20:21:19 -07001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
ZhiQiang Fan39f97222013-09-20 04:49:44 +08003# Copyright 2012 OpenStack Foundation
Gavin Brebner0f465a32013-03-14 13:26:09 +00004# Copyright 2013 Hewlett-Packard Development Company, L.P.
Maru Newby81f07a02012-09-05 20:21:19 -07005# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
Sean Dague1937d092013-05-17 16:36:38 -040019from tempest.api.network import common as net_common
Attila Fazekasaeeeefd2013-08-06 17:01:56 +020020from tempest.common import debug
Masayuki Igawa259c1132013-10-31 17:48:44 +090021from tempest.common.utils import data_utils
Matthew Treinishcb569942013-08-09 16:33:44 -040022from tempest import config
Brent Eaglesc26d4522013-12-02 13:28:49 -050023from tempest.openstack.common import jsonutils
Matthew Treinish2b59f842013-09-09 20:32:51 +000024from tempest.openstack.common import log as logging
Sean Dague6dbc6da2013-05-08 17:49:46 -040025from tempest.scenario import manager
Brent Eaglesc26d4522013-12-02 13:28:49 -050026
27import tempest.test
Maru Newbybd360222013-04-08 22:48:50 +000028from tempest.test import attr
Matthew Treinish2153ec02013-09-09 20:57:30 +000029from tempest.test import services
Maru Newby81f07a02012-09-05 20:21:19 -070030
Matthew Treinish2b59f842013-09-09 20:32:51 +000031LOG = logging.getLogger(__name__)
32
Maru Newby81f07a02012-09-05 20:21:19 -070033
Brent Eaglesc26d4522013-12-02 13:28:49 -050034class FloatingIPCheckTracker(object):
35 """
36 Checking VM connectivity through floating IP addresses is bound to fail
37 if the floating IP has not actually been associated with the VM yet.
38 This helper class facilitates checking for floating IP assignments on
39 VMs. It only checks for a given IP address once.
40 """
41
42 def __init__(self, compute_client, floating_ip_map):
43 self.compute_client = compute_client
Yair Fried9a551c42013-12-15 14:59:34 +020044 self.unchecked = floating_ip_map.copy()
Brent Eaglesc26d4522013-12-02 13:28:49 -050045
46 def run_checks(self):
47 """Check for any remaining unverified floating IPs
48
49 Gets VM details from nova and checks for floating IPs
50 within the returned information. Returns true when all
51 checks are complete and is suitable for use with
52 tempest.test.call_until_true()
53 """
54 to_delete = []
55 loggable_map = {}
Yair Fried9a551c42013-12-15 14:59:34 +020056 for check_addr, server in self.unchecked.iteritems():
57 serverdata = self.compute_client.servers.get(server.id)
58 ip_addr = [addr for sublist in serverdata.networks.values() for
59 addr in sublist]
60 if check_addr.floating_ip_address in ip_addr:
61 to_delete.append(check_addr)
Brent Eaglesc26d4522013-12-02 13:28:49 -050062 else:
Yair Fried9a551c42013-12-15 14:59:34 +020063 loggable_map[server.id] = check_addr
Brent Eaglesc26d4522013-12-02 13:28:49 -050064
65 for to_del in to_delete:
66 del self.unchecked[to_del]
67
68 LOG.debug('Unchecked floating IPs: %s',
69 jsonutils.dumps(loggable_map))
70 return len(self.unchecked) == 0
71
72
Sean Dague6dbc6da2013-05-08 17:49:46 -040073class TestNetworkBasicOps(manager.NetworkScenarioTest):
Maru Newby81f07a02012-09-05 20:21:19 -070074
75 """
76 This smoke test suite assumes that Nova has been configured to
Mark McClainf2982e82013-07-06 17:48:03 -040077 boot VM's with Neutron-managed networking, and attempts to
Maru Newby81f07a02012-09-05 20:21:19 -070078 verify network connectivity as follows:
79
80 * For a freshly-booted VM with an IP address ("port") on a given network:
81
Maru Newbyaf292e82013-05-20 21:32:28 +000082 - the Tempest host can ping the IP address. This implies, but
83 does not guarantee (see the ssh check that follows), that the
84 VM has been assigned the correct IP address and has
Maru Newby81f07a02012-09-05 20:21:19 -070085 connectivity to the Tempest host.
86
Maru Newbyaf292e82013-05-20 21:32:28 +000087 - the Tempest host can perform key-based authentication to an
88 ssh server hosted at the IP address. This check guarantees
89 that the IP address is associated with the target VM.
90
Yair Fried9a551c42013-12-15 14:59:34 +020091 - detach the floating-ip from the VM and verify that it becomes
92 unreachable
93
Attila Fazekasc3a095b2013-08-17 09:15:44 +020094 # TODO(mnewby) - Need to implement the following:
Maru Newby81f07a02012-09-05 20:21:19 -070095 - the Tempest host can ssh into the VM via the IP address and
96 successfully execute the following:
97
98 - ping an external IP address, implying external connectivity.
99
100 - ping an external hostname, implying that dns is correctly
101 configured.
102
103 - ping an internal IP address, implying connectivity to another
104 VM on the same network.
105
106 There are presumed to be two types of networks: tenant and
107 public. A tenant network may or may not be reachable from the
108 Tempest host. A public network is assumed to be reachable from
109 the Tempest host, and it should be possible to associate a public
110 ('floating') IP address with a tenant ('fixed') IP address to
Chang Bo Guocc1623c2013-09-13 20:11:27 -0700111 facilitate external connectivity to a potentially unroutable
Maru Newby81f07a02012-09-05 20:21:19 -0700112 tenant IP address.
113
114 This test suite can be configured to test network connectivity to
115 a VM via a tenant network, a public network, or both. If both
116 networking types are to be evaluated, tests that need to be
117 executed remotely on the VM (via ssh) will only be run against
118 one of the networks (to minimize test execution time).
119
120 Determine which types of networks to test as follows:
121
122 * Configure tenant network checks (via the
123 'tenant_networks_reachable' key) if the Tempest host should
124 have direct connectivity to tenant networks. This is likely to
125 be the case if Tempest is running on the same host as a
126 single-node devstack installation with IP namespaces disabled.
127
128 * Configure checks for a public network if a public network has
129 been configured prior to the test suite being run and if the
130 Tempest host should have connectivity to that public network.
131 Checking connectivity for a public network requires that a
132 value be provided for 'public_network_id'. A value can
133 optionally be provided for 'public_router_id' if tenants will
134 use a shared router to access a public network (as is likely to
135 be the case when IP namespaces are not enabled). If a value is
136 not provided for 'public_router_id', a router will be created
137 for each tenant and use the network identified by
138 'public_network_id' as its gateway.
139
140 """
141
Matthew Treinishcb569942013-08-09 16:33:44 -0400142 CONF = config.TempestConfig()
143
Maru Newby81f07a02012-09-05 20:21:19 -0700144 @classmethod
145 def check_preconditions(cls):
Gavin Brebner0f465a32013-03-14 13:26:09 +0000146 super(TestNetworkBasicOps, cls).check_preconditions()
Maru Newby81f07a02012-09-05 20:21:19 -0700147 cfg = cls.config.network
Maru Newby81f07a02012-09-05 20:21:19 -0700148 if not (cfg.tenant_networks_reachable or cfg.public_network_id):
149 msg = ('Either tenant_networks_reachable must be "true", or '
150 'public_network_id must be defined.')
Gavin Brebner0f465a32013-03-14 13:26:09 +0000151 cls.enabled = False
ivan-zhu1feeb382013-01-24 10:14:39 +0800152 raise cls.skipException(msg)
Maru Newby81f07a02012-09-05 20:21:19 -0700153
154 @classmethod
155 def setUpClass(cls):
156 super(TestNetworkBasicOps, cls).setUpClass()
157 cls.check_preconditions()
Maru Newby81f07a02012-09-05 20:21:19 -0700158 # TODO(mnewby) Consider looking up entities as needed instead
159 # of storing them as collections on the class.
160 cls.keypairs = {}
161 cls.security_groups = {}
162 cls.networks = []
Gavin Brebner851c3502013-01-18 13:14:10 +0000163 cls.subnets = []
164 cls.routers = []
Maru Newby81f07a02012-09-05 20:21:19 -0700165 cls.servers = []
166 cls.floating_ips = {}
167
Maru Newby81f07a02012-09-05 20:21:19 -0700168 def _get_router(self, tenant_id):
169 """Retrieve a router for the given tenant id.
170
171 If a public router has been configured, it will be returned.
172
173 If a public router has not been configured, but a public
174 network has, a tenant router will be created and returned that
175 routes traffic to the public network.
176
177 """
178 router_id = self.config.network.public_router_id
179 network_id = self.config.network.public_network_id
180 if router_id:
181 result = self.network_client.show_router(router_id)
Maru Newby207d68c2013-04-09 01:06:03 +0000182 return net_common.AttributeDict(**result['router'])
Maru Newby81f07a02012-09-05 20:21:19 -0700183 elif network_id:
184 router = self._create_router(tenant_id)
185 router.add_gateway(network_id)
186 return router
187 else:
188 raise Exception("Neither of 'public_router_id' or "
189 "'public_network_id' has been defined.")
190
Gavin Brebner0f465a32013-03-14 13:26:09 +0000191 def _create_router(self, tenant_id, namestart='router-smoke-'):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900192 name = data_utils.rand_name(namestart)
Maru Newby81f07a02012-09-05 20:21:19 -0700193 body = dict(
194 router=dict(
195 name=name,
196 admin_state_up=True,
197 tenant_id=tenant_id,
198 ),
199 )
200 result = self.network_client.create_router(body=body)
Maru Newby207d68c2013-04-09 01:06:03 +0000201 router = net_common.DeletableRouter(client=self.network_client,
202 **result['router'])
Maru Newby81f07a02012-09-05 20:21:19 -0700203 self.assertEqual(router.name, name)
204 self.set_resource(name, router)
205 return router
206
Matthew Treinish2b59f842013-09-09 20:32:51 +0000207 def _create_keypairs(self):
Ken'ichi Ohmichi599d1b82013-08-19 18:48:37 +0900208 self.keypairs[self.tenant_id] = self.create_keypair(
Masayuki Igawa259c1132013-10-31 17:48:44 +0900209 name=data_utils.rand_name('keypair-smoke-'))
Maru Newby81f07a02012-09-05 20:21:19 -0700210
Matthew Treinish2b59f842013-09-09 20:32:51 +0000211 def _create_security_groups(self):
Yair Friedeb69f3f2013-10-10 13:18:16 +0300212 self.security_groups[self.tenant_id] =\
213 self._create_security_group_neutron(tenant_id=self.tenant_id)
Maru Newby81f07a02012-09-05 20:21:19 -0700214
Matthew Treinish2b59f842013-09-09 20:32:51 +0000215 def _create_networks(self):
Maru Newby81f07a02012-09-05 20:21:19 -0700216 network = self._create_network(self.tenant_id)
217 router = self._get_router(self.tenant_id)
218 subnet = self._create_subnet(network)
219 subnet.add_to_router(router.id)
220 self.networks.append(network)
Gavin Brebner851c3502013-01-18 13:14:10 +0000221 self.subnets.append(subnet)
222 self.routers.append(router)
Maru Newby81f07a02012-09-05 20:21:19 -0700223
Matthew Treinish2b59f842013-09-09 20:32:51 +0000224 def _check_networks(self):
Attila Fazekasc3a095b2013-08-17 09:15:44 +0200225 # Checks that we see the newly created network/subnet/router via
226 # checking the result of list_[networks,routers,subnets]
Gavin Brebner851c3502013-01-18 13:14:10 +0000227 seen_nets = self._list_networks()
228 seen_names = [n['name'] for n in seen_nets]
229 seen_ids = [n['id'] for n in seen_nets]
230 for mynet in self.networks:
Gavin Brebner0f465a32013-03-14 13:26:09 +0000231 self.assertIn(mynet.name, seen_names)
232 self.assertIn(mynet.id, seen_ids)
Gavin Brebner851c3502013-01-18 13:14:10 +0000233 seen_subnets = self._list_subnets()
234 seen_net_ids = [n['network_id'] for n in seen_subnets]
235 seen_subnet_ids = [n['id'] for n in seen_subnets]
236 for mynet in self.networks:
Gavin Brebner0f465a32013-03-14 13:26:09 +0000237 self.assertIn(mynet.id, seen_net_ids)
Gavin Brebner851c3502013-01-18 13:14:10 +0000238 for mysubnet in self.subnets:
Gavin Brebner0f465a32013-03-14 13:26:09 +0000239 self.assertIn(mysubnet.id, seen_subnet_ids)
Gavin Brebner851c3502013-01-18 13:14:10 +0000240 seen_routers = self._list_routers()
241 seen_router_ids = [n['id'] for n in seen_routers]
242 seen_router_names = [n['name'] for n in seen_routers]
243 for myrouter in self.routers:
Gavin Brebner0f465a32013-03-14 13:26:09 +0000244 self.assertIn(myrouter.name, seen_router_names)
245 self.assertIn(myrouter.id, seen_router_ids)
Gavin Brebner851c3502013-01-18 13:14:10 +0000246
Salvatore Orlando5ed3b6e2013-09-17 01:27:26 -0700247 def _create_server(self, name, network):
248 tenant_id = network.tenant_id
249 keypair_name = self.keypairs[tenant_id].name
250 security_groups = [self.security_groups[tenant_id].name]
251 create_kwargs = {
252 'nics': [
253 {'net-id': network.id},
254 ],
255 'key_name': keypair_name,
256 'security_groups': security_groups,
257 }
Giulio Fidente61cadca2013-09-24 18:33:37 +0200258 server = self.create_server(name=name, create_kwargs=create_kwargs)
Salvatore Orlando5ed3b6e2013-09-17 01:27:26 -0700259 return server
260
Matthew Treinish2b59f842013-09-09 20:32:51 +0000261 def _create_servers(self):
Maru Newby81f07a02012-09-05 20:21:19 -0700262 for i, network in enumerate(self.networks):
Masayuki Igawa259c1132013-10-31 17:48:44 +0900263 name = data_utils.rand_name('server-smoke-%d-' % i)
Salvatore Orlando5ed3b6e2013-09-17 01:27:26 -0700264 server = self._create_server(name, network)
Maru Newby81f07a02012-09-05 20:21:19 -0700265 self.servers.append(server)
266
Matthew Treinish2b59f842013-09-09 20:32:51 +0000267 def _check_tenant_network_connectivity(self):
Maru Newby81f07a02012-09-05 20:21:19 -0700268 if not self.config.network.tenant_networks_reachable:
269 msg = 'Tenant networks not configured to be reachable.'
Matthew Treinish2b59f842013-09-09 20:32:51 +0000270 LOG.info(msg)
271 return
Maru Newbyaf292e82013-05-20 21:32:28 +0000272 # The target login is assumed to have been configured for
273 # key-based authentication by cloud-init.
274 ssh_login = self.config.compute.image_ssh_user
275 private_key = self.keypairs[self.tenant_id].private_key
Brent Eaglesc26d4522013-12-02 13:28:49 -0500276 try:
277 for server in self.servers:
278 for net_name, ip_addresses in server.networks.iteritems():
279 for ip_address in ip_addresses:
280 self._check_vm_connectivity(ip_address, ssh_login,
281 private_key)
282 except Exception as exc:
283 LOG.exception(exc)
284 debug.log_ip_ns()
285 raise exc
286
287 def _wait_for_floating_ip_association(self):
288 ip_tracker = FloatingIPCheckTracker(self.compute_client,
289 self.floating_ips)
290
291 self.assertTrue(
292 tempest.test.call_until_true(
293 ip_tracker.run_checks, self.config.compute.build_timeout,
294 self.config.compute.build_interval),
295 "Timed out while waiting for the floating IP assignments "
296 "to propagate")
Maru Newby81f07a02012-09-05 20:21:19 -0700297
Yair Fried9a551c42013-12-15 14:59:34 +0200298 def _create_and_associate_floating_ips(self):
Maru Newby81f07a02012-09-05 20:21:19 -0700299 public_network_id = self.config.network.public_network_id
Maru Newby81f07a02012-09-05 20:21:19 -0700300 for server in self.servers:
301 floating_ip = self._create_floating_ip(server, public_network_id)
Yair Fried9a551c42013-12-15 14:59:34 +0200302 self.floating_ips[floating_ip] = server
Maru Newby81f07a02012-09-05 20:21:19 -0700303
Yair Fried9a551c42013-12-15 14:59:34 +0200304 def _check_public_network_connectivity(self, should_connect=True):
Maru Newbyaf292e82013-05-20 21:32:28 +0000305 # The target login is assumed to have been configured for
306 # key-based authentication by cloud-init.
307 ssh_login = self.config.compute.image_ssh_user
308 private_key = self.keypairs[self.tenant_id].private_key
Attila Fazekasaeeeefd2013-08-06 17:01:56 +0200309 try:
Yair Fried9a551c42013-12-15 14:59:34 +0200310 for floating_ip, server in self.floating_ips.iteritems():
311 ip_address = floating_ip.floating_ip_address
312 self._check_vm_connectivity(ip_address,
313 ssh_login,
314 private_key,
315 should_connect=should_connect)
Attila Fazekasaeeeefd2013-08-06 17:01:56 +0200316 except Exception as exc:
317 LOG.exception(exc)
318 debug.log_ip_ns()
319 raise exc
Matthew Treinish2b59f842013-09-09 20:32:51 +0000320
Yair Fried9a551c42013-12-15 14:59:34 +0200321 def _disassociate_floating_ips(self):
322 for floating_ip, server in self.floating_ips.iteritems():
323 self._disassociate_floating_ip(floating_ip)
324 self.floating_ips[floating_ip] = None
325
Matthew Treinish2b59f842013-09-09 20:32:51 +0000326 @attr(type='smoke')
Matthew Treinish2153ec02013-09-09 20:57:30 +0000327 @services('compute', 'network')
Matthew Treinish2b59f842013-09-09 20:32:51 +0000328 def test_network_basic_ops(self):
329 self._create_keypairs()
330 self._create_security_groups()
331 self._create_networks()
332 self._check_networks()
333 self._create_servers()
Yair Fried9a551c42013-12-15 14:59:34 +0200334 self._create_and_associate_floating_ips()
Brent Eaglesc26d4522013-12-02 13:28:49 -0500335 self._wait_for_floating_ip_association()
Salvatore Orlando8d076cb2013-09-16 11:00:44 -0700336 self._check_tenant_network_connectivity()
Yair Fried9a551c42013-12-15 14:59:34 +0200337 self._check_public_network_connectivity(should_connect=True)
338 self._disassociate_floating_ips()
339 self._check_public_network_connectivity(should_connect=False)