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
# License for the specific language governing permissions and limitations
# 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.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_lib import exceptions as nexception
from vmware_nsx._i18n import _
from vmware_nsx.db import nsxv_models
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_local_ip
class NotIngressRule(nexception.BadRequest):
@ -28,41 +31,55 @@ class NotIngressRule(nexception.BadRequest):
"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):
def _check_local_ip_prefix(self, context, rule):
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':
raise NotIngressRule()
return rule_specify_local_ip_prefix
def _save_extended_rule_properties(self, context, rule):
if not attr.is_attr_set(rule.get(ext_loip.LOCAL_IP_PREFIX)):
def _process_security_group_rule_properties(self, context,
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
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):
try:
properties = (context.session.query(
nsxv_models.NsxvExtendedSecurityGroupRuleProperties).filter_by(
rule_id=sgr['id']).one())
except exc.NoResultFound:
sgr[ext_loip.LOCAL_IP_PREFIX] = None
else:
sgr[ext_loip.LOCAL_IP_PREFIX] = properties.local_ip_prefix
return sgr
with context.session.begin(subtransactions=True):
properties = NsxExtendedSecurityGroupRuleProperties(
rule_id=rule_res['id'],
local_ip_prefix=rule_req[ext_local_ip.LOCAL_IP_PREFIX])
context.session.add(properties)
rule_res[ext_local_ip.LOCAL_IP_PREFIX] = (
rule_req[ext_local_ip.LOCAL_IP_PREFIX])
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
ext_sg.SECURITYGROUPRULES, ['_extend_security_group_rule_with_params'])
def _extend_security_group_rule_with_params(self, sg_rule_res, sg_rule_db):
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)
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 model_base
from neutron.db import models_v2
from neutron.db import securitygroups_db
from vmware_nsx.common import nsxv_constants
@ -328,23 +327,3 @@ class NsxvSubnetExtAttributes(model_base.BASEV2):
models_v2.Subnet,
backref=orm.backref("nsxv_subnet_attributes", lazy='joined',
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 utils
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.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()
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
local_group = firewall.get_nsgroup_reference(nsgroup_id)
if sg_rule['remote_ip_prefix'] is not None:
source = firewall.get_ip_cidr_reference(sg_rule['remote_ip_prefix'],
ip_protocol)
destination = local_group
destination = local_ip_prefix or local_group
else:
if 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:
source, destination = destination, source

View File

@ -80,7 +80,7 @@ from vmware_nsx.extensions import (
vnicindex as ext_vnic_idx)
from vmware_nsx.extensions import dns_search_domain as ext_dns_search_domain
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.plugins.nsx_v import managers
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
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(
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(
remote_nsx_sg_id, rule['remote_ip_prefix'])
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:
rule = r['security_group_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()
ruleids.add(rule['id'])
nsx_rules.append(self._create_nsx_rule(
@ -2204,10 +2204,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
nsxv_db.add_neutron_nsx_rule_mapping(
context.session, neutron_rule_id, nsx_rule_id)
for i, r in enumerate(sg_rules):
rule = r['security_group_rule']
self._save_extended_rule_properties(context, rule)
self._get_security_group_rule_properties(context,
new_rule_list[i])
self._process_security_group_rule_properties(
context, new_rule_list[i], r['security_group_rule'])
except Exception:
with excutils.save_and_reraise_exception():
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.db import db as nsx_db
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.extensions import securitygrouplogging as sg_logging
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,
db_base_plugin_v2.NeutronDbPluginV2,
extend_sg_rule.ExtendedSecurityGroupRuleMixin,
securitygroups_db.SecurityGroupDbMixin,
extended_security_group.ExtendedSecurityGroupPropertiesMixin,
external_net_db.External_net_db_mixin,
@ -111,6 +113,7 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
"dhcp_agent_scheduler",
"ext-gw-mode",
"security-group",
"secgroup-rule-local-ip-prefix",
"port-security",
"provider",
"external-net",
@ -1827,25 +1830,32 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin,
return self.create_security_group_rule_bulk(context, bulk_rule)[0]
def create_security_group_rule_bulk(self, context, security_group_rules):
security_group_rules_db = (
super(NsxV3Plugin, self).create_security_group_rule_bulk_native(
sg_rules = security_group_rules['security_group_rules']
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))
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,
sg_id)
logging_enabled = (cfg.CONF.nsx_v3.log_security_groups_allowed_traffic
or self._is_security_group_logged(context, sg_id))
try:
rules = security.create_firewall_rules(context, section_id,
nsgroup_id, logging_enabled,
security_group_rules_db)
rules = security.create_firewall_rules(
context, section_id, nsgroup_id, logging_enabled, rules_db)
except nsx_exc.ManagerError:
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(
context, rule['id'])
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):
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.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.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.'
@ -46,14 +48,12 @@ class ExtendedRuleTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
def create_security_group_rule(self, context, security_group_rule):
rule = security_group_rule['security_group_rule']
rule['id'] = _uuid()
self._check_local_ip_prefix(context, rule)
with context.session.begin(subtransactions=True):
res = super(ExtendedRuleTestPlugin,
self).create_security_group_rule(
context, security_group_rule)
self._save_extended_rule_properties(context, rule)
self._get_security_group_rule_properties(context, res)
self._process_security_group_rule_properties(context, res, rule)
return res
@ -111,10 +111,6 @@ class TestNsxVExtendedSGRule(test_nsxv_plugin.NsxVSecurityGroupsTestCase,
plugin = manager.NeutronManager.get_plugin()
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(
side_effect=sg_utils.get_rule_config)
super(TestNsxVExtendedSGRule,
@ -123,3 +119,20 @@ class TestNsxVExtendedSGRule(test_nsxv_plugin.NsxVSecurityGroupsTestCase,
source=mock.ANY, destination=dest, services=mock.ANY,
name=mock.ANY, applied_to_ids=mock.ANY, flags=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)