Support for stateless security groups
Blueprint: stateless-security-groups Change-Id: Iae39a89b762786e4f05aa61aa0db634941806d41
This commit is contained in:
parent
f71b6b361e
commit
cbc473e066
@ -396,17 +396,54 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
||||
# match by interface for bridge input
|
||||
match_interface = '-i %s'
|
||||
match_physdev = '-m physdev --physdev-in %s'
|
||||
# comment to prevent duplicate warnings for different devices using
|
||||
# same bridge. truncate start to remove prefixes
|
||||
comment = '-m comment --comment "Set zone for %s"' % port['device'][4:]
|
||||
port_sg_rules = self._get_port_sg_rules(port)
|
||||
if self._are_sg_rules_stateful(port_sg_rules):
|
||||
# comment to prevent duplicate warnings for different devices using
|
||||
# same bridge. truncate start to remove prefixes
|
||||
comment = 'Set zone for %s' % port['device'][4:]
|
||||
conntrack = '--zone %s' % self.ipconntrack.get_device_zone(port)
|
||||
else:
|
||||
comment = 'Make %s stateless' % port['device'][4:]
|
||||
conntrack = '--notrack'
|
||||
rules = []
|
||||
for dev, match in ((br_dev, match_physdev), (br_dev, match_interface),
|
||||
(port_dev, match_physdev)):
|
||||
match = match % dev
|
||||
rule = '%s %s -j CT --zone %s' % (match, comment, zone)
|
||||
rule = '%s -m comment --comment "%s" -j CT %s' % (match, comment,
|
||||
conntrack)
|
||||
rules.append(rule)
|
||||
return rules
|
||||
|
||||
def _get_port_sg_rules(self, port):
|
||||
port_sg_rules = []
|
||||
if not any(port.get('device_owner', '').startswith(prefix)
|
||||
for prefix in constants.DEVICE_OWNER_PREFIXES):
|
||||
port_sg_ids = port.get('security_groups', [])
|
||||
if port_sg_ids:
|
||||
for rule in self.sg_rules.get(port_sg_ids[0], []):
|
||||
if self.enable_ipset:
|
||||
port_sg_rules.append(rule)
|
||||
break
|
||||
else:
|
||||
port_sg_rules.extend(
|
||||
self._expand_sg_rule_with_remote_ips(
|
||||
rule, port, constants.INGRESS_DIRECTION))
|
||||
if port_sg_rules:
|
||||
break
|
||||
else:
|
||||
port_sg_rules.extend(
|
||||
self._expand_sg_rule_with_remote_ips(
|
||||
rule, port, constants.EGRESS_DIRECTION))
|
||||
if port_sg_rules:
|
||||
break
|
||||
return port_sg_rules
|
||||
|
||||
@staticmethod
|
||||
def _are_sg_rules_stateful(security_group_rules):
|
||||
for rule in security_group_rules:
|
||||
return rule.get('stateful', True)
|
||||
return True
|
||||
|
||||
def _add_conntrack_jump(self, port):
|
||||
for jump_rule in self._get_jump_rules(port):
|
||||
self._add_raw_rule('PREROUTING', jump_rule)
|
||||
|
@ -17,6 +17,7 @@
|
||||
import functools
|
||||
|
||||
from neutron_lib.api.definitions import rbac_security_groups as rbac_sg_apidef
|
||||
from neutron_lib.api.definitions import stateful_security_group as stateful_sg
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
@ -46,6 +47,7 @@ def disable_security_group_extension_by_config(aliases):
|
||||
LOG.info('Disabled security-group extension.')
|
||||
_disable_extension('security-group', aliases)
|
||||
_disable_extension(rbac_sg_apidef.ALIAS, aliases)
|
||||
_disable_extension(stateful_sg.ALIAS, aliases)
|
||||
LOG.info('Disabled allowed-address-pairs extension.')
|
||||
_disable_extension('allowed-address-pairs', aliases)
|
||||
|
||||
|
@ -24,6 +24,7 @@ from neutron_lib.utils import net
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
from neutron.api.rpc.callbacks import resources
|
||||
from neutron.api.rpc.handlers import resources_rpc
|
||||
from neutron.db import securitygroups_rpc_base as sg_rpc_base
|
||||
|
||||
@ -354,3 +355,7 @@ class SecurityGroupServerAPIShim(sg_rpc_base.SecurityGroupInfoAPIMixin):
|
||||
sg_ids = set((sg_id for p in ports.values()
|
||||
for sg_id in p['security_group_ids']))
|
||||
return [(sg_id, ) for sg_id in sg_ids]
|
||||
|
||||
def _is_security_group_stateful(self, context, sg_id):
|
||||
sg = self.rcache.get_resource_by_id(resources.SECURITYGROUP, sg_id)
|
||||
return sg.stateful
|
||||
|
@ -1 +1 @@
|
||||
2217c4222de6
|
||||
18a7e90ae768
|
||||
|
@ -0,0 +1,37 @@
|
||||
# Copyright 2018 NOKIA
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
"""add security group stateful
|
||||
|
||||
Revision ID: 18a7e90ae768
|
||||
Revises: 2217c4222de6
|
||||
Create Date: 2018-04-26 14:44:52.635576
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '18a7e90ae768'
|
||||
down_revision = '2217c4222de6'
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('securitygroups',
|
||||
sa.Column('stateful',
|
||||
sa.Boolean(),
|
||||
server_default=sa.sql.true(),
|
||||
nullable=False))
|
@ -16,6 +16,7 @@ from neutron_lib.db import constants as db_const
|
||||
from neutron_lib.db import model_base
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy import sql
|
||||
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import rbac_db_models
|
||||
@ -28,6 +29,9 @@ class SecurityGroup(standard_attr.HasStandardAttributes, model_base.BASEV2,
|
||||
"""Represents a v2 neutron security group."""
|
||||
|
||||
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE))
|
||||
stateful = sa.Column(sa.Boolean,
|
||||
default=True, server_default=sql.true(),
|
||||
nullable=False)
|
||||
rbac_entries = sa.orm.relationship(rbac_db_models.SecurityGroupRBAC,
|
||||
backref='security_group',
|
||||
lazy='subquery',
|
||||
|
@ -96,6 +96,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
desired_state=s))
|
||||
|
||||
tenant_id = s['tenant_id']
|
||||
stateful = s.get('stateful', True)
|
||||
|
||||
if not default_sg:
|
||||
self._ensure_default_security_group(context, tenant_id)
|
||||
@ -109,7 +110,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
sg = sg_obj.SecurityGroup(
|
||||
context, id=s.get('id') or uuidutils.generate_uuid(),
|
||||
description=s['description'], project_id=tenant_id,
|
||||
name=s['name'], is_default=default_sg)
|
||||
name=s['name'], is_default=default_sg, stateful=stateful)
|
||||
sg.create()
|
||||
|
||||
delta = len(ext_sg.sg_supported_ethertypes)
|
||||
@ -277,6 +278,13 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
@db_api.retry_if_session_inactive()
|
||||
def update_security_group(self, context, id, security_group):
|
||||
s = security_group['security_group']
|
||||
if 'stateful' in s:
|
||||
filters = {'security_group_id': [id]}
|
||||
with db_api.CONTEXT_READER.using(context):
|
||||
ports = self._get_port_security_group_bindings(context,
|
||||
filters)
|
||||
if ports:
|
||||
raise ext_sg.SecurityGroupInUse(id=id)
|
||||
|
||||
kwargs = {
|
||||
'context': context,
|
||||
@ -311,6 +319,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
def _make_security_group_dict(self, security_group, fields=None):
|
||||
res = {'id': security_group['id'],
|
||||
'name': security_group['name'],
|
||||
'stateful': security_group['stateful'],
|
||||
'tenant_id': security_group['tenant_id'],
|
||||
'description': security_group['description']}
|
||||
if security_group.rules:
|
||||
@ -621,6 +630,15 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
tenant_id=rule['tenant_id'])
|
||||
return security_group_id
|
||||
|
||||
@staticmethod
|
||||
def _validate_sgs_for_port(security_groups):
|
||||
if not security_groups:
|
||||
return
|
||||
if not len(set(sg.stateful for sg in security_groups)) == 1:
|
||||
msg = ("Cannot apply both stateful and stateless security "
|
||||
"groups on the same port at the same time")
|
||||
raise ext_sg.SecurityGroupConflict(reason=msg)
|
||||
|
||||
def _validate_security_group_rules(self, context, security_group_rules):
|
||||
sg_id = self._validate_single_tenant_and_group(security_group_rules)
|
||||
for rule in security_group_rules['security_group_rules']:
|
||||
@ -793,15 +811,16 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
return port_res
|
||||
|
||||
def _process_port_create_security_group(self, context, port,
|
||||
security_group_ids):
|
||||
if validators.is_attr_set(security_group_ids):
|
||||
for security_group_id in security_group_ids:
|
||||
security_groups):
|
||||
self._validate_sgs_for_port(security_groups)
|
||||
if validators.is_attr_set(security_groups):
|
||||
for sg in security_groups:
|
||||
self._create_port_security_group_binding(context, port['id'],
|
||||
security_group_id)
|
||||
sg.id)
|
||||
# Convert to list as a set might be passed here and
|
||||
# this has to be serialized
|
||||
port[ext_sg.SECURITYGROUPS] = (security_group_ids and
|
||||
list(security_group_ids) or [])
|
||||
port[ext_sg.SECURITYGROUPS] = ([sg.id for sg in security_groups] if
|
||||
security_groups else [])
|
||||
|
||||
def _get_default_sg_id(self, context, tenant_id):
|
||||
default_group = sg_obj.DefaultSecurityGroup.get_object(
|
||||
@ -844,7 +863,8 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
def _get_security_groups_on_port(self, context, port):
|
||||
"""Check that all security groups on port belong to tenant.
|
||||
|
||||
:returns: all security groups IDs on port belonging to tenant.
|
||||
:returns: all security groups on port belonging to tenant)
|
||||
|
||||
"""
|
||||
port = port['port']
|
||||
if not validators.is_attr_set(port.get(ext_sg.SECURITYGROUPS)):
|
||||
@ -869,7 +889,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
if port_sg_missing:
|
||||
raise ext_sg.SecurityGroupNotFound(id=', '.join(port_sg_missing))
|
||||
|
||||
return list(requested_groups)
|
||||
return sg_objs
|
||||
|
||||
def _ensure_default_security_group_on_port(self, context, port):
|
||||
# we don't apply security groups for dhcp, router
|
||||
@ -920,13 +940,13 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
|
||||
original_port.get(ext_sg.SECURITYGROUPS),
|
||||
port_updates[ext_sg.SECURITYGROUPS])):
|
||||
# delete the port binding and read it with the new rules
|
||||
port_updates[ext_sg.SECURITYGROUPS] = (
|
||||
self._get_security_groups_on_port(context, port))
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
port_updates[ext_sg.SECURITYGROUPS] = [sg.id for sg in sgs]
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
self._process_port_create_security_group(
|
||||
context,
|
||||
updated_port,
|
||||
port_updates[ext_sg.SECURITYGROUPS])
|
||||
sgs)
|
||||
need_notify = True
|
||||
else:
|
||||
updated_port[ext_sg.SECURITYGROUPS] = (
|
||||
|
@ -27,6 +27,7 @@ from neutron.db.models import securitygroup as sg_models
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import securitygroups_db as sg_db
|
||||
from neutron.extensions import securitygroup as ext_sg
|
||||
from neutron.objects import securitygroup as sg_obj
|
||||
|
||||
|
||||
DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix',
|
||||
@ -189,9 +190,12 @@ class SecurityGroupInfoAPIMixin(object):
|
||||
remote_security_group_info[remote_gid][ethertype] = set()
|
||||
|
||||
direction = rule_in_db['direction']
|
||||
stateful = self._is_security_group_stateful(context,
|
||||
security_group_id)
|
||||
rule_dict = {
|
||||
'direction': direction,
|
||||
'ethertype': ethertype}
|
||||
'ethertype': ethertype,
|
||||
'stateful': stateful}
|
||||
|
||||
for key in ('protocol', 'port_range_min', 'port_range_max',
|
||||
'remote_ip_prefix', 'remote_group_id'):
|
||||
@ -351,6 +355,14 @@ class SecurityGroupInfoAPIMixin(object):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _is_security_group_stateful(self, context, sg_id):
|
||||
"""Return whether the security group is stateful or not.
|
||||
|
||||
Return True if the security group associated with the given ID
|
||||
is stateful, else False.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
class SecurityGroupServerRpcMixin(SecurityGroupInfoAPIMixin,
|
||||
SecurityGroupServerNotifierRpcMixin):
|
||||
@ -415,3 +427,7 @@ class SecurityGroupServerRpcMixin(SecurityGroupInfoAPIMixin,
|
||||
if allowed_addr_ip:
|
||||
ips_by_group[security_group_id].add(allowed_addr_ip)
|
||||
return ips_by_group
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def _is_security_group_stateful(self, context, sg_id):
|
||||
return sg_obj.SecurityGroup.get_sg_by_id(context, sg_id).stateful
|
||||
|
22
neutron/extensions/stateful_security_group.py
Normal file
22
neutron/extensions/stateful_security_group.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2018 Nokia. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from neutron_lib.api.definitions import stateful_security_group
|
||||
from neutron_lib.api import extensions
|
||||
|
||||
|
||||
class Stateful_security_group(extensions.APIExtensionDescriptor):
|
||||
"""Extension class supporting stateful security group."""
|
||||
|
||||
api_definition = stateful_security_group
|
@ -35,7 +35,8 @@ class SecurityGroupRBAC(rbac.RBACBaseObject):
|
||||
class SecurityGroup(rbac_db.NeutronRbacObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Add RBAC support
|
||||
VERSION = '1.1'
|
||||
# Version 1.2: Added stateful support
|
||||
VERSION = '1.2'
|
||||
|
||||
# required by RbacNeutronMetaclass
|
||||
rbac_db_cls = SecurityGroupRBAC
|
||||
@ -46,6 +47,7 @@ class SecurityGroup(rbac_db.NeutronRbacObject):
|
||||
'name': obj_fields.StringField(nullable=True),
|
||||
'project_id': obj_fields.StringField(nullable=True),
|
||||
'shared': obj_fields.BooleanField(default=False),
|
||||
'stateful': obj_fields.BooleanField(default=True),
|
||||
'is_default': obj_fields.BooleanField(default=False),
|
||||
'rules': obj_fields.ListOfObjectsField(
|
||||
'SecurityGroupRule', nullable=True
|
||||
@ -83,10 +85,16 @@ class SecurityGroup(rbac_db.NeutronRbacObject):
|
||||
bool(db_obj.get('default_security_group')))
|
||||
self.obj_reset_changes(['is_default'])
|
||||
|
||||
@classmethod
|
||||
def get_sg_by_id(cls, context, sg_id):
|
||||
return super(SecurityGroup, cls).get_object(context, id=sg_id)
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
_target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
if _target_version < (1, 1):
|
||||
primitive.pop('shared')
|
||||
if _target_version < (1, 2):
|
||||
primitive.pop('stateful')
|
||||
|
||||
@classmethod
|
||||
def get_bound_tenant_ids(cls, context, obj_id):
|
||||
|
@ -45,6 +45,7 @@ from neutron_lib.api.definitions import portbindings_extended as pbe_ext
|
||||
from neutron_lib.api.definitions import provider_net
|
||||
from neutron_lib.api.definitions import rbac_security_groups as rbac_sg_apidef
|
||||
from neutron_lib.api.definitions import security_groups_port_filtering
|
||||
from neutron_lib.api.definitions import stateful_security_group
|
||||
from neutron_lib.api.definitions import subnet as subnet_def
|
||||
from neutron_lib.api.definitions import subnet_onboard as subnet_onboard_def
|
||||
from neutron_lib.api.definitions import subnetpool_prefix_ops \
|
||||
@ -203,7 +204,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
pbe_ext.ALIAS,
|
||||
agent_resources_synced.ALIAS,
|
||||
subnet_onboard_def.ALIAS,
|
||||
subnetpool_prefix_ops_def.ALIAS]
|
||||
subnetpool_prefix_ops_def.ALIAS,
|
||||
stateful_security_group.ALIAS]
|
||||
|
||||
# List of agent types for which all binding_failed ports should try to be
|
||||
# rebound when agent revive
|
||||
@ -1373,8 +1375,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
self._portsec_ext_port_create_processing(context, result, port)
|
||||
|
||||
# sgids must be got after portsec checked with security group
|
||||
sgids = self._get_security_groups_on_port(context, port)
|
||||
self._process_port_create_security_group(context, result, sgids)
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
self._process_port_create_security_group(context, result, sgs)
|
||||
network = self.get_network(context, result['network_id'])
|
||||
binding = db.add_port_binding(context, result['id'])
|
||||
mech_context = driver_context.PortContext(self, context, result,
|
||||
@ -1425,11 +1427,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
return bound_context.current
|
||||
|
||||
def _ensure_security_groups_on_port(self, context, port_dict):
|
||||
port_compat = {'port': port_dict}
|
||||
sgids = self._get_security_groups_on_port(context, port_compat)
|
||||
self._process_port_create_security_group(context, port_dict, sgids)
|
||||
|
||||
@utils.transaction_guard
|
||||
@db_api.retry_if_session_inactive()
|
||||
def create_port_bulk(self, context, ports):
|
||||
@ -1464,7 +1461,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
const.PORT_STATUS_ACTIVE),
|
||||
device_id=pdata.get('device_id'),
|
||||
device_owner=pdata.get('device_owner'),
|
||||
security_group_ids=security_group_ids,
|
||||
description=pdata.get('description'))
|
||||
|
||||
# Ensure that the networks exist.
|
||||
@ -1527,18 +1523,16 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
process_extensions=False)
|
||||
port_dict[portbindings.HOST_ID] = pdata.get(
|
||||
portbindings.HOST_ID)
|
||||
port_compat = {'port': port_dict}
|
||||
|
||||
# Activities immediately post-port-creation
|
||||
self.extension_manager.process_create_port(context, pdata,
|
||||
port_dict)
|
||||
self._portsec_ext_port_create_processing(context, port_dict,
|
||||
port_compat)
|
||||
port)
|
||||
|
||||
# Ensure the default security group is assigned, unless one was
|
||||
# specifically requested
|
||||
if security_group_ids is None:
|
||||
self._ensure_security_groups_on_port(context, port_dict)
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
self._process_port_create_security_group(context, port_dict,
|
||||
sgs)
|
||||
|
||||
# process port binding
|
||||
binding = db.add_port_binding(context, port_dict['id'])
|
||||
@ -1554,7 +1548,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
# process allowed address pairs
|
||||
db_port_obj[addr_apidef.ADDRESS_PAIRS] = (
|
||||
self._process_create_allowed_address_pairs(
|
||||
context, port_compat,
|
||||
context, port_dict,
|
||||
port_dict.get(addr_apidef.ADDRESS_PAIRS)))
|
||||
|
||||
# handle DHCP setup
|
||||
@ -1582,13 +1576,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
# Perform actions after the transaction is committed
|
||||
completed_ports = []
|
||||
for port in port_data:
|
||||
# Ensure security groups are assigned to the port, if
|
||||
# specifically requested
|
||||
port_dict = port['port_dict']
|
||||
if port_dict.get('security_group_ids') is not None:
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
self._ensure_security_groups_on_port(context, port_dict)
|
||||
|
||||
resource_extend.apply_funcs('ports',
|
||||
port['port_dict'],
|
||||
port['port_obj'].db_obj)
|
||||
|
@ -61,6 +61,7 @@ NETWORK_API_EXTENSIONS+=",standard-attr-revisions"
|
||||
NETWORK_API_EXTENSIONS+=",standard-attr-segment"
|
||||
NETWORK_API_EXTENSIONS+=",standard-attr-timestamp"
|
||||
NETWORK_API_EXTENSIONS+=",standard-attr-tag"
|
||||
NETWORK_API_EXTENSIONS+=",stateful-security-group"
|
||||
NETWORK_API_EXTENSIONS+=",subnet_allocation"
|
||||
NETWORK_API_EXTENSIONS+=",subnet-dns-publish-fixed-ip"
|
||||
NETWORK_API_EXTENSIONS+=",tag-ports-during-bulk-creation"
|
||||
|
@ -513,13 +513,16 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
ctx, devices=devices)
|
||||
expected = {
|
||||
'security_groups': {sg1_id: [
|
||||
{'direction': 'egress', 'ethertype': const.IPv4},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6},
|
||||
{'direction': 'egress', 'ethertype': const.IPv4,
|
||||
'stateful': True},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'stateful': True},
|
||||
{'direction': u'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv4,
|
||||
'port_range_max': 25, 'port_range_min': 24,
|
||||
'remote_group_id': sg2_id}
|
||||
'remote_group_id': sg2_id,
|
||||
'stateful': True}
|
||||
]},
|
||||
'sg_member_ips': {sg2_id: {
|
||||
'IPv4': set([port_ip2]),
|
||||
@ -628,13 +631,16 @@ class SGServerRpcCallBackTestCase(test_sg.SecurityGroupDBTestCase):
|
||||
ctx, devices=devices)
|
||||
expected = {
|
||||
'security_groups': {sg1_id: [
|
||||
{'direction': 'egress', 'ethertype': const.IPv4},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6},
|
||||
{'direction': 'egress', 'ethertype': const.IPv4,
|
||||
'stateful': True},
|
||||
{'direction': 'egress', 'ethertype': const.IPv6,
|
||||
'stateful': True},
|
||||
{'direction': u'ingress',
|
||||
'protocol': const.PROTO_NAME_TCP,
|
||||
'ethertype': const.IPv6,
|
||||
'port_range_max': 22, 'port_range_min': 22,
|
||||
'remote_group_id': sg1_id}
|
||||
'remote_group_id': sg1_id,
|
||||
'stateful': True}
|
||||
]},
|
||||
'sg_member_ips': {sg1_id: {
|
||||
'IPv6': set(),
|
||||
|
@ -97,7 +97,7 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase):
|
||||
def test_update_security_group_conflict(self):
|
||||
with mock.patch.object(registry, "notify") as mock_notify:
|
||||
mock_notify.side_effect = exceptions.CallbackFailure(Exception())
|
||||
secgroup = {'security_group': mock.ANY}
|
||||
secgroup = {'security_group': FAKE_SECGROUP}
|
||||
with testtools.ExpectedException(
|
||||
securitygroup.SecurityGroupConflict):
|
||||
self.mixin.update_security_group(self.ctx, 'foo_id', secgroup)
|
||||
@ -272,6 +272,7 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase):
|
||||
'project_id': FAKE_SECGROUP['security_group']['tenant_id'],
|
||||
'name': 'default',
|
||||
'description': 'Default security group',
|
||||
'stateful': mock.ANY,
|
||||
'security_group_rules': [
|
||||
# Four rules for egress/ingress and ipv4/ipv6
|
||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY,
|
||||
|
@ -92,8 +92,6 @@ class PortSecurityTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
def create_port(self, context, port):
|
||||
p = port['port']
|
||||
p[ext_sg.SECURITYGROUPS] = self._get_security_groups_on_port(
|
||||
context, port)
|
||||
neutron_db = super(PortSecurityTestPlugin, self).create_port(
|
||||
context, port)
|
||||
p.update(neutron_db)
|
||||
@ -111,9 +109,12 @@ class PortSecurityTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
if has_ip and port_security:
|
||||
self._ensure_default_security_group_on_port(context, port)
|
||||
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
p[ext_sg.SECURITYGROUPS] = [sg['id'] for sg in sgs] if sgs else None
|
||||
|
||||
if (p.get(ext_sg.SECURITYGROUPS) and p[psec.PORTSECURITY]):
|
||||
self._process_port_create_security_group(
|
||||
context, p, p[ext_sg.SECURITYGROUPS])
|
||||
context, p, sgs)
|
||||
|
||||
return port['port']
|
||||
|
||||
@ -156,11 +157,11 @@ class PortSecurityTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
if (delete_security_groups or has_security_groups):
|
||||
# delete the port binding and read it with the new rules.
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
sgids = self._get_security_groups_on_port(context, port)
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
# process port create sec groups needs port id
|
||||
port['id'] = id
|
||||
self._process_port_create_security_group(context,
|
||||
ret_port, sgids)
|
||||
ret_port, sgs)
|
||||
|
||||
if psec.PORTSECURITY in port['port']:
|
||||
self._process_port_port_security_update(
|
||||
|
@ -210,24 +210,24 @@ class SecurityGroupTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
if not validators.is_attr_set(port['port'].get(ext_sg.SECURITYGROUPS)):
|
||||
port['port'][ext_sg.SECURITYGROUPS] = [default_sg]
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
sgids = self._get_security_groups_on_port(context, port)
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
port = super(SecurityGroupTestPlugin, self).create_port(context,
|
||||
port)
|
||||
self._process_port_create_security_group(context, port,
|
||||
sgids)
|
||||
sgs)
|
||||
return port
|
||||
|
||||
def update_port(self, context, id, port):
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
if ext_sg.SECURITYGROUPS in port['port']:
|
||||
port['port'][ext_sg.SECURITYGROUPS] = (
|
||||
self._get_security_groups_on_port(context, port))
|
||||
sgs = self._get_security_groups_on_port(context, port)
|
||||
port['port'][ext_sg.SECURITYGROUPS] = [
|
||||
sg['id'] for sg in sgs] if sgs else None
|
||||
# delete the port binding and read it with the new rules
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
port['port']['id'] = id
|
||||
self._process_port_create_security_group(
|
||||
context, port['port'],
|
||||
port['port'].get(ext_sg.SECURITYGROUPS))
|
||||
context, port['port'], sgs)
|
||||
port = super(SecurityGroupTestPlugin, self).update_port(
|
||||
context, id, port)
|
||||
return port
|
||||
|
@ -98,7 +98,7 @@ object_data = {
|
||||
'RouterL3AgentBinding': '1.0-c5ba6c95e3a4c1236a55f490cd67da82',
|
||||
'RouterPort': '1.0-c8c8f499bcdd59186fcd83f323106908',
|
||||
'RouterRoute': '1.0-07fc5337c801fb8c6ccfbcc5afb45907',
|
||||
'SecurityGroup': '1.1-f712265418f154f7c080e02857ffe2ef',
|
||||
'SecurityGroup': '1.2-7b63b834e511856f54a09282d6843ecc',
|
||||
'SecurityGroupPortBinding': '1.0-6879d5c0af80396ef5a72934b6a6ef20',
|
||||
'SecurityGroupRBAC': '1.0-192845c5ed0718e1c54fac36936fcd7d',
|
||||
'SecurityGroupRule': '1.0-e9b8dace9d48b936c62ad40fe1f339d5',
|
||||
|
@ -80,6 +80,16 @@ class SecurityGroupDbObjTestCase(test_base.BaseDbObjectTestCase,
|
||||
# generated; we picked the former here
|
||||
rule['remote_group_id'] = None
|
||||
|
||||
def _create_test_security_group(self):
|
||||
self.objs[0].create()
|
||||
return self.objs[0]
|
||||
|
||||
def test_object_version_degradation_1_2_to_1_1_no_stateful(self):
|
||||
sg_stateful_obj = self._create_test_security_group()
|
||||
sg_no_stateful_obj = sg_stateful_obj.obj_to_primitive('1.1')
|
||||
self.assertNotIn('stateful',
|
||||
sg_no_stateful_obj['versioned_object.data'])
|
||||
|
||||
def test_is_default_True(self):
|
||||
fields = self.obj_fields[0].copy()
|
||||
sg_obj = self._make_object(fields)
|
||||
|
@ -56,7 +56,12 @@ class BaseTestEventHandler(object):
|
||||
get_sec_group_port_patch = mock.patch(
|
||||
'neutron.db.securitygroups_db.SecurityGroupDbMixin.'
|
||||
'_get_security_groups_on_port')
|
||||
|
||||
get_sec_group_port_patch.start()
|
||||
process_port_create_security_group_patch = mock.patch(
|
||||
'neutron.db.securitygroups_db.SecurityGroupDbMixin.'
|
||||
'_process_port_create_security_group')
|
||||
process_port_create_security_group_patch.start()
|
||||
handler_patch = mock.patch(
|
||||
'neutron.quota.resource.TrackedResource._db_event_handler')
|
||||
self.handler_mock = handler_patch.start()
|
||||
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
prelude: >
|
||||
Added support to create stateless security groups.
|
||||
features:
|
||||
- |
|
||||
Added support for a new stateful-security-group api extension that
|
||||
implements stateless security groups for the iptables drivers.
|
||||
upgrade:
|
||||
- |
|
||||
Currently existing security groups will all be set to stateful during
|
||||
the alembic migration.
|
||||
security:
|
||||
- |
|
||||
The ``stateless security group`` feature does not work with
|
||||
OVS nor OVN driver as the driver is not aware of the ``stateful`` attribute
|
||||
in the security group. If ``stateful`` attribute is provided with a
|
||||
``False`` value then the attribute value is ignored and the security
|
||||
group would behave as stateful.
|
Loading…
Reference in New Issue
Block a user