Network: Add tag support for security group
Change-Id: Icccb23429913724c6a8bd15d4737672b47a5f13a Closes-Bug: #1750983
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
							
								
								
									
										8
									
								
								releasenotes/notes/bug-1750983-420945d6c0afb509.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								releasenotes/notes/bug-1750983-420945d6c0afb509.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -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>`_] | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Dongcan Ye
					Dongcan Ye