From 51224f04e1c6af997996dee709b1fc478b56dc18 Mon Sep 17 00:00:00 2001 From: Tom Stappaerts Date: Tue, 3 Mar 2020 10:55:13 +0100 Subject: [PATCH] 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 --- openstack/cloud/_normalize.py | 3 ++ openstack/cloud/_security_group.py | 8 +++- openstack/network/v2/security_group.py | 4 +- openstack/tests/fakes.py | 3 +- openstack/tests/unit/cloud/test_normalize.py | 9 ++++- .../tests/unit/cloud/test_security_groups.py | 40 ++++++++++++++++++- .../unit/network/v2/test_security_group.py | 4 +- ...teful-security-group-f32a78b9bbb49874.yaml | 4 ++ 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/stateful-security-group-f32a78b9bbb49874.yaml diff --git a/openstack/cloud/_normalize.py b/openstack/cloud/_normalize.py index f13673d56..c44283be6 100644 --- a/openstack/cloud/_normalize.py +++ b/openstack/cloud/_normalize.py @@ -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 diff --git a/openstack/cloud/_security_group.py b/openstack/cloud/_security_group.py index 7142e69b1..1e31e659b 100644 --- a/openstack/cloud/_security_group.py +++ b/openstack/cloud/_security_group.py @@ -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 diff --git a/openstack/network/v2/security_group.py b/openstack/network/v2/security_group.py index 6b772f639..5c9cc2d97 100644 --- a/openstack/network/v2/security_group.py +++ b/openstack/network/v2/security_group.py @@ -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 diff --git a/openstack/tests/fakes.py b/openstack/tests/fakes.py index fc3033072..9fae3c7f5 100644 --- a/openstack/tests/fakes.py +++ b/openstack/tests/fakes.py @@ -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, diff --git a/openstack/tests/unit/cloud/test_normalize.py b/openstack/tests/unit/cloud/test_normalize.py index 86db56771..c0f9fbe5f 100644 --- a/openstack/tests/unit/cloud/test_normalize.py +++ b/openstack/tests/unit/cloud/test_normalize.py @@ -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): diff --git a/openstack/tests/unit/cloud/test_security_groups.py b/openstack/tests/unit/cloud/test_security_groups.py index 3b62f79fa..152455514 100644 --- a/openstack/tests/unit/cloud/test_security_groups.py +++ b/openstack/tests/unit/cloud/test_security_groups.py @@ -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): diff --git a/openstack/tests/unit/network/v2/test_security_group.py b/openstack/tests/unit/network/v2/test_security_group.py index f91833093..82cda9845 100644 --- a/openstack/tests/unit/network/v2/test_security_group.py +++ b/openstack/tests/unit/network/v2/test_security_group.py @@ -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) diff --git a/releasenotes/notes/stateful-security-group-f32a78b9bbb49874.yaml b/releasenotes/notes/stateful-security-group-f32a78b9bbb49874.yaml new file mode 100644 index 000000000..d0d8945e5 --- /dev/null +++ b/releasenotes/notes/stateful-security-group-f32a78b9bbb49874.yaml @@ -0,0 +1,4 @@ +--- +features: + - New stateful parameter can be used in security group +