python-openstackclient 6.4.0__py3-none-any.whl → 6.5.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/common/availability_zone.py +4 -4
- openstackclient/compute/v2/server.py +173 -99
- openstackclient/identity/v3/mapping.py +25 -3
- openstackclient/identity/v3/policy.py +3 -1
- openstackclient/image/v2/image.py +37 -0
- openstackclient/image/v2/metadef_namespaces.py +25 -21
- openstackclient/image/v2/metadef_objects.py +26 -30
- openstackclient/image/v2/metadef_properties.py +60 -38
- openstackclient/network/v2/default_security_group_rule.py +23 -4
- openstackclient/network/v2/local_ip_association.py +1 -1
- openstackclient/network/v2/ndp_proxy.py +7 -3
- openstackclient/network/v2/network.py +2 -2
- openstackclient/network/v2/port.py +23 -7
- openstackclient/network/v2/subnet.py +1 -0
- openstackclient/tests/functional/base.py +7 -4
- openstackclient/tests/unit/compute/v2/test_server.py +72 -54
- openstackclient/tests/unit/identity/v3/test_mappings.py +9 -4
- openstackclient/tests/unit/identity/v3/test_trust.py +0 -2
- openstackclient/tests/unit/image/v1/fakes.py +1 -1
- openstackclient/tests/unit/image/v2/test_image.py +60 -0
- openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +5 -19
- openstackclient/tests/unit/integ/cli/test_shell.py +0 -2
- openstackclient/tests/unit/network/v2/fakes.py +1 -0
- openstackclient/tests/unit/network/v2/test_network.py +33 -0
- openstackclient/tests/unit/network/v2/test_network_trunk.py +6 -8
- openstackclient/tests/unit/network/v2/test_port.py +60 -18
- openstackclient/tests/unit/network/v2/test_subnet.py +92 -0
- openstackclient/tests/unit/network/v2/test_subnet_pool.py +11 -13
- openstackclient/tests/unit/test_shell.py +1 -7
- openstackclient/tests/unit/utils.py +8 -1
- openstackclient/tests/unit/volume/v1/test_volume.py +4 -6
- openstackclient/tests/unit/volume/v2/test_volume.py +4 -6
- openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +3 -5
- openstackclient/volume/v2/volume_type.py +3 -3
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/AUTHORS +4 -0
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/METADATA +3 -1
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/RECORD +42 -43
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/entry_points.txt +6 -5
- python_openstackclient-6.5.0.dist-info/pbr.json +1 -0
- openstackclient/tests/unit/common/test_parseractions.py +0 -233
- python_openstackclient-6.4.0.dist-info/pbr.json +0 -1
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/LICENSE +0 -0
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/WHEEL +0 -0
- {python_openstackclient-6.4.0.dist-info → python_openstackclient-6.5.0.dist-info}/top_level.txt +0 -0
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import copy
|
|
17
17
|
import logging
|
|
18
18
|
|
|
19
|
-
from
|
|
19
|
+
from openstack import exceptions as sdk_exceptions
|
|
20
20
|
from osc_lib.command import command
|
|
21
21
|
from osc_lib import utils
|
|
22
22
|
|
|
@@ -119,8 +119,8 @@ class ListAvailabilityZone(command.Lister):
|
|
|
119
119
|
def _get_compute_availability_zones(self, parsed_args):
|
|
120
120
|
compute_client = self.app.client_manager.sdk_connection.compute
|
|
121
121
|
try:
|
|
122
|
-
data = compute_client.availability_zones(details=True)
|
|
123
|
-
except
|
|
122
|
+
data = list(compute_client.availability_zones(details=True))
|
|
123
|
+
except sdk_exceptions.ForbiddenException: # policy doesn't allow
|
|
124
124
|
try:
|
|
125
125
|
data = compute_client.availability_zones(details=False)
|
|
126
126
|
except Exception:
|
|
@@ -135,7 +135,7 @@ class ListAvailabilityZone(command.Lister):
|
|
|
135
135
|
volume_client = self.app.client_manager.sdk_connection.volume
|
|
136
136
|
data = []
|
|
137
137
|
try:
|
|
138
|
-
data = volume_client.availability_zones()
|
|
138
|
+
data = list(volume_client.availability_zones())
|
|
139
139
|
except Exception as e:
|
|
140
140
|
LOG.debug('Volume availability zone exception: %s', e)
|
|
141
141
|
if parsed_args.volume:
|
|
@@ -149,16 +149,13 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
|
|
|
149
149
|
# Some commands using this routine were originally implemented with the
|
|
150
150
|
# nova python wrappers, and were later migrated to use the SDK. Map the
|
|
151
151
|
# SDK's property names to the original property names to maintain backward
|
|
152
|
-
# compatibility for existing users.
|
|
153
|
-
# and new name so users can consume the data by either name.
|
|
152
|
+
# compatibility for existing users.
|
|
154
153
|
column_map = {
|
|
155
154
|
'access_ipv4': 'accessIPv4',
|
|
156
155
|
'access_ipv6': 'accessIPv6',
|
|
157
156
|
'admin_password': 'adminPass',
|
|
158
|
-
'
|
|
159
|
-
'volumes': 'os-extended-volumes:volumes_attached',
|
|
157
|
+
'attached_volumes': 'volumes_attached',
|
|
160
158
|
'availability_zone': 'OS-EXT-AZ:availability_zone',
|
|
161
|
-
'block_device_mapping': 'block_device_mapping_v2',
|
|
162
159
|
'compute_host': 'OS-EXT-SRV-ATTR:host',
|
|
163
160
|
'created_at': 'created',
|
|
164
161
|
'disk_config': 'OS-DCF:diskConfig',
|
|
@@ -168,7 +165,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
|
|
|
168
165
|
'fault': 'fault',
|
|
169
166
|
'hostname': 'OS-EXT-SRV-ATTR:hostname',
|
|
170
167
|
'hypervisor_hostname': 'OS-EXT-SRV-ATTR:hypervisor_hostname',
|
|
171
|
-
'image_id': 'imageRef',
|
|
172
168
|
'instance_name': 'OS-EXT-SRV-ATTR:instance_name',
|
|
173
169
|
'is_locked': 'locked',
|
|
174
170
|
'kernel_id': 'OS-EXT-SRV-ATTR:kernel_id',
|
|
@@ -179,21 +175,56 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
|
|
|
179
175
|
'ramdisk_id': 'OS-EXT-SRV-ATTR:ramdisk_id',
|
|
180
176
|
'reservation_id': 'OS-EXT-SRV-ATTR:reservation_id',
|
|
181
177
|
'root_device_name': 'OS-EXT-SRV-ATTR:root_device_name',
|
|
182
|
-
'scheduler_hints': 'OS-SCH-HNT:scheduler_hints',
|
|
183
178
|
'task_state': 'OS-EXT-STS:task_state',
|
|
184
179
|
'terminated_at': 'OS-SRV-USG:terminated_at',
|
|
185
180
|
'updated_at': 'updated',
|
|
186
181
|
'user_data': 'OS-EXT-SRV-ATTR:user_data',
|
|
187
182
|
'vm_state': 'OS-EXT-STS:vm_state',
|
|
188
183
|
}
|
|
184
|
+
# Some columns returned by openstacksdk should not be shown because they're
|
|
185
|
+
# either irrelevant or duplicates
|
|
186
|
+
ignored_columns = {
|
|
187
|
+
# computed columns
|
|
188
|
+
'interface_ip',
|
|
189
|
+
'location',
|
|
190
|
+
'private_v4',
|
|
191
|
+
'private_v6',
|
|
192
|
+
'public_v4',
|
|
193
|
+
'public_v6',
|
|
194
|
+
# create-only columns
|
|
195
|
+
'block_device_mapping',
|
|
196
|
+
'image_id',
|
|
197
|
+
'max_count',
|
|
198
|
+
'min_count',
|
|
199
|
+
'scheduler_hints',
|
|
200
|
+
# aliases
|
|
201
|
+
'volumes',
|
|
202
|
+
# unnecessary
|
|
203
|
+
'links',
|
|
204
|
+
}
|
|
205
|
+
# Some columns are only present in certain responses and should not be
|
|
206
|
+
# shown otherwise.
|
|
207
|
+
optional_columns = {
|
|
208
|
+
'admin_password', # removed in 2.14
|
|
209
|
+
'fault', # only present in errored servers
|
|
210
|
+
'flavor_id', # removed in 2.47
|
|
211
|
+
'networks', # only present in create responses
|
|
212
|
+
'security_groups', # only present in create, detail responses
|
|
213
|
+
}
|
|
189
214
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
215
|
+
data = {}
|
|
216
|
+
for key, value in info.items():
|
|
217
|
+
if key in ignored_columns:
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
if key in optional_columns:
|
|
221
|
+
if info[key] is None:
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
alias = column_map.get(key)
|
|
225
|
+
data[alias or key] = value
|
|
226
|
+
|
|
227
|
+
info = data
|
|
197
228
|
|
|
198
229
|
# Convert the image blob to a name
|
|
199
230
|
image_info = info.get('image', {})
|
|
@@ -214,46 +245,57 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
|
|
|
214
245
|
# Convert the flavor blob to a name
|
|
215
246
|
flavor_info = info.get('flavor', {})
|
|
216
247
|
# Microversion 2.47 puts the embedded flavor into the server response
|
|
217
|
-
# body
|
|
218
|
-
|
|
219
|
-
if 'id' in flavor_info:
|
|
248
|
+
# body. The presence of the 'original_name' attribute indicates this.
|
|
249
|
+
if flavor_info.get('original_name') is None: # microversion < 2.47
|
|
220
250
|
flavor_id = flavor_info.get('id', '')
|
|
221
251
|
try:
|
|
222
252
|
flavor = utils.find_resource(compute_client.flavors, flavor_id)
|
|
223
253
|
info['flavor'] = "%s (%s)" % (flavor.name, flavor_id)
|
|
224
254
|
except Exception:
|
|
225
255
|
info['flavor'] = flavor_id
|
|
226
|
-
else:
|
|
256
|
+
else: # microversion >= 2.47
|
|
227
257
|
info['flavor'] = format_columns.DictColumn(flavor_info)
|
|
228
258
|
|
|
229
|
-
|
|
259
|
+
# there's a lot of redundant information in BDMs - strip it
|
|
260
|
+
if 'volumes_attached' in info:
|
|
230
261
|
info.update(
|
|
231
262
|
{
|
|
232
263
|
'volumes_attached': format_columns.ListDictColumn(
|
|
233
|
-
|
|
264
|
+
[
|
|
265
|
+
{
|
|
266
|
+
k: v
|
|
267
|
+
for k, v in volume.items()
|
|
268
|
+
if v is not None and k != 'location'
|
|
269
|
+
}
|
|
270
|
+
for volume in info.pop('volumes_attached') or []
|
|
271
|
+
]
|
|
234
272
|
)
|
|
235
273
|
}
|
|
236
274
|
)
|
|
275
|
+
|
|
237
276
|
if 'security_groups' in info:
|
|
238
277
|
info.update(
|
|
239
278
|
{
|
|
240
279
|
'security_groups': format_columns.ListDictColumn(
|
|
241
|
-
info.pop('security_groups')
|
|
280
|
+
info.pop('security_groups'),
|
|
242
281
|
)
|
|
243
282
|
}
|
|
244
283
|
)
|
|
284
|
+
|
|
245
285
|
if 'tags' in info:
|
|
246
286
|
info.update({'tags': format_columns.ListColumn(info.pop('tags'))})
|
|
247
287
|
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
288
|
+
# Map 'networks' to 'addresses', if present. Note that the 'networks' key
|
|
289
|
+
# is used for create responses, otherwise it's 'addresses'. We know it'll
|
|
290
|
+
# be set because this is one of our optional columns.
|
|
291
|
+
if 'networks' in info:
|
|
292
|
+
info['addresses'] = format_columns.DictListColumn(
|
|
293
|
+
info.pop('networks', {}),
|
|
294
|
+
)
|
|
295
|
+
else:
|
|
296
|
+
info['addresses'] = AddressesColumn(info.get('addresses', {}))
|
|
255
297
|
|
|
256
|
-
# Map 'metadata' field to 'properties'
|
|
298
|
+
# Map 'metadata' field to 'properties' and format
|
|
257
299
|
info['properties'] = format_columns.DictColumn(info.pop('metadata'))
|
|
258
300
|
|
|
259
301
|
# Migrate tenant_id to project_id naming
|
|
@@ -266,9 +308,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
|
|
|
266
308
|
info['OS-EXT-STS:power_state']
|
|
267
309
|
)
|
|
268
310
|
|
|
269
|
-
# Remove values that are long and not too useful
|
|
270
|
-
info.pop('links', None)
|
|
271
|
-
|
|
272
311
|
return info
|
|
273
312
|
|
|
274
313
|
|
|
@@ -841,7 +880,7 @@ class NICAction(argparse.Action):
|
|
|
841
880
|
"Invalid argument %s; characters ',' and '=' are not "
|
|
842
881
|
"allowed"
|
|
843
882
|
)
|
|
844
|
-
raise argparse.
|
|
883
|
+
raise argparse.ArgumentError(self, msg % values)
|
|
845
884
|
|
|
846
885
|
values = '='.join([self.key, values])
|
|
847
886
|
else:
|
|
@@ -869,7 +908,7 @@ class NICAction(argparse.Action):
|
|
|
869
908
|
"'net-id=net-uuid,port-id=port-uuid,v4-fixed-ip=ip-addr,"
|
|
870
909
|
"v6-fixed-ip=ip-addr,tag=tag'"
|
|
871
910
|
)
|
|
872
|
-
raise argparse.
|
|
911
|
+
raise argparse.ArgumentError(self, msg % values)
|
|
873
912
|
|
|
874
913
|
info[k] = v
|
|
875
914
|
|
|
@@ -878,7 +917,7 @@ class NICAction(argparse.Action):
|
|
|
878
917
|
'Invalid argument %s; either network or port should be '
|
|
879
918
|
'specified but not both'
|
|
880
919
|
)
|
|
881
|
-
raise argparse.
|
|
920
|
+
raise argparse.ArgumenteError(self, msg % values)
|
|
882
921
|
|
|
883
922
|
getattr(namespace, self.dest).append(info)
|
|
884
923
|
|
|
@@ -896,7 +935,7 @@ class BDMLegacyAction(argparse.Action):
|
|
|
896
935
|
"Invalid argument %s; argument must be of form "
|
|
897
936
|
"'dev-name=id[:type[:size[:delete-on-terminate]]]'"
|
|
898
937
|
)
|
|
899
|
-
raise argparse.
|
|
938
|
+
raise argparse.ArgumentError(self, msg % values)
|
|
900
939
|
|
|
901
940
|
mapping = {
|
|
902
941
|
'device_name': dev_name,
|
|
@@ -913,7 +952,7 @@ class BDMLegacyAction(argparse.Action):
|
|
|
913
952
|
"Invalid argument %s; 'type' must be one of: volume, "
|
|
914
953
|
"snapshot, image"
|
|
915
954
|
)
|
|
916
|
-
raise argparse.
|
|
955
|
+
raise argparse.ArgumentError(self, msg % values)
|
|
917
956
|
|
|
918
957
|
mapping['source_type'] = dev_map[1]
|
|
919
958
|
|
|
@@ -966,12 +1005,13 @@ class BDMAction(parseractions.MultiKeyValueAction):
|
|
|
966
1005
|
"Invalid keys %(invalid_keys)s specified.\n"
|
|
967
1006
|
"Valid keys are: %(valid_keys)s"
|
|
968
1007
|
)
|
|
969
|
-
raise argparse.
|
|
1008
|
+
raise argparse.ArgumentError(
|
|
1009
|
+
self,
|
|
970
1010
|
msg
|
|
971
1011
|
% {
|
|
972
1012
|
'invalid_keys': ', '.join(invalid_keys),
|
|
973
1013
|
'valid_keys': ', '.join(valid_keys),
|
|
974
|
-
}
|
|
1014
|
+
},
|
|
975
1015
|
)
|
|
976
1016
|
|
|
977
1017
|
missing_keys = [k for k in self.required_keys if k not in keys]
|
|
@@ -980,12 +1020,13 @@ class BDMAction(parseractions.MultiKeyValueAction):
|
|
|
980
1020
|
"Missing required keys %(missing_keys)s.\n"
|
|
981
1021
|
"Required keys are: %(required_keys)s"
|
|
982
1022
|
)
|
|
983
|
-
raise argparse.
|
|
1023
|
+
raise argparse.ArgumentError(
|
|
1024
|
+
self,
|
|
984
1025
|
msg
|
|
985
1026
|
% {
|
|
986
1027
|
'missing_keys': ', '.join(missing_keys),
|
|
987
1028
|
'required_keys': ', '.join(self.required_keys),
|
|
988
|
-
}
|
|
1029
|
+
},
|
|
989
1030
|
)
|
|
990
1031
|
|
|
991
1032
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
@@ -1336,10 +1377,19 @@ class CreateServer(command.ShowOne):
|
|
|
1336
1377
|
'(supported by --os-compute-api-version 2.74 or above)'
|
|
1337
1378
|
),
|
|
1338
1379
|
)
|
|
1380
|
+
parser.add_argument(
|
|
1381
|
+
'--server-group',
|
|
1382
|
+
metavar='<server-group>',
|
|
1383
|
+
help=_(
|
|
1384
|
+
"Server group to create the server within "
|
|
1385
|
+
"(this is an alias for '--hint group=<server-group-id>')"
|
|
1386
|
+
),
|
|
1387
|
+
)
|
|
1339
1388
|
parser.add_argument(
|
|
1340
1389
|
'--hint',
|
|
1341
1390
|
metavar='<key=value>',
|
|
1342
1391
|
action=parseractions.KeyValueAppendAction,
|
|
1392
|
+
dest='hints',
|
|
1343
1393
|
default={},
|
|
1344
1394
|
help=_('Hints for the scheduler'),
|
|
1345
1395
|
)
|
|
@@ -1858,13 +1908,20 @@ class CreateServer(command.ShowOne):
|
|
|
1858
1908
|
security_group_names.append(sg['name'])
|
|
1859
1909
|
|
|
1860
1910
|
hints = {}
|
|
1861
|
-
for key, values in parsed_args.
|
|
1911
|
+
for key, values in parsed_args.hints.items():
|
|
1862
1912
|
# only items with multiple values will result in a list
|
|
1863
1913
|
if len(values) == 1:
|
|
1864
1914
|
hints[key] = values[0]
|
|
1865
1915
|
else:
|
|
1866
1916
|
hints[key] = values
|
|
1867
1917
|
|
|
1918
|
+
if parsed_args.server_group:
|
|
1919
|
+
server_group_obj = utils.find_resource(
|
|
1920
|
+
compute_client.server_groups,
|
|
1921
|
+
parsed_args.server_group,
|
|
1922
|
+
)
|
|
1923
|
+
hints['group'] = server_group_obj.id
|
|
1924
|
+
|
|
1868
1925
|
if isinstance(parsed_args.config_drive, bool):
|
|
1869
1926
|
# NOTE(stephenfin): The API doesn't accept False as a value :'(
|
|
1870
1927
|
config_drive = parsed_args.config_drive or None
|
|
@@ -1987,9 +2044,8 @@ class CreateServer(command.ShowOne):
|
|
|
1987
2044
|
):
|
|
1988
2045
|
self.app.stdout.write('\n')
|
|
1989
2046
|
else:
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
raise SystemExit
|
|
2047
|
+
msg = _('Error creating server: %s') % parsed_args.server_name
|
|
2048
|
+
raise exceptions.CommandError(msg)
|
|
1993
2049
|
|
|
1994
2050
|
details = _prep_server_detail(compute_client, image_client, server)
|
|
1995
2051
|
return zip(*sorted(details.items()))
|
|
@@ -2081,17 +2137,52 @@ class DeleteServer(command.Command):
|
|
|
2081
2137
|
server_obj.id,
|
|
2082
2138
|
callback=_show_progress,
|
|
2083
2139
|
):
|
|
2084
|
-
msg = _('Error deleting server: %s')
|
|
2085
|
-
|
|
2086
|
-
self.app.stdout.write(_('Error deleting server\n'))
|
|
2087
|
-
raise SystemExit
|
|
2140
|
+
msg = _('Error deleting server: %s') % server_obj.id
|
|
2141
|
+
raise exceptions.CommandError(msg)
|
|
2088
2142
|
|
|
2089
2143
|
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2144
|
+
class PercentAction(argparse.Action):
|
|
2145
|
+
def __init__(
|
|
2146
|
+
self,
|
|
2147
|
+
option_strings,
|
|
2148
|
+
dest,
|
|
2149
|
+
nargs=None,
|
|
2150
|
+
const=None,
|
|
2151
|
+
default=None,
|
|
2152
|
+
type=None,
|
|
2153
|
+
choices=None,
|
|
2154
|
+
required=False,
|
|
2155
|
+
help=None,
|
|
2156
|
+
metavar=None,
|
|
2157
|
+
):
|
|
2158
|
+
if nargs == 0:
|
|
2159
|
+
raise ValueError(
|
|
2160
|
+
'nargs for store actions must be != 0; if you '
|
|
2161
|
+
'have nothing to store, actions such as store '
|
|
2162
|
+
'true or store const may be more appropriate'
|
|
2163
|
+
)
|
|
2164
|
+
|
|
2165
|
+
if const is not None:
|
|
2166
|
+
raise ValueError('const does not make sense for PercentAction')
|
|
2167
|
+
|
|
2168
|
+
super().__init__(
|
|
2169
|
+
option_strings=option_strings,
|
|
2170
|
+
dest=dest,
|
|
2171
|
+
nargs=nargs,
|
|
2172
|
+
const=const,
|
|
2173
|
+
default=default,
|
|
2174
|
+
type=type,
|
|
2175
|
+
choices=choices,
|
|
2176
|
+
required=required,
|
|
2177
|
+
help=help,
|
|
2178
|
+
metavar=metavar,
|
|
2179
|
+
)
|
|
2180
|
+
|
|
2181
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
2182
|
+
x = int(values)
|
|
2183
|
+
if not 0 < x <= 100:
|
|
2184
|
+
raise argparse.ArgumentError(self, "Must be between 0 and 100")
|
|
2185
|
+
setattr(namespace, self.dest, x)
|
|
2095
2186
|
|
|
2096
2187
|
|
|
2097
2188
|
class ListServer(command.Lister):
|
|
@@ -2244,7 +2335,7 @@ class ListServer(command.Lister):
|
|
|
2244
2335
|
)
|
|
2245
2336
|
parser.add_argument(
|
|
2246
2337
|
'--progress',
|
|
2247
|
-
|
|
2338
|
+
action=PercentAction,
|
|
2248
2339
|
default=None,
|
|
2249
2340
|
help=_(
|
|
2250
2341
|
'Search by progress value (%%) '
|
|
@@ -2751,7 +2842,7 @@ class ListServer(command.Lister):
|
|
|
2751
2842
|
try:
|
|
2752
2843
|
# some deployments can have *loads* of images so we only
|
|
2753
2844
|
# want to list the ones we care about. It would be better
|
|
2754
|
-
# to only
|
|
2845
|
+
# to only return the *fields* we care about (name) but
|
|
2755
2846
|
# glance doesn't support that
|
|
2756
2847
|
# NOTE(stephenfin): This could result in super long URLs
|
|
2757
2848
|
# but it seems unlikely to cause issues. Apache supports
|
|
@@ -3110,9 +3201,8 @@ revert to release the new server and restart the old one."""
|
|
|
3110
3201
|
):
|
|
3111
3202
|
self.app.stdout.write(_('Complete\n'))
|
|
3112
3203
|
else:
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
raise SystemExit
|
|
3204
|
+
msg = _('Error migrating server: %s') % server.id
|
|
3205
|
+
raise exceptions.CommandError(msg)
|
|
3116
3206
|
|
|
3117
3207
|
|
|
3118
3208
|
class PauseServer(command.Command):
|
|
@@ -3194,9 +3284,8 @@ class RebootServer(command.Command):
|
|
|
3194
3284
|
):
|
|
3195
3285
|
self.app.stdout.write(_('Complete\n'))
|
|
3196
3286
|
else:
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
raise SystemExit
|
|
3287
|
+
msg = _('Error rebooting server: %s') % server_id
|
|
3288
|
+
raise exceptions.CommandError(msg)
|
|
3200
3289
|
|
|
3201
3290
|
|
|
3202
3291
|
class RebuildServer(command.ShowOne):
|
|
@@ -3566,9 +3655,8 @@ class RebuildServer(command.ShowOne):
|
|
|
3566
3655
|
):
|
|
3567
3656
|
self.app.stdout.write(_('Complete\n'))
|
|
3568
3657
|
else:
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
raise SystemExit
|
|
3658
|
+
msg = _('Error rebuilding server: %s') % server.id
|
|
3659
|
+
raise exceptions.CommandError(msg)
|
|
3572
3660
|
|
|
3573
3661
|
details = _prep_server_detail(
|
|
3574
3662
|
compute_client, image_client, server, refresh=False
|
|
@@ -3689,9 +3777,8 @@ host."""
|
|
|
3689
3777
|
):
|
|
3690
3778
|
self.app.stdout.write(_('Complete\n'))
|
|
3691
3779
|
else:
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
raise SystemExit
|
|
3780
|
+
msg = _('Error evacuating server: %s') % server.id
|
|
3781
|
+
raise exceptions.CommandError(msg)
|
|
3695
3782
|
|
|
3696
3783
|
details = _prep_server_detail(
|
|
3697
3784
|
compute_client, image_client, server, refresh=True
|
|
@@ -4042,9 +4129,8 @@ release the new server and restart the old one."""
|
|
|
4042
4129
|
):
|
|
4043
4130
|
self.app.stdout.write(_('Complete\n'))
|
|
4044
4131
|
else:
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
raise SystemExit
|
|
4132
|
+
msg = _('Error resizing server: %s') % server.id
|
|
4133
|
+
raise exceptions.CommandError(msg)
|
|
4048
4134
|
elif parsed_args.confirm:
|
|
4049
4135
|
self.log.warning(
|
|
4050
4136
|
_(
|
|
@@ -4416,6 +4502,7 @@ class ShelveServer(command.Command):
|
|
|
4416
4502
|
self.app.stdout.flush()
|
|
4417
4503
|
|
|
4418
4504
|
compute_client = self.app.client_manager.sdk_connection.compute
|
|
4505
|
+
server_ids = []
|
|
4419
4506
|
|
|
4420
4507
|
for server in parsed_args.servers:
|
|
4421
4508
|
server_obj = compute_client.find_server(
|
|
@@ -4425,6 +4512,8 @@ class ShelveServer(command.Command):
|
|
|
4425
4512
|
if server_obj.status.lower() in ('shelved', 'shelved_offloaded'):
|
|
4426
4513
|
continue
|
|
4427
4514
|
|
|
4515
|
+
server_ids.append(server_obj.id)
|
|
4516
|
+
|
|
4428
4517
|
compute_client.shelve_server(server_obj.id)
|
|
4429
4518
|
|
|
4430
4519
|
# if we don't have to wait, either because it was requested explicitly
|
|
@@ -4432,56 +4521,44 @@ class ShelveServer(command.Command):
|
|
|
4432
4521
|
if not parsed_args.wait and not parsed_args.offload:
|
|
4433
4522
|
return
|
|
4434
4523
|
|
|
4435
|
-
for
|
|
4524
|
+
for server_id in server_ids:
|
|
4436
4525
|
# We use osc-lib's wait_for_status since that allows for a callback
|
|
4437
4526
|
# TODO(stephenfin): We should wait for these in parallel using e.g.
|
|
4438
4527
|
# https://review.opendev.org/c/openstack/osc-lib/+/762503/
|
|
4439
4528
|
if not utils.wait_for_status(
|
|
4440
4529
|
compute_client.get_server,
|
|
4441
|
-
|
|
4530
|
+
server_id,
|
|
4442
4531
|
success_status=('shelved', 'shelved_offloaded'),
|
|
4443
4532
|
callback=_show_progress,
|
|
4444
4533
|
):
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
_('Error shelving server: %s\n') % server_obj.id
|
|
4448
|
-
)
|
|
4449
|
-
raise SystemExit
|
|
4534
|
+
msg = _('Error shelving server: %s') % server_id
|
|
4535
|
+
raise exceptions.CommandError(msg)
|
|
4450
4536
|
|
|
4451
4537
|
if not parsed_args.offload:
|
|
4452
4538
|
return
|
|
4453
4539
|
|
|
4454
|
-
for
|
|
4455
|
-
server_obj = compute_client.
|
|
4456
|
-
server,
|
|
4457
|
-
ignore_missing=False,
|
|
4458
|
-
)
|
|
4540
|
+
for server_id in server_ids:
|
|
4541
|
+
server_obj = compute_client.get_server(server_id)
|
|
4459
4542
|
if server_obj.status.lower() == 'shelved_offloaded':
|
|
4460
4543
|
continue
|
|
4461
4544
|
|
|
4462
|
-
compute_client.shelve_offload_server(
|
|
4545
|
+
compute_client.shelve_offload_server(server_id)
|
|
4463
4546
|
|
|
4464
4547
|
if not parsed_args.wait:
|
|
4465
4548
|
return
|
|
4466
4549
|
|
|
4467
|
-
for
|
|
4550
|
+
for server_id in server_ids:
|
|
4468
4551
|
# We use osc-lib's wait_for_status since that allows for a callback
|
|
4469
4552
|
# TODO(stephenfin): We should wait for these in parallel using e.g.
|
|
4470
4553
|
# https://review.opendev.org/c/openstack/osc-lib/+/762503/
|
|
4471
4554
|
if not utils.wait_for_status(
|
|
4472
4555
|
compute_client.get_server,
|
|
4473
|
-
|
|
4556
|
+
server_id,
|
|
4474
4557
|
success_status=('shelved_offloaded',),
|
|
4475
4558
|
callback=_show_progress,
|
|
4476
4559
|
):
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
server_obj.id,
|
|
4480
|
-
)
|
|
4481
|
-
self.app.stdout.write(
|
|
4482
|
-
_('Error offloading shelved server: %s\n') % server_obj.id
|
|
4483
|
-
)
|
|
4484
|
-
raise SystemExit
|
|
4560
|
+
msg = _('Error offloading shelved server: %s') % server_id
|
|
4561
|
+
raise exceptions.CommandError(msg)
|
|
4485
4562
|
|
|
4486
4563
|
|
|
4487
4564
|
class ShowServer(command.ShowOne):
|
|
@@ -5073,8 +5150,5 @@ class UnshelveServer(command.Command):
|
|
|
5073
5150
|
success_status=('active', 'shutoff'),
|
|
5074
5151
|
callback=_show_progress,
|
|
5075
5152
|
):
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
_('Error unshelving server: %s\n') % server_obj.id
|
|
5079
|
-
)
|
|
5080
|
-
raise SystemExit
|
|
5153
|
+
msg = _('Error unshelving server: %s') % server_obj.id
|
|
5154
|
+
raise exceptions.CommandError(msg)
|
|
@@ -81,6 +81,21 @@ class _RulesReader(object):
|
|
|
81
81
|
else:
|
|
82
82
|
return rules
|
|
83
83
|
|
|
84
|
+
@staticmethod
|
|
85
|
+
def add_federated_schema_version_option(parser):
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
'--schema-version',
|
|
88
|
+
metavar='<schema_version>',
|
|
89
|
+
required=False,
|
|
90
|
+
default=None,
|
|
91
|
+
help=_(
|
|
92
|
+
"The federated attribute mapping schema version. The "
|
|
93
|
+
"default value on the client side is 'None'; however, that "
|
|
94
|
+
"will lead the backend to set the default according to "
|
|
95
|
+
"'attribute_mapping_default_schema_version' option."
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
|
|
84
99
|
|
|
85
100
|
class CreateMapping(command.ShowOne, _RulesReader):
|
|
86
101
|
_description = _("Create new mapping")
|
|
@@ -98,6 +113,7 @@ class CreateMapping(command.ShowOne, _RulesReader):
|
|
|
98
113
|
required=True,
|
|
99
114
|
help=_('Filename that contains a set of mapping rules (required)'),
|
|
100
115
|
)
|
|
116
|
+
_RulesReader.add_federated_schema_version_option(parser)
|
|
101
117
|
return parser
|
|
102
118
|
|
|
103
119
|
def take_action(self, parsed_args):
|
|
@@ -105,7 +121,9 @@ class CreateMapping(command.ShowOne, _RulesReader):
|
|
|
105
121
|
|
|
106
122
|
rules = self._read_rules(parsed_args.rules)
|
|
107
123
|
mapping = identity_client.federation.mappings.create(
|
|
108
|
-
mapping_id=parsed_args.mapping,
|
|
124
|
+
mapping_id=parsed_args.mapping,
|
|
125
|
+
rules=rules,
|
|
126
|
+
schema_version=parsed_args.schema_version,
|
|
109
127
|
)
|
|
110
128
|
|
|
111
129
|
mapping._info.pop('links', None)
|
|
@@ -158,7 +176,7 @@ class ListMapping(command.Lister):
|
|
|
158
176
|
# rules, (s)he should show specific ones.
|
|
159
177
|
identity_client = self.app.client_manager.identity
|
|
160
178
|
data = identity_client.federation.mappings.list()
|
|
161
|
-
columns = ('ID',)
|
|
179
|
+
columns = ('ID', 'schema_version')
|
|
162
180
|
items = [utils.get_item_properties(s, columns) for s in data]
|
|
163
181
|
return (columns, items)
|
|
164
182
|
|
|
@@ -178,6 +196,8 @@ class SetMapping(command.Command, _RulesReader):
|
|
|
178
196
|
metavar='<filename>',
|
|
179
197
|
help=_('Filename that contains a new set of mapping rules'),
|
|
180
198
|
)
|
|
199
|
+
|
|
200
|
+
_RulesReader.add_federated_schema_version_option(parser)
|
|
181
201
|
return parser
|
|
182
202
|
|
|
183
203
|
def take_action(self, parsed_args):
|
|
@@ -186,7 +206,9 @@ class SetMapping(command.Command, _RulesReader):
|
|
|
186
206
|
rules = self._read_rules(parsed_args.rules)
|
|
187
207
|
|
|
188
208
|
mapping = identity_client.federation.mappings.update(
|
|
189
|
-
mapping=parsed_args.mapping,
|
|
209
|
+
mapping=parsed_args.mapping,
|
|
210
|
+
rules=rules,
|
|
211
|
+
schema_version=parsed_args.schema_version,
|
|
190
212
|
)
|
|
191
213
|
|
|
192
214
|
mapping._info.pop('links', None)
|
|
@@ -92,7 +92,9 @@ class DeletePolicy(command.Command):
|
|
|
92
92
|
|
|
93
93
|
if result > 0:
|
|
94
94
|
total = len(parsed_args.policy)
|
|
95
|
-
msg = _(
|
|
95
|
+
msg = _(
|
|
96
|
+
"%(result)s of %(total)s policies failed " "to delete."
|
|
97
|
+
) % {
|
|
96
98
|
'result': result,
|
|
97
99
|
'total': total,
|
|
98
100
|
}
|