Support for stateless security groups

Add support for stateful attribute of security groups,
allowing a user to create security groups with stateful
false.

Change-Id: I380b2ab0fa4f81f676711d97138c6097c4790cd7
Blueprint: stateless-security-groups
This commit is contained in:
Tom Stappaerts 2020-03-03 10:55:13 +01:00 committed by Monty Taylor
parent a0e68c318f
commit 51224f04e1
8 changed files with 67 additions and 8 deletions

View File

@ -388,6 +388,9 @@ class Normalizer(object):
ret['description'] = group.pop('description')
ret['properties'] = group
if self._use_neutron_secgroups():
ret['stateful'] = group.pop('stateful', True)
# Backwards compat with Neutron
if not self.strict_mode:
ret['tenant_id'] = project_id

View File

@ -115,7 +115,8 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
return self._normalize_secgroup(
self._get_and_munchify('security_group', data))
def create_security_group(self, name, description, project_id=None):
def create_security_group(self, name, description,
project_id=None, stateful=None):
"""Create a new security group
:param string name: A name for the security group.
@ -123,6 +124,7 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
:param string project_id:
Specify the project ID this security group will be created
on (admin-only).
:param string stateful: Whether the security group is stateful or not.
:returns: A ``munch.Munch`` representing the new security group.
@ -141,6 +143,8 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
security_group_json = {
'name': name, 'description': description
}
if stateful is not None:
security_group_json['stateful'] = stateful
if project_id is not None:
security_group_json['tenant_id'] = project_id
if self._use_neutron_secgroups():
@ -188,7 +192,7 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
'/os-security-groups/{id}'.format(id=secgroup['id'])))
return True
@_utils.valid_kwargs('name', 'description')
@_utils.valid_kwargs('name', 'description', 'stateful')
def update_security_group(self, name_or_id, **kwargs):
"""Update a security group

View File

@ -27,7 +27,7 @@ class SecurityGroup(_base.NetworkResource, resource.TagMixin):
allow_list = True
_query_mapping = resource.QueryParameters(
'description', 'fields', 'name', 'project_id', 'tenant_id',
'description', 'fields', 'name', 'stateful', 'project_id', 'tenant_id',
'revision_number', 'sort_dir', 'sort_key',
**resource.TagMixin._tag_query_parameters
)
@ -39,6 +39,8 @@ class SecurityGroup(_base.NetworkResource, resource.TagMixin):
description = resource.Body('description')
#: The security group name.
name = resource.Body('name')
#: Whether the security group is stateful or not.
stateful = resource.Body('stateful')
#: The ID of the project this security group is associated with.
project_id = resource.Body('project_id')
#: A list of

View File

@ -402,7 +402,7 @@ class FakeMachinePort(object):
def make_fake_neutron_security_group(
id, name, description, rules, project_id=None):
id, name, description, rules, stateful=True, project_id=None):
if not rules:
rules = []
if not project_id:
@ -411,6 +411,7 @@ def make_fake_neutron_security_group(
'id': id,
'name': name,
'description': description,
'stateful': stateful,
'project_id': project_id,
'tenant_id': project_id,
'security_group_rules': rules,

View File

@ -698,8 +698,11 @@ class TestNormalize(base.TestCase):
cloud='_test_cloud_'))
]
)
# Set secgroup source to nova for this test as stateful parameter
# is only valid for neutron security groups.
self.cloud.secgroup_source = 'nova'
retval = self.cloud._normalize_secgroup(nova_secgroup)
self.cloud.secgroup_source = 'neutron'
self.assertEqual(expected, retval)
def test_normalize_secgroups_negone_port(self):
@ -1239,7 +1242,11 @@ class TestStrictNormalize(base.TestCase):
]
)
# Set secgroup source to nova for this test as stateful parameter
# is only valid for neutron security groups.
self.cloud.secgroup_source = 'nova'
retval = self.cloud._normalize_secgroup(nova_secgroup)
self.cloud.secgroup_source = 'neutron'
self.assertEqual(expected, retval)
def test_normalize_volumes_v1(self):

View File

@ -176,6 +176,7 @@ class TestSecurityGroups(base.TestCase):
r = self.cloud.create_security_group(group_name, group_desc)
self.assertEqual(group_name, r['name'])
self.assertEqual(group_desc, r['description'])
self.assertEqual(True, r['stateful'])
self.assert_calls()
@ -216,6 +217,37 @@ class TestSecurityGroups(base.TestCase):
self.assert_calls()
def test_create_security_group_stateless_neutron(self):
self.cloud.secgroup_source = 'neutron'
group_name = self.getUniqueString()
group_desc = self.getUniqueString('description')
new_group = fakes.make_fake_neutron_security_group(
id='2',
name=group_name,
description=group_desc,
stateful=False,
rules=[])
self.register_uris([
dict(method='POST',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups']),
json={'security_group': new_group},
validate=dict(
json={'security_group': {
'name': group_name,
'description': group_desc,
'stateful': False
}}))
])
r = self.cloud.create_security_group(group_name, group_desc,
stateful=False)
self.assertEqual(group_name, r['name'])
self.assertEqual(group_desc, r['description'])
self.assertEqual(False, r['stateful'])
self.assert_calls()
def test_create_security_group_nova(self):
group_name = self.getUniqueString()
self.has_neutron = False
@ -257,6 +289,7 @@ class TestSecurityGroups(base.TestCase):
sg_id = neutron_grp_dict['id']
update_return = neutron_grp_dict.copy()
update_return['name'] = new_name
update_return['stateful'] = False
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
@ -269,10 +302,12 @@ class TestSecurityGroups(base.TestCase):
append=['v2.0', 'security-groups', '%s' % sg_id]),
json={'security_group': update_return},
validate=dict(json={
'security_group': {'name': new_name}}))
'security_group': {'name': new_name, 'stateful': False}}))
])
r = self.cloud.update_security_group(sg_id, name=new_name)
r = self.cloud.update_security_group(sg_id, name=new_name,
stateful=False)
self.assertEqual(r['name'], new_name)
self.assertEqual(r['stateful'], False)
self.assert_calls()
def test_update_security_group_nova(self):
@ -793,6 +828,7 @@ class TestSecurityGroups(base.TestCase):
self.assertEqual(neutron_grp_dict['name'], ret_sg['name'])
self.assertEqual(neutron_grp_dict['description'],
ret_sg['description'])
self.assertEqual(neutron_grp_dict['stateful'], ret_sg['stateful'])
self.assert_calls()
def test_get_security_group_by_id_nova(self):

View File

@ -54,6 +54,7 @@ EXAMPLE = {
'description': '1',
'id': IDENTIFIER,
'name': '2',
'stateful': True,
'revision_number': 3,
'security_group_rules': RULES,
'tenant_id': '4',
@ -89,7 +90,8 @@ class TestSecurityGroup(base.TestCase):
'sort_dir': 'sort_dir',
'sort_key': 'sort_key',
'tags': 'tags',
'tenant_id': 'tenant_id'
'tenant_id': 'tenant_id',
'stateful': 'stateful',
},
sot._query_mapping._mapping)

View File

@ -0,0 +1,4 @@
---
features:
- New stateful parameter can be used in security group