NSXv3: Adding support for 'secgroup-rule-local-ip-prefix' extension

Supporting this extension will allow users to define rules with the notation
of local-prefix-ip, which matches on the destination address of packets going
into the port.
One may use this extended API in order to specify a specific set of
multicast groups addresses in which a port (or group of ports) should
be allowed to accept packets from.

Change-Id: I2bd6b3381c715c1286dfa10bf3b143c73fecf49d
This commit is contained in:
Roey Chen 2016-03-13 06:33:20 -07:00
parent 1ac25e8896
commit 82660993e7
8 changed files with 128 additions and 71 deletions

View File

@ -12,15 +12,18 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from sqlalchemy.orm import exc
import sqlalchemy as sa
from sqlalchemy import orm
from neutron.api.v2 import attributes as attr from neutron.api.v2 import attributes as attr
from neutron.db import db_base_plugin_v2 from neutron.db import db_base_plugin_v2
from neutron.db import model_base
from neutron.db import securitygroups_db
from neutron.extensions import securitygroup as ext_sg from neutron.extensions import securitygroup as ext_sg
from neutron_lib import exceptions as nexception from neutron_lib import exceptions as nexception
from vmware_nsx._i18n import _ from vmware_nsx._i18n import _
from vmware_nsx.db import nsxv_models from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as ext_local_ip
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as ext_loip
class NotIngressRule(nexception.BadRequest): class NotIngressRule(nexception.BadRequest):
@ -28,41 +31,55 @@ class NotIngressRule(nexception.BadRequest):
"with ingress rules only.") "with ingress rules only.")
class NsxExtendedSecurityGroupRuleProperties(model_base.BASEV2):
"""Persist security group rule properties for the
extended-security-group-rule extension.
"""
__tablename__ = 'nsx_extended_security_group_rule_properties'
rule_id = sa.Column(sa.String(36),
sa.ForeignKey('securitygrouprules.id',
ondelete='CASCADE'),
primary_key=True,
nullable=False)
local_ip_prefix = sa.Column(sa.String(255), nullable=False)
rule = orm.relationship(
securitygroups_db.SecurityGroupRule,
backref=orm.backref('ext_properties', lazy='joined',
uselist=False, cascade='delete'))
class ExtendedSecurityGroupRuleMixin(object): class ExtendedSecurityGroupRuleMixin(object):
def _check_local_ip_prefix(self, context, rule): def _check_local_ip_prefix(self, context, rule):
rule_specify_local_ip_prefix = attr.is_attr_set( rule_specify_local_ip_prefix = attr.is_attr_set(
rule.get(ext_loip.LOCAL_IP_PREFIX)) rule.get(ext_local_ip.LOCAL_IP_PREFIX))
if rule_specify_local_ip_prefix and rule['direction'] != 'ingress': if rule_specify_local_ip_prefix and rule['direction'] != 'ingress':
raise NotIngressRule() raise NotIngressRule()
return rule_specify_local_ip_prefix return rule_specify_local_ip_prefix
def _save_extended_rule_properties(self, context, rule): def _process_security_group_rule_properties(self, context,
if not attr.is_attr_set(rule.get(ext_loip.LOCAL_IP_PREFIX)): rule_res, rule_req):
rule_res[ext_local_ip.LOCAL_IP_PREFIX] = None
if not attr.is_attr_set(rule_req.get(ext_local_ip.LOCAL_IP_PREFIX)):
return return
with context.session.begin(subtransactions=True):
properties = nsxv_models.NsxvExtendedSecurityGroupRuleProperties(
rule_id=rule['id'],
local_ip_prefix=rule[ext_loip.LOCAL_IP_PREFIX])
context.session.add(properties)
def _get_security_group_rule_properties(self, context, sgr): with context.session.begin(subtransactions=True):
try: properties = NsxExtendedSecurityGroupRuleProperties(
properties = (context.session.query( rule_id=rule_res['id'],
nsxv_models.NsxvExtendedSecurityGroupRuleProperties).filter_by( local_ip_prefix=rule_req[ext_local_ip.LOCAL_IP_PREFIX])
rule_id=sgr['id']).one()) context.session.add(properties)
except exc.NoResultFound: rule_res[ext_local_ip.LOCAL_IP_PREFIX] = (
sgr[ext_loip.LOCAL_IP_PREFIX] = None rule_req[ext_local_ip.LOCAL_IP_PREFIX])
else:
sgr[ext_loip.LOCAL_IP_PREFIX] = properties.local_ip_prefix
return sgr
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs( db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
ext_sg.SECURITYGROUPRULES, ['_extend_security_group_rule_with_params']) ext_sg.SECURITYGROUPRULES, ['_extend_security_group_rule_with_params'])
def _extend_security_group_rule_with_params(self, sg_rule_res, sg_rule_db): def _extend_security_group_rule_with_params(self, sg_rule_res, sg_rule_db):
if sg_rule_db.ext_properties: if sg_rule_db.ext_properties:
sg_rule_res[ext_loip.LOCAL_IP_PREFIX] = ( sg_rule_res[ext_local_ip.LOCAL_IP_PREFIX] = (
sg_rule_db.ext_properties.local_ip_prefix) sg_rule_db.ext_properties.local_ip_prefix)
else: else:
sg_rule_res[ext_loip.LOCAL_IP_PREFIX] = None sg_rule_res[ext_local_ip.LOCAL_IP_PREFIX] = None

View File

@ -1 +1 @@
5ed1ffbc0d2a 081af0e396d7

View File

@ -0,0 +1,32 @@
# Copyright 2016 VMware, Inc.
#
# 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.
"""nsxv3_secgroup_local_ip_prefix
Revision ID: 081af0e396d7
Revises: 5ed1ffbc0d2a
Create Date: 2016-03-24 07:11:30.300482
"""
# revision identifiers, used by Alembic.
revision = '081af0e396d7'
down_revision = '5ed1ffbc0d2a'
from alembic import op
def upgrade():
op.rename_table('nsxv_extended_security_group_rule_properties',
'nsx_extended_security_group_rule_properties')

View File

@ -21,7 +21,6 @@ from sqlalchemy import orm
from neutron.db import l3_db from neutron.db import l3_db
from neutron.db import model_base from neutron.db import model_base
from neutron.db import models_v2 from neutron.db import models_v2
from neutron.db import securitygroups_db
from vmware_nsx.common import nsxv_constants from vmware_nsx.common import nsxv_constants
@ -328,23 +327,3 @@ class NsxvSubnetExtAttributes(model_base.BASEV2):
models_v2.Subnet, models_v2.Subnet,
backref=orm.backref("nsxv_subnet_attributes", lazy='joined', backref=orm.backref("nsxv_subnet_attributes", lazy='joined',
uselist=False, cascade='delete')) uselist=False, cascade='delete'))
class NsxvExtendedSecurityGroupRuleProperties(model_base.BASEV2):
"""Persist security group rule properties for the
extended-security-group-rule extension.
"""
__tablename__ = 'nsxv_extended_security_group_rule_properties'
rule_id = sa.Column(sa.String(36),
sa.ForeignKey('securitygrouprules.id',
ondelete='CASCADE'),
primary_key=True,
nullable=False)
local_ip_prefix = sa.Column(sa.String(255), nullable=False)
rule = orm.relationship(
securitygroups_db.SecurityGroupRule,
backref=orm.backref('ext_properties', lazy='joined',
uselist=False, cascade='delete'))

View File

@ -28,6 +28,7 @@ from vmware_nsx._i18n import _, _LW
from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import utils from vmware_nsx.common import utils
from vmware_nsx.db import nsx_models from vmware_nsx.db import nsx_models
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix
from vmware_nsx.extensions import securitygrouplogging as sg_logging from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.nsxlib.v3 import dfw_api as firewall from vmware_nsx.nsxlib.v3 import dfw_api as firewall
@ -97,16 +98,23 @@ def _get_fw_rule_from_sg_rule(sg_rule, nsgroup_id, rmt_nsgroup_id, logged):
ip_protocol = sg_rule['ethertype'].upper() ip_protocol = sg_rule['ethertype'].upper()
direction = _get_direction(sg_rule) direction = _get_direction(sg_rule)
if sg_rule.get(secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX):
local_ip_prefix = firewall.get_ip_cidr_reference(
sg_rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX],
ip_protocol)
else:
local_ip_prefix = None
source = None source = None
local_group = firewall.get_nsgroup_reference(nsgroup_id) local_group = firewall.get_nsgroup_reference(nsgroup_id)
if sg_rule['remote_ip_prefix'] is not None: if sg_rule['remote_ip_prefix'] is not None:
source = firewall.get_ip_cidr_reference(sg_rule['remote_ip_prefix'], source = firewall.get_ip_cidr_reference(sg_rule['remote_ip_prefix'],
ip_protocol) ip_protocol)
destination = local_group destination = local_ip_prefix or local_group
else: else:
if rmt_nsgroup_id: if rmt_nsgroup_id:
source = firewall.get_nsgroup_reference(rmt_nsgroup_id) source = firewall.get_nsgroup_reference(rmt_nsgroup_id)
destination = local_group destination = local_ip_prefix or local_group
if direction == firewall.OUT: if direction == firewall.OUT:
source, destination = destination, source source, destination = destination, source

View File

@ -80,7 +80,7 @@ from vmware_nsx.extensions import (
vnicindex as ext_vnic_idx) vnicindex as ext_vnic_idx)
from vmware_nsx.extensions import dns_search_domain as ext_dns_search_domain from vmware_nsx.extensions import dns_search_domain as ext_dns_search_domain
from vmware_nsx.extensions import routersize from vmware_nsx.extensions import routersize
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as ext_loip from vmware_nsx.extensions import secgroup_rule_local_ip_prefix
from vmware_nsx.extensions import securitygrouplogging as sg_logging from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.plugins.nsx_v import managers from vmware_nsx.plugins.nsx_v import managers
from vmware_nsx.plugins.nsx_v import md_proxy as nsx_v_md_proxy from vmware_nsx.plugins.nsx_v import md_proxy as nsx_v_md_proxy
@ -2111,9 +2111,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Get source and destination containers from rule # Get source and destination containers from rule
if rule['direction'] == 'ingress': if rule['direction'] == 'ingress':
if rule.get(ext_loip.LOCAL_IP_PREFIX): if rule.get(secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX):
dest = self.nsx_sg_utils.get_remote_container( dest = self.nsx_sg_utils.get_remote_container(
None, rule[ext_loip.LOCAL_IP_PREFIX]) None, rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX])
src = self.nsx_sg_utils.get_remote_container( src = self.nsx_sg_utils.get_remote_container(
remote_nsx_sg_id, rule['remote_ip_prefix']) remote_nsx_sg_id, rule['remote_ip_prefix'])
dest = dest or self.nsx_sg_utils.get_container(nsx_sg_id) dest = dest or self.nsx_sg_utils.get_container(nsx_sg_id)
@ -2177,7 +2177,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
for r in sg_rules: for r in sg_rules:
rule = r['security_group_rule'] rule = r['security_group_rule']
if not self._check_local_ip_prefix(context, rule): if not self._check_local_ip_prefix(context, rule):
rule[ext_loip.LOCAL_IP_PREFIX] = None rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX] = None
rule['id'] = uuidutils.generate_uuid() rule['id'] = uuidutils.generate_uuid()
ruleids.add(rule['id']) ruleids.add(rule['id'])
nsx_rules.append(self._create_nsx_rule( nsx_rules.append(self._create_nsx_rule(
@ -2204,10 +2204,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
nsxv_db.add_neutron_nsx_rule_mapping( nsxv_db.add_neutron_nsx_rule_mapping(
context.session, neutron_rule_id, nsx_rule_id) context.session, neutron_rule_id, nsx_rule_id)
for i, r in enumerate(sg_rules): for i, r in enumerate(sg_rules):
rule = r['security_group_rule'] self._process_security_group_rule_properties(
self._save_extended_rule_properties(context, rule) context, new_rule_list[i], r['security_group_rule'])
self._get_security_group_rule_properties(context,
new_rule_list[i])
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
for nsx_rule_id in [p['nsx_id'] for p in rule_pairs for nsx_rule_id in [p['nsx_id'] for p in rule_pairs

View File

@ -69,6 +69,7 @@ from vmware_nsx.common import nsx_constants
from vmware_nsx.common import utils from vmware_nsx.common import utils
from vmware_nsx.db import db as nsx_db from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group from vmware_nsx.db import extended_security_group
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
from vmware_nsx.dhcp_meta import rpc as nsx_rpc from vmware_nsx.dhcp_meta import rpc as nsx_rpc
from vmware_nsx.extensions import securitygrouplogging as sg_logging from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.nsxlib import v3 as nsxlib
@ -88,6 +89,7 @@ NSX_V3_DHCP_PROFILE_NAME = 'neutron_port_dhcp_profile'
class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
db_base_plugin_v2.NeutronDbPluginV2, db_base_plugin_v2.NeutronDbPluginV2,
extend_sg_rule.ExtendedSecurityGroupRuleMixin,
securitygroups_db.SecurityGroupDbMixin, securitygroups_db.SecurityGroupDbMixin,
extended_security_group.ExtendedSecurityGroupPropertiesMixin, extended_security_group.ExtendedSecurityGroupPropertiesMixin,
external_net_db.External_net_db_mixin, external_net_db.External_net_db_mixin,
@ -111,6 +113,7 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
"dhcp_agent_scheduler", "dhcp_agent_scheduler",
"ext-gw-mode", "ext-gw-mode",
"security-group", "security-group",
"secgroup-rule-local-ip-prefix",
"port-security", "port-security",
"provider", "provider",
"external-net", "external-net",
@ -1827,25 +1830,32 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
return self.create_security_group_rule_bulk(context, bulk_rule)[0] return self.create_security_group_rule_bulk(context, bulk_rule)[0]
def create_security_group_rule_bulk(self, context, security_group_rules): def create_security_group_rule_bulk(self, context, security_group_rules):
security_group_rules_db = ( sg_rules = security_group_rules['security_group_rules']
super(NsxV3Plugin, self).create_security_group_rule_bulk_native( for r in sg_rules:
self._check_local_ip_prefix(context, r['security_group_rule'])
with context.session.begin(subtransactions=True):
rules_db = (super(NsxV3Plugin,
self).create_security_group_rule_bulk_native(
context, security_group_rules)) context, security_group_rules))
sg_id = security_group_rules_db[0]['security_group_id'] for i, r in enumerate(sg_rules):
self._process_security_group_rule_properties(
context, rules_db[i], r['security_group_rule'])
sg_id = rules_db[0]['security_group_id']
nsgroup_id, section_id = security.get_sg_mappings(context.session, nsgroup_id, section_id = security.get_sg_mappings(context.session,
sg_id) sg_id)
logging_enabled = (cfg.CONF.nsx_v3.log_security_groups_allowed_traffic logging_enabled = (cfg.CONF.nsx_v3.log_security_groups_allowed_traffic
or self._is_security_group_logged(context, sg_id)) or self._is_security_group_logged(context, sg_id))
try: try:
rules = security.create_firewall_rules(context, section_id, rules = security.create_firewall_rules(
nsgroup_id, logging_enabled, context, section_id, nsgroup_id, logging_enabled, rules_db)
security_group_rules_db)
except nsx_exc.ManagerError: except nsx_exc.ManagerError:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
for rule in security_group_rules_db: for rule in rules_db:
super(NsxV3Plugin, self).delete_security_group_rule( super(NsxV3Plugin, self).delete_security_group_rule(
context, rule['id']) context, rule['id'])
security.save_sg_rule_mappings(context.session, rules['rules']) security.save_sg_rule_mappings(context.session, rules['rules'])
return security_group_rules_db return rules_db
def delete_security_group_rule(self, context, id): def delete_security_group_rule(self, context, id):
rule_db = self._get_security_group_rule(context, id) rule_db = self._get_security_group_rule(context, id)

View File

@ -27,8 +27,10 @@ from neutron_lib import constants as const
from vmware_nsx.db import extended_security_group_rule as ext_rule_db 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.extensions import secgroup_rule_local_ip_prefix as ext_loip
from vmware_nsx.nsxlib.v3 import dfw_api as v3_fw
from vmware_nsx.plugins.nsx_v.vshield import securitygroup_utils from vmware_nsx.plugins.nsx_v.vshield import securitygroup_utils
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_nsxv_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
PLUGIN_NAME = ('vmware_nsx.tests.unit.extensions.' PLUGIN_NAME = ('vmware_nsx.tests.unit.extensions.'
@ -46,14 +48,12 @@ class ExtendedRuleTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
def create_security_group_rule(self, context, security_group_rule): def create_security_group_rule(self, context, security_group_rule):
rule = security_group_rule['security_group_rule'] rule = security_group_rule['security_group_rule']
rule['id'] = _uuid()
self._check_local_ip_prefix(context, rule) self._check_local_ip_prefix(context, rule)
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
res = super(ExtendedRuleTestPlugin, res = super(ExtendedRuleTestPlugin,
self).create_security_group_rule( self).create_security_group_rule(
context, security_group_rule) context, security_group_rule)
self._save_extended_rule_properties(context, rule) self._process_security_group_rule_properties(context, res, rule)
self._get_security_group_rule_properties(context, res)
return res return res
@ -111,10 +111,6 @@ class TestNsxVExtendedSGRule(test_nsxv_plugin.NsxVSecurityGroupsTestCase,
plugin = manager.NeutronManager.get_plugin() plugin = manager.NeutronManager.get_plugin()
dest = {'type': 'Ipv4Address', 'value': local_ip_prefix} dest = {'type': 'Ipv4Address', 'value': local_ip_prefix}
def _assert_destination_as_expected(*args, **kwargs):
self.assertEqual(dest, kwargs['destination'])
return sg_utils.get_rule_config(*args, **kwargs)
plugin.nsx_sg_utils.get_rule_config = mock.Mock( plugin.nsx_sg_utils.get_rule_config = mock.Mock(
side_effect=sg_utils.get_rule_config) side_effect=sg_utils.get_rule_config)
super(TestNsxVExtendedSGRule, super(TestNsxVExtendedSGRule,
@ -123,3 +119,20 @@ class TestNsxVExtendedSGRule(test_nsxv_plugin.NsxVSecurityGroupsTestCase,
source=mock.ANY, destination=dest, services=mock.ANY, source=mock.ANY, destination=dest, services=mock.ANY,
name=mock.ANY, applied_to_ids=mock.ANY, flags=mock.ANY, name=mock.ANY, applied_to_ids=mock.ANY, flags=mock.ANY,
logged=mock.ANY) logged=mock.ANY)
class TestNSXv3ExtendedSGRule(test_nsxv3_plugin.NsxV3PluginTestCaseMixin,
LocalIPPrefixExtTestCase):
def test_create_rule_with_local_ip_prefix(self):
local_ip_prefix = '239.255.0.0/16'
dest = v3_fw.get_ip_cidr_reference(local_ip_prefix, v3_fw.IPV4)
with mock.patch.object(v3_fw, 'get_firewall_rule_dict',
side_effect=v3_fw.get_firewall_rule_dict) as mock_rule:
super(TestNSXv3ExtendedSGRule,
self).test_create_rule_with_local_ip_prefix()
mock_rule.assert_called_with(mock.ANY, mock.ANY, dest, mock.ANY,
v3_fw.IPV4, mock.ANY, v3_fw.ALLOW,
mock.ANY)