NSX|P: Policy pluging initial SG support

Initial support for security groups & rules for the NSX policy plugin

Change-Id: Ida67b34eaf01921aece261f7ad9446db53b0bfb4
changes/00/599500/26
Adit Sarfaty 4 years ago
parent e86f7a1af5
commit 2ba3f795a0
  1. 16
      vmware_nsx/common/config.py
  2. 11
      vmware_nsx/db/extended_security_group_rule.py
  3. 12
      vmware_nsx/plugins/common/plugin.py
  4. 470
      vmware_nsx/plugins/nsx_p/plugin.py
  5. 9
      vmware_nsx/plugins/nsx_v3/plugin.py
  6. 36
      vmware_nsx/tests/unit/extensions/test_provider_security_groups.py
  7. 6
      vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py
  8. 0
      vmware_nsx/tests/unit/nsx_p/__init__.py
  9. 116
      vmware_nsx/tests/unit/nsx_p/test_plugin.py

@ -336,6 +336,14 @@ nsx_v3_and_p = [
cfg.IntOpt('redirects',
default=2,
help=_('Number of times a HTTP redirect should be followed.')),
cfg.BoolOpt('log_security_groups_blocked_traffic',
default=False,
help=_("(Optional) Indicates whether distributed-firewall "
"rule for security-groups blocked traffic is logged.")),
cfg.BoolOpt('log_security_groups_allowed_traffic',
default=False,
help=_("(Optional) Indicates whether distributed-firewall "
"security-groups rules are logged.")),
]
nsx_v3_opts = nsx_v3_and_p + [
@ -422,14 +430,6 @@ nsx_v3_opts = nsx_v3_and_p + [
help=_("(Optional) This is the name or UUID of the NSX dhcp "
"relay service that will be used to enable DHCP relay "
"on router ports.")),
cfg.BoolOpt('log_security_groups_blocked_traffic',
default=False,
help=_("(Optional) Indicates whether distributed-firewall "
"rule for security-groups blocked traffic is logged.")),
cfg.BoolOpt('log_security_groups_allowed_traffic',
default=False,
help=_("(Optional) Indicates whether distributed-firewall "
"security-groups rules are logged.")),
cfg.ListOpt('availability_zones',
default=[],
help=_('Optional parameter defining the networks availability '

@ -16,6 +16,7 @@
from neutron_lib.db import model_base
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.orm import exc
from neutron.db import _resource_extend as resource_extend
from neutron.db import api as db_api
@ -90,3 +91,13 @@ class ExtendedSecurityGroupRuleMixin(object):
sg_rule_db.ext_properties.local_ip_prefix)
else:
sg_rule_res[ext_local_ip.LOCAL_IP_PREFIX] = None
def _get_security_group_rule_local_ip(self, context, rule_id):
with db_api.context_manager.reader.using(context):
try:
prop = context.session.query(
NsxExtendedSecurityGroupRuleProperties).filter_by(
rule_id=rule_id).one()
except exc.NoResultFound:
return False
return prop[ext_local_ip.LOCAL_IP_PREFIX]

@ -45,6 +45,7 @@ from neutron_lib.utils import net
from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.vpnaas.nsxv3 import ipsec_utils
@ -583,6 +584,17 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
interface_info['subnet_id'])['network_id']
return net_id
def _fix_sg_rule_dict_ips(self, sg_rule):
# 0.0.0.0/# is not a valid entry for local and remote so we need
# to change this to None
if (sg_rule.get('remote_ip_prefix') and
sg_rule['remote_ip_prefix'].startswith('0.0.0.0/')):
sg_rule['remote_ip_prefix'] = None
if (sg_rule.get(sg_prefix.LOCAL_IP_PREFIX) and
validators.is_attr_set(sg_rule[sg_prefix.LOCAL_IP_PREFIX]) and
sg_rule[sg_prefix.LOCAL_IP_PREFIX].startswith('0.0.0.0/')):
sg_rule[sg_prefix.LOCAL_IP_PREFIX] = None
def get_housekeeper(self, context, name, fields=None):
# run the job in readonly mode and get the results
self.housekeeper.run(context, name, readonly=True)

@ -13,9 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import sys
from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import uuidutils
import webob.exc
from neutron.db import _resource_extend as resource_extend
@ -55,18 +58,34 @@ 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 utils
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group as extend_sg
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
from vmware_nsx.db import maclearning as mac_db
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
from vmware_nsxlib.v3 import policy_constants
from vmware_nsxlib.v3 import utils as nsxlib_utils
LOG = log.getLogger(__name__)
NSX_P_SECURITY_GROUP_TAG = 'os-security-group'
NSX_P_GLOBAL_DOMAIN_ID = policy_constants.DEFAULT_DOMAIN
NSX_P_DEFAULT_GROUP = 'os_default_group'
NSX_P_DEFAULT_GROUP_DESC = 'Default Group for the openstack plugin'
NSX_P_DEFAULT_SECTION = 'os_default_section'
NSX_P_DEFAULT_SECTION_DESC = ('This section is handled by OpenStack to '
'contain default rules on security-groups.')
NSX_P_DEFAULT_SECTION_CATEGORY = policy_constants.CATEGORY_APPLICATION
NSX_P_REGULAR_SECTION_CATEGORY = policy_constants.CATEGORY_ENVIRONMENT
NSX_P_PROVIDER_SECTION_CATEGORY = policy_constants.CATEGORY_INFRASTRUCTURE
@resource_extend.has_resource_extenders
@ -74,6 +93,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
addr_pair_db.AllowedAddressPairsMixin,
nsx_plugin_common.NsxPluginBase,
extend_sg_rule.ExtendedSecurityGroupRuleMixin,
extend_sg.ExtendedSecurityGroupPropertiesMixin,
securitygroups_db.SecurityGroupDbMixin,
external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
@ -106,6 +126,8 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"extraroute",
"router",
"subnet_allocation",
"security-group-logging",
"provider-security-group",
"port-security-groups-filtering"]
@resource_registry.tracked_resources(
@ -156,10 +178,6 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"%(ver)s") % {'ver': self._nsx_version})
raise nsx_exc.NsxPluginException(err_msg=msg)
def _prepare_default_rules(self):
#TODO(asarfaty): implement
pass
@staticmethod
def plugin_type():
return projectpluginmap.NsxPlugins.NSX_P
@ -618,47 +636,381 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
super(NsxPolicyPlugin, self).disassociate_floatingips(
context, port_id, do_notify=False)
def _create_security_group_backend_resources(self, secgroup):
# TODO(asarfaty): implement
pass
def _prepare_default_rules(self):
"""Create a default group & communication map in the default domain"""
# Run this code only on one worker at the time
with locking.LockManager.get_lock('nsx_p_prepare_default_rules'):
# Return if the objects were already created
try:
self.nsxpolicy.comm_map.get(NSX_P_GLOBAL_DOMAIN_ID,
NSX_P_DEFAULT_SECTION)
self.nsxpolicy.group.get(NSX_P_GLOBAL_DOMAIN_ID,
NSX_P_DEFAULT_GROUP)
except nsx_lib_exc.ResourceNotFound:
# prevent logger from logging this exception
sys.exc_clear()
LOG.info("Going to create default group & "
"communication map under the default domain")
else:
return
# Create the default group membership criteria to match all neutron
# ports by scope & tag
scope_and_tag = "%s:%s" % (NSX_P_SECURITY_GROUP_TAG,
NSX_P_DEFAULT_SECTION)
conditions = [self.nsxpolicy.group.build_condition(
cond_val=scope_and_tag,
cond_key=policy_constants.CONDITION_KEY_TAG,
cond_member_type=policy_constants.CONDITION_MEMBER_PORT)]
# Create the default OpenStack group
# (This will not fail if the group already exists)
try:
self.nsxpolicy.group.create_or_overwrite_with_conditions(
name=NSX_P_DEFAULT_GROUP,
domain_id=NSX_P_GLOBAL_DOMAIN_ID,
group_id=NSX_P_DEFAULT_GROUP,
description=NSX_P_DEFAULT_GROUP_DESC,
conditions=conditions)
except Exception as e:
msg = (_("Failed to create NSX default group: %(e)s") % {
'e': e})
raise nsx_exc.NsxPluginException(err_msg=msg)
# create default section and rules
logged = cfg.CONF.nsx_p.log_security_groups_blocked_traffic
rule_id = 1
dhcp_client_rule = self.nsxpolicy.comm_map.build_entry(
'DHCP Reply', NSX_P_GLOBAL_DOMAIN_ID,
NSX_P_DEFAULT_SECTION,
rule_id, sequence_number=rule_id,
service_ids=['DHCP-Client'],
action=policy_constants.ACTION_ALLOW,
source_groups=None,
dest_groups=[NSX_P_DEFAULT_GROUP],
direction=nsxlib_consts.IN,
logged=logged)
rule_id += 1
dhcp_server_rule = self.nsxpolicy.comm_map.build_entry(
'DHCP Request', NSX_P_GLOBAL_DOMAIN_ID,
NSX_P_DEFAULT_SECTION,
rule_id, sequence_number=rule_id,
service_ids=['DHCP-Server'],
action=policy_constants.ACTION_ALLOW,
source_groups=[NSX_P_DEFAULT_GROUP],
dest_groups=None,
direction=nsxlib_consts.OUT,
logged=logged)
rule_id += 1
block_rule = self.nsxpolicy.comm_map.build_entry(
'Block All', NSX_P_GLOBAL_DOMAIN_ID,
NSX_P_DEFAULT_SECTION,
rule_id, sequence_number=rule_id, service_ids=None,
action=policy_constants.ACTION_DENY,
source_groups=None,
dest_groups=[NSX_P_DEFAULT_GROUP],
direction=nsxlib_consts.IN_OUT,
logged=logged)
rules = [dhcp_client_rule, dhcp_server_rule, block_rule]
try:
# This will not fail if the map already exists
self.nsxpolicy.comm_map.create_with_entries(
name=NSX_P_DEFAULT_SECTION,
domain_id=NSX_P_GLOBAL_DOMAIN_ID,
map_id=NSX_P_DEFAULT_SECTION,
description=NSX_P_DEFAULT_SECTION_DESC,
category=NSX_P_DEFAULT_SECTION_CATEGORY,
entries=rules)
except Exception as e:
msg = (_("Failed to create NSX default communication map: "
"%(e)s") % {'e': e})
raise nsx_exc.NsxPluginException(err_msg=msg)
# create exclude port group
# TODO(asarfaty): add this while handling port security disabled
def _create_security_group_backend_resources(self, secgroup, domain_id):
"""Create communication map (=section) and group (=NS group)
Both will have the security group id as their NSX id.
"""
sg_id = secgroup['id']
tags = self.nsxpolicy.build_v3_tags_payload(
secgroup, resource_type='os-neutron-secgr-id',
project_name=secgroup['tenant_id'])
nsx_name = utils.get_name_and_uuid(secgroup['name'] or 'securitygroup',
sg_id)
# Create the groups membership criteria for ports by scope & tag
scope_and_tag = "%s:%s" % (NSX_P_SECURITY_GROUP_TAG, sg_id)
condition = self.nsxpolicy.group.build_condition(
cond_val=scope_and_tag,
cond_key=policy_constants.CONDITION_KEY_TAG,
cond_member_type=policy_constants.CONDITION_MEMBER_PORT)
# Create the group
try:
self.nsxpolicy.group.create_or_overwrite_with_conditions(
nsx_name, domain_id, group_id=sg_id,
description=secgroup.get('description'),
conditions=[condition], tags=tags)
except Exception as e:
msg = (_("Failed to create NSX group for SG %(sg)s: "
"%(e)s") % {'sg': sg_id, 'e': e})
raise nsx_exc.NsxPluginException(err_msg=msg)
category = NSX_P_REGULAR_SECTION_CATEGORY
if secgroup.get(provider_sg.PROVIDER) is True:
category = NSX_P_PROVIDER_SECTION_CATEGORY
# create the communication map (=section) without and entries (=rules)
try:
self.nsxpolicy.comm_map.create_or_overwrite_map_only(
nsx_name, domain_id, map_id=sg_id,
description=secgroup.get('description'),
tags=tags, category=category)
except Exception as e:
msg = (_("Failed to create NSX communication map for SG %(sg)s: "
"%(e)s") % {'sg': sg_id, 'e': e})
self.nsxpolicy.group.delete(domain_id, sg_id)
raise nsx_exc.NsxPluginException(err_msg=msg)
def _get_rule_service_id(self, sg_rule):
"""Return the NSX Policy service id matching the SG rule"""
srv = None
l4_protocol = nsxlib_utils.get_l4_protocol_name(sg_rule['protocol'])
srv_name = 'Service for OS rule %s' % sg_rule['id']
if l4_protocol in [nsxlib_consts.TCP, nsxlib_consts.UDP]:
# If port_range_min is not specified then we assume all ports are
# matched, relying on neutron to perform validation.
if sg_rule['port_range_min'] is None:
destination_ports = []
elif sg_rule['port_range_min'] != sg_rule['port_range_max']:
# NSX API requires a non-empty range (e.g - '22-23')
destination_ports = ['%(port_range_min)s-%(port_range_max)s'
% sg_rule]
else:
destination_ports = ['%(port_range_min)s' % sg_rule]
srv = self.nsxpolicy.service.create_or_overwrite(
srv_name, service_id=sg_rule['id'],
description=sg_rule.get('description'),
protocol=l4_protocol,
dest_ports=destination_ports)
elif l4_protocol in [nsxlib_consts.ICMPV4, nsxlib_consts.ICMPV6]:
# Validate the icmp type & code
version = 4 if l4_protocol == nsxlib_consts.ICMPV4 else 6
icmp_type = sg_rule['port_range_min']
icmp_code = sg_rule['port_range_max']
nsxlib_utils.validate_icmp_params(
icmp_type, icmp_code, icmp_version=version, strict=True)
srv = self.nsxpolicy.icmp_service.create_or_overwrite(
srv_name, service_id=sg_rule['id'],
description=sg_rule.get('description'),
version=version,
icmp_type=icmp_type,
icmp_code=icmp_code)
elif l4_protocol:
srv = self.nsxpolicy.ip_protocol_service.create_or_overwrite(
srv_name, service_id=sg_rule['id'],
description=sg_rule.get('description'),
protocol_number=l4_protocol)
if srv:
return srv['id']
def _get_sg_rule_remote_ip_group_id(self, sg_rule):
return '%s_remote_group' % sg_rule['id']
def _get_sg_rule_local_ip_group_id(self, sg_rule):
return '%s_local_group' % sg_rule['id']
def _create_security_group_backend_rule(self, domain_id, map_id, sg_rule,
secgroup_logging):
# The id of the map and group is the same as the security group id
this_group_id = map_id
# There is no rule name in neutron. Using ID instead
nsx_name = sg_rule['id']
direction = (nsxlib_consts.IN if sg_rule.get('direction') == 'ingress'
else nsxlib_consts.OUT)
self._fix_sg_rule_dict_ips(sg_rule)
source = None
destination = this_group_id
if sg_rule.get('remote_group_id'):
# This is the ID of a security group that already exists,
# so it should be known to the policy manager
source = sg_rule.get('remote_group_id')
elif sg_rule.get('remote_ip_prefix'):
# Create a group for the remote IPs
remote_ip = sg_rule['remote_ip_prefix']
remote_group_id = self._get_sg_rule_remote_ip_group_id(sg_rule)
tags = self.nsxpolicy.build_v3_tags_payload(
sg_rule, resource_type='os-neutron-sgrule-id',
project_name=sg_rule['tenant_id'])
expr = self.nsxpolicy.group.build_ip_address_expression(
[remote_ip])
self.nsxpolicy.group.create_or_overwrite_with_conditions(
remote_group_id, domain_id, group_id=remote_group_id,
description='%s for OS rule %s' % (remote_ip, sg_rule['id']),
conditions=[expr], tags=tags)
source = remote_group_id
if sg_rule.get(sg_prefix.LOCAL_IP_PREFIX):
# Create a group for the local ips
local_ip = sg_rule[sg_prefix.LOCAL_IP_PREFIX]
local_group_id = self._get_sg_rule_local_ip_group_id(sg_rule)
tags = self.nsxpolicy.build_v3_tags_payload(
sg_rule, resource_type='os-neutron-sgrule-id',
project_name=sg_rule['tenant_id'])
expr = self.nsxpolicy.group.build_ip_address_expression(
[local_ip])
self.nsxpolicy.group.create_or_overwrite_with_conditions(
local_group_id, domain_id, group_id=local_group_id,
description='%s for OS rule %s' % (local_ip, sg_rule['id']),
conditions=[expr], tags=tags)
destination = local_group_id
if direction == nsxlib_consts.OUT:
# Swap source and destination
source, destination = destination, source
service = self._get_rule_service_id(sg_rule)
logging = (cfg.CONF.nsx_p.log_security_groups_allowed_traffic or
secgroup_logging)
self.nsxpolicy.comm_map.create_entry(
nsx_name, domain_id, map_id, entry_id=sg_rule['id'],
description=sg_rule.get('description'),
service_ids=[service] if service else None,
action=policy_constants.ACTION_ALLOW,
source_groups=[source] if source else None,
dest_groups=[destination] if destination else None,
direction=direction, logged=logging)
def _create_project_domain(self, project_id):
"""Return the NSX domain id of a neutron project
The ID of the created domain will be the same as the project ID
so there is no need to keep it in the neutron DB
"""
try:
domain = self.nsxpolicy.domain.create_or_overwrite(
name=project_id,
domain_id=project_id,
description="Domain for OS project %s" % project_id)
domain_id = domain['id']
except Exception as e:
msg = (_("Failed to create NSX domain for project %(proj)s: "
"%(e)s") % {'proj': project_id, 'e': e})
raise nsx_exc.NsxPluginException(err_msg=msg)
LOG.info("NSX Domain was created for project %s", project_id)
return domain_id
def create_security_group(self, context, security_group, default_sg=False):
secgroup = security_group['security_group']
# Make sure the ID is initialized, as it is used for the backend
# objects too
secgroup['id'] = secgroup.get('id') or uuidutils.generate_uuid()
project_id = secgroup['tenant_id']
if not default_sg:
tenant_id = secgroup['tenant_id']
self._ensure_default_security_group(context, tenant_id)
self._ensure_default_security_group(context, project_id)
else:
# create the NSX policy domain for this new project
self._create_project_domain(project_id)
self._create_security_group_backend_resources(secgroup)
# create the Neutron SG
with db_api.context_manager.writer.using(context):
secgroup_db = (
super(NsxPolicyPlugin, self).create_security_group(
context, security_group, default_sg))
if secgroup.get(provider_sg.PROVIDER) is True:
secgroup_db = self.create_provider_security_group(
context, security_group)
else:
secgroup_db = (
super(NsxPolicyPlugin, self).create_security_group(
context, security_group, default_sg))
# TODO(asarfaty) save NSX->Neutron mappings
self._process_security_group_properties_create(context,
secgroup_db,
secgroup,
default_sg)
try:
# Create Group & communication map on the NSX
self._create_security_group_backend_resources(
secgroup, project_id)
# Add the security-group rules
sg_rules = secgroup_db['security_group_rules']
secgroup_logging = secgroup.get(sg_logging.LOGGING, False)
for sg_rule in sg_rules:
self._create_security_group_backend_rule(
project_id, secgroup_db['id'], sg_rule, secgroup_logging)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.exception("Failed to create backend SG rules "
"for security-group %(name)s (%(id)s), "
"rolling back changes. Error: %(e)s",
{'name': secgroup_db['name'],
'id': secgroup_db['id'],
'e': e})
# rollback SG creation (which will also delete the backend
# objects)
super(NsxPolicyPlugin, self).delete_security_group(
context, secgroup['id'])
return secgroup_db
def update_security_group(self, context, id, security_group):
orig_secgroup = self.get_security_group(
context, id, fields=['id', 'name', 'description'])
LOG.debug("Updating SG %s -> %s", orig_secgroup,
security_group['security_group'])
def update_security_group(self, context, sg_id, security_group):
self._prevent_non_admin_edit_provider_sg(context, sg_id)
sg_data = security_group['security_group']
# update the neutron security group
with db_api.context_manager.writer.using(context):
secgroup_res = super(NsxPolicyPlugin, self).update_security_group(
context, id, security_group)
context, sg_id, security_group)
self._process_security_group_properties_update(
context, secgroup_res, security_group['security_group'])
#TODO(asarfaty): Update the NSX backend
context, secgroup_res, sg_data)
# Update the name and description on NSX backend
if 'name' in sg_data or 'description' in sg_data:
nsx_name = utils.get_name_and_uuid(
secgroup_res['name'] or 'securitygroup', sg_id)
domain_id = secgroup_res['tenant_id']
try:
self.nsxpolicy.group.create_or_overwrite(
nsx_name, domain_id, sg_id,
description=secgroup_res.get('description'))
self.nsxpolicy.comm_map.create_or_overwrite_map_only(
nsx_name, domain_id, sg_id,
description=secgroup_res.get('description'))
except Exception as e:
LOG.warning("Failed to update SG %s NSX resources: %s",
sg_id, e)
# Go on with the update anyway (it's just the name & desc)
# If the logging of the SG changed - update the backend rules
if sg_logging.LOGGING in sg_data:
logged = (sg_data[sg_logging.LOGGING] or
cfg.CONF.nsx_p.log_security_groups_allowed_traffic)
self.nsxpolicy.comm_map.update_entries_logged(domain_id, sg_id,
logged)
return secgroup_res
def delete_security_group(self, context, id):
super(NsxPolicyPlugin, self).delete_security_group(context, id)
#TODO(asarfaty): Update the nSX backend
def delete_security_group(self, context, sg_id):
self._prevent_non_admin_edit_provider_sg(context, sg_id)
sg = self.get_security_group(context, sg_id)
super(NsxPolicyPlugin, self).delete_security_group(context, sg_id)
domain_id = sg['tenant_id']
try:
self.nsxpolicy.comm_map.delete(domain_id, sg_id)
self.nsxpolicy.group.delete(domain_id, sg_id)
for rule in sg['security_group_rules']:
self._delete_security_group_rule_backend_resources(
context, domain_id, rule)
except Exception as e:
LOG.warning("Failed to delete SG %s NSX resources: %s",
sg_id, e)
# Go on with the deletion anyway
def create_security_group_rule(self, context, security_group_rule):
bulk_rule = {'security_group_rules': [security_group_rule]}
@ -666,9 +1018,10 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
def create_security_group_rule_bulk(self, context, security_group_rules):
sg_rules = security_group_rules['security_group_rules']
for r in sg_rules:
# TODO(asarfaty): create rules at the NSX
pass
# Tenant & security group are the same for all rules in the bulk
example_rule = sg_rules[0]['security_group_rule']
sg_id = example_rule['security_group_id']
self._prevent_non_admin_edit_provider_sg(context, sg_id)
with db_api.context_manager.writer.using(context):
rules_db = (super(NsxPolicyPlugin,
@ -677,8 +1030,63 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
for i, r in enumerate(sg_rules):
self._process_security_group_rule_properties(
context, rules_db[i], r['security_group_rule'])
domain_id = example_rule['tenant_id']
secgroup_logging = self._is_security_group_logged(context, sg_id)
for sg_rule in sg_rules:
# create the NSX rule
rule_data = sg_rule['security_group_rule']
self._check_local_ip_prefix(context, rule_data)
rule_data['id'] = rule_data.get('id') or uuidutils.generate_uuid()
self._create_security_group_backend_rule(
domain_id, sg_id, rule_data, secgroup_logging)
return rules_db
def delete_security_group_rule(self, context, id):
#TODO(asarfaty): Update the nSX backend
super(NsxPolicyPlugin, self).delete_security_group_rule(context, id)
def _delete_security_group_rule_backend_resources(
self, context, domain_id, rule_db):
rule_id = rule_db['id']
# try to delete the service of this rule, if exists
if rule_db['protocol']:
try:
self.nsxpolicy.service.delete(rule_id)
except nsx_lib_exc.ResourceNotFound:
LOG.warning("Failed to delete SG rule %s service", rule_id)
# Try to delete the remote ip prefix group, if exists
if rule_db['remote_ip_prefix']:
try:
remote_group_id = self._get_sg_rule_remote_ip_group_id(rule_db)
self.nsxpolicy.group.delete(domain_id, remote_group_id)
except nsx_lib_exc.ResourceNotFound:
LOG.warning("Failed to delete SG rule %s remote ip prefix "
"group", rule_id)
# Try to delete the local ip prefix group, if exists
if self._get_security_group_rule_local_ip(context, rule_id):
try:
local_group_id = self._get_sg_rule_local_ip_group_id(rule_db)
self.nsxpolicy.group.delete(domain_id, local_group_id)
except nsx_lib_exc.ResourceNotFound:
LOG.warning("Failed to delete SG rule %s local ip prefix "
"group", rule_id)
def delete_security_group_rule(self, context, rule_id):
rule_db = self._get_security_group_rule(context, rule_id)
sg_id = rule_db['security_group_id']
self._prevent_non_admin_edit_provider_sg(context, sg_id)
domain_id = rule_db['tenant_id']
# Delete the rule itself
try:
self.nsxpolicy.comm_map.delete_entry(domain_id, sg_id, rule_id)
except Exception as e:
LOG.warning("Failed to delete SG rule %s NSX resources: %s",
rule_id, e)
# Go on with the deletion anyway
self._delete_security_group_rule_backend_resources(
context, domain_id, rule_db)
super(NsxPolicyPlugin, self).delete_security_group_rule(
context, rule_id)

@ -4747,6 +4747,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
ruleid_2_remote_nsgroup_map = {}
_sg_rules = copy.deepcopy(sg_rules)
for sg_rule in _sg_rules:
self._fix_sg_rule_dict_ips(sg_rule)
remote_nsgroup_id = None
remote_group_id = sg_rule.get('remote_group_id')
# skip unnecessary db access when possible
@ -4756,14 +4757,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
remote_nsgroup_id = nsx_db.get_nsx_security_group_id(
context.session, remote_group_id)
ruleid_2_remote_nsgroup_map[sg_rule['id']] = remote_nsgroup_id
# 0.0.0.0/# is not a valid entry for local and remote so we need
# to change this to None
if (sg_rule.get('remote_ip_prefix') and
sg_rule['remote_ip_prefix'].startswith('0.0.0.0/')):
sg_rule['remote_ip_prefix'] = None
if (sg_rule.get('local_ip_prefix') and
sg_rule['local_ip_prefix'].startswith('0.0.0.0/')):
sg_rule['local_ip_prefix'] = None
return self.nsxlib.firewall_section.create_section_rules(
section_id, nsgroup_id,

@ -24,6 +24,7 @@ from neutron_lib import context
from vmware_nsx.db import extended_security_group
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.tests.unit.nsx_p import test_plugin as test_nsxp_plugin
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_nsxv_plugin
from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsxv3_plugin
@ -390,3 +391,38 @@ class TestNSXvProviderSecurityGroup(test_nsxv_plugin.NsxVPluginV2TestCase,
create_rule_m.assert_called_with(mock.ANY, mock.ANY,
logged=mock.ANY,
action='deny')
class TestNSXpProviderSecurityGrp(test_nsxp_plugin.NsxPPluginTestCaseMixin,
ProviderSecurityGroupExtTestCase):
# Temporarily skip all port related tests until the plugin supports it
def test_update_port_security_groups(self):
self.skipTest('Temporarily not supported')
def test_update_port_remove_provider_sg_with_empty_list(self):
self.skipTest('Temporarily not supported')
def test_update_port_security_groups_only(self):
self.skipTest('Temporarily not supported')
def test_create_port_with_no_provider_sg(self):
self.skipTest('Temporarily not supported')
def test_create_port_gets_multi_provider_sg(self):
self.skipTest('Temporarily not supported')
def test_cannot_update_port_with_provider_group_as_sec_group(self):
self.skipTest('Temporarily not supported')
def test_update_port_remove_provider_sg_with_none(self):
self.skipTest('Temporarily not supported')
def test_create_port_gets_provider_sg(self):
self.skipTest('Temporarily not supported')
def test_cannot_update_port_with_different_tenant_provider_secgroup(self):
self.skipTest('Temporarily not supported')
def test_cannot_update_port_with_sec_group_as_provider(self):
self.skipTest('Temporarily not supported')

@ -29,6 +29,7 @@ from neutron_lib.plugins import directory
from vmware_nsx.db import extended_security_group_rule as ext_rule_db
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as ext_loip
from vmware_nsx.plugins.nsx_v.vshield import securitygroup_utils
from vmware_nsx.tests.unit.nsx_p import test_plugin as test_nsxp_plugin
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_nsxv_plugin
from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsxv3_plugin
@ -185,3 +186,8 @@ class TestNSXv3ExtendedSGRule(test_nsxv3_plugin.NsxV3PluginTestCaseMixin,
'ALLOW', # action
sg_rules, # sg_rules
mock.ANY) # ruleid_2_remote_nsgroup_map
class TestNSXpExtendedSGRule(test_nsxp_plugin.NsxPPluginTestCaseMixin,
LocalIPPrefixExtTestCase):
pass

@ -0,0 +1,116 @@
# Copyright (c) 2018 OpenStack Foundation.
#
# 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 mock
from neutron.tests.unit.db import test_db_base_plugin_v2
from neutron.tests.unit.extensions import test_securitygroup
from vmware_nsxlib.v3 import nsx_constants
PLUGIN_NAME = 'vmware_nsx.plugin.NsxPolicyPlugin'
class NsxPPluginTestCaseMixin(
test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
def setUp(self, plugin=PLUGIN_NAME,
ext_mgr=None,
service_plugins=None, **kwargs):
self._mock_nsx_policy_backend_calls()
self.setup_conf_overrides()
super(NsxPPluginTestCaseMixin, self).setUp(plugin=plugin,
ext_mgr=ext_mgr)
def _mock_nsx_policy_backend_calls(self):
mock.patch(
"vmware_nsxlib.v3.NsxPolicyLib.get_version",
return_value=nsx_constants.NSX_VERSION_2_4_0).start()
mock.patch(
"vmware_nsxlib.v3.client.RESTClient.get").start()
mock.patch(
"vmware_nsxlib.v3.client.RESTClient.patch").start()
mock.patch(
"vmware_nsxlib.v3.client.RESTClient.delete").start()
mock.patch("vmware_nsxlib.v3.policy_resources."
"NsxPolicyCommunicationMapApi._get_last_seq_num",
return_value=-1).start()
def setup_conf_overrides(self):
#TODO(asarfaty): will be needed in the future
#cfg.CONF.set_override('default_overlay_tz', NSX_TZ_NAME, 'nsx_p')
#cfg.CONF.set_override('native_dhcp_metadata', False, 'nsx_p')
#cfg.CONF.set_override('dhcp_profile',
# NSX_DHCP_PROFILE_ID, 'nsx_p')
#cfg.CONF.set_override('metadata_proxy',
# NSX_METADATA_PROXY_ID, 'nsx_p')
pass
class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
test_securitygroup.TestSecurityGroups,
test_securitygroup.SecurityGroupDBTestCase):
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
super(NsxPTestSecurityGroup, self).setUp(plugin=plugin,
ext_mgr=ext_mgr)
def test_create_security_group_rule_icmp_with_type_and_code(self):
"""No non-zero icmp codes are currently supported by the NSX"""
self.skipTest('not supported')
def test_create_security_group_rule_icmp_with_type(self):
name = 'webservers'
description = 'my webservers'
with self.security_group(name, description) as sg:
security_group_id = sg['security_group']['id']
direction = "ingress"
remote_ip_prefix = "10.0.0.0/24"
protocol = "icmp"
# port_range_min (ICMP type) is greater than port_range_max
# (ICMP code) in order to confirm min <= max port check is
# not called for ICMP.
port_range_min = 14
port_range_max = None
keys = [('remote_ip_prefix', remote_ip_prefix),
('security_group_id', security_group_id),
('direction', direction),
('protocol', protocol),
('port_range_min', port_range_min),
('port_range_max', port_range_max)]
with self.security_group_rule(security_group_id, direction,
protocol, port_range_min,
port_range_max,
remote_ip_prefix) as rule:
for k, v, in keys:
self.assertEqual(rule['security_group_rule'][k], v)
# Temporarily skip all port related tests until the plugin supports it
def test_create_port_with_no_security_groups(self):
self.skipTest('Temporarily not supported')
def test_create_delete_security_group_port_in_use(self):
self.skipTest('Temporarily not supported')
def test_create_port_with_multiple_security_groups(self):
self.skipTest('Temporarily not supported')
def test_list_ports_security_group(self):
self.skipTest('Temporarily not supported')
def test_update_port_with_security_group(self):
self.skipTest('Temporarily not supported')
Loading…
Cancel
Save