python-openstackclient 8.0.0__py3-none-any.whl → 8.2.0__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.
- openstackclient/api/compute_v2.py +2 -2
- openstackclient/api/volume_v2.py +60 -0
- openstackclient/api/volume_v3.py +60 -0
- openstackclient/compute/client.py +5 -0
- openstackclient/compute/v2/console.py +7 -0
- openstackclient/compute/v2/console_connection.py +48 -0
- openstackclient/compute/v2/flavor.py +14 -1
- openstackclient/compute/v2/keypair.py +10 -3
- openstackclient/compute/v2/server.py +76 -13
- openstackclient/compute/v2/server_event.py +1 -1
- openstackclient/identity/common.py +85 -11
- openstackclient/identity/v3/application_credential.py +88 -87
- openstackclient/identity/v3/domain.py +67 -49
- openstackclient/identity/v3/group.py +113 -68
- openstackclient/identity/v3/project.py +42 -20
- openstackclient/identity/v3/role.py +7 -2
- openstackclient/identity/v3/user.py +38 -5
- openstackclient/image/client.py +5 -0
- openstackclient/image/v1/image.py +16 -1
- openstackclient/image/v2/cache.py +10 -6
- openstackclient/image/v2/image.py +59 -12
- openstackclient/image/v2/metadef_objects.py +8 -2
- openstackclient/image/v2/metadef_properties.py +9 -2
- openstackclient/network/client.py +0 -6
- openstackclient/network/v2/floating_ip.py +58 -29
- openstackclient/network/v2/network_qos_rule.py +3 -11
- openstackclient/network/v2/port.py +16 -0
- openstackclient/network/v2/router.py +1 -1
- openstackclient/network/v2/security_group.py +49 -7
- openstackclient/network/v2/security_group_rule.py +18 -1
- openstackclient/shell.py +1 -1
- openstackclient/tests/functional/base.py +5 -1
- openstackclient/tests/functional/compute/v2/test_keypair.py +41 -5
- openstackclient/tests/functional/identity/v3/test_access_rule.py +1 -1
- openstackclient/tests/functional/identity/v3/test_application_credential.py +7 -7
- openstackclient/tests/functional/image/v2/test_image.py +36 -14
- openstackclient/tests/functional/volume/v2/test_volume.py +1 -1
- openstackclient/tests/functional/volume/v3/test_volume.py +2 -2
- openstackclient/tests/unit/api/test_volume_v2.py +124 -0
- openstackclient/tests/unit/api/test_volume_v3.py +124 -0
- openstackclient/tests/unit/compute/v2/fakes.py +81 -305
- openstackclient/tests/unit/compute/v2/test_console.py +18 -1
- openstackclient/tests/unit/compute/v2/test_console_connection.py +72 -0
- openstackclient/tests/unit/compute/v2/test_flavor.py +160 -175
- openstackclient/tests/unit/compute/v2/test_keypair.py +12 -5
- openstackclient/tests/unit/compute/v2/test_server.py +211 -97
- openstackclient/tests/unit/compute/v2/test_server_backup.py +32 -71
- openstackclient/tests/unit/compute/v2/test_server_event.py +2 -2
- openstackclient/tests/unit/compute/v2/test_server_image.py +33 -72
- openstackclient/tests/unit/compute/v2/test_server_migration.py +4 -4
- openstackclient/tests/unit/identity/v3/test_application_credential.py +93 -65
- openstackclient/tests/unit/identity/v3/test_domain.py +117 -107
- openstackclient/tests/unit/identity/v3/test_group.py +353 -202
- openstackclient/tests/unit/identity/v3/test_project.py +46 -53
- openstackclient/tests/unit/identity/v3/test_role.py +2 -8
- openstackclient/tests/unit/identity/v3/test_user.py +86 -6
- openstackclient/tests/unit/image/v1/test_image.py +55 -9
- openstackclient/tests/unit/image/v2/test_image.py +128 -58
- openstackclient/tests/unit/image/v2/test_metadef_objects.py +22 -0
- openstackclient/tests/unit/image/v2/test_metadef_properties.py +24 -10
- openstackclient/tests/unit/network/v2/fakes.py +406 -485
- openstackclient/tests/unit/network/v2/test_floating_ip_network.py +13 -19
- openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +2 -2
- openstackclient/tests/unit/network/v2/test_ndp_proxy.py +3 -5
- openstackclient/tests/unit/network/v2/test_network.py +4 -4
- openstackclient/tests/unit/network/v2/test_network_agent.py +15 -29
- openstackclient/tests/unit/network/v2/test_network_qos_policy.py +16 -19
- openstackclient/tests/unit/network/v2/test_network_qos_rule.py +79 -152
- openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +4 -6
- openstackclient/tests/unit/network/v2/test_network_rbac.py +2 -2
- openstackclient/tests/unit/network/v2/test_port.py +57 -17
- openstackclient/tests/unit/network/v2/test_router.py +73 -57
- openstackclient/tests/unit/network/v2/test_security_group_network.py +31 -27
- openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +1 -3
- openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +82 -39
- openstackclient/tests/unit/volume/v2/fakes.py +1 -2
- openstackclient/tests/unit/volume/v2/test_service.py +57 -91
- openstackclient/tests/unit/volume/v2/test_volume.py +466 -410
- openstackclient/tests/unit/volume/v2/test_volume_backup.py +141 -148
- openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +293 -283
- openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +61 -71
- openstackclient/tests/unit/volume/v3/test_service.py +221 -141
- openstackclient/tests/unit/volume/v3/test_volume.py +569 -534
- openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
- openstackclient/tests/unit/volume/v3/test_volume_backup.py +198 -203
- openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +682 -47
- openstackclient/volume/v2/service.py +41 -38
- openstackclient/volume/v2/volume.py +140 -88
- openstackclient/volume/v2/volume_backup.py +9 -3
- openstackclient/volume/v2/volume_snapshot.py +121 -84
- openstackclient/volume/v3/block_storage_log_level.py +22 -28
- openstackclient/volume/v3/service.py +105 -14
- openstackclient/volume/v3/volume.py +287 -99
- openstackclient/volume/v3/volume_backup.py +24 -19
- openstackclient/volume/v3/volume_group.py +1 -1
- openstackclient/volume/v3/volume_snapshot.py +485 -10
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/AUTHORS +13 -0
- python_openstackclient-8.2.0.dist-info/METADATA +264 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/RECORD +104 -98
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/entry_points.txt +7 -6
- python_openstackclient-8.2.0.dist-info/pbr.json +1 -0
- python_openstackclient-8.0.0.dist-info/METADATA +0 -166
- python_openstackclient-8.0.0.dist-info/pbr.json +0 -1
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/LICENSE +0 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/WHEEL +0 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
19
|
from base64 import b64encode
|
|
20
|
+
import copy
|
|
20
21
|
import logging
|
|
21
22
|
import os
|
|
22
23
|
import sys
|
|
23
24
|
import typing as ty
|
|
25
|
+
import urllib.parse
|
|
24
26
|
|
|
25
|
-
from cinderclient import api_versions
|
|
26
27
|
from openstack import exceptions as sdk_exceptions
|
|
27
28
|
from openstack.image import image_signer
|
|
29
|
+
from openstack import utils as sdk_utils
|
|
28
30
|
from osc_lib.api import utils as api_utils
|
|
29
31
|
from osc_lib.cli import format_columns
|
|
30
32
|
from osc_lib.cli import parseractions
|
|
@@ -53,6 +55,19 @@ DISK_CHOICES = [
|
|
|
53
55
|
"iso",
|
|
54
56
|
"ploop",
|
|
55
57
|
]
|
|
58
|
+
# A list of openstacksdk Image object attributes (values) that named
|
|
59
|
+
# differently from actual properties stored by Glance (keys).
|
|
60
|
+
IMAGE_ATTRIBUTES_CUSTOM_NAMES = {
|
|
61
|
+
'os_hidden': 'is_hidden',
|
|
62
|
+
'protected': 'is_protected',
|
|
63
|
+
'os_hash_algo': 'hash_algo',
|
|
64
|
+
'os_hash_value': 'hash_value',
|
|
65
|
+
'img_config_drive': 'needs_config_drive',
|
|
66
|
+
'os_secure_boot': 'needs_secure_boot',
|
|
67
|
+
'hw_vif_multiqueue_enabled': 'is_hw_vif_multiqueue_enabled',
|
|
68
|
+
'hw_boot_menu': 'is_hw_boot_menu_enabled',
|
|
69
|
+
'auto_disk_config': 'has_auto_disk_config',
|
|
70
|
+
}
|
|
56
71
|
MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"]
|
|
57
72
|
|
|
58
73
|
LOG = logging.getLogger(__name__)
|
|
@@ -83,6 +98,9 @@ def _format_image(image, human_readable=False):
|
|
|
83
98
|
'virtual_size',
|
|
84
99
|
'min_ram',
|
|
85
100
|
'schema',
|
|
101
|
+
'is_hidden',
|
|
102
|
+
'hash_algo',
|
|
103
|
+
'hash_value',
|
|
86
104
|
]
|
|
87
105
|
|
|
88
106
|
# TODO(gtema/anybody): actually it should be possible to drop this method,
|
|
@@ -576,7 +594,7 @@ class CreateImage(command.ShowOne):
|
|
|
576
594
|
return _format_image(image)
|
|
577
595
|
|
|
578
596
|
def _take_action_volume(self, parsed_args):
|
|
579
|
-
volume_client = self.app.client_manager.volume
|
|
597
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
580
598
|
|
|
581
599
|
unsupported_opts = {
|
|
582
600
|
# 'name', # 'name' is a positional argument and will always exist
|
|
@@ -607,15 +625,14 @@ class CreateImage(command.ShowOne):
|
|
|
607
625
|
# version
|
|
608
626
|
LOG.warning(msg % opt_name)
|
|
609
627
|
|
|
610
|
-
source_volume =
|
|
611
|
-
|
|
612
|
-
parsed_args.volume,
|
|
628
|
+
source_volume = volume_client.find_volume(
|
|
629
|
+
parsed_args.volume, ignore_missing=False
|
|
613
630
|
)
|
|
614
631
|
kwargs: dict[str, ty.Any] = {
|
|
615
632
|
'visibility': None,
|
|
616
633
|
'protected': None,
|
|
617
634
|
}
|
|
618
|
-
if
|
|
635
|
+
if not sdk_utils.supports_microversion(volume_client, '3.1'):
|
|
619
636
|
if parsed_args.visibility or parsed_args.is_protected is not None:
|
|
620
637
|
msg = _(
|
|
621
638
|
'--os-volume-api-version 3.1 or greater is required '
|
|
@@ -627,15 +644,15 @@ class CreateImage(command.ShowOne):
|
|
|
627
644
|
kwargs['visibility'] = parsed_args.visibility or 'private'
|
|
628
645
|
kwargs['protected'] = parsed_args.is_protected or False
|
|
629
646
|
|
|
630
|
-
response
|
|
647
|
+
response = volume_client.upload_volume_to_image(
|
|
631
648
|
source_volume.id,
|
|
632
|
-
parsed_args.force,
|
|
633
649
|
parsed_args.name,
|
|
634
|
-
parsed_args.
|
|
635
|
-
parsed_args.disk_format,
|
|
650
|
+
force=parsed_args.force,
|
|
651
|
+
disk_format=parsed_args.disk_format,
|
|
652
|
+
container_format=parsed_args.container_format,
|
|
636
653
|
**kwargs,
|
|
637
654
|
)
|
|
638
|
-
info =
|
|
655
|
+
info = copy.deepcopy(response)
|
|
639
656
|
try:
|
|
640
657
|
info['volume_type'] = info['volume_type']['name']
|
|
641
658
|
except TypeError:
|
|
@@ -889,6 +906,8 @@ class ListImage(command.Lister):
|
|
|
889
906
|
'visibility',
|
|
890
907
|
'is_protected',
|
|
891
908
|
'owner_id',
|
|
909
|
+
'hash_algo',
|
|
910
|
+
'hash_value',
|
|
892
911
|
'tags',
|
|
893
912
|
)
|
|
894
913
|
column_headers: tuple[str, ...] = (
|
|
@@ -902,6 +921,8 @@ class ListImage(command.Lister):
|
|
|
902
921
|
'Visibility',
|
|
903
922
|
'Protected',
|
|
904
923
|
'Project',
|
|
924
|
+
'Hash Algorithm',
|
|
925
|
+
'Hash Value',
|
|
905
926
|
'Tags',
|
|
906
927
|
)
|
|
907
928
|
else:
|
|
@@ -1052,6 +1073,16 @@ class SaveImage(command.Command):
|
|
|
1052
1073
|
|
|
1053
1074
|
def get_parser(self, prog_name):
|
|
1054
1075
|
parser = super().get_parser(prog_name)
|
|
1076
|
+
parser.add_argument(
|
|
1077
|
+
"--chunk-size",
|
|
1078
|
+
type=int,
|
|
1079
|
+
default=1024,
|
|
1080
|
+
metavar="<chunk-size>",
|
|
1081
|
+
help=_(
|
|
1082
|
+
"Size in bytes to read from the wire and buffer at one "
|
|
1083
|
+
"time (default: 1024)"
|
|
1084
|
+
),
|
|
1085
|
+
)
|
|
1055
1086
|
parser.add_argument(
|
|
1056
1087
|
"--file",
|
|
1057
1088
|
metavar="<filename>",
|
|
@@ -1076,7 +1107,12 @@ class SaveImage(command.Command):
|
|
|
1076
1107
|
if output_file is None:
|
|
1077
1108
|
output_file = getattr(sys.stdout, "buffer", sys.stdout)
|
|
1078
1109
|
|
|
1079
|
-
image_client.download_image(
|
|
1110
|
+
image_client.download_image(
|
|
1111
|
+
image.id,
|
|
1112
|
+
stream=True,
|
|
1113
|
+
output=output_file,
|
|
1114
|
+
chunk_size=parsed_args.chunk_size,
|
|
1115
|
+
)
|
|
1080
1116
|
|
|
1081
1117
|
|
|
1082
1118
|
class SetImage(command.Command):
|
|
@@ -1477,6 +1513,11 @@ class UnsetImage(command.Command):
|
|
|
1477
1513
|
)
|
|
1478
1514
|
new_props.pop(k, None)
|
|
1479
1515
|
kwargs['properties'] = new_props
|
|
1516
|
+
elif (
|
|
1517
|
+
k in IMAGE_ATTRIBUTES_CUSTOM_NAMES
|
|
1518
|
+
and IMAGE_ATTRIBUTES_CUSTOM_NAMES[k] in image
|
|
1519
|
+
):
|
|
1520
|
+
delattr(image, IMAGE_ATTRIBUTES_CUSTOM_NAMES[k])
|
|
1480
1521
|
else:
|
|
1481
1522
|
LOG.error(
|
|
1482
1523
|
_(
|
|
@@ -1744,6 +1785,12 @@ class ImportImage(command.ShowOne):
|
|
|
1744
1785
|
"'--method=web-download'"
|
|
1745
1786
|
)
|
|
1746
1787
|
raise exceptions.CommandError(msg)
|
|
1788
|
+
_parsed = urllib.parse.urlparse(parsed_args.uri)
|
|
1789
|
+
if not all({_parsed.scheme, _parsed.netloc}):
|
|
1790
|
+
msg = _("'%(uri)s' is not a valid url")
|
|
1791
|
+
raise exceptions.CommandError(
|
|
1792
|
+
msg % {'uri': parsed_args.uri},
|
|
1793
|
+
)
|
|
1747
1794
|
else:
|
|
1748
1795
|
if parsed_args.uri:
|
|
1749
1796
|
msg = _(
|
|
@@ -123,8 +123,11 @@ class DeleteMetadefObject(command.Command):
|
|
|
123
123
|
parser.add_argument(
|
|
124
124
|
"objects",
|
|
125
125
|
metavar="<object>",
|
|
126
|
-
nargs="
|
|
127
|
-
help=_(
|
|
126
|
+
nargs="*",
|
|
127
|
+
help=_(
|
|
128
|
+
"Metadef object(s) to delete (name) "
|
|
129
|
+
"(omit this argument to delete all objects in the namespace)"
|
|
130
|
+
),
|
|
128
131
|
)
|
|
129
132
|
return parser
|
|
130
133
|
|
|
@@ -133,6 +136,9 @@ class DeleteMetadefObject(command.Command):
|
|
|
133
136
|
|
|
134
137
|
namespace = parsed_args.namespace
|
|
135
138
|
|
|
139
|
+
if not parsed_args.objects:
|
|
140
|
+
return image_client.delete_all_metadef_objects(namespace)
|
|
141
|
+
|
|
136
142
|
result = 0
|
|
137
143
|
for obj in parsed_args.objects:
|
|
138
144
|
try:
|
|
@@ -124,14 +124,21 @@ class DeleteMetadefProperty(command.Command):
|
|
|
124
124
|
parser.add_argument(
|
|
125
125
|
"properties",
|
|
126
126
|
metavar="<property>",
|
|
127
|
-
nargs="
|
|
128
|
-
help=_(
|
|
127
|
+
nargs="*",
|
|
128
|
+
help=_(
|
|
129
|
+
"Metadef properties to delete (name) "
|
|
130
|
+
"(omit this argument to delete all properties in the namespace)"
|
|
131
|
+
),
|
|
129
132
|
)
|
|
130
133
|
return parser
|
|
131
134
|
|
|
132
135
|
def take_action(self, parsed_args):
|
|
133
136
|
image_client = self.app.client_manager.image
|
|
134
137
|
|
|
138
|
+
if not parsed_args.properties:
|
|
139
|
+
image_client.delete_all_metadef_properties(parsed_args.namespace)
|
|
140
|
+
return
|
|
141
|
+
|
|
135
142
|
result = 0
|
|
136
143
|
for prop in parsed_args.properties:
|
|
137
144
|
try:
|
|
@@ -27,12 +27,6 @@ API_VERSIONS = ('2.0', '2')
|
|
|
27
27
|
|
|
28
28
|
def make_client(instance):
|
|
29
29
|
"""Returns a network proxy"""
|
|
30
|
-
# NOTE(dtroyer): As of osc-lib 1.8.0 and OpenStackSDK 0.10.0 the
|
|
31
|
-
# old Profile interface and separate client creation
|
|
32
|
-
# for each API that uses the SDK is unnecessary. This
|
|
33
|
-
# callback remains as a remnant of the original plugin
|
|
34
|
-
# interface and to avoid the code churn of changing all
|
|
35
|
-
# of the existing references.
|
|
36
30
|
LOG.debug(
|
|
37
31
|
'Network client initialized using OpenStack SDK: %s',
|
|
38
32
|
instance.sdk_connection.network,
|
|
@@ -243,18 +243,26 @@ class ListFloatingIP(common.NetworkAndComputeLister):
|
|
|
243
243
|
parser.add_argument(
|
|
244
244
|
'--network',
|
|
245
245
|
metavar='<network>',
|
|
246
|
+
dest='networks',
|
|
247
|
+
action='append',
|
|
246
248
|
help=self.enhance_help_neutron(
|
|
247
249
|
_(
|
|
248
|
-
"List floating IP(s) according to "
|
|
249
|
-
"
|
|
250
|
+
"List floating IP(s) according to given network "
|
|
251
|
+
"(name or ID) "
|
|
252
|
+
"(repeat option to fiter on multiple networks)"
|
|
250
253
|
)
|
|
251
254
|
),
|
|
252
255
|
)
|
|
253
256
|
parser.add_argument(
|
|
254
257
|
'--port',
|
|
255
258
|
metavar='<port>',
|
|
259
|
+
dest='ports',
|
|
260
|
+
action='append',
|
|
256
261
|
help=self.enhance_help_neutron(
|
|
257
|
-
_(
|
|
262
|
+
_(
|
|
263
|
+
"List floating IP(s) according to given port (name or ID) "
|
|
264
|
+
"(repeat option to fiter on multiple ports)"
|
|
265
|
+
)
|
|
258
266
|
),
|
|
259
267
|
)
|
|
260
268
|
parser.add_argument(
|
|
@@ -271,14 +279,6 @@ class ListFloatingIP(common.NetworkAndComputeLister):
|
|
|
271
279
|
_("List floating IP(s) according to given floating IP address")
|
|
272
280
|
),
|
|
273
281
|
)
|
|
274
|
-
parser.add_argument(
|
|
275
|
-
'--long',
|
|
276
|
-
action='store_true',
|
|
277
|
-
default=False,
|
|
278
|
-
help=self.enhance_help_neutron(
|
|
279
|
-
_("List additional fields in output")
|
|
280
|
-
),
|
|
281
|
-
)
|
|
282
282
|
parser.add_argument(
|
|
283
283
|
'--status',
|
|
284
284
|
metavar='<status>',
|
|
@@ -295,8 +295,8 @@ class ListFloatingIP(common.NetworkAndComputeLister):
|
|
|
295
295
|
metavar='<project>',
|
|
296
296
|
help=self.enhance_help_neutron(
|
|
297
297
|
_(
|
|
298
|
-
"List floating IP(s) according to given project
|
|
299
|
-
"ID)"
|
|
298
|
+
"List floating IP(s) according to given project "
|
|
299
|
+
"(name or ID) "
|
|
300
300
|
)
|
|
301
301
|
),
|
|
302
302
|
)
|
|
@@ -304,13 +304,27 @@ class ListFloatingIP(common.NetworkAndComputeLister):
|
|
|
304
304
|
parser.add_argument(
|
|
305
305
|
'--router',
|
|
306
306
|
metavar='<router>',
|
|
307
|
+
dest='routers',
|
|
308
|
+
action='append',
|
|
307
309
|
help=self.enhance_help_neutron(
|
|
308
|
-
_(
|
|
310
|
+
_(
|
|
311
|
+
"List floating IP(s) according to given router "
|
|
312
|
+
"(name or ID) "
|
|
313
|
+
"(repeat option to fiter on multiple routers)"
|
|
314
|
+
)
|
|
309
315
|
),
|
|
310
316
|
)
|
|
311
317
|
_tag.add_tag_filtering_option_to_parser(
|
|
312
318
|
parser, _('floating IP'), enhance_help=self.enhance_help_neutron
|
|
313
319
|
)
|
|
320
|
+
parser.add_argument(
|
|
321
|
+
'--long',
|
|
322
|
+
action='store_true',
|
|
323
|
+
default=False,
|
|
324
|
+
help=self.enhance_help_neutron(
|
|
325
|
+
_("List additional fields in output")
|
|
326
|
+
),
|
|
327
|
+
)
|
|
314
328
|
|
|
315
329
|
return parser
|
|
316
330
|
|
|
@@ -354,22 +368,33 @@ class ListFloatingIP(common.NetworkAndComputeLister):
|
|
|
354
368
|
|
|
355
369
|
query = {}
|
|
356
370
|
|
|
357
|
-
if parsed_args.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
371
|
+
if parsed_args.networks is not None:
|
|
372
|
+
network_ids = []
|
|
373
|
+
for network in parsed_args.networks:
|
|
374
|
+
network_id = network_client.find_network(
|
|
375
|
+
network, ignore_missing=False
|
|
376
|
+
).id
|
|
377
|
+
network_ids.append(network_id)
|
|
378
|
+
query['floating_network_id'] = network_ids
|
|
379
|
+
|
|
380
|
+
if parsed_args.ports is not None:
|
|
381
|
+
port_ids = []
|
|
382
|
+
for port in parsed_args.ports:
|
|
383
|
+
port_id = network_client.find_port(
|
|
384
|
+
port, ignore_missing=False
|
|
385
|
+
).id
|
|
386
|
+
port_ids.append(port_id)
|
|
387
|
+
query['port_id'] = port_ids
|
|
388
|
+
|
|
367
389
|
if parsed_args.fixed_ip_address is not None:
|
|
368
390
|
query['fixed_ip_address'] = parsed_args.fixed_ip_address
|
|
391
|
+
|
|
369
392
|
if parsed_args.floating_ip_address is not None:
|
|
370
393
|
query['floating_ip_address'] = parsed_args.floating_ip_address
|
|
394
|
+
|
|
371
395
|
if parsed_args.status:
|
|
372
396
|
query['status'] = parsed_args.status
|
|
397
|
+
|
|
373
398
|
if parsed_args.project is not None:
|
|
374
399
|
project = identity_common.find_project(
|
|
375
400
|
identity_client,
|
|
@@ -377,11 +402,15 @@ class ListFloatingIP(common.NetworkAndComputeLister):
|
|
|
377
402
|
parsed_args.project_domain,
|
|
378
403
|
)
|
|
379
404
|
query['project_id'] = project.id
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
405
|
+
|
|
406
|
+
if parsed_args.routers is not None:
|
|
407
|
+
router_ids = []
|
|
408
|
+
for router in parsed_args.routers:
|
|
409
|
+
router_id = network_client.find_router(
|
|
410
|
+
router, ignore_missing=False
|
|
411
|
+
).id
|
|
412
|
+
router_ids.append(router_id)
|
|
413
|
+
query['router_id'] = router_ids
|
|
385
414
|
|
|
386
415
|
_tag.get_tag_filtering_args(parsed_args, query)
|
|
387
416
|
|
|
@@ -73,7 +73,7 @@ ACTION_SHOW = 'get'
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def _get_columns(item):
|
|
76
|
-
hidden_columns = ['location', 'tenant_id']
|
|
76
|
+
hidden_columns = ['location', 'name', 'tenant_id']
|
|
77
77
|
return utils.get_osc_show_columns_for_sdk_resource(
|
|
78
78
|
item, {}, hidden_columns
|
|
79
79
|
)
|
|
@@ -148,14 +148,6 @@ def _get_attrs(network_client, parsed_args, is_create=False):
|
|
|
148
148
|
return attrs
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
def _get_item_properties(item, fields):
|
|
152
|
-
"""Return a tuple containing the item properties."""
|
|
153
|
-
row = []
|
|
154
|
-
for field in fields:
|
|
155
|
-
row.append(item.get(field, ''))
|
|
156
|
-
return tuple(row)
|
|
157
|
-
|
|
158
|
-
|
|
159
151
|
def _rule_action_call(client, action, rule_type):
|
|
160
152
|
rule_type = rule_type.replace('-', '_')
|
|
161
153
|
func_name = f'{action}_qos_{rule_type}_rule'
|
|
@@ -357,10 +349,10 @@ class ListNetworkQosRule(command.Lister):
|
|
|
357
349
|
qos = client.find_qos_policy(
|
|
358
350
|
parsed_args.qos_policy, ignore_missing=False
|
|
359
351
|
)
|
|
360
|
-
|
|
352
|
+
|
|
361
353
|
return (
|
|
362
354
|
column_headers,
|
|
363
|
-
(
|
|
355
|
+
(utils.get_dict_properties(s, columns) for s in qos.rules),
|
|
364
356
|
)
|
|
365
357
|
|
|
366
358
|
|
|
@@ -1316,6 +1316,18 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs):
|
|
|
1316
1316
|
default=False,
|
|
1317
1317
|
help=_("Clear hints for the port"),
|
|
1318
1318
|
)
|
|
1319
|
+
parser.add_argument(
|
|
1320
|
+
'--device',
|
|
1321
|
+
action='store_true',
|
|
1322
|
+
default=False,
|
|
1323
|
+
help=_("Clear device ID for the port."),
|
|
1324
|
+
)
|
|
1325
|
+
parser.add_argument(
|
|
1326
|
+
'--device-owner',
|
|
1327
|
+
action='store_true',
|
|
1328
|
+
default=False,
|
|
1329
|
+
help=_("Clear device owner for the port."),
|
|
1330
|
+
)
|
|
1319
1331
|
_tag.add_tag_option_to_parser_for_unset(parser, _('port'))
|
|
1320
1332
|
parser.add_argument(
|
|
1321
1333
|
'port',
|
|
@@ -1382,6 +1394,10 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs):
|
|
|
1382
1394
|
attrs['binding:host_id'] = None
|
|
1383
1395
|
if parsed_args.hints:
|
|
1384
1396
|
attrs['hints'] = None
|
|
1397
|
+
if parsed_args.device:
|
|
1398
|
+
attrs['device_id'] = ''
|
|
1399
|
+
if parsed_args.device_owner:
|
|
1400
|
+
attrs['device_owner'] = ''
|
|
1385
1401
|
|
|
1386
1402
|
attrs.update(
|
|
1387
1403
|
self._parse_extra_properties(parsed_args.extra_properties)
|
|
@@ -76,7 +76,7 @@ def _get_columns(item):
|
|
|
76
76
|
}
|
|
77
77
|
if hasattr(item, 'interfaces_info'):
|
|
78
78
|
column_map['interfaces_info'] = 'interfaces_info'
|
|
79
|
-
invisible_columns = ['location']
|
|
79
|
+
invisible_columns = ['location', 'tenant_id']
|
|
80
80
|
if item.is_ha is None:
|
|
81
81
|
invisible_columns.append('is_ha')
|
|
82
82
|
column_map.pop('is_ha')
|
|
@@ -89,9 +89,8 @@ def _get_columns(item):
|
|
|
89
89
|
# We still support Nova managed security groups, where we have tenant_id.
|
|
90
90
|
column_map = {
|
|
91
91
|
'security_group_rules': 'rules',
|
|
92
|
-
'tenant_id': 'project_id',
|
|
93
92
|
}
|
|
94
|
-
hidden_columns = ['location']
|
|
93
|
+
hidden_columns = ['location', 'tenant_id']
|
|
95
94
|
return utils.get_osc_show_columns_for_sdk_resource(
|
|
96
95
|
item, column_map, hidden_columns
|
|
97
96
|
)
|
|
@@ -186,7 +185,8 @@ class CreateSecurityGroup(
|
|
|
186
185
|
parsed_args.name,
|
|
187
186
|
description,
|
|
188
187
|
)
|
|
189
|
-
display_columns,
|
|
188
|
+
display_columns = ('description', 'id', 'name', 'project_id', 'rules')
|
|
189
|
+
property_columns = ('description', 'id', 'name', 'tenant_id', 'rules')
|
|
190
190
|
data = utils.get_dict_properties(
|
|
191
191
|
obj, property_columns, formatters=_formatters_compute
|
|
192
192
|
)
|
|
@@ -222,7 +222,14 @@ class DeleteSecurityGroup(common.NetworkAndComputeDelete):
|
|
|
222
222
|
# the OSC minimum requirements include SDK 1.0.
|
|
223
223
|
class ListSecurityGroup(common.NetworkAndComputeLister):
|
|
224
224
|
_description = _("List security groups")
|
|
225
|
-
FIELDS_TO_RETRIEVE = [
|
|
225
|
+
FIELDS_TO_RETRIEVE = [
|
|
226
|
+
'id',
|
|
227
|
+
'name',
|
|
228
|
+
'description',
|
|
229
|
+
'project_id',
|
|
230
|
+
'tags',
|
|
231
|
+
'shared',
|
|
232
|
+
]
|
|
226
233
|
|
|
227
234
|
def update_parser_network(self, parser):
|
|
228
235
|
if not self.is_docs_build:
|
|
@@ -245,6 +252,23 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
|
|
|
245
252
|
identity_common.add_project_domain_option_to_parser(
|
|
246
253
|
parser, enhance_help=self.enhance_help_neutron
|
|
247
254
|
)
|
|
255
|
+
|
|
256
|
+
shared_group = parser.add_mutually_exclusive_group()
|
|
257
|
+
shared_group.add_argument(
|
|
258
|
+
'--share',
|
|
259
|
+
action='store_true',
|
|
260
|
+
dest='shared',
|
|
261
|
+
default=None,
|
|
262
|
+
help=_("List security groups shared between projects"),
|
|
263
|
+
)
|
|
264
|
+
shared_group.add_argument(
|
|
265
|
+
'--no-share',
|
|
266
|
+
action='store_false',
|
|
267
|
+
dest='shared',
|
|
268
|
+
default=None,
|
|
269
|
+
help=_("List security groups not shared between projects"),
|
|
270
|
+
)
|
|
271
|
+
|
|
248
272
|
_tag.add_tag_filtering_option_to_parser(
|
|
249
273
|
parser, _('security group'), enhance_help=self.enhance_help_neutron
|
|
250
274
|
)
|
|
@@ -272,13 +296,30 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
|
|
|
272
296
|
).id
|
|
273
297
|
filters['project_id'] = project_id
|
|
274
298
|
|
|
299
|
+
if parsed_args.shared is not None:
|
|
300
|
+
filters['shared'] = parsed_args.shared
|
|
301
|
+
|
|
275
302
|
_tag.get_tag_filtering_args(parsed_args, filters)
|
|
276
303
|
data = client.security_groups(
|
|
277
304
|
fields=self.FIELDS_TO_RETRIEVE, **filters
|
|
278
305
|
)
|
|
279
306
|
|
|
280
|
-
columns = (
|
|
281
|
-
|
|
307
|
+
columns = (
|
|
308
|
+
"id",
|
|
309
|
+
"name",
|
|
310
|
+
"description",
|
|
311
|
+
"project_id",
|
|
312
|
+
"tags",
|
|
313
|
+
"is_shared",
|
|
314
|
+
)
|
|
315
|
+
column_headers = (
|
|
316
|
+
"ID",
|
|
317
|
+
"Name",
|
|
318
|
+
"Description",
|
|
319
|
+
"Project",
|
|
320
|
+
"Tags",
|
|
321
|
+
"Shared",
|
|
322
|
+
)
|
|
282
323
|
return (
|
|
283
324
|
column_headers,
|
|
284
325
|
(
|
|
@@ -420,7 +461,8 @@ class ShowSecurityGroup(common.NetworkAndComputeShowOne):
|
|
|
420
461
|
|
|
421
462
|
def take_action_compute(self, client, parsed_args):
|
|
422
463
|
obj = compute_v2.find_security_group(client, parsed_args.group)
|
|
423
|
-
display_columns,
|
|
464
|
+
display_columns = ('description', 'id', 'name', 'project_id', 'rules')
|
|
465
|
+
property_columns = ('description', 'id', 'name', 'tenant_id', 'rules')
|
|
424
466
|
data = utils.get_dict_properties(
|
|
425
467
|
obj, property_columns, formatters=_formatters_compute
|
|
426
468
|
)
|
|
@@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__)
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def _get_columns(item):
|
|
33
|
-
hidden_columns = ['location', 'tenant_id']
|
|
33
|
+
hidden_columns = ['location', 'name', 'tenant_id', 'tags']
|
|
34
34
|
return utils.get_osc_show_columns_for_sdk_resource(
|
|
35
35
|
item, {}, hidden_columns
|
|
36
36
|
)
|
|
@@ -427,6 +427,14 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
|
|
|
427
427
|
_("**Deprecated** This argument is no longer needed")
|
|
428
428
|
),
|
|
429
429
|
)
|
|
430
|
+
parser.add_argument(
|
|
431
|
+
'--project',
|
|
432
|
+
metavar='<project>',
|
|
433
|
+
help=self.enhance_help_neutron(_("Owner's project (name or ID)")),
|
|
434
|
+
)
|
|
435
|
+
identity_common.add_project_domain_option_to_parser(
|
|
436
|
+
parser, enhance_help=self.enhance_help_neutron
|
|
437
|
+
)
|
|
430
438
|
return parser
|
|
431
439
|
|
|
432
440
|
def update_parser_compute(self, parser):
|
|
@@ -503,6 +511,15 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
|
|
|
503
511
|
query['direction'] = 'egress'
|
|
504
512
|
if parsed_args.protocol is not None:
|
|
505
513
|
query['protocol'] = parsed_args.protocol
|
|
514
|
+
if parsed_args.project is not None:
|
|
515
|
+
identity_client = self.app.client_manager.identity
|
|
516
|
+
project_id = identity_common.find_project(
|
|
517
|
+
identity_client,
|
|
518
|
+
parsed_args.project,
|
|
519
|
+
parsed_args.project_domain,
|
|
520
|
+
).id
|
|
521
|
+
query['tenant_id'] = project_id
|
|
522
|
+
query['project_id'] = project_id
|
|
506
523
|
|
|
507
524
|
rules = [
|
|
508
525
|
self._format_network_security_group_rule(r)
|
openstackclient/shell.py
CHANGED
|
@@ -94,7 +94,7 @@ class OpenStackShell(shell.OpenStackShell):
|
|
|
94
94
|
# instead.
|
|
95
95
|
mod_versions = getattr(mod, 'API_VERSIONS', None)
|
|
96
96
|
if mod_versions is not None and not isinstance(
|
|
97
|
-
mod_versions,
|
|
97
|
+
mod_versions, dict | tuple
|
|
98
98
|
):
|
|
99
99
|
raise TypeError(
|
|
100
100
|
f'Plugin {mod} has incompatible API_VERSIONS. '
|
|
@@ -97,7 +97,11 @@ class TestCase(testtools.TestCase):
|
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
if parse_output:
|
|
100
|
-
|
|
100
|
+
try:
|
|
101
|
+
return json.loads(output)
|
|
102
|
+
except json.JSONDecodeError:
|
|
103
|
+
print(f'failed to decode: {output}')
|
|
104
|
+
raise
|
|
101
105
|
else:
|
|
102
106
|
return output
|
|
103
107
|
|