Skip to content

Commit d905c0e

Browse files
committed
Add security group default statefulness CLI commands
Add CLI commands for managing the default statefulness setting for security groups, backed by the new Neutron ``security-groups-default-statefulness`` API extension. New commands: - security group default statefulness create - security group default statefulness delete - security group default statefulness list - security group default statefulness set - security group default statefulness show This patch needs [1], present in openstacksdk 4.13.0. [1]https://review.opendev.org/c/openstack/openstacksdk/+/985787 Related-Bug: #2146803 Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Change-Id: Ifcb0b88830032a6404c17a9a8f38a6dead5668ac
1 parent db4a8b4 commit d905c0e

3 files changed

Lines changed: 586 additions & 0 deletions

File tree

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""Security Groups Default Statefulness action implementations"""
15+
16+
import argparse
17+
import logging
18+
from collections.abc import Iterable, Sequence
19+
from typing import Any
20+
21+
from osc_lib import exceptions
22+
from osc_lib import utils
23+
24+
from openstackclient import command
25+
from openstackclient.i18n import _
26+
from openstackclient.identity import common as identity_common
27+
28+
LOG = logging.getLogger(__name__)
29+
30+
31+
def _get_columns(item: Any) -> tuple[tuple[str, ...], tuple[str, ...]]:
32+
hidden_columns = ['location', 'name', 'revision_number']
33+
return utils.get_osc_show_columns_for_sdk_resource(
34+
item, {}, hidden_columns
35+
)
36+
37+
38+
class CreateSecurityGroupDefaultStatefulness(command.ShowOne):
39+
_description = _(
40+
"Create a default statefulness setting for security groups"
41+
)
42+
43+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
44+
parser = super().get_parser(prog_name)
45+
46+
stateful_group = parser.add_mutually_exclusive_group(required=True)
47+
stateful_group.add_argument(
48+
"--stateful",
49+
action='store_true',
50+
default=None,
51+
dest='stateful',
52+
help=_("Set default statefulness to stateful"),
53+
)
54+
stateful_group.add_argument(
55+
"--stateless",
56+
action='store_false',
57+
default=None,
58+
dest='stateful',
59+
help=_("Set default statefulness to stateless"),
60+
)
61+
parser.add_argument(
62+
'--project',
63+
metavar='<project>',
64+
help=_(
65+
"Apply the setting to this project (name or ID). "
66+
"If not specified, the setting applies system-wide"
67+
),
68+
)
69+
identity_common.add_project_domain_option_to_parser(parser)
70+
return parser
71+
72+
def take_action(
73+
self, parsed_args: argparse.Namespace
74+
) -> tuple[Sequence[str], Iterable[Any]]:
75+
client = self.app.client_manager.network
76+
attrs: dict[str, Any] = {}
77+
if parsed_args.stateful is not None:
78+
attrs['stateful'] = parsed_args.stateful
79+
project_id = None
80+
if parsed_args.project is not None:
81+
identity_client = self.app.client_manager.sdk_connection.identity
82+
project_id = identity_common.find_project_id_sdk(
83+
identity_client,
84+
parsed_args.project,
85+
parsed_args.project_domain,
86+
)
87+
# Always set this, even if `None`, otherwise the Neutron API will
88+
# fulfill this value with the user project ID instead.
89+
attrs['project_id'] = project_id
90+
91+
obj = client.create_security_groups_default_statefulness(**attrs)
92+
display_columns, columns = _get_columns(obj)
93+
data = utils.get_item_properties(obj, columns)
94+
return (display_columns, data)
95+
96+
97+
class DeleteSecurityGroupDefaultStatefulness(command.Command):
98+
_description = _("Delete security group default statefulness setting(s)")
99+
100+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
101+
parser = super().get_parser(prog_name)
102+
parser.add_argument(
103+
'setting',
104+
metavar='<setting>',
105+
nargs="+",
106+
help=_("Default statefulness setting(s) to delete (ID only)"),
107+
)
108+
return parser
109+
110+
def take_action(self, parsed_args: argparse.Namespace) -> None:
111+
result = 0
112+
client = self.app.client_manager.network
113+
for s in parsed_args.setting:
114+
try:
115+
obj = client.find_security_groups_default_statefulness(
116+
s, ignore_missing=False
117+
)
118+
client.delete_security_groups_default_statefulness(obj)
119+
except Exception as e:
120+
result += 1
121+
LOG.error(
122+
_(
123+
"Failed to delete default statefulness setting "
124+
"with ID '%(id)s': %(e)s"
125+
),
126+
{'id': s, 'e': e},
127+
)
128+
129+
if result > 0:
130+
total = len(parsed_args.setting)
131+
msg = _(
132+
"%(result)s of %(total)s default statefulness settings "
133+
"failed to delete."
134+
) % {'result': result, 'total': total}
135+
raise exceptions.CommandError(msg)
136+
137+
138+
class ListSecurityGroupDefaultStatefulness(command.Lister):
139+
_description = _("List security group default statefulness settings")
140+
141+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
142+
parser = super().get_parser(prog_name)
143+
parser.add_argument(
144+
'--project',
145+
metavar='<project>',
146+
help=_("List only settings for this project (name or ID)"),
147+
)
148+
identity_common.add_project_domain_option_to_parser(parser)
149+
return parser
150+
151+
def take_action(
152+
self, parsed_args: argparse.Namespace
153+
) -> tuple[tuple[str, ...], Iterable[tuple[Any, ...]]]:
154+
client = self.app.client_manager.network
155+
column_headers = (
156+
'ID',
157+
'Project ID',
158+
'Stateful',
159+
)
160+
columns = (
161+
'id',
162+
'project_id',
163+
'stateful',
164+
)
165+
166+
query: dict[str, Any] = {}
167+
if parsed_args.project is not None:
168+
identity_client = self.app.client_manager.sdk_connection.identity
169+
project_id = identity_common.find_project_id_sdk(
170+
identity_client,
171+
parsed_args.project,
172+
parsed_args.project_domain,
173+
)
174+
query['project_id'] = project_id
175+
176+
data = client.security_groups_default_statefulness(**query)
177+
178+
return (
179+
column_headers,
180+
(utils.get_item_properties(s, columns) for s in data),
181+
)
182+
183+
184+
class SetSecurityGroupDefaultStatefulness(command.Command):
185+
_description = _("Update a security group default statefulness setting")
186+
187+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
188+
parser = super().get_parser(prog_name)
189+
parser.add_argument(
190+
'setting',
191+
metavar='<setting>',
192+
help=_("Default statefulness setting to modify (ID only)"),
193+
)
194+
stateful_group = parser.add_mutually_exclusive_group(required=True)
195+
stateful_group.add_argument(
196+
"--stateful",
197+
action='store_true',
198+
default=None,
199+
dest='stateful',
200+
help=_("Set default statefulness to stateful"),
201+
)
202+
stateful_group.add_argument(
203+
"--stateless",
204+
action='store_false',
205+
default=None,
206+
dest='stateful',
207+
help=_("Set default statefulness to stateless"),
208+
)
209+
return parser
210+
211+
def take_action(self, parsed_args: argparse.Namespace) -> None:
212+
client = self.app.client_manager.network
213+
obj = client.find_security_groups_default_statefulness(
214+
parsed_args.setting, ignore_missing=False
215+
)
216+
attrs: dict[str, Any] = {}
217+
if parsed_args.stateful is not None:
218+
attrs['stateful'] = parsed_args.stateful
219+
client.update_security_groups_default_statefulness(obj, **attrs)
220+
221+
222+
class ShowSecurityGroupDefaultStatefulness(command.ShowOne):
223+
_description = _("Show a security group default statefulness setting")
224+
225+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
226+
parser = super().get_parser(prog_name)
227+
parser.add_argument(
228+
'setting',
229+
metavar='<setting>',
230+
help=_("Default statefulness setting to display (ID only)"),
231+
)
232+
return parser
233+
234+
def take_action(
235+
self, parsed_args: argparse.Namespace
236+
) -> tuple[Sequence[str], Iterable[Any]]:
237+
client = self.app.client_manager.network
238+
obj = client.find_security_groups_default_statefulness(
239+
parsed_args.setting, ignore_missing=False
240+
)
241+
display_columns, columns = _get_columns(obj)
242+
data = utils.get_item_properties(obj, columns)
243+
return (display_columns, data)

0 commit comments

Comments
 (0)