Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 1 | # Copyright 2013 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. |
| 15 | |
Andrea Frittoli | f9cde7e | 2014-02-18 09:57:04 +0000 | [diff] [blame^] | 16 | from tempest import clients |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 17 | from tempest.common import debug |
| 18 | from tempest.common.utils import data_utils |
Matthew Treinish | 6c07229 | 2014-01-29 19:15:52 +0000 | [diff] [blame] | 19 | from tempest import config |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 20 | from tempest import exceptions |
| 21 | from tempest.openstack.common import log as logging |
| 22 | from tempest.scenario import manager |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 23 | from tempest.test import attr |
| 24 | from tempest.test import call_until_true |
| 25 | from tempest.test import services |
| 26 | |
Matthew Treinish | 6c07229 | 2014-01-29 19:15:52 +0000 | [diff] [blame] | 27 | CONF = config.CONF |
| 28 | |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 29 | LOG = logging.getLogger(__name__) |
| 30 | |
| 31 | |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 32 | class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest): |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 33 | |
| 34 | """ |
| 35 | This test suite assumes that Nova has been configured to |
| 36 | boot VM's with Neutron-managed networking, and attempts to |
| 37 | verify cross tenant connectivity as follows |
| 38 | |
| 39 | ssh: |
| 40 | in order to overcome "ip namespace", each tenant has an "access point" |
| 41 | VM with floating-ip open to incoming ssh connection allowing network |
| 42 | commands (ping/ssh) to be executed from within the |
| 43 | tenant-network-namespace |
| 44 | Tempest host performs key-based authentication to the ssh server via |
| 45 | floating IP address |
| 46 | |
| 47 | connectivity test is done by pinging destination server via source server |
| 48 | ssh connection. |
| 49 | success - ping returns |
| 50 | failure - ping_timeout reached |
| 51 | |
| 52 | setup: |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 53 | for primary tenant: |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 54 | 1. create a network&subnet |
| 55 | 2. create a router (if public router isn't configured) |
| 56 | 3. connect tenant network to public network via router |
| 57 | 4. create an access point: |
| 58 | a. a security group open to incoming ssh connection |
| 59 | b. a VM with a floating ip |
| 60 | 5. create a general empty security group (same as "default", but |
| 61 | without rules allowing in-tenant traffic) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 62 | |
| 63 | tests: |
| 64 | 1. _verify_network_details |
| 65 | 2. _verify_mac_addr: for each access point verify that |
| 66 | (subnet, fix_ip, mac address) are as defined in the port list |
| 67 | 3. _test_in_tenant_block: test that in-tenant traffic is disabled |
| 68 | without rules allowing it |
| 69 | 4. _test_in_tenant_allow: test that in-tenant traffic is enabled |
| 70 | once an appropriate rule has been created |
| 71 | 5. _test_cross_tenant_block: test that cross-tenant traffic is disabled |
| 72 | without a rule allowing it on destination tenant |
| 73 | 6. _test_cross_tenant_allow: |
| 74 | * test that cross-tenant traffic is enabled once an appropriate |
| 75 | rule has been created on destination tenant. |
| 76 | * test that reverse traffic is still blocked |
| 77 | * test than revesre traffic is enabled once an appropriate rule has |
| 78 | been created on source tenant |
| 79 | |
| 80 | assumptions: |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 81 | 1. alt_tenant/user existed and is different from primary_tenant/user |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 82 | 2. Public network is defined and reachable from the Tempest host |
| 83 | 3. Public router can either be: |
| 84 | * defined, in which case all tenants networks can connect directly |
| 85 | to it, and cross tenant check will be done on the private IP of the |
| 86 | destination tenant |
| 87 | or |
| 88 | * not defined (empty string), in which case each tanant will have |
| 89 | its own router connected to the public network |
| 90 | """ |
| 91 | |
| 92 | class TenantProperties(): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 93 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 94 | helper class to save tenant details |
| 95 | id |
| 96 | credentials |
| 97 | network |
| 98 | subnet |
| 99 | security groups |
| 100 | servers |
| 101 | access point |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 102 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 103 | |
| 104 | def __init__(self, tenant_id, tenant_user, tenant_pass, tenant_name): |
Andrea Frittoli | f9cde7e | 2014-02-18 09:57:04 +0000 | [diff] [blame^] | 105 | self.manager = clients.OfficialClientManager( |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 106 | tenant_user, |
| 107 | tenant_pass, |
| 108 | tenant_name |
| 109 | ) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 110 | self.keypair = None |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 111 | self.tenant_id = tenant_id |
| 112 | self.tenant_name = tenant_name |
| 113 | self.tenant_user = tenant_user |
| 114 | self.tenant_pass = tenant_pass |
| 115 | self.network = None |
| 116 | self.subnet = None |
| 117 | self.router = None |
| 118 | self.security_groups = {} |
| 119 | self.servers = list() |
| 120 | |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 121 | def set_network(self, network, subnet, router): |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 122 | self.network = network |
| 123 | self.subnet = subnet |
| 124 | self.router = router |
| 125 | |
| 126 | def _get_tenant_credentials(self): |
| 127 | return self.tenant_user, self.tenant_pass, self.tenant_name |
| 128 | |
| 129 | @classmethod |
| 130 | def check_preconditions(cls): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 131 | super(TestSecurityGroupsBasicOps, cls).check_preconditions() |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 132 | if (cls.alt_tenant_id is None) or (cls.tenant_id is cls.alt_tenant_id): |
| 133 | msg = 'No alt_tenant defined' |
| 134 | cls.enabled = False |
| 135 | raise cls.skipException(msg) |
Matthew Treinish | 6c07229 | 2014-01-29 19:15:52 +0000 | [diff] [blame] | 136 | if not (CONF.network.tenant_networks_reachable or |
| 137 | CONF.network.public_network_id): |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 138 | msg = ('Either tenant_networks_reachable must be "true", or ' |
| 139 | 'public_network_id must be defined.') |
| 140 | cls.enabled = False |
| 141 | raise cls.skipException(msg) |
| 142 | |
| 143 | @classmethod |
| 144 | def setUpClass(cls): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 145 | super(TestSecurityGroupsBasicOps, cls).setUpClass() |
Yair Fried | 769bbff | 2013-12-18 16:33:17 +0200 | [diff] [blame] | 146 | alt_creds = cls.alt_credentials() |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 147 | cls.alt_tenant_id = cls.manager._get_identity_client( |
Yair Fried | 769bbff | 2013-12-18 16:33:17 +0200 | [diff] [blame] | 148 | *alt_creds |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 149 | ).tenant_id |
| 150 | cls.check_preconditions() |
| 151 | # TODO(mnewby) Consider looking up entities as needed instead |
| 152 | # of storing them as collections on the class. |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 153 | cls.networks = [] |
| 154 | cls.subnets = [] |
| 155 | cls.routers = [] |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 156 | cls.floating_ips = {} |
| 157 | cls.tenants = {} |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 158 | cls.primary_tenant = cls.TenantProperties(cls.tenant_id, |
| 159 | *cls.credentials()) |
| 160 | cls.alt_tenant = cls.TenantProperties(cls.alt_tenant_id, |
| 161 | *alt_creds) |
| 162 | for tenant in [cls.primary_tenant, cls.alt_tenant]: |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 163 | cls.tenants[tenant.tenant_id] = tenant |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 164 | cls.floating_ip_access = not CONF.network.public_router_id |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 165 | |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 166 | def cleanup_wrapper(self, resource): |
| 167 | self.cleanup_resource(resource, self.__class__.__name__) |
| 168 | |
| 169 | def setUp(self): |
| 170 | super(TestSecurityGroupsBasicOps, self).setUp() |
| 171 | self._deploy_tenant(self.primary_tenant) |
| 172 | self._verify_network_details(self.primary_tenant) |
| 173 | self._verify_mac_addr(self.primary_tenant) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 174 | |
| 175 | def _create_tenant_keypairs(self, tenant_id): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 176 | keypair = self.create_keypair( |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 177 | name=data_utils.rand_name('keypair-smoke-')) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 178 | self.addCleanup(self.cleanup_wrapper, keypair) |
| 179 | self.tenants[tenant_id].keypair = keypair |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 180 | |
| 181 | def _create_tenant_security_groups(self, tenant): |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 182 | access_sg = self._create_empty_security_group( |
| 183 | namestart='secgroup_access-', |
| 184 | tenant_id=tenant.tenant_id |
| 185 | ) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 186 | self.addCleanup(self.cleanup_wrapper, access_sg) |
| 187 | |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 188 | # don't use default secgroup since it allows in-tenant traffic |
| 189 | def_sg = self._create_empty_security_group( |
| 190 | namestart='secgroup_general-', |
| 191 | tenant_id=tenant.tenant_id |
| 192 | ) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 193 | self.addCleanup(self.cleanup_wrapper, def_sg) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 194 | tenant.security_groups.update(access=access_sg, default=def_sg) |
| 195 | ssh_rule = dict( |
| 196 | protocol='tcp', |
| 197 | port_range_min=22, |
| 198 | port_range_max=22, |
| 199 | direction='ingress', |
| 200 | ) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 201 | rule = self._create_security_group_rule(secgroup=access_sg, |
| 202 | **ssh_rule) |
| 203 | self.addCleanup(self.cleanup_wrapper, rule) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 204 | |
| 205 | def _verify_network_details(self, tenant): |
| 206 | # Checks that we see the newly created network/subnet/router via |
| 207 | # checking the result of list_[networks,routers,subnets] |
| 208 | # Check that (router, subnet) couple exist in port_list |
| 209 | seen_nets = self._list_networks() |
| 210 | seen_names = [n['name'] for n in seen_nets] |
| 211 | seen_ids = [n['id'] for n in seen_nets] |
| 212 | |
| 213 | self.assertIn(tenant.network.name, seen_names) |
| 214 | self.assertIn(tenant.network.id, seen_ids) |
| 215 | |
| 216 | seen_subnets = [(n['id'], n['cidr'], n['network_id']) |
| 217 | for n in self._list_subnets()] |
| 218 | mysubnet = (tenant.subnet.id, tenant.subnet.cidr, tenant.network.id) |
| 219 | self.assertIn(mysubnet, seen_subnets) |
| 220 | |
| 221 | seen_routers = self._list_routers() |
| 222 | seen_router_ids = [n['id'] for n in seen_routers] |
| 223 | seen_router_names = [n['name'] for n in seen_routers] |
| 224 | |
| 225 | self.assertIn(tenant.router.name, seen_router_names) |
| 226 | self.assertIn(tenant.router.id, seen_router_ids) |
| 227 | |
| 228 | myport = (tenant.router.id, tenant.subnet.id) |
| 229 | router_ports = [(i['device_id'], i['fixed_ips'][0]['subnet_id']) for i |
| 230 | in self.network_client.list_ports()['ports'] |
| 231 | if i['device_owner'] == 'network:router_interface'] |
| 232 | |
| 233 | self.assertIn(myport, router_ports) |
| 234 | |
| 235 | def _create_server(self, name, tenant, security_groups=None): |
| 236 | """ |
| 237 | creates a server and assigns to security group |
| 238 | """ |
| 239 | self._set_compute_context(tenant) |
| 240 | if security_groups is None: |
| 241 | security_groups = [tenant.security_groups['default'].name] |
| 242 | create_kwargs = { |
| 243 | 'nics': [ |
| 244 | {'net-id': tenant.network.id}, |
| 245 | ], |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 246 | 'key_name': tenant.keypair.name, |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 247 | 'security_groups': security_groups, |
| 248 | 'tenant_id': tenant.tenant_id |
| 249 | } |
| 250 | server = self.create_server(name=name, create_kwargs=create_kwargs) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 251 | self.addCleanup(self.cleanup_wrapper, server) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 252 | return server |
| 253 | |
| 254 | def _create_tenant_servers(self, tenant, num=1): |
| 255 | for i in range(num): |
| 256 | name = 'server-{tenant}-gen-{num}-'.format( |
| 257 | tenant=tenant.tenant_name, |
| 258 | num=i |
| 259 | ) |
| 260 | name = data_utils.rand_name(name) |
| 261 | server = self._create_server(name, tenant) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 262 | tenant.servers.append(server) |
| 263 | |
| 264 | def _set_access_point(self, tenant): |
| 265 | """ |
| 266 | creates a server in a secgroup with rule allowing external ssh |
| 267 | in order to access tenant internal network |
| 268 | workaround ip namespace |
| 269 | """ |
| 270 | secgroups = [sg.name for sg in tenant.security_groups.values()] |
| 271 | name = 'server-{tenant}-access_point-'.format(tenant=tenant.tenant_name |
| 272 | ) |
| 273 | name = data_utils.rand_name(name) |
| 274 | server = self._create_server(name, tenant, |
| 275 | security_groups=secgroups) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 276 | tenant.access_point = server |
| 277 | self._assign_floating_ips(server) |
| 278 | |
| 279 | def _assign_floating_ips(self, server): |
Matthew Treinish | 6c07229 | 2014-01-29 19:15:52 +0000 | [diff] [blame] | 280 | public_network_id = CONF.network.public_network_id |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 281 | floating_ip = self._create_floating_ip(server, public_network_id) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 282 | self.addCleanup(self.cleanup_wrapper, floating_ip) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 283 | self.floating_ips.setdefault(server, floating_ip) |
| 284 | |
| 285 | def _create_tenant_network(self, tenant): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 286 | network, subnet, router = self._create_networks(tenant.tenant_id) |
| 287 | for r in [network, router, subnet]: |
| 288 | self.addCleanup(self.cleanup_wrapper, r) |
| 289 | tenant.set_network(network, subnet, router) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 290 | |
| 291 | def _set_compute_context(self, tenant): |
| 292 | self.compute_client = tenant.manager.compute_client |
| 293 | return self.compute_client |
| 294 | |
| 295 | def _deploy_tenant(self, tenant_or_id): |
| 296 | """ |
| 297 | creates: |
| 298 | network |
| 299 | subnet |
| 300 | router (if public not defined) |
| 301 | access security group |
| 302 | access-point server |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 303 | """ |
| 304 | if not isinstance(tenant_or_id, self.TenantProperties): |
| 305 | tenant = self.tenants[tenant_or_id] |
| 306 | tenant_id = tenant_or_id |
| 307 | else: |
| 308 | tenant = tenant_or_id |
| 309 | tenant_id = tenant.tenant_id |
| 310 | self._set_compute_context(tenant) |
| 311 | self._create_tenant_keypairs(tenant_id) |
| 312 | self._create_tenant_network(tenant) |
| 313 | self._create_tenant_security_groups(tenant) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 314 | self._set_access_point(tenant) |
| 315 | |
| 316 | def _get_server_ip(self, server, floating=False): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 317 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 318 | returns the ip (floating/internal) of a server |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 319 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 320 | if floating: |
| 321 | return self.floating_ips[server].floating_ip_address |
| 322 | else: |
| 323 | network_name = self.tenants[server.tenant_id].network.name |
| 324 | return server.networks[network_name][0] |
| 325 | |
| 326 | def _connect_to_access_point(self, tenant): |
| 327 | """ |
| 328 | create ssh connection to tenant access point |
| 329 | """ |
| 330 | access_point_ssh = \ |
| 331 | self.floating_ips[tenant.access_point].floating_ip_address |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 332 | private_key = tenant.keypair.private_key |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 333 | access_point_ssh = self._ssh_to_server(access_point_ssh, |
| 334 | private_key=private_key) |
| 335 | return access_point_ssh |
| 336 | |
| 337 | def _test_remote_connectivity(self, source, dest, should_succeed=True): |
| 338 | """ |
| 339 | check ping server via source ssh connection |
| 340 | |
| 341 | :param source: RemoteClient: an ssh connection from which to ping |
| 342 | :param dest: and IP to ping against |
| 343 | :param should_succeed: boolean should ping succeed or not |
| 344 | :returns: boolean -- should_succeed == ping |
| 345 | :returns: ping is false if ping failed |
| 346 | """ |
| 347 | def ping_remote(): |
| 348 | try: |
| 349 | source.ping_host(dest) |
| 350 | except exceptions.SSHExecCommandFailed as ex: |
| 351 | LOG.debug(ex) |
| 352 | return not should_succeed |
| 353 | return should_succeed |
| 354 | |
| 355 | return call_until_true(ping_remote, |
Matthew Treinish | 6c07229 | 2014-01-29 19:15:52 +0000 | [diff] [blame] | 356 | CONF.compute.ping_timeout, |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 357 | 1) |
| 358 | |
| 359 | def _check_connectivity(self, access_point, ip, should_succeed=True): |
| 360 | if should_succeed: |
| 361 | msg = "Timed out waiting for %s to become reachable" % ip |
| 362 | else: |
| 363 | # todo(yfried): remove this line when bug 1252620 is fixed |
| 364 | return True |
| 365 | msg = "%s is reachable" % ip |
| 366 | try: |
| 367 | self.assertTrue(self._test_remote_connectivity(access_point, ip, |
| 368 | should_succeed), |
| 369 | msg) |
| 370 | except Exception: |
| 371 | debug.log_ip_ns() |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 372 | raise |
| 373 | |
| 374 | def _test_in_tenant_block(self, tenant): |
| 375 | access_point_ssh = self._connect_to_access_point(tenant) |
| 376 | for server in tenant.servers: |
| 377 | self._check_connectivity(access_point=access_point_ssh, |
| 378 | ip=self._get_server_ip(server), |
| 379 | should_succeed=False) |
| 380 | |
| 381 | def _test_in_tenant_allow(self, tenant): |
| 382 | ruleset = dict( |
| 383 | protocol='icmp', |
| 384 | remote_group_id=tenant.security_groups['default'].id, |
| 385 | direction='ingress' |
| 386 | ) |
| 387 | rule = self._create_security_group_rule( |
| 388 | secgroup=tenant.security_groups['default'], |
| 389 | **ruleset |
| 390 | ) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 391 | self.addCleanup(self.cleanup_wrapper, rule) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 392 | access_point_ssh = self._connect_to_access_point(tenant) |
| 393 | for server in tenant.servers: |
| 394 | self._check_connectivity(access_point=access_point_ssh, |
| 395 | ip=self._get_server_ip(server)) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 396 | |
| 397 | def _test_cross_tenant_block(self, source_tenant, dest_tenant): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 398 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 399 | if public router isn't defined, then dest_tenant access is via |
| 400 | floating-ip |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 401 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 402 | access_point_ssh = self._connect_to_access_point(source_tenant) |
| 403 | ip = self._get_server_ip(dest_tenant.access_point, |
| 404 | floating=self.floating_ip_access) |
| 405 | self._check_connectivity(access_point=access_point_ssh, ip=ip, |
| 406 | should_succeed=False) |
| 407 | |
| 408 | def _test_cross_tenant_allow(self, source_tenant, dest_tenant): |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 409 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 410 | check for each direction: |
| 411 | creating rule for tenant incoming traffic enables only 1way traffic |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 412 | """ |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 413 | ruleset = dict( |
| 414 | protocol='icmp', |
| 415 | direction='ingress' |
| 416 | ) |
| 417 | rule_s2d = self._create_security_group_rule( |
| 418 | secgroup=dest_tenant.security_groups['default'], |
| 419 | **ruleset |
| 420 | ) |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 421 | self.addCleanup(self.cleanup_wrapper, rule_s2d) |
| 422 | access_point_ssh = self._connect_to_access_point(source_tenant) |
| 423 | ip = self._get_server_ip(dest_tenant.access_point, |
| 424 | floating=self.floating_ip_access) |
| 425 | self._check_connectivity(access_point_ssh, ip) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 426 | |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 427 | # test that reverse traffic is still blocked |
| 428 | self._test_cross_tenant_block(dest_tenant, source_tenant) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 429 | |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 430 | # allow reverse traffic and check |
| 431 | rule_d2s = self._create_security_group_rule( |
| 432 | secgroup=source_tenant.security_groups['default'], |
| 433 | **ruleset |
| 434 | ) |
| 435 | self.addCleanup(self.cleanup_wrapper, rule_d2s) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 436 | |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 437 | access_point_ssh_2 = self._connect_to_access_point(dest_tenant) |
| 438 | ip = self._get_server_ip(source_tenant.access_point, |
| 439 | floating=self.floating_ip_access) |
| 440 | self._check_connectivity(access_point_ssh_2, ip) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 441 | |
| 442 | def _verify_mac_addr(self, tenant): |
| 443 | """ |
| 444 | verify that VM (tenant's access point) has the same ip,mac as listed in |
| 445 | port list |
| 446 | """ |
| 447 | access_point_ssh = self._connect_to_access_point(tenant) |
| 448 | mac_addr = access_point_ssh.get_mac_address() |
| 449 | mac_addr = mac_addr.strip().lower() |
| 450 | port_list = self.network_client.list_ports()['ports'] |
| 451 | port_detail_list = [ |
| 452 | (port['fixed_ips'][0]['subnet_id'], |
| 453 | port['fixed_ips'][0]['ip_address'], |
| 454 | port['mac_address'].lower()) for port in port_list |
| 455 | ] |
| 456 | server_ip = self._get_server_ip(tenant.access_point) |
| 457 | subnet_id = tenant.subnet.id |
| 458 | self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list) |
| 459 | |
| 460 | @attr(type='smoke') |
| 461 | @services('compute', 'network') |
| 462 | def test_cross_tenant_traffic(self): |
Nachi Ueno | 26b4c97 | 2014-01-17 06:15:13 -0800 | [diff] [blame] | 463 | try: |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 464 | # deploy new tenant |
| 465 | self._deploy_tenant(self.alt_tenant) |
| 466 | self._verify_network_details(self.alt_tenant) |
| 467 | self._verify_mac_addr(self.alt_tenant) |
Yair Fried | 4d7efa6 | 2013-11-17 17:12:29 +0200 | [diff] [blame] | 468 | |
Nachi Ueno | 26b4c97 | 2014-01-17 06:15:13 -0800 | [diff] [blame] | 469 | # cross tenant check |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 470 | source_tenant = self.primary_tenant |
Nachi Ueno | 26b4c97 | 2014-01-17 06:15:13 -0800 | [diff] [blame] | 471 | dest_tenant = self.alt_tenant |
| 472 | self._test_cross_tenant_block(source_tenant, dest_tenant) |
| 473 | self._test_cross_tenant_allow(source_tenant, dest_tenant) |
| 474 | except Exception: |
Yair Fried | bf2e2c4 | 2014-01-28 12:06:38 +0200 | [diff] [blame] | 475 | for tenant in self.tenants.values(): |
| 476 | self._log_console_output(servers=tenant.servers) |
| 477 | raise |
| 478 | |
| 479 | @attr(type='smoke') |
| 480 | @services('compute', 'network') |
| 481 | def test_in_tenant_traffic(self): |
| 482 | try: |
| 483 | self._create_tenant_servers(self.primary_tenant, num=1) |
| 484 | |
| 485 | # in-tenant check |
| 486 | self._test_in_tenant_block(self.primary_tenant) |
| 487 | self._test_in_tenant_allow(self.primary_tenant) |
| 488 | |
| 489 | except Exception: |
| 490 | for tenant in self.tenants.values(): |
| 491 | self._log_console_output(servers=tenant.servers) |
Nachi Ueno | 26b4c97 | 2014-01-17 06:15:13 -0800 | [diff] [blame] | 492 | raise |