1010# License for the specific language governing permissions and limitations
1111# under the License.
1212
13- import logging
13+ import argparse
1414
1515from cinderclient import api_versions
1616from osc_lib .command import command
1919
2020from openstackclient .i18n import _
2121
22- LOG = logging .getLogger (__name__ )
23-
2422
2523def _format_group (group ):
2624 columns = (
@@ -82,19 +80,72 @@ class CreateVolumeGroup(command.ShowOne):
8280
8381 def get_parser (self , prog_name ):
8482 parser = super ().get_parser (prog_name )
83+ # This is a bit complicated. We accept two patterns: a legacy pattern
84+ #
85+ # volume group create \
86+ # <volume-group-type> <volume-type> [<volume-type>...]
87+ #
88+ # and the modern approach
89+ #
90+ # volume group create \
91+ # --volume-group-type <volume-group-type>
92+ # --volume-type <volume-type>
93+ # [--volume-type <volume-type> ...]
94+ #
95+ # Because argparse doesn't properly support nested exclusive groups, we
96+ # use two groups: one to ensure users don't pass <volume-group-type> as
97+ # both a positional and an option argument and another to ensure users
98+ # don't pass <volume-type> this way. It's a bit weird but it catches
99+ # everything we care about.
85100 source_parser = parser .add_mutually_exclusive_group ()
101+ # we use a different name purely so we can issue a deprecation warning
86102 source_parser .add_argument (
87- 'volume_group_type ' ,
103+ 'volume_group_type_legacy ' ,
88104 metavar = '<volume_group_type>' ,
89105 nargs = '?' ,
90- help = _ ( 'Name or ID of volume group type to use.' ) ,
106+ help = argparse . SUPPRESS ,
91107 )
92- parser .add_argument (
93- 'volume_types' ,
108+ volume_types_parser = parser .add_mutually_exclusive_group ()
109+ # We need to use a separate dest
110+ # https://github.com/python/cpython/issues/101990
111+ volume_types_parser .add_argument (
112+ 'volume_types_legacy' ,
94113 metavar = '<volume_type>' ,
95114 nargs = '*' ,
96115 default = [],
97- help = _ ('Name or ID of volume type(s) to use.' ),
116+ help = argparse .SUPPRESS ,
117+ )
118+ source_parser .add_argument (
119+ '--volume-group-type' ,
120+ metavar = '<volume_group_type>' ,
121+ help = _ ('Volume group type to use (name or ID)' ),
122+ )
123+ volume_types_parser .add_argument (
124+ '--volume-type' ,
125+ metavar = '<volume_type>' ,
126+ dest = 'volume_types' ,
127+ action = 'append' ,
128+ default = [],
129+ help = _ (
130+ 'Volume type(s) to use (name or ID) '
131+ '(required with --volume-group-type)'
132+ ),
133+ )
134+ source_parser .add_argument (
135+ '--source-group' ,
136+ metavar = '<source-group>' ,
137+ help = _ (
138+ 'Existing volume group to use (name or ID) '
139+ '(supported by --os-volume-api-version 3.14 or later)'
140+ ),
141+ )
142+ source_parser .add_argument (
143+ '--group-snapshot' ,
144+ metavar = '<group-snapshot>' ,
145+ help = _ (
146+ 'Existing group snapshot to use (name or ID) '
147+ '(supported by --os-volume-api-version 3.14 or later)'
148+ ),
98149 )
99150 parser .add_argument (
100151 '--name' ,
@@ -109,59 +160,63 @@ def get_parser(self, prog_name):
109160 parser .add_argument (
110161 '--availability-zone' ,
111162 metavar = '<availability-zone>' ,
112- help = _ ('Availability zone for volume group. '
113- '(not available if creating group from source)' ),
114- )
115- source_parser .add_argument (
116- '--source-group' ,
117- metavar = '<source-group>' ,
118- help = _ ('Existing volume group (name or ID) '
119- '(supported by --os-volume-api-version 3.14 or later)' ),
120- )
121- source_parser .add_argument (
122- '--group-snapshot' ,
123- metavar = '<group-snapshot>' ,
124- help = _ ('Existing group snapshot (name or ID) '
125- '(supported by --os-volume-api-version 3.14 or later)' ),
163+ help = _ (
164+ 'Availability zone for volume group. '
165+ '(not available if creating group from source)'
166+ ),
126167 )
127168 return parser
128169
129170 def take_action (self , parsed_args ):
130171 volume_client = self .app .client_manager .volume
131172
132- if parsed_args .volume_group_type :
173+ if parsed_args .volume_group_type_legacy :
174+ msg = _ (
175+ "Passing volume group type and volume types as positional "
176+ "arguments is deprecated. Use the --volume-group-type and "
177+ "--volume-type option arguments instead."
178+ )
179+ self .log .warning (msg )
180+
181+ volume_group_type = parsed_args .volume_group_type or \
182+ parsed_args .volume_group_type_legacy
183+ volume_types = parsed_args .volume_types [:]
184+ volume_types .extend (parsed_args .volume_types_legacy )
185+
186+ if volume_group_type :
133187 if volume_client .api_version < api_versions .APIVersion ('3.13' ):
134188 msg = _ (
135189 "--os-volume-api-version 3.13 or greater is required to "
136190 "support the 'volume group create' command"
137191 )
138192 raise exceptions .CommandError (msg )
139- if not parsed_args . volume_types :
193+ if not volume_types :
140194 msg = _ (
141- "<volume_types> is a required argument when creating a "
195+ "--volume-types is a required argument when creating a "
142196 "group from group type."
143197 )
144198 raise exceptions .CommandError (msg )
145199
146- volume_group_type = utils .find_resource (
200+ volume_group_type_id = utils .find_resource (
147201 volume_client .group_types ,
148- parsed_args . volume_group_type ,
149- )
150- volume_types = []
151- for volume_type in parsed_args . volume_types :
152- volume_types .append (
202+ volume_group_type ,
203+ ). id
204+ volume_types_ids = []
205+ for volume_type in volume_types :
206+ volume_types_ids .append (
153207 utils .find_resource (
154208 volume_client .volume_types ,
155209 volume_type ,
156- )
210+ ). id
157211 )
158212
159213 group = volume_client .groups .create (
160- volume_group_type . id ,
161- ',' .join (x . id for x in volume_types ),
214+ volume_group_type_id ,
215+ ',' .join (volume_types_ids ),
162216 parsed_args .name ,
163217 parsed_args .description ,
164- availability_zone = parsed_args .availability_zone )
218+ availability_zone = parsed_args .availability_zone ,
219+ )
165220
166221 group = volume_client .groups .get (group .id )
167222 return _format_group (group )
@@ -186,7 +241,7 @@ def take_action(self, parsed_args):
186241 if parsed_args .availability_zone :
187242 msg = _ ("'--availability-zone' option will not work "
188243 "if creating group from source." )
189- LOG .warning (msg )
244+ self . log .warning (msg )
190245
191246 source_group = None
192247 if parsed_args .source_group :
0 commit comments