Browse Source

Merge "Network: Add tag support for security group"

tags/3.16.0
Zuul 11 months ago
parent
commit
4bde9af892

+ 74
- 0
doc/source/cli/command-objects/security-group.rst View File

@@ -19,6 +19,7 @@ Create a new security group
19 19
     openstack security group create
20 20
         [--description <description>]
21 21
         [--project <project> [--project-domain <project-domain>]]
22
+        [--tag <tag> | --no-tag]
22 23
         <name>
23 24
 
24 25
 .. option:: --description <description>
@@ -38,6 +39,18 @@ Create a new security group
38 39
 
39 40
     *Network version 2 only*
40 41
 
42
+.. option:: --tag <tag>
43
+
44
+    Tag to be added to the security group (repeat option to set multiple tags)
45
+
46
+    *Network version 2 only*
47
+
48
+.. option:: --no-tag
49
+
50
+    No tags associated with the security group
51
+
52
+    *Network version 2 only*
53
+
41 54
 .. describe:: <name>
42 55
 
43 56
     New security group name
@@ -68,6 +81,8 @@ List security groups
68 81
     openstack security group list
69 82
         [--all-projects]
70 83
         [--project <project> [--project-domain <project-domain>]]
84
+        [--tags <tag>[,<tag>,...]] [--any-tags <tag>[,<tag>,...]]
85
+        [--not-tags <tag>[,<tag>,...]] [--not-any-tags <tag>[,<tag>,...]]
71 86
 
72 87
 .. option:: --all-projects
73 88
 
@@ -89,6 +104,30 @@ List security groups
89 104
 
90 105
     *Network version 2 only*
91 106
 
107
+.. option:: --tags <tag>[,<tag>,...]
108
+
109
+    List security groups which have all given tag(s)
110
+
111
+    *Network version 2 only*
112
+
113
+.. option:: --any-tags <tag>[,<tag>,...]
114
+
115
+    List security groups which have any given tag(s)
116
+
117
+    *Network version 2 only*
118
+
119
+.. option:: --not-tags <tag>[,<tag>,...]
120
+
121
+    Exclude security groups which have all given tag(s)
122
+
123
+    *Network version 2 only*
124
+
125
+.. option:: --not-any-tags <tag>[,<tag>,...]
126
+
127
+    Exclude security groups which have any given tag(s)
128
+
129
+    *Network version 2 only*
130
+
92 131
 security group set
93 132
 ------------------
94 133
 
@@ -100,6 +139,7 @@ Set security group properties
100 139
     openstack security group set
101 140
         [--name <new-name>]
102 141
         [--description <description>]
142
+        [--tag <tag>] [--no-tag]
103 143
         <group>
104 144
 
105 145
 .. option:: --name <new-name>
@@ -110,6 +150,15 @@ Set security group properties
110 150
 
111 151
     New security group description
112 152
 
153
+.. option:: --tag <tag>
154
+
155
+    Tag to be added to the security group (repeat option to set multiple tags)
156
+
157
+.. option:: --no-tag
158
+
159
+    Clear tags associated with the security group. Specify both --tag
160
+    and --no-tag to overwrite current tags
161
+
113 162
 .. describe:: <group>
114 163
 
115 164
     Security group to modify (name or ID)
@@ -128,3 +177,28 @@ Display security group details
128 177
 .. describe:: <group>
129 178
 
130 179
     Security group to display (name or ID)
180
+
181
+security group unset
182
+--------------------
183
+
184
+Unset security group properties
185
+
186
+.. program:: security group unset
187
+.. code:: bash
188
+
189
+    openstack security group unset
190
+        [--tag <tag> | --all-tag]
191
+        <group>
192
+
193
+.. option:: --tag <tag>
194
+
195
+    Tag to be removed from the security group
196
+    (repeat option to remove multiple tags)
197
+
198
+.. option:: --all-tag
199
+
200
+    Clear all tags associated with the security group
201
+
202
+.. describe:: <group>
203
+
204
+    Security group to modify (name or ID)

+ 41
- 2
openstackclient/network/v2/security_group.py View File

@@ -15,6 +15,7 @@
15 15
 
16 16
 import argparse
17 17
 
18
+from osc_lib.command import command
18 19
 from osc_lib import utils
19 20
 import six
20 21
 
@@ -23,6 +24,7 @@ from openstackclient.identity import common as identity_common
23 24
 from openstackclient.network import common
24 25
 from openstackclient.network import sdk_utils
25 26
 from openstackclient.network import utils as network_utils
27
+from openstackclient.network.v2 import _tag
26 28
 
27 29
 
28 30
 def _format_network_security_group_rules(sg_rules):
@@ -106,6 +108,7 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
106 108
             help=_("Owner's project (name or ID)")
107 109
         )
108 110
         identity_common.add_project_domain_option_to_parser(parser)
111
+        _tag.add_tag_option_to_parser_for_create(parser, _('security group'))
109 112
         return parser
110 113
 
111 114
     def _get_description(self, parsed_args):
@@ -130,6 +133,8 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
130 133
 
131 134
         # Create the security group and display the results.
132 135
         obj = client.create_security_group(**attrs)
136
+        # tags cannot be set when created, so tags need to be set later.
137
+        _tag.update_tags_for_set(client, obj, parsed_args)
133 138
         display_columns, property_columns = _get_columns(obj)
134 139
         data = utils.get_item_properties(
135 140
             obj,
@@ -198,6 +203,7 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
198 203
                    "(name or ID)")
199 204
         )
200 205
         identity_common.add_project_domain_option_to_parser(parser)
206
+        _tag.add_tag_filtering_option_to_parser(parser, _('security group'))
201 207
         return parser
202 208
 
203 209
     def update_parser_compute(self, parser):
@@ -220,19 +226,23 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
220 226
             ).id
221 227
             filters['tenant_id'] = project_id
222 228
             filters['project_id'] = project_id
229
+
230
+        _tag.get_tag_filtering_args(parsed_args, filters)
223 231
         data = client.security_groups(**filters)
224 232
 
225 233
         columns = (
226 234
             "ID",
227 235
             "Name",
228 236
             "Description",
229
-            "Project ID"
237
+            "Project ID",
238
+            "tags"
230 239
         )
231 240
         column_headers = (
232 241
             "ID",
233 242
             "Name",
234 243
             "Description",
235
-            "Project"
244
+            "Project",
245
+            "Tags"
236 246
         )
237 247
         return (column_headers,
238 248
                 (utils.get_item_properties(
@@ -282,6 +292,10 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
282 292
         )
283 293
         return parser
284 294
 
295
+    def update_parser_network(self, parser):
296
+        _tag.add_tag_option_to_parser_for_set(parser, _('security group'))
297
+        return parser
298
+
285 299
     def take_action_network(self, client, parsed_args):
286 300
         obj = client.find_security_group(parsed_args.group,
287 301
                                          ignore_missing=False)
@@ -295,6 +309,9 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
295 309
         # the update.
296 310
         client.update_security_group(obj, **attrs)
297 311
 
312
+        # tags is a subresource and it needs to be updated separately.
313
+        _tag.update_tags_for_set(client, obj, parsed_args)
314
+
298 315
     def take_action_compute(self, client, parsed_args):
299 316
         data = client.api.security_group_find(parsed_args.group)
300 317
 
@@ -344,3 +361,25 @@ class ShowSecurityGroup(common.NetworkAndComputeShowOne):
344 361
             formatters=_formatters_compute
345 362
         )
346 363
         return (display_columns, data)
364
+
365
+
366
+class UnsetSecurityGroup(command.Command):
367
+    _description = _("Unset security group properties")
368
+
369
+    def get_parser(self, prog_name):
370
+        parser = super(UnsetSecurityGroup, self).get_parser(prog_name)
371
+        parser.add_argument(
372
+            'group',
373
+            metavar="<group>",
374
+            help=_("Security group to modify (name or ID)")
375
+        )
376
+        _tag.add_tag_option_to_parser_for_unset(parser, _('security group'))
377
+        return parser
378
+
379
+    def take_action(self, parsed_args):
380
+        client = self.app.client_manager.network
381
+        obj = client.find_security_group(parsed_args.group,
382
+                                         ignore_missing=False)
383
+
384
+        # tags is a subresource and it needs to be updated separately.
385
+        _tag.update_tags_for_unset(client, obj, parsed_args)

+ 1
- 0
openstackclient/tests/unit/network/v2/fakes.py View File

@@ -1132,6 +1132,7 @@ class FakeSecurityGroup(object):
1132 1132
             'description': 'security-group-description-' + uuid.uuid4().hex,
1133 1133
             'project_id': 'project-id-' + uuid.uuid4().hex,
1134 1134
             'security_group_rules': [],
1135
+            'tags': []
1135 1136
         }
1136 1137
 
1137 1138
         # Overwrite default attributes.

+ 170
- 4
openstackclient/tests/unit/network/v2/test_security_group_network.py View File

@@ -40,8 +40,8 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
40 40
     project = identity_fakes.FakeProject.create_one_project()
41 41
     domain = identity_fakes.FakeDomain.create_one_domain()
42 42
     # The security group to be created.
43
-    _security_group = \
44
-        network_fakes.FakeSecurityGroup.create_one_security_group()
43
+    _security_group = (
44
+        network_fakes.FakeSecurityGroup.create_one_security_group())
45 45
 
46 46
     columns = (
47 47
         'description',
@@ -49,6 +49,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
49 49
         'name',
50 50
         'project_id',
51 51
         'rules',
52
+        'tags',
52 53
     )
53 54
 
54 55
     data = (
@@ -57,6 +58,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
57 58
         _security_group.name,
58 59
         _security_group.project_id,
59 60
         '',
61
+        _security_group.tags,
60 62
     )
61 63
 
62 64
     def setUp(self):
@@ -67,6 +69,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
67 69
 
68 70
         self.projects_mock.get.return_value = self.project
69 71
         self.domains_mock.get.return_value = self.domain
72
+        self.network.set_tags = mock.Mock(return_value=None)
70 73
 
71 74
         # Get the command object to test
72 75
         self.cmd = security_group.CreateSecurityGroup(self.app, self.namespace)
@@ -118,6 +121,43 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
118 121
         self.assertEqual(self.columns, columns)
119 122
         self.assertEqual(self.data, data)
120 123
 
124
+    def _test_create_with_tag(self, add_tags=True):
125
+        arglist = [self._security_group.name]
126
+        if add_tags:
127
+            arglist += ['--tag', 'red', '--tag', 'blue']
128
+        else:
129
+            arglist += ['--no-tag']
130
+
131
+        verifylist = [
132
+            ('name', self._security_group.name),
133
+        ]
134
+        if add_tags:
135
+            verifylist.append(('tags', ['red', 'blue']))
136
+        else:
137
+            verifylist.append(('no_tag', True))
138
+
139
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
140
+        columns, data = (self.cmd.take_action(parsed_args))
141
+
142
+        self.network.create_security_group.assert_called_once_with(**{
143
+            'description': self._security_group.name,
144
+            'name': self._security_group.name,
145
+        })
146
+        if add_tags:
147
+            self.network.set_tags.assert_called_once_with(
148
+                self._security_group,
149
+                tests_utils.CompareBySet(['red', 'blue']))
150
+        else:
151
+            self.assertFalse(self.network.set_tags.called)
152
+        self.assertEqual(self.columns, columns)
153
+        self.assertEqual(self.data, data)
154
+
155
+    def test_create_with_tags(self):
156
+        self._test_create_with_tag(add_tags=True)
157
+
158
+    def test_create_with_no_tag(self):
159
+        self._test_create_with_tag(add_tags=False)
160
+
121 161
 
122 162
 class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork):
123 163
 
@@ -214,6 +254,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
214 254
         'Name',
215 255
         'Description',
216 256
         'Project',
257
+        'Tags',
217 258
     )
218 259
 
219 260
     data = []
@@ -223,6 +264,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
223 264
             grp.name,
224 265
             grp.description,
225 266
             grp.project_id,
267
+            grp.tags,
226 268
         ))
227 269
 
228 270
     def setUp(self):
@@ -300,12 +342,38 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
300 342
         self.assertEqual(self.columns, columns)
301 343
         self.assertEqual(self.data, list(data))
302 344
 
345
+    def test_list_with_tag_options(self):
346
+        arglist = [
347
+            '--tags', 'red,blue',
348
+            '--any-tags', 'red,green',
349
+            '--not-tags', 'orange,yellow',
350
+            '--not-any-tags', 'black,white',
351
+        ]
352
+        verifylist = [
353
+            ('tags', ['red', 'blue']),
354
+            ('any_tags', ['red', 'green']),
355
+            ('not_tags', ['orange', 'yellow']),
356
+            ('not_any_tags', ['black', 'white']),
357
+        ]
358
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
359
+        columns, data = self.cmd.take_action(parsed_args)
360
+
361
+        self.network.security_groups.assert_called_once_with(
362
+            **{'tags': 'red,blue',
363
+               'any_tags': 'red,green',
364
+               'not_tags': 'orange,yellow',
365
+               'not_any_tags': 'black,white'}
366
+        )
367
+        self.assertEqual(self.columns, columns)
368
+        self.assertEqual(self.data, list(data))
369
+
303 370
 
304 371
 class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
305 372
 
306 373
     # The security group to be set.
307
-    _security_group = \
308
-        network_fakes.FakeSecurityGroup.create_one_security_group()
374
+    _security_group = (
375
+        network_fakes.FakeSecurityGroup.create_one_security_group(
376
+            attrs={'tags': ['green', 'red']}))
309 377
 
310 378
     def setUp(self):
311 379
         super(TestSetSecurityGroupNetwork, self).setUp()
@@ -314,6 +382,7 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
314 382
 
315 383
         self.network.find_security_group = mock.Mock(
316 384
             return_value=self._security_group)
385
+        self.network.set_tags = mock.Mock(return_value=None)
317 386
 
318 387
         # Get the command object to test
319 388
         self.cmd = security_group.SetSecurityGroup(self.app, self.namespace)
@@ -366,6 +435,34 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
366 435
         )
367 436
         self.assertIsNone(result)
368 437
 
438
+    def _test_set_tags(self, with_tags=True):
439
+        if with_tags:
440
+            arglist = ['--tag', 'red', '--tag', 'blue']
441
+            verifylist = [('tags', ['red', 'blue'])]
442
+            expected_args = ['red', 'blue', 'green']
443
+        else:
444
+            arglist = ['--no-tag']
445
+            verifylist = [('no_tag', True)]
446
+            expected_args = []
447
+        arglist.append(self._security_group.name)
448
+        verifylist.append(
449
+            ('group', self._security_group.name))
450
+
451
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
452
+        result = self.cmd.take_action(parsed_args)
453
+
454
+        self.assertTrue(self.network.update_security_group.called)
455
+        self.network.set_tags.assert_called_once_with(
456
+            self._security_group,
457
+            tests_utils.CompareBySet(expected_args))
458
+        self.assertIsNone(result)
459
+
460
+    def test_set_with_tags(self):
461
+        self._test_set_tags(with_tags=True)
462
+
463
+    def test_set_with_no_tag(self):
464
+        self._test_set_tags(with_tags=False)
465
+
369 466
 
370 467
 class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
371 468
 
@@ -385,6 +482,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
385 482
         'name',
386 483
         'project_id',
387 484
         'rules',
485
+        'tags',
388 486
     )
389 487
 
390 488
     data = (
@@ -394,6 +492,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
394 492
         _security_group.project_id,
395 493
         security_group._format_network_security_group_rules(
396 494
             [_security_group_rule._info]),
495
+        _security_group.tags,
397 496
     )
398 497
 
399 498
     def setUp(self):
@@ -424,3 +523,70 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
424 523
             self._security_group.id, ignore_missing=False)
425 524
         self.assertEqual(self.columns, columns)
426 525
         self.assertEqual(self.data, data)
526
+
527
+
528
+class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork):
529
+
530
+    # The security group to be unset.
531
+    _security_group = (
532
+        network_fakes.FakeSecurityGroup.create_one_security_group(
533
+            attrs={'tags': ['green', 'red']}))
534
+
535
+    def setUp(self):
536
+        super(TestUnsetSecurityGroupNetwork, self).setUp()
537
+
538
+        self.network.update_security_group = mock.Mock(return_value=None)
539
+
540
+        self.network.find_security_group = mock.Mock(
541
+            return_value=self._security_group)
542
+        self.network.set_tags = mock.Mock(return_value=None)
543
+
544
+        # Get the command object to test
545
+        self.cmd = security_group.UnsetSecurityGroup(self.app, self.namespace)
546
+
547
+    def test_set_no_options(self):
548
+        self.assertRaises(tests_utils.ParserException,
549
+                          self.check_parser, self.cmd, [], [])
550
+
551
+    def test_set_no_updates(self):
552
+        arglist = [
553
+            self._security_group.name,
554
+        ]
555
+        verifylist = [
556
+            ('group', self._security_group.name),
557
+        ]
558
+
559
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
560
+        result = self.cmd.take_action(parsed_args)
561
+
562
+        self.assertFalse(self.network.update_security_group.called)
563
+        self.assertFalse(self.network.set_tags.called)
564
+        self.assertIsNone(result)
565
+
566
+    def _test_unset_tags(self, with_tags=True):
567
+        if with_tags:
568
+            arglist = ['--tag', 'red', '--tag', 'blue']
569
+            verifylist = [('tags', ['red', 'blue'])]
570
+            expected_args = ['green']
571
+        else:
572
+            arglist = ['--all-tag']
573
+            verifylist = [('all_tag', True)]
574
+            expected_args = []
575
+        arglist.append(self._security_group.name)
576
+        verifylist.append(
577
+            ('group', self._security_group.name))
578
+
579
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
580
+        result = self.cmd.take_action(parsed_args)
581
+
582
+        self.assertFalse(self.network.update_security_group.called)
583
+        self.network.set_tags.assert_called_once_with(
584
+            self._security_group,
585
+            tests_utils.CompareBySet(expected_args))
586
+        self.assertIsNone(result)
587
+
588
+    def test_unset_with_tags(self):
589
+        self._test_unset_tags(with_tags=True)
590
+
591
+    def test_unset_with_all_tag(self):
592
+        self._test_unset_tags(with_tags=False)

+ 8
- 0
releasenotes/notes/bug-1750983-420945d6c0afb509.yaml View File

@@ -0,0 +1,8 @@
1
+---
2
+fixes:
3
+  - |
4
+    Add supports tagging for Network security group.
5
+    Add ``tag``, ``no-tag`` to ``security group create`` and ``security group set`` commands.
6
+    Add ``tags``, ``any-tags``, ``not-tags``, ``not-any-tags`` to ``security group list`` command.
7
+    Add ``tag`` and ``all-tag`` to ``security group unset`` command.
8
+    [Bug `1750983 <https://bugs.launchpad.net/python-openstackclient/+bug/1750983>`_]

+ 1
- 0
setup.cfg View File

@@ -474,6 +474,7 @@ openstack.network.v2 =
474 474
     security_group_list = openstackclient.network.v2.security_group:ListSecurityGroup
475 475
     security_group_set = openstackclient.network.v2.security_group:SetSecurityGroup
476 476
     security_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup
477
+    security_group_unset = openstackclient.network.v2.security_group:UnsetSecurityGroup
477 478
 
478 479
     security_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule
479 480
     security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule

Loading…
Cancel
Save