osc-lib 3.2.0__py3-none-any.whl → 4.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- osc_lib/api/api.py +62 -27
- osc_lib/api/auth.py +36 -21
- osc_lib/api/utils.py +10 -5
- osc_lib/cli/client_config.py +53 -32
- osc_lib/cli/format_columns.py +19 -17
- osc_lib/cli/identity.py +14 -3
- osc_lib/cli/pagination.py +83 -0
- osc_lib/cli/parseractions.py +98 -20
- osc_lib/clientmanager.py +45 -24
- osc_lib/command/command.py +19 -8
- osc_lib/command/timing.py +11 -1
- osc_lib/exceptions.py +12 -2
- osc_lib/logs.py +17 -8
- osc_lib/py.typed +0 -0
- osc_lib/shell.py +64 -44
- osc_lib/tests/test_shell.py +4 -4
- osc_lib/tests/utils/__init__.py +0 -19
- osc_lib/tests/utils/test_tags.py +22 -7
- osc_lib/tests/utils/test_utils.py +1 -11
- osc_lib/utils/__init__.py +179 -108
- osc_lib/utils/columns.py +15 -4
- osc_lib/utils/tags.py +38 -21
- {osc_lib-3.2.0.dist-info → osc_lib-4.0.1.dist-info}/METADATA +11 -13
- osc_lib-4.0.1.dist-info/RECORD +53 -0
- {osc_lib-3.2.0.dist-info → osc_lib-4.0.1.dist-info}/WHEEL +1 -1
- osc_lib-4.0.1.dist-info/pbr.json +1 -0
- osc_lib-3.2.0.dist-info/RECORD +0 -51
- osc_lib-3.2.0.dist-info/pbr.json +0 -1
- {osc_lib-3.2.0.dist-info → osc_lib-4.0.1.dist-info}/AUTHORS +0 -0
- {osc_lib-3.2.0.dist-info → osc_lib-4.0.1.dist-info}/LICENSE +0 -0
- {osc_lib-3.2.0.dist-info → osc_lib-4.0.1.dist-info}/top_level.txt +0 -0
osc_lib/utils/__init__.py
CHANGED
@@ -15,25 +15,33 @@
|
|
15
15
|
|
16
16
|
"""Common client utilities"""
|
17
17
|
|
18
|
+
import argparse
|
18
19
|
import copy
|
19
20
|
import functools
|
20
21
|
import getpass
|
21
22
|
import logging
|
22
23
|
import os
|
23
24
|
import time
|
25
|
+
import typing as ty
|
24
26
|
import warnings
|
25
27
|
|
26
28
|
from cliff import columns as cliff_columns
|
29
|
+
from openstack import resource
|
27
30
|
from oslo_utils import importutils
|
28
31
|
|
29
32
|
from osc_lib import exceptions
|
30
33
|
from osc_lib.i18n import _
|
31
34
|
|
32
|
-
|
33
35
|
LOG = logging.getLogger(__name__)
|
34
36
|
|
37
|
+
_T = ty.TypeVar('_T')
|
38
|
+
|
35
39
|
|
36
|
-
def backward_compat_col_lister(
|
40
|
+
def backward_compat_col_lister(
|
41
|
+
column_headers: ty.List[str],
|
42
|
+
columns: ty.List[str],
|
43
|
+
column_map: ty.Dict[str, str],
|
44
|
+
) -> ty.List[str]:
|
37
45
|
"""Convert the column headers to keep column backward compatibility.
|
38
46
|
|
39
47
|
Replace the new column name of column headers by old name, so that
|
@@ -65,7 +73,11 @@ def backward_compat_col_lister(column_headers, columns, column_map):
|
|
65
73
|
return column_headers
|
66
74
|
|
67
75
|
|
68
|
-
def backward_compat_col_showone(
|
76
|
+
def backward_compat_col_showone(
|
77
|
+
show_object: ty.MutableMapping[str, _T],
|
78
|
+
columns: ty.List[str],
|
79
|
+
column_map: ty.Dict[str, str],
|
80
|
+
) -> ty.MutableMapping[str, _T]:
|
69
81
|
"""Convert the output object to keep column backward compatibility.
|
70
82
|
|
71
83
|
Replace the new column name of output object by old name, so that
|
@@ -95,7 +107,7 @@ def backward_compat_col_showone(show_object, columns, column_map):
|
|
95
107
|
return show_object
|
96
108
|
|
97
109
|
|
98
|
-
def build_kwargs_dict(arg_name, value):
|
110
|
+
def build_kwargs_dict(arg_name: str, value: _T) -> ty.Dict[str, _T]:
|
99
111
|
"""Return a dictionary containing `arg_name` if `value` is set."""
|
100
112
|
kwargs = {}
|
101
113
|
if value:
|
@@ -103,7 +115,11 @@ def build_kwargs_dict(arg_name, value):
|
|
103
115
|
return kwargs
|
104
116
|
|
105
117
|
|
106
|
-
def calculate_header_and_attrs(
|
118
|
+
def calculate_header_and_attrs(
|
119
|
+
column_headers: ty.Sequence[str],
|
120
|
+
attrs: ty.Sequence[str],
|
121
|
+
parsed_args: argparse.Namespace,
|
122
|
+
) -> ty.Tuple[ty.Sequence[str], ty.Sequence[str]]:
|
107
123
|
"""Calculate headers and attribute names based on parsed_args.column.
|
108
124
|
|
109
125
|
When --column (-c) option is specified, this function calculates
|
@@ -138,7 +154,7 @@ def calculate_header_and_attrs(column_headers, attrs, parsed_args):
|
|
138
154
|
return column_headers, attrs
|
139
155
|
|
140
156
|
|
141
|
-
def env(*vars, **kwargs):
|
157
|
+
def env(*vars: str, **kwargs: ty.Any) -> ty.Optional[str]:
|
142
158
|
"""Search for the first defined of possibly many env vars
|
143
159
|
|
144
160
|
Returns the first environment variable defined in vars, or
|
@@ -148,10 +164,18 @@ def env(*vars, **kwargs):
|
|
148
164
|
value = os.environ.get(v, None)
|
149
165
|
if value:
|
150
166
|
return value
|
151
|
-
return kwargs.get('default', '')
|
152
167
|
|
168
|
+
if 'default' in kwargs and kwargs['default'] is not None:
|
169
|
+
return str(kwargs['default'])
|
170
|
+
|
171
|
+
return None
|
153
172
|
|
154
|
-
|
173
|
+
|
174
|
+
def find_min_match(
|
175
|
+
items: ty.Sequence[_T],
|
176
|
+
sort_attr: str,
|
177
|
+
**kwargs: ty.Any,
|
178
|
+
) -> ty.Sequence[_T]:
|
155
179
|
"""Find all resources meeting the given minimum constraints
|
156
180
|
|
157
181
|
:param items: A List of objects to consider
|
@@ -160,7 +184,7 @@ def find_min_match(items, sort_attr, **kwargs):
|
|
160
184
|
:rtype: A list of resources osrted by sort_attr that meet the minimums
|
161
185
|
"""
|
162
186
|
|
163
|
-
def minimum_pieces_of_flair(item):
|
187
|
+
def minimum_pieces_of_flair(item: _T) -> bool:
|
164
188
|
"""Find lowest value greater than the minumum"""
|
165
189
|
|
166
190
|
result = True
|
@@ -169,10 +193,17 @@ def find_min_match(items, sort_attr, **kwargs):
|
|
169
193
|
result = result and kwargs[k] <= get_field(item, k)
|
170
194
|
return result
|
171
195
|
|
172
|
-
return sort_items(filter(minimum_pieces_of_flair, items), sort_attr)
|
196
|
+
return sort_items(list(filter(minimum_pieces_of_flair, items)), sort_attr)
|
173
197
|
|
174
198
|
|
175
|
-
|
199
|
+
# TODO(stephenfin): We should return a proper type, but how to do so without
|
200
|
+
# using generics? We should also deprecate this but there are a lot of users
|
201
|
+
# still.
|
202
|
+
def find_resource(
|
203
|
+
manager: ty.Any,
|
204
|
+
name_or_id: str,
|
205
|
+
**kwargs: ty.Any,
|
206
|
+
) -> ty.Any:
|
176
207
|
"""Helper for the _find_* methods.
|
177
208
|
|
178
209
|
:param manager: A client manager class
|
@@ -200,7 +231,7 @@ def find_resource(manager, name_or_id, **kwargs):
|
|
200
231
|
# enough information, and domain information is necessary.
|
201
232
|
try:
|
202
233
|
return manager.get(name_or_id)
|
203
|
-
except Exception:
|
234
|
+
except Exception: # noqa: S110
|
204
235
|
pass
|
205
236
|
|
206
237
|
if kwargs:
|
@@ -208,7 +239,7 @@ def find_resource(manager, name_or_id, **kwargs):
|
|
208
239
|
# for example: /projects/demo&domain_id=30524568d64447fbb3fa8b7891c10dd
|
209
240
|
try:
|
210
241
|
return manager.get(name_or_id, **kwargs)
|
211
|
-
except Exception:
|
242
|
+
except Exception: # noqa: S110
|
212
243
|
pass
|
213
244
|
|
214
245
|
# Case 3: Try to get entity as integer id. Keystone does not have integer
|
@@ -242,7 +273,7 @@ def find_resource(manager, name_or_id, **kwargs):
|
|
242
273
|
kwargs[manager.resource_class.NAME_ATTR] = name_or_id
|
243
274
|
else:
|
244
275
|
kwargs['name'] = name_or_id
|
245
|
-
except Exception:
|
276
|
+
except Exception: # noqa: S110
|
246
277
|
pass
|
247
278
|
|
248
279
|
# finally try to find entity by name
|
@@ -279,26 +310,25 @@ def find_resource(manager, name_or_id, **kwargs):
|
|
279
310
|
# Case 5: For client with no find function, list all resources and hope
|
280
311
|
# to find a matching name or ID.
|
281
312
|
count = 0
|
282
|
-
for
|
283
|
-
if (
|
284
|
-
resource.get('id') == name_or_id
|
285
|
-
or resource.get('name') == name_or_id
|
286
|
-
):
|
313
|
+
for res in manager.list():
|
314
|
+
if res.get('id') == name_or_id or res.get('name') == name_or_id:
|
287
315
|
count += 1
|
288
|
-
|
316
|
+
_res = res
|
289
317
|
if count == 0:
|
290
318
|
# we found no match, report back this error:
|
291
319
|
msg = _("Could not find resource %s")
|
292
320
|
raise exceptions.CommandError(msg % name_or_id)
|
293
321
|
elif count == 1:
|
294
|
-
return
|
322
|
+
return _res
|
295
323
|
else:
|
296
324
|
# we found multiple matches, report back this error
|
297
325
|
msg = _("More than one resource exists with the name or ID '%s'.")
|
298
326
|
raise exceptions.CommandError(msg % name_or_id)
|
299
327
|
|
300
328
|
|
301
|
-
def format_dict(
|
329
|
+
def format_dict(
|
330
|
+
data: ty.Dict[str, ty.Any], prefix: ty.Optional[str] = None
|
331
|
+
) -> str:
|
302
332
|
"""Return a formatted string of key value pairs
|
303
333
|
|
304
334
|
:param data: a dict
|
@@ -326,11 +356,13 @@ def format_dict(data, prefix=None):
|
|
326
356
|
return output[:-2]
|
327
357
|
|
328
358
|
|
329
|
-
def format_dict_of_list(
|
359
|
+
def format_dict_of_list(
|
360
|
+
data: ty.Optional[ty.Dict[str, ty.List[ty.Any]]], separator: str = '; '
|
361
|
+
) -> ty.Optional[str]:
|
330
362
|
"""Return a formatted string of key value pair
|
331
363
|
|
332
364
|
:param data: a dict, key is string, value is a list of string, for example:
|
333
|
-
{
|
365
|
+
{'public': ['2001:db8::8', '172.24.4.6']}
|
334
366
|
:param separator: the separator to use between key/value pair
|
335
367
|
(default: '; ')
|
336
368
|
:return: a string formatted to {'key1'=['value1', 'value2']} with separated
|
@@ -351,7 +383,9 @@ def format_dict_of_list(data, separator='; '):
|
|
351
383
|
return separator.join(output)
|
352
384
|
|
353
385
|
|
354
|
-
def format_list(
|
386
|
+
def format_list(
|
387
|
+
data: ty.Optional[ty.List[ty.Any]], separator: str = ', '
|
388
|
+
) -> ty.Optional[str]:
|
355
389
|
"""Return a formatted strings
|
356
390
|
|
357
391
|
:param data: a list of strings
|
@@ -364,7 +398,9 @@ def format_list(data, separator=', '):
|
|
364
398
|
return separator.join(sorted(data))
|
365
399
|
|
366
400
|
|
367
|
-
def format_list_of_dicts(
|
401
|
+
def format_list_of_dicts(
|
402
|
+
data: ty.Optional[ty.List[ty.Dict[str, ty.Any]]],
|
403
|
+
) -> ty.Optional[str]:
|
368
404
|
"""Return a formatted string of key value pairs for each dict
|
369
405
|
|
370
406
|
:param data: a list of dicts
|
@@ -376,10 +412,10 @@ def format_list_of_dicts(data):
|
|
376
412
|
return '\n'.join(format_dict(i) for i in data)
|
377
413
|
|
378
414
|
|
379
|
-
def format_size(size):
|
415
|
+
def format_size(size: ty.Union[int, float, None]) -> str:
|
380
416
|
"""Display size of a resource in a human readable format
|
381
417
|
|
382
|
-
:param
|
418
|
+
:param size:
|
383
419
|
The size of the resource in bytes.
|
384
420
|
|
385
421
|
:returns:
|
@@ -394,19 +430,22 @@ def format_size(size):
|
|
394
430
|
base = 1000.0
|
395
431
|
index = 0
|
396
432
|
|
397
|
-
if size is None
|
398
|
-
|
399
|
-
while size >= base:
|
433
|
+
size_ = float(size) if size is not None else 0.0
|
434
|
+
while size_ >= base:
|
400
435
|
index = index + 1
|
401
|
-
|
436
|
+
size_ = size_ / base
|
402
437
|
|
403
|
-
padded = f'{
|
438
|
+
padded = f'{size_:.1f}'
|
404
439
|
stripped = padded.rstrip('0').rstrip('.')
|
405
440
|
|
406
441
|
return f'{stripped}{suffix[index]}'
|
407
442
|
|
408
443
|
|
409
|
-
def get_client_class(
|
444
|
+
def get_client_class(
|
445
|
+
api_name: str,
|
446
|
+
version: ty.Union[str, int, float],
|
447
|
+
version_map: ty.Dict[str, ty.Type[_T]],
|
448
|
+
) -> ty.Any:
|
410
449
|
"""Returns the client class for the requested API version
|
411
450
|
|
412
451
|
:param api_name: the name of the API, e.g. 'compute', 'image', etc
|
@@ -414,6 +453,12 @@ def get_client_class(api_name, version, version_map):
|
|
414
453
|
:param version_map: a dict of client classes keyed by version
|
415
454
|
:rtype: a client class for the requested API version
|
416
455
|
"""
|
456
|
+
warnings.warn(
|
457
|
+
"This function is deprecated and is not necessary with openstacksdk."
|
458
|
+
"Consider vendoring this if necessary.",
|
459
|
+
category=DeprecationWarning,
|
460
|
+
)
|
461
|
+
|
417
462
|
try:
|
418
463
|
client_path = version_map[str(version)]
|
419
464
|
except (KeyError, ValueError):
|
@@ -436,7 +481,14 @@ def get_client_class(api_name, version, version_map):
|
|
436
481
|
return importutils.import_class(client_path)
|
437
482
|
|
438
483
|
|
439
|
-
def get_dict_properties(
|
484
|
+
def get_dict_properties(
|
485
|
+
item: ty.Dict[str, _T],
|
486
|
+
fields: ty.Sequence[str],
|
487
|
+
mixed_case_fields: ty.Optional[ty.Sequence[str]] = None,
|
488
|
+
formatters: ty.Optional[ # type: ignore
|
489
|
+
ty.Dict[str, ty.Type[cliff_columns.FormattableColumn]]
|
490
|
+
] = None,
|
491
|
+
) -> ty.Tuple[ty.Any, ...]:
|
440
492
|
"""Return a tuple containing the item properties.
|
441
493
|
|
442
494
|
:param item: a single dict resource
|
@@ -457,7 +509,7 @@ def get_dict_properties(item, fields, mixed_case_fields=None, formatters=None):
|
|
457
509
|
field_name = field.replace(' ', '_')
|
458
510
|
else:
|
459
511
|
field_name = field.lower().replace(' ', '_')
|
460
|
-
data = item[field_name] if field_name in item else ''
|
512
|
+
data: ty.Any = item[field_name] if field_name in item else ''
|
461
513
|
if field in formatters:
|
462
514
|
formatter = formatters[field]
|
463
515
|
# columns must be either a subclass of FormattableColumn
|
@@ -472,16 +524,7 @@ def get_dict_properties(item, fields, mixed_case_fields=None, formatters=None):
|
|
472
524
|
and issubclass(formatter.func, cliff_columns.FormattableColumn)
|
473
525
|
):
|
474
526
|
data = formatter(data)
|
475
|
-
# otherwise it's
|
476
|
-
elif callable(formatter):
|
477
|
-
warnings.warn(
|
478
|
-
'The usage of formatter functions is now discouraged. '
|
479
|
-
'Consider using cliff.columns.FormattableColumn instead. '
|
480
|
-
'See reviews linked with bug 1687955 for more detail.',
|
481
|
-
category=DeprecationWarning,
|
482
|
-
)
|
483
|
-
if data is not None:
|
484
|
-
data = formatter(data)
|
527
|
+
# otherwise it's invalid
|
485
528
|
else:
|
486
529
|
msg = "Invalid formatter provided."
|
487
530
|
raise exceptions.CommandError(msg)
|
@@ -490,31 +533,14 @@ def get_dict_properties(item, fields, mixed_case_fields=None, formatters=None):
|
|
490
533
|
return tuple(row)
|
491
534
|
|
492
535
|
|
493
|
-
def
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
for handler in root_log.handlers:
|
502
|
-
min_log_lvl = min(min_log_lvl, handler.level)
|
503
|
-
return min_log_lvl
|
504
|
-
|
505
|
-
|
506
|
-
def get_field(item, field):
|
507
|
-
try:
|
508
|
-
if isinstance(item, dict):
|
509
|
-
return item[field]
|
510
|
-
else:
|
511
|
-
return getattr(item, field)
|
512
|
-
except Exception:
|
513
|
-
msg = _("Resource doesn't have field %s")
|
514
|
-
raise exceptions.CommandError(msg % field)
|
515
|
-
|
516
|
-
|
517
|
-
def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
|
536
|
+
def get_item_properties(
|
537
|
+
item: ty.Dict[str, _T],
|
538
|
+
fields: ty.Sequence[str],
|
539
|
+
mixed_case_fields: ty.Optional[ty.Sequence[str]] = None,
|
540
|
+
formatters: ty.Optional[ # type: ignore
|
541
|
+
ty.Dict[str, ty.Type[cliff_columns.FormattableColumn]]
|
542
|
+
] = None,
|
543
|
+
) -> ty.Tuple[ty.Any, ...]:
|
518
544
|
"""Return a tuple containing the item properties.
|
519
545
|
|
520
546
|
:param item: a single item resource (e.g. Server, Project, etc)
|
@@ -538,19 +564,19 @@ def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
|
|
538
564
|
data = getattr(item, field_name, '')
|
539
565
|
if field in formatters:
|
540
566
|
formatter = formatters[field]
|
567
|
+
# columns must be either a subclass of FormattableColumn
|
541
568
|
if isinstance(formatter, type) and issubclass(
|
542
569
|
formatter, cliff_columns.FormattableColumn
|
543
570
|
):
|
544
571
|
data = formatter(data)
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
)
|
552
|
-
|
553
|
-
data = formatter(data)
|
572
|
+
# or a partial wrapping one (to allow us to pass extra parameters)
|
573
|
+
elif (
|
574
|
+
isinstance(formatter, functools.partial)
|
575
|
+
and isinstance(formatter.func, type)
|
576
|
+
and issubclass(formatter.func, cliff_columns.FormattableColumn)
|
577
|
+
):
|
578
|
+
data = formatter(data)
|
579
|
+
# otherwise it's invalid
|
554
580
|
else:
|
555
581
|
msg = "Invalid formatter provided."
|
556
582
|
raise exceptions.CommandError(msg)
|
@@ -559,7 +585,35 @@ def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
|
|
559
585
|
return tuple(row)
|
560
586
|
|
561
587
|
|
562
|
-
def
|
588
|
+
def get_effective_log_level() -> int:
|
589
|
+
"""Returns the lowest logging level considered by logging handlers
|
590
|
+
|
591
|
+
Retrieve and return the smallest log level set among the root
|
592
|
+
logger's handlers (in case of multiple handlers).
|
593
|
+
"""
|
594
|
+
root_log = logging.getLogger()
|
595
|
+
min_log_lvl = logging.CRITICAL
|
596
|
+
for handler in root_log.handlers:
|
597
|
+
min_log_lvl = min(min_log_lvl, handler.level)
|
598
|
+
return min_log_lvl
|
599
|
+
|
600
|
+
|
601
|
+
def get_field(item: _T, field: str) -> ty.Any:
|
602
|
+
try:
|
603
|
+
if isinstance(item, dict):
|
604
|
+
return item[field]
|
605
|
+
else:
|
606
|
+
return getattr(item, field)
|
607
|
+
except Exception:
|
608
|
+
msg = _("Resource doesn't have field %s")
|
609
|
+
raise exceptions.CommandError(msg % field)
|
610
|
+
|
611
|
+
|
612
|
+
def get_password(
|
613
|
+
stdin: ty.TextIO,
|
614
|
+
prompt: ty.Optional[str] = None,
|
615
|
+
confirm: bool = True,
|
616
|
+
) -> str:
|
563
617
|
message = prompt or "User Password:"
|
564
618
|
if hasattr(stdin, 'isatty') and stdin.isatty():
|
565
619
|
try:
|
@@ -579,19 +633,18 @@ def get_password(stdin, prompt=None, confirm=True):
|
|
579
633
|
raise exceptions.CommandError(msg)
|
580
634
|
|
581
635
|
|
582
|
-
def is_ascii(string):
|
636
|
+
def is_ascii(string: ty.Union[str, bytes]) -> bool:
|
583
637
|
try:
|
584
|
-
(
|
638
|
+
if isinstance(string, bytes):
|
585
639
|
string.decode('ascii')
|
586
|
-
|
587
|
-
|
588
|
-
)
|
640
|
+
else:
|
641
|
+
string.encode('ascii')
|
589
642
|
return True
|
590
643
|
except (UnicodeEncodeError, UnicodeDecodeError):
|
591
644
|
return False
|
592
645
|
|
593
646
|
|
594
|
-
def read_blob_file_contents(blob_file):
|
647
|
+
def read_blob_file_contents(blob_file: str) -> str:
|
595
648
|
try:
|
596
649
|
with open(blob_file) as file:
|
597
650
|
blob = file.read().strip()
|
@@ -601,7 +654,11 @@ def read_blob_file_contents(blob_file):
|
|
601
654
|
raise exceptions.CommandError(msg % blob_file)
|
602
655
|
|
603
656
|
|
604
|
-
def sort_items(
|
657
|
+
def sort_items(
|
658
|
+
items: ty.Sequence[_T],
|
659
|
+
sort_str: str,
|
660
|
+
sort_type: ty.Optional[ty.Type[ty.Any]] = None,
|
661
|
+
) -> ty.Sequence[_T]:
|
605
662
|
"""Sort items based on sort keys and sort directions given by sort_str.
|
606
663
|
|
607
664
|
:param items: a list or generator object of items
|
@@ -640,7 +697,7 @@ def sort_items(items, sort_str, sort_type=None):
|
|
640
697
|
if direction == 'desc':
|
641
698
|
reverse = True
|
642
699
|
|
643
|
-
def f(x):
|
700
|
+
def f(x: ty.Any) -> ty.Any:
|
644
701
|
# Attempts to convert items to same 'sort_type' if provided.
|
645
702
|
# This is due to Python 3 throwing TypeError if you attempt to
|
646
703
|
# compare different types
|
@@ -659,15 +716,15 @@ def sort_items(items, sort_str, sort_type=None):
|
|
659
716
|
|
660
717
|
|
661
718
|
def wait_for_delete(
|
662
|
-
manager,
|
663
|
-
res_id,
|
664
|
-
status_field='status',
|
665
|
-
error_status=['error'],
|
666
|
-
exception_name=['NotFound'],
|
667
|
-
sleep_time=5,
|
668
|
-
timeout=300,
|
669
|
-
callback=None,
|
670
|
-
):
|
719
|
+
manager: ty.Any,
|
720
|
+
res_id: str,
|
721
|
+
status_field: str = 'status',
|
722
|
+
error_status: ty.Sequence[str] = ['error'],
|
723
|
+
exception_name: ty.Sequence[str] = ['NotFound'],
|
724
|
+
sleep_time: int = 5,
|
725
|
+
timeout: int = 300,
|
726
|
+
callback: ty.Optional[ty.Callable[[int], None]] = None,
|
727
|
+
) -> bool:
|
671
728
|
"""Wait for resource deletion
|
672
729
|
|
673
730
|
:param manager: the manager from which we can get the resource
|
@@ -685,6 +742,12 @@ def wait_for_delete(
|
|
685
742
|
:rtype: True on success, False if the resource has gone to error state or
|
686
743
|
the timeout has been reached
|
687
744
|
"""
|
745
|
+
warnings.warn(
|
746
|
+
"This function is deprecated as it does not support openstacksdk "
|
747
|
+
"clients. Consider vendoring this if necessary.",
|
748
|
+
category=DeprecationWarning,
|
749
|
+
)
|
750
|
+
|
688
751
|
total_time = 0
|
689
752
|
while total_time < timeout:
|
690
753
|
try:
|
@@ -712,14 +775,14 @@ def wait_for_delete(
|
|
712
775
|
|
713
776
|
|
714
777
|
def wait_for_status(
|
715
|
-
status_f,
|
716
|
-
res_id,
|
717
|
-
status_field='status',
|
718
|
-
success_status=['active'],
|
719
|
-
error_status=['error'],
|
720
|
-
sleep_time=5,
|
721
|
-
callback=None,
|
722
|
-
):
|
778
|
+
status_f: ty.Callable[[str], object],
|
779
|
+
res_id: str,
|
780
|
+
status_field: str = 'status',
|
781
|
+
success_status: ty.Sequence[str] = ['active'],
|
782
|
+
error_status: ty.Sequence[str] = ['error'],
|
783
|
+
sleep_time: int = 5,
|
784
|
+
callback: ty.Optional[ty.Callable[[int], None]] = None,
|
785
|
+
) -> bool:
|
723
786
|
"""Wait for status change on a resource during a long-running operation
|
724
787
|
|
725
788
|
:param status_f: a status function that takes a single id argument
|
@@ -731,6 +794,12 @@ def wait_for_status(
|
|
731
794
|
:param callback: called per sleep cycle, useful to display progress
|
732
795
|
:rtype: True on success
|
733
796
|
"""
|
797
|
+
warnings.warn(
|
798
|
+
"This function is deprecated as it does not support openstacksdk "
|
799
|
+
"clients. Consider vendoring this if necessary.",
|
800
|
+
category=DeprecationWarning,
|
801
|
+
)
|
802
|
+
|
734
803
|
while True:
|
735
804
|
res = status_f(res_id)
|
736
805
|
status = getattr(res, status_field, '').lower()
|
@@ -748,8 +817,10 @@ def wait_for_status(
|
|
748
817
|
|
749
818
|
|
750
819
|
def get_osc_show_columns_for_sdk_resource(
|
751
|
-
sdk_resource
|
752
|
-
|
820
|
+
sdk_resource: resource.Resource,
|
821
|
+
osc_column_map: ty.Dict[str, str],
|
822
|
+
invisible_columns: ty.Optional[ty.Sequence[str]] = None,
|
823
|
+
) -> ty.Tuple[ty.Tuple[str, ...], ty.Tuple[str, ...]]:
|
753
824
|
"""Get and filter the display and attribute columns for an SDK resource.
|
754
825
|
|
755
826
|
Common utility function for preparing the output of an OSC show command.
|
osc_lib/utils/columns.py
CHANGED
@@ -11,13 +11,16 @@
|
|
11
11
|
# under the License.
|
12
12
|
|
13
13
|
import operator
|
14
|
+
import typing as ty
|
14
15
|
|
15
16
|
LIST_BOTH = 'both'
|
16
17
|
LIST_SHORT_ONLY = 'short_only'
|
17
18
|
LIST_LONG_ONLY = 'long_only'
|
18
19
|
|
19
20
|
|
20
|
-
def get_column_definitions(
|
21
|
+
def get_column_definitions(
|
22
|
+
attr_map: ty.List[ty.Tuple[str, str, str]], long_listing: bool
|
23
|
+
) -> ty.Tuple[ty.List[str], ty.List[str]]:
|
21
24
|
"""Return table headers and column names for a listing table.
|
22
25
|
|
23
26
|
An attribute map (attr_map) is a list of table entry definitions
|
@@ -73,7 +76,10 @@ def get_column_definitions(attr_map, long_listing):
|
|
73
76
|
return headers, columns
|
74
77
|
|
75
78
|
|
76
|
-
def get_columns(
|
79
|
+
def get_columns(
|
80
|
+
item: ty.Dict[str, ty.Any],
|
81
|
+
attr_map: ty.Optional[ty.List[ty.Tuple[str, str, str]]] = None,
|
82
|
+
) -> ty.Tuple[ty.Tuple[str, ...], ty.Tuple[str, ...]]:
|
77
83
|
"""Return pair of resource attributes and corresponding display names.
|
78
84
|
|
79
85
|
:param item: a dictionary which represents a resource.
|
@@ -82,7 +88,12 @@ def get_columns(item, attr_map=None):
|
|
82
88
|
|
83
89
|
.. code-block:: python
|
84
90
|
|
85
|
-
{
|
91
|
+
{
|
92
|
+
'id': 'myid',
|
93
|
+
'name': 'myname',
|
94
|
+
'foo': 'bar',
|
95
|
+
'tenant_id': 'mytenan',
|
96
|
+
}
|
86
97
|
|
87
98
|
:param attr_map: a list of mapping from attribute to display name.
|
88
99
|
The same format is used as for get_column_definitions attr_map.
|
@@ -106,7 +117,7 @@ def get_columns(item, attr_map=None):
|
|
106
117
|
in the alphabetical order.
|
107
118
|
Attributes not found in a given attr_map are kept as-is.
|
108
119
|
"""
|
109
|
-
attr_map = attr_map or
|
120
|
+
attr_map = attr_map or []
|
110
121
|
_attr_map_dict = dict((col, hdr) for col, hdr, listing_mode in attr_map)
|
111
122
|
|
112
123
|
columns = [
|