You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
7.3 KiB
168 lines
7.3 KiB
# Copyright (c) 2013 OpenStack Foundation |
|
# Copyright 2013, Nachi Ueno, NTT MCL, Inc. |
|
# All Rights Reserved. |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
# not use this file except in compliance with the License. You may obtain |
|
# a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
# License for the specific language governing permissions and limitations |
|
# under the License. |
|
|
|
import math |
|
from unittest import mock |
|
|
|
from neutron_lib.callbacks import events |
|
from neutron_lib.callbacks import registry |
|
from neutron_lib.callbacks import resources |
|
from neutron_lib import constants as const |
|
from neutron_lib import context |
|
from neutron_lib import fixture |
|
from neutron_lib.plugins import directory |
|
|
|
from neutron.extensions import securitygroup as ext_sg |
|
from neutron.tests.unit.agent import test_securitygroups_rpc as test_sg_rpc |
|
from neutron.tests.unit.api.v2 import test_base |
|
from neutron.tests.unit.extensions import test_securitygroup as test_sg |
|
|
|
NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi' |
|
|
|
|
|
class Ml2SecurityGroupsTestCase(test_sg.SecurityGroupDBTestCase): |
|
|
|
def setUp(self, plugin=None): |
|
test_sg_rpc.set_firewall_driver(test_sg_rpc.FIREWALL_HYBRID_DRIVER) |
|
notifier_p = mock.patch(NOTIFIER) |
|
notifier_cls = notifier_p.start() |
|
self.notifier = mock.Mock() |
|
notifier_cls.return_value = self.notifier |
|
self.useFixture(fixture.APIDefinitionFixture()) |
|
super(Ml2SecurityGroupsTestCase, self).setUp('ml2') |
|
|
|
|
|
class TestMl2SecurityGroups(Ml2SecurityGroupsTestCase, |
|
test_sg.TestSecurityGroups, |
|
test_sg_rpc.SGNotificationTestMixin): |
|
def setUp(self): |
|
super(TestMl2SecurityGroups, self).setUp() |
|
self.ctx = context.get_admin_context() |
|
plugin = directory.get_plugin() |
|
plugin.start_rpc_listeners() |
|
|
|
def _make_port_with_new_sec_group(self, net_id): |
|
sg = self._make_security_group(self.fmt, 'name', 'desc') |
|
port = self._make_port( |
|
self.fmt, net_id, security_groups=[sg['security_group']['id']]) |
|
return port['port'] |
|
|
|
def _make_port_without_sec_group(self, net_id): |
|
port = self._make_port( |
|
self.fmt, net_id, security_groups=[]) |
|
return port['port'] |
|
|
|
def test_security_group_get_ports_from_devices(self): |
|
with self.network() as n: |
|
with self.subnet(n): |
|
orig_ports = [ |
|
self._make_port_with_new_sec_group(n['network']['id']), |
|
self._make_port_with_new_sec_group(n['network']['id']), |
|
self._make_port_without_sec_group(n['network']['id']) |
|
] |
|
plugin = directory.get_plugin() |
|
# should match full ID and starting chars |
|
ports = plugin.get_ports_from_devices(self.ctx, |
|
[orig_ports[0]['id'], orig_ports[1]['id'][0:8], |
|
orig_ports[2]['id']]) |
|
self.assertEqual(len(orig_ports), len(ports)) |
|
for port_dict in ports: |
|
p = next(p for p in orig_ports |
|
if p['id'] == port_dict['id']) |
|
self.assertEqual(p['id'], port_dict['id']) |
|
self.assertEqual(p['security_groups'], |
|
port_dict[ext_sg.SECURITYGROUPS]) |
|
self.assertEqual([], port_dict['security_group_rules']) |
|
self.assertEqual([p['fixed_ips'][0]['ip_address']], |
|
port_dict['fixed_ips']) |
|
self._delete('ports', p['id']) |
|
|
|
def test_security_group_get_ports_from_devices_with_bad_id(self): |
|
plugin = directory.get_plugin() |
|
ports = plugin.get_ports_from_devices(self.ctx, ['bad_device_id']) |
|
self.assertFalse(ports) |
|
|
|
def test_security_group_no_db_calls_with_no_ports(self): |
|
plugin = directory.get_plugin() |
|
with mock.patch( |
|
'neutron.plugins.ml2.db.get_sg_ids_grouped_by_port' |
|
) as get_mock: |
|
self.assertFalse(plugin.get_ports_from_devices(self.ctx, [])) |
|
self.assertFalse(get_mock.called) |
|
|
|
def test_large_port_count_broken_into_parts(self): |
|
plugin = directory.get_plugin() |
|
max_ports_per_query = 5 |
|
ports_to_query = 73 |
|
for max_ports_per_query in (1, 2, 5, 7, 9, 31): |
|
with mock.patch('neutron.plugins.ml2.db.MAX_PORTS_PER_QUERY', |
|
new=max_ports_per_query),\ |
|
mock.patch( |
|
'neutron.plugins.ml2.db.get_sg_ids_grouped_by_port', |
|
return_value={}) as get_mock: |
|
plugin.get_ports_from_devices(self.ctx, |
|
['%s%s' % (const.TAP_DEVICE_PREFIX, i) |
|
for i in range(ports_to_query)]) |
|
all_call_args = [x[1][1] for x in get_mock.mock_calls] |
|
last_call_args = all_call_args.pop() |
|
# all but last should be getting MAX_PORTS_PER_QUERY ports |
|
self.assertTrue( |
|
all(map(lambda x: len(x) == max_ports_per_query, |
|
all_call_args)) |
|
) |
|
remaining = ports_to_query % max_ports_per_query |
|
if remaining: |
|
self.assertEqual(remaining, len(last_call_args)) |
|
# should be broken into ceil(total/MAX_PORTS_PER_QUERY) calls |
|
self.assertEqual( |
|
math.ceil(ports_to_query / float(max_ports_per_query)), |
|
get_mock.call_count |
|
) |
|
|
|
def test_full_uuids_skip_port_id_lookup(self): |
|
plugin = directory.get_plugin() |
|
# when full UUIDs are provided, the _or statement should only |
|
# have one matching 'IN' criteria for all of the IDs |
|
with mock.patch('neutron.plugins.ml2.db.or_') as or_mock,\ |
|
mock.patch('sqlalchemy.orm.Session.query') as qmock: |
|
fmock = qmock.return_value.outerjoin.return_value.filter |
|
# return no ports to exit the method early since we are mocking |
|
# the query |
|
fmock.return_value = [] |
|
plugin.get_ports_from_devices(self.ctx, |
|
[test_base._uuid(), |
|
test_base._uuid()]) |
|
# the or_ function should only have one argument |
|
or_mock.assert_called_once_with(mock.ANY) |
|
|
|
def test_security_groups_created_outside_transaction(self): |
|
def record_after_state(r, e, t, context, *args, **kwargs): |
|
self.was_active = context.session.is_active |
|
|
|
registry.subscribe(record_after_state, resources.SECURITY_GROUP, |
|
events.AFTER_CREATE) |
|
with self.subnet() as s: |
|
self.assertFalse(self.was_active) |
|
self._delete( |
|
'security-groups', |
|
self._list('security-groups')['security_groups'][0]['id']) |
|
with self.port(subnet=s): |
|
self.assertFalse(self.was_active) |
|
|
|
|
|
class TestMl2SGServerRpcCallBack(Ml2SecurityGroupsTestCase, |
|
test_sg_rpc.SGServerRpcCallBackTestCase): |
|
pass
|
|
|