From 82660993e7c63bb1934f1d33463fd06f8585e41e Mon Sep 17 00:00:00 2001 From: Roey Chen Date: Sun, 13 Mar 2016 06:33:20 -0700 Subject: [PATCH] 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 --- vmware_nsx/db/extended_security_group_rule.py | 63 ++++++++++++------- .../alembic_migrations/versions/CONTRACT_HEAD | 2 +- ...f0e396d7_nsx_extended_rule_table_rename.py | 32 ++++++++++ vmware_nsx/db/nsxv_models.py | 21 ------- vmware_nsx/nsxlib/v3/security.py | 12 +++- vmware_nsx/plugins/nsx_v/plugin.py | 14 ++--- vmware_nsx/plugins/nsx_v3/plugin.py | 28 ++++++--- .../test_secgroup_rule_local_ip_prefix.py | 27 +++++--- 8 files changed, 128 insertions(+), 71 deletions(-) create mode 100644 vmware_nsx/db/migration/alembic_migrations/versions/newton/contract/081af0e396d7_nsx_extended_rule_table_rename.py diff --git a/vmware_nsx/db/extended_security_group_rule.py b/vmware_nsx/db/extended_security_group_rule.py index b4b93306bd..a91c459d5d 100644 --- a/vmware_nsx/db/extended_security_group_rule.py +++ b/vmware_nsx/db/extended_security_group_rule.py @@ -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 diff --git a/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD b/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD index 3af6dcfef2..24479e836c 100644 --- a/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD +++ b/vmware_nsx/db/migration/alembic_migrations/versions/CONTRACT_HEAD @@ -1 +1 @@ -5ed1ffbc0d2a +081af0e396d7 diff --git a/vmware_nsx/db/migration/alembic_migrations/versions/newton/contract/081af0e396d7_nsx_extended_rule_table_rename.py b/vmware_nsx/db/migration/alembic_migrations/versions/newton/contract/081af0e396d7_nsx_extended_rule_table_rename.py new file mode 100644 index 0000000000..d840246a4a --- /dev/null +++ b/vmware_nsx/db/migration/alembic_migrations/versions/newton/contract/081af0e396d7_nsx_extended_rule_table_rename.py @@ -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') diff --git a/vmware_nsx/db/nsxv_models.py b/vmware_nsx/db/nsxv_models.py index 29153bea35..2d4b432d86 100644 --- a/vmware_nsx/db/nsxv_models.py +++ b/vmware_nsx/db/nsxv_models.py @@ -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')) diff --git a/vmware_nsx/nsxlib/v3/security.py b/vmware_nsx/nsxlib/v3/security.py index 223927d523..a2065816b6 100644 --- a/vmware_nsx/nsxlib/v3/security.py +++ b/vmware_nsx/nsxlib/v3/security.py @@ -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 diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 43ac4b8809..5228d73551 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -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 diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 9f0d57e57d..453c64433c 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -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( - context, security_group_rules)) - sg_id = security_group_rules_db[0]['security_group_id'] + 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)) + 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) diff --git a/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py b/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py index 8abdbf20db..55d3538ecc 100644 --- a/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py +++ b/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py @@ -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)