summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/cli/command-objects/security-group.rst74
-rw-r--r--openstackclient/network/v2/security_group.py43
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py1
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_network.py174
-rw-r--r--releasenotes/notes/bug-1750983-420945d6c0afb509.yaml8
-rw-r--r--setup.cfg1
6 files changed, 295 insertions, 6 deletions
diff --git a/doc/source/cli/command-objects/security-group.rst b/doc/source/cli/command-objects/security-group.rst
index a95a96f4..403e5fc0 100644
--- a/doc/source/cli/command-objects/security-group.rst
+++ b/doc/source/cli/command-objects/security-group.rst
@@ -19,6 +19,7 @@ Create a new security group
openstack security group create
[--description <description>]
[--project <project> [--project-domain <project-domain>]]
+ [--tag <tag> | --no-tag]
<name>
.. option:: --description <description>
@@ -38,6 +39,18 @@ Create a new security group
*Network version 2 only*
+.. option:: --tag <tag>
+
+ Tag to be added to the security group (repeat option to set multiple tags)
+
+ *Network version 2 only*
+
+.. option:: --no-tag
+
+ No tags associated with the security group
+
+ *Network version 2 only*
+
.. describe:: <name>
New security group name
@@ -68,6 +81,8 @@ List security groups
openstack security group list
[--all-projects]
[--project <project> [--project-domain <project-domain>]]
+ [--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
+ [--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
.. option:: --all-projects
@@ -89,6 +104,30 @@ List security groups
*Network version 2 only*
+.. option:: --tags <tag>[,<tag>,...]
+
+ List security groups which have all given tag(s)
+
+ *Network version 2 only*
+
+.. option:: --any-tags <tag>[,<tag>,...]
+
+ List security groups which have any given tag(s)
+
+ *Network version 2 only*
+
+.. option:: --not-tags <tag>[,<tag>,...]
+
+ Exclude security groups which have all given tag(s)
+
+ *Network version 2 only*
+
+.. option:: --not-any-tags <tag>[,<tag>,...]
+
+ Exclude security groups which have any given tag(s)
+
+ *Network version 2 only*
+
security group set
------------------
@@ -100,6 +139,7 @@ Set security group properties
openstack security group set
[--name <new-name>]
[--description <description>]
+ [--tag <tag>] [--no-tag]
<group>
.. option:: --name <new-name>
@@ -110,6 +150,15 @@ Set security group properties
New security group description
+.. option:: --tag <tag>
+
+ Tag to be added to the security group (repeat option to set multiple tags)
+
+.. option:: --no-tag
+
+ Clear tags associated with the security group. Specify both --tag
+ and --no-tag to overwrite current tags
+
.. describe:: <group>
Security group to modify (name or ID)
@@ -128,3 +177,28 @@ Display security group details
.. describe:: <group>
Security group to display (name or ID)
+
+security group unset
+--------------------
+
+Unset security group properties
+
+.. program:: security group unset
+.. code:: bash
+
+ openstack security group unset
+ [--tag <tag> | --all-tag]
+ <group>
+
+.. option:: --tag <tag>
+
+ Tag to be removed from the security group
+ (repeat option to remove multiple tags)
+
+.. option:: --all-tag
+
+ Clear all tags associated with the security group
+
+.. describe:: <group>
+
+ Security group to modify (name or ID)
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 75af3587..ed6c8d7c 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -15,6 +15,7 @@
import argparse
+from osc_lib.command import command
from osc_lib import utils
import six
@@ -23,6 +24,7 @@ from openstackclient.identity import common as identity_common
from openstackclient.network import common
from openstackclient.network import sdk_utils
from openstackclient.network import utils as network_utils
+from openstackclient.network.v2 import _tag
def _format_network_security_group_rules(sg_rules):
@@ -106,6 +108,7 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
help=_("Owner's project (name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
+ _tag.add_tag_option_to_parser_for_create(parser, _('security group'))
return parser
def _get_description(self, parsed_args):
@@ -130,6 +133,8 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
# Create the security group and display the results.
obj = client.create_security_group(**attrs)
+ # tags cannot be set when created, so tags need to be set later.
+ _tag.update_tags_for_set(client, obj, parsed_args)
display_columns, property_columns = _get_columns(obj)
data = utils.get_item_properties(
obj,
@@ -198,6 +203,7 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
"(name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
+ _tag.add_tag_filtering_option_to_parser(parser, _('security group'))
return parser
def update_parser_compute(self, parser):
@@ -220,19 +226,23 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
).id
filters['tenant_id'] = project_id
filters['project_id'] = project_id
+
+ _tag.get_tag_filtering_args(parsed_args, filters)
data = client.security_groups(**filters)
columns = (
"ID",
"Name",
"Description",
- "Project ID"
+ "Project ID",
+ "tags"
)
column_headers = (
"ID",
"Name",
"Description",
- "Project"
+ "Project",
+ "Tags"
)
return (column_headers,
(utils.get_item_properties(
@@ -282,6 +292,10 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
)
return parser
+ def update_parser_network(self, parser):
+ _tag.add_tag_option_to_parser_for_set(parser, _('security group'))
+ return parser
+
def take_action_network(self, client, parsed_args):
obj = client.find_security_group(parsed_args.group,
ignore_missing=False)
@@ -295,6 +309,9 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
# the update.
client.update_security_group(obj, **attrs)
+ # tags is a subresource and it needs to be updated separately.
+ _tag.update_tags_for_set(client, obj, parsed_args)
+
def take_action_compute(self, client, parsed_args):
data = client.api.security_group_find(parsed_args.group)
@@ -344,3 +361,25 @@ class ShowSecurityGroup(common.NetworkAndComputeShowOne):
formatters=_formatters_compute
)
return (display_columns, data)
+
+
+class UnsetSecurityGroup(command.Command):
+ _description = _("Unset security group properties")
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetSecurityGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'group',
+ metavar="<group>",
+ help=_("Security group to modify (name or ID)")
+ )
+ _tag.add_tag_option_to_parser_for_unset(parser, _('security group'))
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_security_group(parsed_args.group,
+ ignore_missing=False)
+
+ # tags is a subresource and it needs to be updated separately.
+ _tag.update_tags_for_unset(client, obj, parsed_args)
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index a77cab8b..e69df88a 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -1131,6 +1131,7 @@ class FakeSecurityGroup(object):
'description': 'security-group-description-' + uuid.uuid4().hex,
'project_id': 'project-id-' + uuid.uuid4().hex,
'security_group_rules': [],
+ 'tags': []
}
# Overwrite default attributes.
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py
index 35b7e366..83208287 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py
@@ -40,8 +40,8 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
project = identity_fakes.FakeProject.create_one_project()
domain = identity_fakes.FakeDomain.create_one_domain()
# The security group to be created.
- _security_group = \
- network_fakes.FakeSecurityGroup.create_one_security_group()
+ _security_group = (
+ network_fakes.FakeSecurityGroup.create_one_security_group())
columns = (
'description',
@@ -49,6 +49,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'name',
'project_id',
'rules',
+ 'tags',
)
data = (
@@ -57,6 +58,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.name,
_security_group.project_id,
'',
+ _security_group.tags,
)
def setUp(self):
@@ -67,6 +69,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
self.projects_mock.get.return_value = self.project
self.domains_mock.get.return_value = self.domain
+ self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test
self.cmd = security_group.CreateSecurityGroup(self.app, self.namespace)
@@ -118,6 +121,43 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def _test_create_with_tag(self, add_tags=True):
+ arglist = [self._security_group.name]
+ if add_tags:
+ arglist += ['--tag', 'red', '--tag', 'blue']
+ else:
+ arglist += ['--no-tag']
+
+ verifylist = [
+ ('name', self._security_group.name),
+ ]
+ if add_tags:
+ verifylist.append(('tags', ['red', 'blue']))
+ else:
+ verifylist.append(('no_tag', True))
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_security_group.assert_called_once_with(**{
+ 'description': self._security_group.name,
+ 'name': self._security_group.name,
+ })
+ if add_tags:
+ self.network.set_tags.assert_called_once_with(
+ self._security_group,
+ tests_utils.CompareBySet(['red', 'blue']))
+ else:
+ self.assertFalse(self.network.set_tags.called)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_with_tags(self):
+ self._test_create_with_tag(add_tags=True)
+
+ def test_create_with_no_tag(self):
+ self._test_create_with_tag(add_tags=False)
+
class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork):
@@ -214,6 +254,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
'Name',
'Description',
'Project',
+ 'Tags',
)
data = []
@@ -223,6 +264,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
grp.name,
grp.description,
grp.project_id,
+ grp.tags,
))
def setUp(self):
@@ -300,12 +342,38 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_list_with_tag_options(self):
+ arglist = [
+ '--tags', 'red,blue',
+ '--any-tags', 'red,green',
+ '--not-tags', 'orange,yellow',
+ '--not-any-tags', 'black,white',
+ ]
+ verifylist = [
+ ('tags', ['red', 'blue']),
+ ('any_tags', ['red', 'green']),
+ ('not_tags', ['orange', 'yellow']),
+ ('not_any_tags', ['black', 'white']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.security_groups.assert_called_once_with(
+ **{'tags': 'red,blue',
+ 'any_tags': 'red,green',
+ 'not_tags': 'orange,yellow',
+ 'not_any_tags': 'black,white'}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
# The security group to be set.
- _security_group = \
- network_fakes.FakeSecurityGroup.create_one_security_group()
+ _security_group = (
+ network_fakes.FakeSecurityGroup.create_one_security_group(
+ attrs={'tags': ['green', 'red']}))
def setUp(self):
super(TestSetSecurityGroupNetwork, self).setUp()
@@ -314,6 +382,7 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.find_security_group = mock.Mock(
return_value=self._security_group)
+ self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test
self.cmd = security_group.SetSecurityGroup(self.app, self.namespace)
@@ -366,6 +435,34 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
)
self.assertIsNone(result)
+ def _test_set_tags(self, with_tags=True):
+ if with_tags:
+ arglist = ['--tag', 'red', '--tag', 'blue']
+ verifylist = [('tags', ['red', 'blue'])]
+ expected_args = ['red', 'blue', 'green']
+ else:
+ arglist = ['--no-tag']
+ verifylist = [('no_tag', True)]
+ expected_args = []
+ arglist.append(self._security_group.name)
+ verifylist.append(
+ ('group', self._security_group.name))
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertTrue(self.network.update_security_group.called)
+ self.network.set_tags.assert_called_once_with(
+ self._security_group,
+ tests_utils.CompareBySet(expected_args))
+ self.assertIsNone(result)
+
+ def test_set_with_tags(self):
+ self._test_set_tags(with_tags=True)
+
+ def test_set_with_no_tag(self):
+ self._test_set_tags(with_tags=False)
+
class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
@@ -385,6 +482,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
'name',
'project_id',
'rules',
+ 'tags',
)
data = (
@@ -394,6 +492,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.project_id,
security_group._format_network_security_group_rules(
[_security_group_rule._info]),
+ _security_group.tags,
)
def setUp(self):
@@ -424,3 +523,70 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
self._security_group.id, ignore_missing=False)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+
+
+class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork):
+
+ # The security group to be unset.
+ _security_group = (
+ network_fakes.FakeSecurityGroup.create_one_security_group(
+ attrs={'tags': ['green', 'red']}))
+
+ def setUp(self):
+ super(TestUnsetSecurityGroupNetwork, self).setUp()
+
+ self.network.update_security_group = mock.Mock(return_value=None)
+
+ self.network.find_security_group = mock.Mock(
+ return_value=self._security_group)
+ self.network.set_tags = mock.Mock(return_value=None)
+
+ # Get the command object to test
+ self.cmd = security_group.UnsetSecurityGroup(self.app, self.namespace)
+
+ def test_set_no_options(self):
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd, [], [])
+
+ def test_set_no_updates(self):
+ arglist = [
+ self._security_group.name,
+ ]
+ verifylist = [
+ ('group', self._security_group.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertFalse(self.network.update_security_group.called)
+ self.assertFalse(self.network.set_tags.called)
+ self.assertIsNone(result)
+
+ def _test_unset_tags(self, with_tags=True):
+ if with_tags:
+ arglist = ['--tag', 'red', '--tag', 'blue']
+ verifylist = [('tags', ['red', 'blue'])]
+ expected_args = ['green']
+ else:
+ arglist = ['--all-tag']
+ verifylist = [('all_tag', True)]
+ expected_args = []
+ arglist.append(self._security_group.name)
+ verifylist.append(
+ ('group', self._security_group.name))
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertFalse(self.network.update_security_group.called)
+ self.network.set_tags.assert_called_once_with(
+ self._security_group,
+ tests_utils.CompareBySet(expected_args))
+ self.assertIsNone(result)
+
+ def test_unset_with_tags(self):
+ self._test_unset_tags(with_tags=True)
+
+ def test_unset_with_all_tag(self):
+ self._test_unset_tags(with_tags=False)
diff --git a/releasenotes/notes/bug-1750983-420945d6c0afb509.yaml b/releasenotes/notes/bug-1750983-420945d6c0afb509.yaml
new file mode 100644
index 00000000..2c37d88f
--- /dev/null
+++ b/releasenotes/notes/bug-1750983-420945d6c0afb509.yaml
@@ -0,0 +1,8 @@
+---
+fixes:
+ - |
+ Add supports tagging for Network security group.
+ Add ``tag``, ``no-tag`` to ``security group create`` and ``security group set`` commands.
+ Add ``tags``, ``any-tags``, ``not-tags``, ``not-any-tags`` to ``security group list`` command.
+ Add ``tag`` and ``all-tag`` to ``security group unset`` command.
+ [Bug `1750983 <https://bugs.launchpad.net/python-openstackclient/+bug/1750983>`_]
diff --git a/setup.cfg b/setup.cfg
index 63bfdafb..37592f49 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -467,6 +467,7 @@ openstack.network.v2 =
security_group_list = openstackclient.network.v2.security_group:ListSecurityGroup
security_group_set = openstackclient.network.v2.security_group:SetSecurityGroup
security_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup
+ security_group_unset = openstackclient.network.v2.security_group:UnsetSecurityGroup
security_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule
security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule