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:
Roey Chen 2016-11-30 00:58:05 -08:00 committed by garyk
parent 56e6b647ec
commit 00475a31a9
3 changed files with 5 additions and 311 deletions

View File

@ -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)

View File

@ -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]}

View File

@ -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)