From a96d83ece64d1ab662be8ee62eca15c3e3ab4c19 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Wed, 28 Feb 2018 01:34:26 -0800 Subject: [PATCH] NSX|V3: prevent duplicate default FW sections When the plugin starts it will check if the global NS group and OS DFW section are created on the NSX. If not it will create these. There is a edge case where two servers are started in parallel and they both create the default section. This will lead to traffic being dropped. This is dealt with in the following way: 1. We store the default OS section and NS group in the database 2. If the entries do not exist then we create them, the DB will indicate if there is a duplicate and then the plugin will do a cleanup of the incorrect resources. In order to do this we need asecurity group. A default global one with ID 00000000-def0-0000-0fed-000000000000 is created. If the admin wishes to delete the global section then she/he should: 1. delete the NSX section 2. delete the security group 3. restart the neutron service Change-Id: Ide7a7c75efac3e49d51e522a11c77e754f3d1447 --- vmware_nsx/common/nsx_constants.py | 2 + vmware_nsx/plugins/nsx_v3/plugin.py | 80 +++++++++++++++++---- vmware_nsx/tests/unit/nsx_v3/test_plugin.py | 6 ++ 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/vmware_nsx/common/nsx_constants.py b/vmware_nsx/common/nsx_constants.py index 4e733b6c68..11d1d12bf9 100644 --- a/vmware_nsx/common/nsx_constants.py +++ b/vmware_nsx/common/nsx_constants.py @@ -18,3 +18,5 @@ VIF_TYPE_DVS = 'dvs' # NSXv3 CORE PLUGIN PATH VMWARE_NSX_V3_PLUGIN_NAME = 'vmware_nsxv3' + +INTERNAL_V3_TENANT_ID = 'v3_internal_project' diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 320f7b56f9..c719544252 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -93,6 +93,7 @@ from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import l3_rpc_agent_api from vmware_nsx.common import locking from vmware_nsx.common import managers +from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils from vmware_nsx.db import db as nsx_db from vmware_nsx.db import extended_security_group @@ -135,6 +136,8 @@ NSX_V3_EXCLUDED_PORT_NSGROUP_NAME = 'neutron_excluded_port_nsgroup' NSX_V3_NON_VIF_PROFILE = 'nsx-default-switch-security-non-vif-profile' NSX_V3_SERVER_SSL_PROFILE = 'nsx-default-server-ssl-profile' NSX_V3_CLIENT_SSL_PROFILE = 'nsx-default-client-ssl-profile' +# Default UUID for the global OS rule +NSX_V3_OS_DFW_UUID = '00000000-def0-0000-0fed-000000000000' def inject_headers(): @@ -217,11 +220,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def __init__(self): self.fwaas_callbacks = None self._is_sub_plugin = tvd_utils.is_tvd_core_plugin() - self.init_is_complete = False - nsxlib_utils.set_is_attr_callback(validators.is_attr_set) self._extend_fault_map() if self._is_sub_plugin: extension_drivers = cfg.CONF.nsx_tvd.nsx_v3_extension_drivers + self._update_project_mapping() else: extension_drivers = cfg.CONF.nsx_extension_drivers self._extension_manager = managers.ExtensionManager( @@ -262,16 +264,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._translate_configured_names_to_uuids() self._init_dhcp_metadata() - # Include default section NSGroup - LOG.debug("Initializing NSX v3 default section NSGroup") - self._default_section_nsgroup = None - self._default_section_nsgroup = self._init_default_section_nsgroup() - if not self._default_section_nsgroup: - msg = _("Unable to initialize NSX v3 default section NSGroup %s" - ) % NSX_V3_FW_DEFAULT_NS_GROUP - raise nsx_exc.NsxPluginException(err_msg=msg) - - self.default_section = self._init_default_section_rules() + self._prepare_default_rules() self._process_security_group_logging() # init profiles on nsx backend @@ -314,6 +307,69 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, resources.PROCESS, events.AFTER_INIT) + def _update_project_mapping(self): + ctx = q_context.get_admin_context() + try: + nsx_db.add_project_plugin_mapping( + ctx.session, + nsx_constants.INTERNAL_V3_TENANT_ID, + projectpluginmap.NsxPlugins.NSX_T) + except db_exc.DBDuplicateEntry: + pass + + def _ensure_default_rules(self): + # Include default section NSGroup + LOG.debug("Initializing NSX v3 default section NSGroup") + self._default_section_nsgroup = None + self._default_section_nsgroup = self._init_default_section_nsgroup() + if not self._default_section_nsgroup: + msg = _("Unable to initialize NSX v3 default section NSGroup %s" + ) % NSX_V3_FW_DEFAULT_NS_GROUP + raise nsx_exc.NsxPluginException(err_msg=msg) + self.default_section = self._init_default_section_rules() + + def _ensure_global_sg_placeholder(self, context): + try: + super(NsxV3Plugin, self).get_security_group( + context, NSX_V3_OS_DFW_UUID, fields=['id']) + except ext_sg.SecurityGroupNotFound: + sec_group = {'security_group': + {'id': NSX_V3_OS_DFW_UUID, + 'tenant_id': nsx_constants.INTERNAL_V3_TENANT_ID, + 'name': 'NSX Internal', + 'description': None}} + try: + # ensure that the global default is created + super(NsxV3Plugin, self).create_security_group( + context, sec_group, True) + except Exception: + # Treat a race of multiple processing creating the seg group + LOG.warning('Unable to create global security group') + + def _prepare_default_rules(self): + ctx = q_context.get_admin_context() + # Need a global placeholder as the DB below has a foreign key to + # this security group + self._ensure_global_sg_placeholder(ctx) + self._ensure_default_rules() + # Validate if there is a race between processes + nsgroup_id, section_id = nsx_db.get_sg_mappings( + ctx.session, NSX_V3_OS_DFW_UUID) + if nsgroup_id is None or section_id is None: + default_ns_group_id = self._default_section_nsgroup.get('id') + try: + nsx_db.save_sg_mappings(ctx, + NSX_V3_OS_DFW_UUID, + default_ns_group_id, + self.default_section) + except Exception: + LOG.warning("Duplicate rules created. Cleaning up!") + # Delete duplicates created + self.nsxlib.firewall_section.delete(self.default_section) + self.nsxlib.ns_group.delete(default_ns_group_id) + # Ensure global variables are updated + self._ensure_default_rules() + @staticmethod def plugin_type(): return projectpluginmap.NsxPlugins.NSX_T diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 0b1b700439..e534229e7b 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -189,6 +189,12 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase, mock_process_security_group_logging = mock.patch.object( nsx_plugin.NsxV3Plugin, '_process_security_group_logging') mock_process_security_group_logging.start() + # need to mock the global placeholder. This is due to the fact that + # the generic security group tests assume that there is just one + # security group. + mock_ensure_global_sg_placeholder = mock.patch.object( + nsx_plugin.NsxV3Plugin, '_ensure_global_sg_placeholder') + mock_ensure_global_sg_placeholder.start() def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None,