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
This commit is contained in:
Gary Kotton 2018-02-28 01:34:26 -08:00
parent 527eca9f73
commit a96d83ece6
3 changed files with 76 additions and 12 deletions

View File

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

View File

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

View File

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