1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
===============
Command Options
===============
OpenStackClient commands all have a set of zero or more options unique to
the command, however there are of course ways in which these options are
common and consistent across all of the commands that include them.
These are the set of guidelines for OSC developers that help keep the
interface and commands consistent.
In some cases (like the boolean variables below) we use the same pattern
for defining and using options in all situations. The alternative of only
using it when necessary leads to errors when copy-n-paste is used for a
new command without understanding why or why not that instance is correct.
The :ref:`hig` describes the guidelines for option names and usage.
In short:
* All option names shall be GNU-style long names (two leading dashes).
* Some global options may have short names, generally limited to those defined
in support libraries such as `cliff <https://docs.openstack.org/cliff/latest>`__.
General Command Options
=======================
Boolean Options
---------------
Boolean options for any command that sets a resource state, such as 'enabled'
or 'public', shall always have both positive and negative options defined.
The names of those options shall either be a naturally occurring pair of
words (in English) or a positive option and a negative option with `no-`
prepended (such as in the traditional GNU option usage) like `--share` and
`--no-share`.
In order to handle those APIs that behave differently when a field is set to
`None` and when the field is not present in a passed argument list or dict,
each of the boolean options shall set its own variable to `True` as part of
a mutually exclusive group, rather than the more common configuration of
setting a single destination variable `True` or `False` directly. This allows
us to detect the situation when neither option is present (both variables will
be `False`) and act accordingly for those APIs where this matters.
This also requires that each of the boolean values be tested in the
`take_action()` method to correctly set (or not) the underlying API field
values.
.. option:: --enable
Enable <resource> (default)
.. option:: --disable
Disable <resource>
Implementation
~~~~~~~~~~~~~~
The parser declaration should look like this:
.. code-block:: python
enable_group = parser.add_mutually_exclusive_group()
enable_group.add_argument(
'--enable',
action='store_true',
help=_('Enable <resource> (default)'),
)
enable_group.add_argument(
'--disable',
action='store_true',
help=_('Disable <resource>'),
)
An example handler in `take_action()`:
.. code-block:: python
# This leaves 'enabled' undefined if neither option is present
if parsed_args.enable:
kwargs['enabled'] = True
if parsed_args.disable:
kwargs['enabled'] = False
Options with Choices
--------------------
Some options have a specific set of values (or choices) that are valid.
These choices may be validated by the CLI. If the underlying API is stable
and the list of choices are unlikely to change then the CLI may validate
the choices. Otherwise, the CLI must defer validation of the choices to
the API. If the option has a default choice then it must be documented.
Having the CLI validate choices will be faster and may provide a better
error message for the user if an invalid choice is specified
(for example: ``argument --test: invalid choice: 'choice4' (choose from 'choice1', 'choice2', 'choice3')``).
The trade-off is that CLI changes are required in order to take advantage
of new choices.
Implementation
~~~~~~~~~~~~~~
An example parser declaration:
.. code-block:: python
choice_option.add_argument(
'--test',
metavar='<test>',
choices=['choice1', 'choice2', 'choice3'],
help=_('Test type (choice1, choice2 or choice3)'),
)
Options with Multiple Values
----------------------------
Some options can be repeated to build a collection of values for a property.
Adding a value to the collection must be provided via the ``set`` action.
Removing a value from the collection must be provided via an ``unset`` action.
As a convenience, removing all values from the collection may be provided via a
``--no`` option on the ``set`` action and a ``--all`` option on ``unset``
action. If both ``--no`` option and option are specified, the values specified
on the command would overwrite the collection property instead of appending on
the ``set`` action. The ``--all`` option must be part of a mutually exclusive
group with the related property option on the ``unset`` action, overwrite case
don't exist in ``unset`` action.
An example behavior for ``set`` action:
Append:
.. code-block:: bash
object set --example-property xxx
Overwrite:
.. code-block:: bash
object set --no-example-property --example-property xxx
The example below assumes a property that contains a list of unique values.
However, this example can also be applied to other collections using the
appropriate parser action and action implementation (e.g. a dict of key/value
pairs). Implementations will vary depending on how the REST API handles
adding/removing values to/from the collection and whether or not duplicate
values are allowed.
Implementation
~~~~~~~~~~~~~~
An example parser declaration for `set` action:
.. code-block:: python
parser.add_argument(
'--no-example-property',
dest='no_example_property',
action='store_true',
help=_('Remove all example properties for this <resource> '
'(specify both --no-example-property and --example-property'
' to remove the current properties before setting'
' new properties.)'),
)
parser.add_argument(
'--example-property',
metavar='<example-property>',
dest='example_property',
action='append',
help=_('Example property for this <resource> '
'(repeat option to set multiple properties)'),
)
Please make `--no-example-property` be shown in front of `--example-property`
in the help, like above, that help make users aware of the processing order.
An example handler in `take_action()` for `set` action:
.. code-block:: python
if parsed_args.no_example_property and parsed_args.example_property:
kwargs['example_property'] = parsed_args.example_property
elif parsed_args.no_example_property:
kwargs['example_property'] = []
elif parsed_args.example_property:
kwargs['example_property'] = \
resource_example_property + parsed_args.example_property
An example parser declaration for `unset` action:
.. code-block:: python
example_property_group = parser.add_mutually_exclusive_group()
example_property_group.add_argument(
'--example-property',
metavar='<example-property>',
dest='example_property',
action='append',
help=_('Example property for this <resource> '
'(repeat option to remove multiple properties)'),
)
example_property_group.add_argument(
'--all-example-property',
dest='all_example_property',
action='store_true',
help=_('Remove all example properties for this <resource>'),
)
An example handler in `take_action()` for `unset` action:
.. code-block:: python
if parsed_args.example_property:
kwargs['example_property'] = \
list(set(resource_example_property) - \
set(parsed_args.example_property))
if parsed_args.all_example_property:
kwargs['example_property'] = []
Required Options
----------------
Some options have no default value and the API does not allow them to be
`None`, then these options are always required when users use the command
to which these options belong.
Required options must be validated by the CLI to avoid omissions. The CLI
validation may provide an error message for the user if a required option
is not specified.
(for example: ``error: argument --test is required``)
.. option:: --test
Test option (required)
Implementation
~~~~~~~~~~~~~~
The parser declaration should look like this:
.. code-block:: python
parser.add_argument(
'--test',
metavar='<test>',
required=True,
help=_('Test option (required)'),
)
List Command Options
====================
Additional Fields
-----------------
Most list commands only return a subset of the available fields by default.
Additional fields are available with the `--long` option. All list
commands should allow `--long` even if they return all fields by default.
.. option:: --long
List additional fields in output
Implementation
~~~~~~~~~~~~~~
The parser declaration should look like this:
.. code-block:: python
parser.add_argument(
'--long',
action='store_true',
default=False,
help='List additional fields in output',
)
Pagination
----------
There are many ways to do pagination, some OpenStack APIs support it, some
don't. OpenStackClient attempts to define a single common way to specify
pagination on the command line.
.. option:: --marker <resource>
Anchor for paging (name or ID)
.. option:: --limit <num-resources>
Limit number of <resource> returned (*integer*)
Implementation
~~~~~~~~~~~~~~
The parser declaration should look like this:
.. code-block:: python
parser.add_argument(
"--marker",
metavar="<resource>",
help="Anchor for paging (name or ID)",
)
parser.add_argument(
"--limit",
metavar="<num-resources>",
type=int,
help="Limit the number of <resource> returned",
)
|