NSXv3: Removing the use of ns-group manager
NSGroupManager will no longer be used as part of the security-group
implementation.
Change-Id: I2fb87d50dcb8c5b48fda793ba0ffda457db7a3e1
(cherry picked from commit 1cc9efcfac
)
This commit is contained in:
parent
56e6b647ec
commit
00475a31a9
|
@ -1,166 +0,0 @@
|
|||
# Copyright 2015 OpenStack Foundation
|
||||
|
||||
# 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 uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from vmware_nsx._i18n import _, _LW
|
||||
from vmware_nsx.common import utils
|
||||
from vmware_nsx.nsxlib import v3
|
||||
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
|
||||
from vmware_nsx.nsxlib.v3 import exceptions
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class NSGroupManager(object):
|
||||
"""
|
||||
This class assists with NSX integration for Neutron security-groups,
|
||||
Each Neutron security-group is associated with NSX NSGroup object.
|
||||
Some specific security policies are the same across all security-groups,
|
||||
i.e - Default drop rule, DHCP. In order to bind these rules to all
|
||||
NSGroups (security-groups), we create a nested NSGroup (which its members
|
||||
are also of type NSGroups) to group the other NSGroups and associate it
|
||||
with these rules.
|
||||
In practice, one NSGroup (nested) can't contain all the other NSGroups, as
|
||||
it has strict size limit. To overcome the limited space challange, we
|
||||
create several nested groups instead of just one, and we evenly distribute
|
||||
NSGroups (security-groups) between them.
|
||||
By using an hashing function on the NSGroup uuid we determine in which
|
||||
group it should be added, and when deleting an NSGroup (security-group) we
|
||||
use the same procedure to find which nested group it was added.
|
||||
"""
|
||||
|
||||
NESTED_GROUP_NAME = 'OS Nested Group'
|
||||
NESTED_GROUP_DESCRIPTION = ('OpenStack NSGroup. Do not delete.')
|
||||
|
||||
def __init__(self, size):
|
||||
# XXX intergrate this in a better way..
|
||||
self.nsx = v3.NsxLib(
|
||||
username=cfg.CONF.nsx_v3.nsx_api_user,
|
||||
password=cfg.CONF.nsx_v3.nsx_api_password,
|
||||
retries=cfg.CONF.nsx_v3.http_retries,
|
||||
insecure=cfg.CONF.nsx_v3.insecure,
|
||||
ca_file=cfg.CONF.nsx_v3.ca_file,
|
||||
concurrent_connections=cfg.CONF.nsx_v3.concurrent_connections,
|
||||
http_timeout=cfg.CONF.nsx_v3.http_timeout,
|
||||
http_read_timeout=cfg.CONF.nsx_v3.http_read_timeout,
|
||||
conn_idle_timeout=cfg.CONF.nsx_v3.conn_idle_timeout,
|
||||
http_provider=None,
|
||||
max_attempts=cfg.CONF.nsx_v3.retries)
|
||||
|
||||
self._nested_groups = self._init_nested_groups(size)
|
||||
self._size = len(self._nested_groups)
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self._size
|
||||
|
||||
@property
|
||||
def nested_groups(self):
|
||||
return self._nested_groups
|
||||
|
||||
def _init_nested_groups(self, requested_size):
|
||||
# Construct the groups dict -
|
||||
# {0: <groups-1>,.., n-1: <groups-n>}
|
||||
size = requested_size
|
||||
nested_groups = {
|
||||
self._get_nested_group_index_from_name(nsgroup): nsgroup['id']
|
||||
for nsgroup in self.nsx.list_nsgroups()
|
||||
if utils.is_internal_resource(nsgroup)}
|
||||
|
||||
if nested_groups:
|
||||
size = max(requested_size, max(nested_groups) + 1)
|
||||
if size > requested_size:
|
||||
LOG.warning(_LW("Lowering the value of "
|
||||
"nsx_v3:number_of_nested_groups isn't "
|
||||
"supported, '%s' nested-groups will be used."),
|
||||
size)
|
||||
|
||||
absent_groups = set(range(size)) - set(nested_groups.keys())
|
||||
if absent_groups:
|
||||
LOG.warning(
|
||||
_LW("Found %(num_present)s Nested Groups, "
|
||||
"creating %(num_absent)s more."),
|
||||
{'num_present': len(nested_groups),
|
||||
'num_absent': len(absent_groups)})
|
||||
for i in absent_groups:
|
||||
cont = self._create_nested_group(i)
|
||||
nested_groups[i] = cont['id']
|
||||
|
||||
return nested_groups
|
||||
|
||||
def _get_nested_group_index_from_name(self, nested_group):
|
||||
# The name format is "Nested Group <index+1>"
|
||||
return int(nested_group['display_name'].split()[-1]) - 1
|
||||
|
||||
def _create_nested_group(self, index):
|
||||
name_prefix = NSGroupManager.NESTED_GROUP_NAME
|
||||
name = '%s %s' % (name_prefix, index + 1)
|
||||
description = NSGroupManager.NESTED_GROUP_DESCRIPTION
|
||||
tags = utils.build_v3_api_version_tag()
|
||||
return self.nsx.create_nsgroup(name, description, tags)
|
||||
|
||||
def _hash_uuid(self, internal_id):
|
||||
return hash(uuid.UUID(internal_id))
|
||||
|
||||
def _suggest_nested_group(self, internal_id):
|
||||
# Suggests a nested group to use, can be iterated to find alternative
|
||||
# group in case that previous suggestions did not help.
|
||||
|
||||
index = self._hash_uuid(internal_id) % self.size
|
||||
yield self.nested_groups[index]
|
||||
|
||||
for i in range(1, self.size):
|
||||
index = (index + 1) % self.size
|
||||
yield self.nested_groups[index]
|
||||
|
||||
def add_nsgroup(self, nsgroup_id):
|
||||
for group in self._suggest_nested_group(nsgroup_id):
|
||||
try:
|
||||
LOG.debug("Adding NSGroup %s to nested group %s",
|
||||
nsgroup_id, group)
|
||||
self.nsx.add_nsgroup_members(group,
|
||||
firewall.NSGROUP,
|
||||
[nsgroup_id])
|
||||
break
|
||||
except exceptions.NSGroupIsFull:
|
||||
LOG.debug("Nested group %(group_id)s is full, trying the "
|
||||
"next group..", {'group_id': group})
|
||||
else:
|
||||
raise exceptions.ManagerError(
|
||||
details=_("Reached the maximum supported amount of "
|
||||
"security groups."))
|
||||
|
||||
def remove_nsgroup(self, nsgroup_id):
|
||||
for group in self._suggest_nested_group(nsgroup_id):
|
||||
try:
|
||||
self.nsx.remove_nsgroup_member(
|
||||
group, firewall.NSGROUP, nsgroup_id, verify=True)
|
||||
break
|
||||
except exceptions.NSGroupMemberNotFound:
|
||||
LOG.warning(_LW("NSGroup %(nsgroup)s was expected to be found "
|
||||
"in group %(group_id)s, but wasn't. "
|
||||
"Looking in the next group.."),
|
||||
{'nsgroup': nsgroup_id, 'group_id': group})
|
||||
continue
|
||||
else:
|
||||
LOG.warning(_LW("NSGroup %s was marked for removal, but its "
|
||||
"reference is missing."), nsgroup_id)
|
|
@ -89,7 +89,6 @@ from vmware_nsx.nsxlib import v3 as nsxlib
|
|||
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
|
||||
from vmware_nsx.nsxlib.v3 import exceptions as nsx_lib_exc
|
||||
from vmware_nsx.nsxlib.v3 import native_dhcp
|
||||
from vmware_nsx.nsxlib.v3 import ns_group_manager
|
||||
from vmware_nsx.nsxlib.v3 import resources as nsx_resources
|
||||
from vmware_nsx.nsxlib.v3 import router
|
||||
from vmware_nsx.nsxlib.v3 import security
|
||||
|
@ -188,8 +187,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||
self._init_dhcp_metadata()
|
||||
|
||||
self._port_client = nsx_resources.LogicalPort(self._nsx_client)
|
||||
self.nsgroup_manager, self.default_section = (
|
||||
self._init_nsgroup_manager_and_default_section_rules())
|
||||
self.default_section = self._init_default_section_rules()
|
||||
self._process_security_group_logging()
|
||||
self._router_client = nsx_resources.LogicalRouter(self._nsx_client)
|
||||
self._router_port_client = nsx_resources.LogicalRouterPort(
|
||||
|
@ -409,16 +407,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||
|
||||
utils.spawn_n(process_security_group_logging)
|
||||
|
||||
def _init_nsgroup_manager_and_default_section_rules(self):
|
||||
with locking.LockManager.get_lock('nsxv3_nsgroup_manager_init'):
|
||||
nsgroup_manager = ns_group_manager.NSGroupManager(
|
||||
cfg.CONF.nsx_v3.number_of_nested_groups)
|
||||
def _init_default_section_rules(self):
|
||||
with locking.LockManager.get_lock('nsxv3_default_section'):
|
||||
section_description = ("This section is handled by OpenStack to "
|
||||
"contain default rules on security-groups.")
|
||||
section_id = self.nsxlib._init_default_section(
|
||||
security.DEFAULT_SECTION, section_description,
|
||||
nsgroup_manager.nested_groups.values())
|
||||
return nsgroup_manager, section_id
|
||||
security.DEFAULT_SECTION, section_description, [])
|
||||
return section_id
|
||||
|
||||
def _init_dhcp_metadata(self):
|
||||
if cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||
|
@ -3054,7 +3049,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||
logging, action, sg_rules)
|
||||
self.nsxlib.save_sg_rule_mappings(context.session,
|
||||
rules['rules'])
|
||||
self.nsgroup_manager.add_nsgroup(ns_group['id'])
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Failed to create backend firewall rules "
|
||||
|
@ -3098,7 +3092,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||
super(NsxV3Plugin, self).delete_security_group(context, id)
|
||||
self.nsxlib.delete_section(section_id)
|
||||
self.nsxlib.delete_nsgroup(nsgroup_id)
|
||||
self.nsgroup_manager.remove_nsgroup(nsgroup_id)
|
||||
|
||||
def create_security_group_rule(self, context, security_group_rule):
|
||||
bulk_rule = {'security_group_rules': [security_group_rule]}
|
||||
|
|
|
@ -19,10 +19,8 @@ from neutron.tests.unit.extensions import test_securitygroup as test_ext_sg
|
|||
|
||||
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
|
||||
from vmware_nsx.nsxlib.v3 import exceptions as nsxlib_exc
|
||||
from vmware_nsx.nsxlib.v3 import ns_group_manager
|
||||
from vmware_nsx.plugins.nsx_v3 import plugin as nsx_plugin
|
||||
from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsxv3
|
||||
from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase
|
||||
|
||||
|
||||
# Pool of fake ns-groups uuids
|
||||
|
@ -172,134 +170,3 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin,
|
|||
|
||||
def test_create_security_group_rule_icmpv6_legacy_protocol_name(self):
|
||||
self.skipTest('not supported')
|
||||
|
||||
|
||||
class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase):
|
||||
"""
|
||||
This test suite is responsible for unittesting of class
|
||||
vmware_nsx.nsxlib.v3.ns_group_manager.NSGroupManager.
|
||||
"""
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
def test_first_initialization(self):
|
||||
size = 5
|
||||
cont_manager = ns_group_manager.NSGroupManager(size)
|
||||
nested_groups = cont_manager.nested_groups
|
||||
self.assertEqual({i: NSG_IDS[i] for i in range(size)},
|
||||
nested_groups)
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
def test_reconfigure_number_of_nested_groups(self):
|
||||
# We need to test that when changing the number of nested groups then
|
||||
# the NSGroupManager picks the ones which were previously created
|
||||
# and create the ones which are missing, which also verifies that it
|
||||
# also recognizes existing nested groups.
|
||||
|
||||
size = 2
|
||||
# Creates 2 nested groups.
|
||||
ns_group_manager.NSGroupManager(size)
|
||||
|
||||
size = 5
|
||||
# Creates another 3 nested groups.
|
||||
nested_groups = ns_group_manager.NSGroupManager(size).nested_groups
|
||||
self.assertEqual({i: NSG_IDS[i] for i in range(size)},
|
||||
nested_groups)
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
@mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member')
|
||||
@mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members')
|
||||
def test_add_and_remove_nsgroups(self,
|
||||
add_member_mock,
|
||||
remove_member_mock):
|
||||
# We verify that when adding a new nsgroup the properly placed
|
||||
# according to its id and the number of nested groups.
|
||||
|
||||
size = 5
|
||||
cont_manager = ns_group_manager.NSGroupManager(size)
|
||||
nsgroup_id = 'nsgroup_id'
|
||||
|
||||
with mock.patch.object(cont_manager, '_hash_uuid', return_value=7):
|
||||
cont_manager.add_nsgroup(nsgroup_id)
|
||||
cont_manager.remove_nsgroup(nsgroup_id)
|
||||
|
||||
# There are 5 nested groups, the hash function will return 7, therefore
|
||||
# we expect that the nsgroup will be placed in the 3rd group.
|
||||
add_member_mock.assert_called_once_with(
|
||||
NSG_IDS[2], firewall.NSGROUP, [nsgroup_id])
|
||||
remove_member_mock.assert_called_once_with(
|
||||
NSG_IDS[2], firewall.NSGROUP, nsgroup_id, verify=True)
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
@mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member')
|
||||
@mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members')
|
||||
def test_when_nested_group_is_full(self,
|
||||
add_member_mock,
|
||||
remove_member_mock):
|
||||
|
||||
def _add_member_mock(nsgroup, target_type, target_id):
|
||||
if nsgroup == NSG_IDS[2]:
|
||||
raise nsxlib_exc.NSGroupIsFull(nsgroup_id=nsgroup)
|
||||
|
||||
def _remove_member_mock(nsgroup, target_type, target_id, verify=False):
|
||||
if nsgroup == NSG_IDS[2]:
|
||||
raise nsxlib_exc.NSGroupMemberNotFound(nsgroup_id=nsgroup,
|
||||
member_id=target_id)
|
||||
|
||||
add_member_mock.side_effect = _add_member_mock
|
||||
remove_member_mock.side_effect = _remove_member_mock
|
||||
|
||||
size = 5
|
||||
cont_manager = ns_group_manager.NSGroupManager(size)
|
||||
nsgroup_id = 'nsgroup_id'
|
||||
|
||||
with mock.patch.object(cont_manager, '_hash_uuid', return_value=7):
|
||||
cont_manager.add_nsgroup(nsgroup_id)
|
||||
cont_manager.remove_nsgroup(nsgroup_id)
|
||||
|
||||
# Trying to add nsgroup to the nested group at index 2 will raise
|
||||
# NSGroupIsFull exception, we expect that the nsgroup will be added to
|
||||
# the nested group at index 3.
|
||||
calls = [mock.call(NSG_IDS[2], firewall.NSGROUP, [nsgroup_id]),
|
||||
mock.call(NSG_IDS[3], firewall.NSGROUP, [nsgroup_id])]
|
||||
add_member_mock.assert_has_calls(calls)
|
||||
|
||||
# Since the nsgroup was added to the nested group at index 3, it will
|
||||
# fail to remove it from the group at index 2, and then will try to
|
||||
# remove it from the group at index 3.
|
||||
calls = [
|
||||
mock.call(
|
||||
NSG_IDS[2], firewall.NSGROUP, nsgroup_id, verify=True),
|
||||
mock.call(
|
||||
NSG_IDS[3], firewall.NSGROUP, nsgroup_id, verify=True)]
|
||||
remove_member_mock.assert_has_calls(calls)
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
@mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member')
|
||||
@mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members')
|
||||
def initialize_with_absent_nested_groups(self,
|
||||
add_member_mock,
|
||||
remove_member_mock):
|
||||
size = 3
|
||||
cont_manager = ns_group_manager.NSGroupManager(size)
|
||||
# list_nsgroups will return nested group 1 and 3, but not group 2.
|
||||
with mock.patch.object(firewall,
|
||||
'list_nsgroups_mock') as list_nsgroups_mock:
|
||||
list_nsgroups_mock = lambda: list_nsgroups_mock()[::2]
|
||||
# invoking the initialization process again, it should process
|
||||
# groups 1 and 3 and create group 2.
|
||||
cont_manager = ns_group_manager.NSGroupManager(size)
|
||||
self.assertEqual({1: NSG_IDS[0],
|
||||
2: NSG_IDS[3],
|
||||
3: NSG_IDS[2]},
|
||||
cont_manager.nested_groups)
|
||||
|
||||
@_mock_create_and_list_nsgroups
|
||||
def test_suggest_nested_group(self):
|
||||
size = 5
|
||||
cont_manager = ns_group_manager.NSGroupManager(size)
|
||||
# We expect that the first suggested index is 2
|
||||
expected_suggested_groups = NSG_IDS[2:5] + NSG_IDS[:2]
|
||||
suggest_group = lambda: cont_manager._suggest_nested_group('fake-id')
|
||||
with mock.patch.object(cont_manager, '_hash_uuid', return_value=7):
|
||||
for i, suggested in enumerate(suggest_group()):
|
||||
self.assertEqual(expected_suggested_groups[i], suggested)
|
||||
|
|
Loading…
Reference in New Issue