mirror of
https://opendev.org/openstack/python-openstackclient.git
synced 2026-01-17 15:31:03 +00:00
Compare commits
343 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a93733293 | ||
|
|
d1a0ede7db | ||
|
|
ed2dc692dd | ||
|
|
85fccc7a91 | ||
|
|
08b1bb70dd | ||
|
|
58210a141a | ||
|
|
0b05fd8968 | ||
|
|
e8ae075c38 | ||
|
|
7246a07834 | ||
|
|
8dbb7126c6 | ||
|
|
43ffea5c4d | ||
|
|
f2f0f92d41 | ||
|
|
748cff5914 | ||
|
|
841d95b095 | ||
|
|
a7e2f31ecc | ||
|
|
de4e119272 | ||
|
|
30f3192b8d | ||
|
|
cc36d929b3 | ||
|
|
e799a4a676 | ||
|
|
3cd544df53 | ||
|
|
c59bf71fb7 | ||
|
|
924822507a | ||
|
|
65801e7e58 | ||
|
|
f2810a83b0 | ||
|
|
3fbe41cd52 | ||
|
|
dedc1a342c | ||
|
|
060299c749 | ||
|
|
92a277ff4c | ||
|
|
9e49047ed1 | ||
|
|
97c2238df1 | ||
|
|
f8effe997e | ||
|
|
79db64f264 | ||
|
|
fb6dad48db | ||
|
|
4132ca1818 | ||
|
|
0411805608 | ||
|
|
c17c5f0df6 | ||
|
|
d6b2f42cfb | ||
|
|
e9b720e1c7 | ||
|
|
0b88ea4fab | ||
|
|
d7e71480d4 | ||
|
|
351d537cb0 | ||
|
|
db6c34c2c7 | ||
|
|
73021165ff | ||
|
|
55fd501657 | ||
|
|
a5e4d5f0fa | ||
|
|
db2c1a5e2b | ||
|
|
0e8aa79f9f | ||
|
|
3b6f9ee7ba | ||
|
|
7505186a41 | ||
|
|
188737c69c | ||
|
|
1b6df0b5bb | ||
|
|
232a0ab68d | ||
|
|
23f47e0dab | ||
|
|
eb7c4c61a9 | ||
|
|
33d34bdfe8 | ||
|
|
7116449190 | ||
|
|
44dfa157e4 | ||
|
|
3cc6b24bb5 | ||
|
|
492a184add | ||
|
|
305e037df2 | ||
|
|
b0763f9f9a | ||
|
|
20ad83bf84 | ||
|
|
b28b3249de | ||
|
|
4282a512c0 | ||
|
|
0ed122094a | ||
|
|
fb8cdd4441 | ||
|
|
de88853de2 | ||
|
|
4cf70113d2 | ||
|
|
6b6a9bafd8 | ||
|
|
c0ada2d6ab | ||
|
|
3c3ea30bd3 | ||
|
|
8f3277326c | ||
|
|
b808b82dfb | ||
|
|
afcce59c8d | ||
|
|
a5a6ec27e5 | ||
|
|
bc98e21559 | ||
|
|
475d69efad | ||
|
|
3dfeb5ed08 | ||
|
|
e7554603ac | ||
|
|
5f1ffe742c | ||
|
|
68d1d01b2a | ||
|
|
94e447af80 | ||
|
|
dbddbf9760 | ||
|
|
34f431bade | ||
|
|
a312e9cdad | ||
|
|
5feaa952ad | ||
|
|
d90e18b08c | ||
|
|
6ba9473cdb | ||
|
|
ae7e8448ba | ||
|
|
2177f07dfb | ||
|
|
a73698490a | ||
|
|
13fe801968 | ||
|
|
81db99b32b | ||
|
|
37228ae2d3 | ||
|
|
87fe04ae08 | ||
|
|
7bb5857081 | ||
|
|
a8751b00fe | ||
|
|
2a492fb58a | ||
|
|
ad2a511434 | ||
|
|
8a88d65a67 | ||
|
|
a99ae364fc | ||
|
|
e8a7db5858 | ||
|
|
9f55b253a3 | ||
|
|
e37148484c | ||
|
|
46b25c7c0b | ||
|
|
c7d465a221 | ||
|
|
5e5f12ba40 | ||
|
|
504cbd24e2 | ||
|
|
51ecb5f984 | ||
|
|
ffa37bab12 | ||
|
|
1b2dfeacf4 | ||
|
|
9d3a956a54 | ||
|
|
572eeb6d38 | ||
|
|
b6af7883b7 | ||
|
|
4ea3deda61 | ||
|
|
1b4fe6fac1 | ||
|
|
ea85c7aa4d | ||
|
|
edb17881d0 | ||
|
|
f4c4a7343c | ||
|
|
d5e6392454 | ||
|
|
9ad18c4967 | ||
|
|
2e301857af | ||
|
|
c923ed5893 | ||
|
|
5f602475cd | ||
|
|
a8d2c56337 | ||
|
|
606fd132bc | ||
|
|
2f03c3ea3c | ||
|
|
1c70e264a4 | ||
|
|
88b59d8975 | ||
|
|
3909e93301 | ||
|
|
5a565ca1ce | ||
|
|
9bcb1c5c00 | ||
|
|
a2be1b014e | ||
|
|
5fb4559a1f | ||
|
|
50dd542519 | ||
|
|
de7762f9b8 | ||
|
|
bea4d834ce | ||
|
|
47a2596b39 | ||
|
|
07909a6a6c | ||
|
|
803c3c6d6a | ||
|
|
110cd9faae | ||
|
|
2e0318629d | ||
|
|
0e47fc4e45 | ||
|
|
5c9c4e3dd7 | ||
|
|
ff7497689c | ||
|
|
d27ff5a650 | ||
|
|
82170be8d6 | ||
|
|
e3bd9a4c01 | ||
|
|
d5238d1dab | ||
|
|
5f2145d102 | ||
|
|
51305d06f7 | ||
|
|
444a1df705 | ||
|
|
f1cd38aabf | ||
|
|
01c1b1e36f | ||
|
|
5e1fc3db05 | ||
|
|
082aca89d6 | ||
|
|
125133d056 | ||
|
|
03aa172fd0 | ||
|
|
267a29d594 | ||
|
|
3c6fa42642 | ||
|
|
e0020aec6a | ||
|
|
fc42f12eb2 | ||
|
|
e1ff450e34 | ||
|
|
a9b9984973 | ||
|
|
8eb1a183fe | ||
|
|
b933330d55 | ||
|
|
1ee3ef33d7 | ||
|
|
00f4cf9c17 | ||
|
|
a74850d2c4 | ||
|
|
eea369e73c | ||
|
|
32762bcda6 | ||
|
|
79de137152 | ||
|
|
f380d029df | ||
|
|
efce69b47f | ||
|
|
e26b447925 | ||
|
|
94d17b8762 | ||
|
|
9de5e58bc8 | ||
|
|
05accdde9a | ||
|
|
7c7c066096 | ||
|
|
ce2a253d5a | ||
|
|
5d730f374b | ||
|
|
a49a290a2b | ||
|
|
871b2a4d80 | ||
|
|
6e4c2879d4 | ||
|
|
0208a24235 | ||
|
|
6cb5d8cd14 | ||
|
|
22eecc54f8 | ||
|
|
f4e97d9733 | ||
|
|
e4d621d24f | ||
|
|
7d64003196 | ||
|
|
abed52f106 | ||
|
|
2c878ad2b7 | ||
|
|
b0fe724caf | ||
|
|
db4739fc5c | ||
|
|
25cd1178b3 | ||
|
|
d123be0819 | ||
|
|
616d6f3a29 | ||
|
|
11495e655a | ||
|
|
c66abfc76f | ||
|
|
c68622402e | ||
|
|
7ecdb69f08 | ||
|
|
0874e2d33f | ||
|
|
d96c81ff7f | ||
|
|
f870548c7f | ||
|
|
80eaa33ffe | ||
|
|
181bb194c7 | ||
|
|
30aa27b7f9 | ||
|
|
dd8e4740ec | ||
|
|
1e8d243986 | ||
|
|
dc68be6b7b | ||
|
|
dae2539490 | ||
|
|
54a5d2f40c | ||
|
|
01f9279dbf | ||
|
|
ac1ad1c4e4 | ||
|
|
2148a86fc5 | ||
|
|
0554ff60b4 | ||
|
|
ade7da8797 | ||
|
|
4ba21fd6f4 | ||
|
|
d95e23d92b | ||
|
|
8e95c0ed31 | ||
|
|
425e430c31 | ||
|
|
1f25a2f935 | ||
|
|
438878a3ed | ||
|
|
c0521743ba | ||
|
|
dc8596fe74 | ||
|
|
ec4fd81c11 | ||
|
|
62c8b8217e | ||
|
|
c9e4e5404f | ||
|
|
77143f9bed | ||
|
|
28bd00a642 | ||
|
|
e6ae7d8533 | ||
|
|
93da5f7af5 | ||
|
|
6e89f9da63 | ||
|
|
514a46ee5d | ||
|
|
cf25526e94 | ||
|
|
662405e55c | ||
|
|
e6be9a3edf | ||
|
|
3eb063d4f7 | ||
|
|
0b9c998d8a | ||
|
|
b2eccdcb1a | ||
|
|
7750fc1cf4 | ||
|
|
9de592ebaf | ||
|
|
e28046cc19 | ||
|
|
9435ef825a | ||
|
|
2c0a3ba137 | ||
|
|
7380fbe300 | ||
|
|
bdd55d989d | ||
|
|
9c7a5d4e51 | ||
|
|
ab2e68f407 | ||
|
|
5918b6b478 | ||
|
|
107c6b143f | ||
|
|
4dbfc47552 | ||
|
|
3483117259 | ||
|
|
07515cd160 | ||
|
|
866009211f | ||
|
|
6d27b2f2b6 | ||
|
|
2e5a830276 | ||
|
|
b01c138e9e | ||
|
|
2883f3fb95 | ||
|
|
f28f97f23f | ||
|
|
9366405806 | ||
|
|
7ef588d695 | ||
|
|
71dbac498e | ||
|
|
669a50be55 | ||
|
|
1763c11963 | ||
|
|
49708f6d3f | ||
|
|
290bc580e6 | ||
|
|
f2df31387b | ||
|
|
702a37c7ca | ||
|
|
1efca54465 | ||
|
|
f1bd417861 | ||
|
|
1458330d3b | ||
|
|
f65e4835d3 | ||
|
|
966aede8ab | ||
|
|
4b7e32ca37 | ||
|
|
1979c20ff0 | ||
|
|
762a3b10d1 | ||
|
|
d22b7732ad | ||
|
|
12e264adc6 | ||
|
|
0ba77e6727 | ||
|
|
426abbdc68 | ||
|
|
1f407afe1c | ||
|
|
b50ac8d2a2 | ||
|
|
3412147372 | ||
|
|
e761ef8e32 | ||
|
|
0c2dee5e1f | ||
|
|
e27fd93226 | ||
|
|
5de803b39f | ||
|
|
060d706bf4 | ||
|
|
3de1ac66e0 | ||
|
|
d2d7219231 | ||
|
|
146a1814b6 | ||
|
|
4f95e0aa18 | ||
|
|
8f1382eda3 | ||
|
|
eb0dbd5c33 | ||
|
|
6ce1f7730c | ||
|
|
2a0431e825 | ||
|
|
a631014551 | ||
|
|
4e8be5aa64 | ||
|
|
03e2fdd162 | ||
|
|
c74af3f01e | ||
|
|
fd232a43bc | ||
|
|
769bf87d0a | ||
|
|
38407c6a78 | ||
|
|
db3d4c98f2 | ||
|
|
32bd5d3562 | ||
|
|
4cef5f5549 | ||
|
|
38029c6988 | ||
|
|
776b7d0c66 | ||
|
|
afc0d3c252 | ||
|
|
42b1698e5c | ||
|
|
d175dea6bc | ||
|
|
329b351cb8 | ||
|
|
56baf50655 | ||
|
|
52b2944b78 | ||
|
|
90148ffc45 | ||
|
|
6579daeac7 | ||
|
|
83de58fa33 | ||
|
|
99cef9354b | ||
|
|
e736394d1b | ||
|
|
22b30b99ce | ||
|
|
03933e9a73 | ||
|
|
c888cf2556 | ||
|
|
5ef5cc9c82 | ||
|
|
ecc744a4fd | ||
|
|
4bdd51cbea | ||
|
|
4c8290012d | ||
|
|
6ff3a92089 | ||
|
|
8f56d3f5e4 | ||
|
|
9c6df823e2 | ||
|
|
fb1f841d2d | ||
|
|
58e21d8e2b | ||
|
|
8890981491 | ||
|
|
79e01a5533 | ||
|
|
d17d99faa9 | ||
|
|
e5ccf1eb1c | ||
|
|
974cdd9a4e | ||
|
|
47144103ca | ||
|
|
2e491191e5 | ||
|
|
695d025f00 | ||
|
|
db115c09a2 | ||
|
|
7c6b47b451 | ||
|
|
b6b18489b0 |
419 changed files with 19118 additions and 23017 deletions
|
|
@ -1,13 +1,13 @@
|
||||||
---
|
---
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.6.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: mixed-line-ending
|
- id: mixed-line-ending
|
||||||
args: ['--fix', 'lf']
|
args: ['--fix', 'lf']
|
||||||
exclude: '.*\.(svg)$'
|
exclude: '.*\.(svg)$'
|
||||||
- id: check-byte-order-marker
|
- id: fix-byte-order-marker
|
||||||
- id: check-executables-have-shebangs
|
- id: check-executables-have-shebangs
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: debug-statements
|
- id: debug-statements
|
||||||
|
|
@ -15,9 +15,9 @@ repos:
|
||||||
files: .*\.(yaml|yml)$
|
files: .*\.(yaml|yml)$
|
||||||
args: ['--unsafe']
|
args: ['--unsafe']
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.6.2
|
rev: v0.14.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff-check
|
||||||
args: ['--fix', '--unsafe-fixes']
|
args: ['--fix', '--unsafe-fixes']
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
- repo: https://opendev.org/openstack/hacking
|
- repo: https://opendev.org/openstack/hacking
|
||||||
|
|
@ -26,3 +26,17 @@ repos:
|
||||||
- id: hacking
|
- id: hacking
|
||||||
additional_dependencies: []
|
additional_dependencies: []
|
||||||
exclude: '^(doc|releasenotes)/.*$'
|
exclude: '^(doc|releasenotes)/.*$'
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v1.18.2
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
additional_dependencies:
|
||||||
|
- types-requests
|
||||||
|
# keep this in-sync with '[tool.mypy] exclude' in 'pyproject.toml'
|
||||||
|
exclude: |
|
||||||
|
(?x)(
|
||||||
|
doc/.*
|
||||||
|
| examples/.*
|
||||||
|
| hacking/.*
|
||||||
|
| releasenotes/.*
|
||||||
|
)
|
||||||
|
|
|
||||||
85
.zuul.yaml
85
.zuul.yaml
|
|
@ -6,6 +6,11 @@
|
||||||
Run unit tests for OpenStackClient with master branch of important libs.
|
Run unit tests for OpenStackClient with master branch of important libs.
|
||||||
|
|
||||||
Takes advantage of the base tox job's install-siblings feature.
|
Takes advantage of the base tox job's install-siblings feature.
|
||||||
|
irrelevant-files: &common-irrelevant-files
|
||||||
|
- ^.*\.rst$
|
||||||
|
- ^doc/.*$
|
||||||
|
- ^releasenotes/.*$
|
||||||
|
- ^\.pre-commit-config\.yaml$
|
||||||
required-projects:
|
required-projects:
|
||||||
- openstack/cliff
|
- openstack/cliff
|
||||||
- openstack/keystoneauth
|
- openstack/keystoneauth
|
||||||
|
|
@ -18,8 +23,8 @@
|
||||||
zuul_work_dir: src/opendev.org/openstack/python-openstackclient
|
zuul_work_dir: src/opendev.org/openstack/python-openstackclient
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: osc-tox-py39-tips
|
name: osc-tox-py310-tips
|
||||||
parent: openstack-tox-py39
|
parent: openstack-tox-py310
|
||||||
description: |
|
description: |
|
||||||
Run unit tests for OpenStackClient with master branch of important libs.
|
Run unit tests for OpenStackClient with master branch of important libs.
|
||||||
|
|
||||||
|
|
@ -38,8 +43,8 @@
|
||||||
zuul_work_dir: src/opendev.org/openstack/python-openstackclient
|
zuul_work_dir: src/opendev.org/openstack/python-openstackclient
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: osc-tox-py312-tips
|
name: osc-tox-py313-tips
|
||||||
parent: openstack-tox-py312
|
parent: openstack-tox-py313
|
||||||
description: |
|
description: |
|
||||||
Run unit tests for OpenStackClient with master branch of important libs.
|
Run unit tests for OpenStackClient with master branch of important libs.
|
||||||
|
|
||||||
|
|
@ -81,13 +86,6 @@
|
||||||
# NOTE(amotoki): Some neutron features are enabled by devstack plugin
|
# NOTE(amotoki): Some neutron features are enabled by devstack plugin
|
||||||
neutron: https://opendev.org/openstack/neutron
|
neutron: https://opendev.org/openstack/neutron
|
||||||
devstack_services:
|
devstack_services:
|
||||||
ceilometer-acentral: false
|
|
||||||
ceilometer-acompute: false
|
|
||||||
ceilometer-alarm-evaluator: false
|
|
||||||
ceilometer-alarm-notifier: false
|
|
||||||
ceilometer-anotification: false
|
|
||||||
ceilometer-api: false
|
|
||||||
ceilometer-collector: false
|
|
||||||
s-account: true
|
s-account: true
|
||||||
s-container: true
|
s-container: true
|
||||||
s-object: true
|
s-object: true
|
||||||
|
|
@ -139,22 +137,6 @@
|
||||||
tox_envlist: functional
|
tox_envlist: functional
|
||||||
tox_install_siblings: true
|
tox_install_siblings: true
|
||||||
|
|
||||||
- secret:
|
|
||||||
name: osc-dockerhub
|
|
||||||
data:
|
|
||||||
username: osclientzuul
|
|
||||||
password: !encrypted/pkcs1-oaep
|
|
||||||
- LbIZjJiVstRVXMpoLQ3+/JcNB6lKVUWJXXo5+Outf+PKAaO7mNnv8XLiFMKnJ6ftopLyu
|
|
||||||
hWbX9rA+NddvplLQkf1xxkh7QBBU8PToLr58quI2SENUclt4tpjxbZfZu451kFSNJvNvR
|
|
||||||
E58cHHpfJZpyRnS2htXmN/Qy24gbV2w7CQxSZD2YhlcrerD8uQ8rWEnlY1wcJEaEGomtS
|
|
||||||
ZTGxsdK2TsZC2cd4b7TG7+xbl2i+hjADzwSQAgUzlLlwuG71667+IWk4SOZ7OycJTv9NN
|
|
||||||
ZTak8+CGfiMKdmsxZ1Z8uD7DC+RIklDjMWyly6zuhWzfhOmsmU0CesR50moodRUvbK79p
|
|
||||||
NZM8u0hBex5cl2EpUEwJL/FSPJXUhDMPoMoTZT/SAuXf25R9eZ9JGrKsIAlmVhpl8ifoE
|
|
||||||
8TpPyvIHGS3YelTQjhqOX0wGb9T4ZauQCcI5Ajzy9NuCTyD9xxme9OX1zz7gMACRnVHvz
|
|
||||||
q7U7Ue90MnmGH6E2SgKjIZhyzy9Efwb7JUvH1Zb3hlrjCjEhwi9MV5FnABTEeXyYwE10s
|
|
||||||
3o/KZg2zvdWkVG6x0dEkjpoQaNuaB7T2Na7Sm421n/z3LCzhiQGuTUjENnL6cMEtuA6Pp
|
|
||||||
BfI5+Qlg7HMwkBXNB73EPfWHzbCR3VNrzGYTy9FvhGud0/cXsuBXgps4WH63ic=
|
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: osc-build-image
|
name: osc-build-image
|
||||||
parent: opendev-build-docker-image
|
parent: opendev-build-docker-image
|
||||||
|
|
@ -164,49 +146,21 @@
|
||||||
- python-builder-3.11-bookworm-container-image
|
- python-builder-3.11-bookworm-container-image
|
||||||
- python-base-3.11-bookworm-container-image
|
- python-base-3.11-bookworm-container-image
|
||||||
provides: osc-container-image
|
provides: osc-container-image
|
||||||
vars: &osc_image_vars
|
vars:
|
||||||
docker_images:
|
docker_images:
|
||||||
- context: .
|
- context: .
|
||||||
repository: osclient/python-openstackclient
|
tags: []
|
||||||
|
|
||||||
- job:
|
|
||||||
name: osc-upload-image
|
|
||||||
parent: opendev-upload-docker-image
|
|
||||||
description: Build Docker images and upload to Docker Hub.
|
|
||||||
allowed-projects: openstack/python-openstackclient
|
|
||||||
requires:
|
|
||||||
- python-builder-3.11-bookworm-container-image
|
|
||||||
- python-base-3.11-bookworm-container-image
|
|
||||||
provides: osc-container-image
|
|
||||||
secrets:
|
|
||||||
- name: docker_credentials
|
|
||||||
secret: osc-dockerhub
|
|
||||||
pass-to-parent: true
|
|
||||||
vars: *osc_image_vars
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: osc-promote-image
|
|
||||||
parent: opendev-promote-docker-image
|
|
||||||
allowed-projects: openstack/python-openstackclient
|
|
||||||
description: Promote previously uploaded Docker images.
|
|
||||||
secrets:
|
|
||||||
- name: docker_credentials
|
|
||||||
secret: osc-dockerhub
|
|
||||||
pass-to-parent: true
|
|
||||||
nodeset:
|
|
||||||
nodes: []
|
|
||||||
vars: *osc_image_vars
|
|
||||||
|
|
||||||
- project-template:
|
- project-template:
|
||||||
name: osc-tox-unit-tips
|
name: osc-tox-unit-tips
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- osc-tox-py39-tips
|
- osc-tox-py310-tips
|
||||||
- osc-tox-py312-tips
|
- osc-tox-py313-tips
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
- osc-tox-py39-tips
|
- osc-tox-py310-tips
|
||||||
- osc-tox-py312-tips
|
- osc-tox-py313-tips
|
||||||
|
|
||||||
- project:
|
- project:
|
||||||
templates:
|
templates:
|
||||||
|
|
@ -219,7 +173,10 @@
|
||||||
- release-notes-jobs-python3
|
- release-notes-jobs-python3
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- osc-build-image
|
- openstackclient-check-plugins:
|
||||||
|
voting: true
|
||||||
|
- osc-build-image:
|
||||||
|
voting: false
|
||||||
- osc-functional-devstack
|
- osc-functional-devstack
|
||||||
- osc-functional-devstack-tips:
|
- osc-functional-devstack-tips:
|
||||||
# The functional-tips job only tests the latest and shouldn't be run
|
# The functional-tips job only tests the latest and shouldn't be run
|
||||||
|
|
@ -227,8 +184,4 @@
|
||||||
branches: ^master$
|
branches: ^master$
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
- osc-upload-image
|
|
||||||
- osc-functional-devstack
|
- osc-functional-devstack
|
||||||
promote:
|
|
||||||
jobs:
|
|
||||||
- osc-promote-image
|
|
||||||
|
|
|
||||||
11
Dockerfile
11
Dockerfile
|
|
@ -13,12 +13,19 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
FROM docker.io/opendevorg/python-builder:3.11-bookworm as builder
|
FROM docker.io/opendevorg/python-builder:3.12-bookworm AS builder
|
||||||
|
|
||||||
COPY . /tmp/src
|
COPY . /tmp/src
|
||||||
RUN assemble
|
RUN assemble
|
||||||
|
|
||||||
FROM docker.io/opendevorg/python-base:3.11-bookworm
|
FROM docker.io/opendevorg/python-base:3.12-bookworm
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="python-openstackclient"
|
||||||
|
LABEL org.opencontainers.image.description="Client for OpenStack services."
|
||||||
|
LABEL org.opencontainers.image.licenses="Apache License 2.0"
|
||||||
|
LABEL org.opencontainers.image.url="https://www.openstack.org/"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://docs.openstack.org/python-openstackclient/latest/"
|
||||||
|
LABEL org.opencontainers.image.source="https://opendev.org/openstack/python-openstackclient"
|
||||||
|
|
||||||
COPY --from=builder /output/ /output
|
COPY --from=builder /output/ /output
|
||||||
RUN /output/install-from-bindep
|
RUN /output/install-from-bindep
|
||||||
|
|
|
||||||
203
README.rst
203
README.rst
|
|
@ -1,12 +1,3 @@
|
||||||
========================
|
|
||||||
Team and repository tags
|
|
||||||
========================
|
|
||||||
|
|
||||||
.. image:: https://governance.openstack.org/tc/badges/python-openstackclient.svg
|
|
||||||
:target: https://governance.openstack.org/tc/reference/tags/index.html
|
|
||||||
|
|
||||||
.. Change things from this point on
|
|
||||||
|
|
||||||
===============
|
===============
|
||||||
OpenStackClient
|
OpenStackClient
|
||||||
===============
|
===============
|
||||||
|
|
@ -15,96 +6,158 @@ OpenStackClient
|
||||||
:target: https://pypi.org/project/python-openstackclient/
|
:target: https://pypi.org/project/python-openstackclient/
|
||||||
:alt: Latest Version
|
:alt: Latest Version
|
||||||
|
|
||||||
OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
|
OpenStackClient (OSC) is a command-line client for OpenStack that brings
|
||||||
the command set for Compute, Identity, Image, Network, Object Store and Block
|
the command set for Compute, Identity, Image, Network, Object Store and Block
|
||||||
Storage APIs together in a single shell with a uniform command structure.
|
Storage APIs together in a single shell with a uniform command structure.
|
||||||
|
Support for additional service APIs is provided via plugins.
|
||||||
|
|
||||||
The primary goal is to provide a unified shell command structure and a common
|
The primary goal is to provide a unified shell command structure and a common
|
||||||
language to describe operations in OpenStack.
|
language to describe operations in OpenStack.
|
||||||
|
|
||||||
* `PyPi`_ - package installation
|
|
||||||
* `Online Documentation`_
|
|
||||||
* `Launchpad project`_ - bugs and feature requests
|
|
||||||
* `Blueprints`_ - feature specifications (historical only)
|
|
||||||
* `Source`_
|
|
||||||
* `Developer`_ - getting started as a developer
|
|
||||||
* `Contributing`_ - contributing code
|
|
||||||
* `Testing`_ - testing code
|
|
||||||
* IRC: #openstack-sdks on OFTC (irc.oftc.net)
|
|
||||||
* License: Apache 2.0
|
|
||||||
|
|
||||||
.. _PyPi: https://pypi.org/project/python-openstackclient
|
|
||||||
.. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/
|
|
||||||
.. _Blueprints: https://blueprints.launchpad.net/python-openstackclient
|
|
||||||
.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient
|
|
||||||
.. _Source: https://opendev.org/openstack/python-openstackclient
|
|
||||||
.. _Developer: https://docs.openstack.org/project-team-guide/project-setup/python.html
|
|
||||||
.. _Contributing: https://docs.openstack.org/infra/manual/developers.html
|
|
||||||
.. _Testing: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html#testing
|
|
||||||
.. _Release Notes: https://docs.openstack.org/releasenotes/python-openstackclient
|
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
===============
|
===============
|
||||||
|
|
||||||
OpenStack Client can be installed from PyPI using pip::
|
OpenStack Client can be installed from PyPI using pip:
|
||||||
|
|
||||||
pip install python-openstackclient
|
.. code-block:: shell
|
||||||
|
|
||||||
There are a few variants on getting help. A list of global options and supported
|
python3 -m pip install python-openstackclient
|
||||||
commands is shown with ``--help``::
|
|
||||||
|
|
||||||
openstack --help
|
You can use ``--help`` or the ``help`` command to get a list of global options
|
||||||
|
and supported commands:
|
||||||
|
|
||||||
There is also a ``help`` command that can be used to get help text for a specific
|
.. code-block:: shell
|
||||||
command::
|
|
||||||
|
|
||||||
|
openstack --help
|
||||||
openstack help
|
openstack help
|
||||||
|
|
||||||
|
You can also get help for a specific command:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
openstack server create --help
|
||||||
openstack help server create
|
openstack help server create
|
||||||
|
|
||||||
If you want to make changes to the OpenStackClient for testing and contribution,
|
You can add support for additional services by installing their clients. For
|
||||||
make any changes and then run::
|
example, to add support for the DNS service (designate):
|
||||||
|
|
||||||
python setup.py develop
|
.. code-block:: shell
|
||||||
|
|
||||||
or::
|
python3 -m pip install python3-designateclient
|
||||||
|
|
||||||
pip install -e .
|
A ``Dockerfile`` is provided for your convenience in the repository. You can
|
||||||
|
use this to build your own container images:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
git clone https://opendev.org/openstack/python-openstackclient
|
||||||
|
cd python-openstackclient
|
||||||
|
podman build . -t example.com/myuser/openstackclient
|
||||||
|
|
||||||
|
For more information the available options and commands, refer to the `Users
|
||||||
|
Guide`__.
|
||||||
|
|
||||||
|
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
=============
|
=============
|
||||||
|
|
||||||
The CLI is configured via environment variables and command-line
|
OpenStack Client must be configured with authentication information in order to
|
||||||
options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
|
communicate with a given OpenStack cloud. This configuration can be achieved
|
||||||
|
via a ``clouds.yaml`` file, a set of environment variables (often shared via an
|
||||||
|
``openrc`` file), a set of command-line options, or a combination of all three.
|
||||||
|
Your cloud provider or deployment tooling will typically provide either a
|
||||||
|
``clouds.yaml`` file or ``openrc`` file for you. If using a ``clouds.yaml``
|
||||||
|
file, OpenStack Client expects to find it in one of the following locations:
|
||||||
|
|
||||||
Authentication using username/password is most commonly used:
|
* If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment
|
||||||
|
variable
|
||||||
|
* ``.`` (the current directory)
|
||||||
|
* ``$HOME/.config/openstack``
|
||||||
|
* ``/etc/openstack``
|
||||||
|
|
||||||
- For a local user, your configuration will look like the one below::
|
The options you should set will depend on the configuration of your cloud and
|
||||||
|
the authentication mechanism(s) supported. For example, consider a cloud that
|
||||||
|
supports username/password authentication. Configuration for this cloud using a
|
||||||
|
``clouds.yaml`` file would look like so:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
clouds:
|
||||||
|
my-cloud:
|
||||||
|
auth:
|
||||||
|
auth_url: '<url-to-openstack-identity>'
|
||||||
|
project_name: '<project-name>'
|
||||||
|
project_domain_name: '<project-domain-name>'
|
||||||
|
username: '<username>'
|
||||||
|
user_domain_name: '<user-domain-name>'
|
||||||
|
password: '<password>' # (optional)
|
||||||
|
region_name: '<region>'
|
||||||
|
|
||||||
|
The corresponding environment variables would look very similar:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
export OS_AUTH_URL=<url-to-openstack-identity>
|
export OS_AUTH_URL=<url-to-openstack-identity>
|
||||||
export OS_IDENTITY_API_VERSION=3
|
export OS_REGION_NAME=<region>
|
||||||
export OS_PROJECT_NAME=<project-name>
|
export OS_PROJECT_NAME=<project-name>
|
||||||
export OS_PROJECT_DOMAIN_NAME=<project-domain-name>
|
export OS_PROJECT_DOMAIN_NAME=<project-domain-name>
|
||||||
export OS_USERNAME=<username>
|
export OS_USERNAME=<username>
|
||||||
export OS_USER_DOMAIN_NAME=<user-domain-name>
|
export OS_USER_DOMAIN_NAME=<user-domain-name>
|
||||||
export OS_PASSWORD=<password> # (optional)
|
export OS_PASSWORD=<password> # (optional)
|
||||||
|
|
||||||
The corresponding command-line options look very similar::
|
Likewise, the corresponding command-line options would look very similar:
|
||||||
|
|
||||||
--os-auth-url <url>
|
::
|
||||||
--os-identity-api-version 3
|
|
||||||
|
openstack
|
||||||
|
--os-auth-url <url-to-openstack-identity>
|
||||||
|
--os-region <region>
|
||||||
--os-project-name <project-name>
|
--os-project-name <project-name>
|
||||||
--os-project-domain-name <project-domain-name>
|
--os-project-domain-name <project-domain-name>
|
||||||
--os-username <username>
|
--os-username <username>
|
||||||
--os-user-domain-name <user-domain-name>
|
--os-user-domain-name <user-domain-name>
|
||||||
[--os-password <password>]
|
[--os-password <password>]
|
||||||
|
|
||||||
- For a federated user, your configuration will look the so::
|
.. note::
|
||||||
|
|
||||||
|
If a password is not provided above (in plaintext), you will be
|
||||||
|
interactively prompted to provide one securely.
|
||||||
|
|
||||||
|
Some clouds use federated authentication. If this is the case, your
|
||||||
|
configuration will be slightly more involved. For example, to configure
|
||||||
|
username/password authentication for a federated user using a ``clouds.yaml``
|
||||||
|
file:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
clouds:
|
||||||
|
my-cloud:
|
||||||
|
auth:
|
||||||
|
auth_url: '<url-to-openstack-identity>'
|
||||||
|
project_name: '<project-name>'
|
||||||
|
project_domain_name: '<project-domain-name>'
|
||||||
|
username: '<username-in-idp>'
|
||||||
|
user_domain_name: '<user-domain-name>'
|
||||||
|
password: '<password-in-idp>'
|
||||||
|
identity_provider: '<the-desired-idp-in-keystone>'
|
||||||
|
client_id: '<the-client-id-configured-in-the-idp>'
|
||||||
|
client_secret: '<the-client-secret-configured-in-the-idp>'
|
||||||
|
openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>'
|
||||||
|
protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>'
|
||||||
|
access_token_type: '<the-access-token-type-used-by-your-idp>'
|
||||||
|
discovery_endpoint: '<the-well-known-endpoint-of-the-idp>'
|
||||||
|
auth_type: 'v3oidcpassword'
|
||||||
|
region_name: '<region>'
|
||||||
|
|
||||||
|
The corresponding environment variables would look very similar:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
export OS_PROJECT_NAME=<project-name>
|
export OS_PROJECT_NAME=<project-name>
|
||||||
export OS_PROJECT_DOMAIN_NAME=<project-domain-name>
|
export OS_PROJECT_DOMAIN_NAME=<project-domain-name>
|
||||||
export OS_AUTH_URL=<url-to-openstack-identity>
|
export OS_AUTH_URL=<url-to-openstack-identity>
|
||||||
export OS_IDENTITY_API_VERSION=3
|
export OS_IDENTITY_API_VERSION=3
|
||||||
export OS_AUTH_PLUGIN=openid
|
|
||||||
export OS_AUTH_TYPE=v3oidcpassword
|
export OS_AUTH_TYPE=v3oidcpassword
|
||||||
export OS_USERNAME=<username-in-idp>
|
export OS_USERNAME=<username-in-idp>
|
||||||
export OS_PASSWORD=<password-in-idp>
|
export OS_PASSWORD=<password-in-idp>
|
||||||
|
|
@ -116,7 +169,9 @@ Authentication using username/password is most commonly used:
|
||||||
export OS_ACCESS_TOKEN_TYPE=<the-access-token-type-used-by-your-idp>
|
export OS_ACCESS_TOKEN_TYPE=<the-access-token-type-used-by-your-idp>
|
||||||
export OS_DISCOVERY_ENDPOINT=<the-well-known-endpoint-of-the-idp>
|
export OS_DISCOVERY_ENDPOINT=<the-well-known-endpoint-of-the-idp>
|
||||||
|
|
||||||
The corresponding command-line options look very similar::
|
Likewise, the corresponding command-line options would look very similar:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
--os-project-name <project-name>
|
--os-project-name <project-name>
|
||||||
--os-project-domain-name <project-domain-name>
|
--os-project-domain-name <project-domain-name>
|
||||||
|
|
@ -134,5 +189,41 @@ Authentication using username/password is most commonly used:
|
||||||
--os-access-token-type <the-access-token-type-used-by-your-idp>
|
--os-access-token-type <the-access-token-type-used-by-your-idp>
|
||||||
--os-discovery-endpoint <the-well-known-endpoint-of-the-idp>
|
--os-discovery-endpoint <the-well-known-endpoint-of-the-idp>
|
||||||
|
|
||||||
If a password is not provided above (in plaintext), you will be interactively
|
For more information on configuring authentication, including an overview of
|
||||||
prompted to provide one securely.
|
the many authentication mechanisms supported, refer to the `Authentication
|
||||||
|
guide`__. For more information on configuration in general, refer to the
|
||||||
|
`Configuration guide`__.
|
||||||
|
|
||||||
|
.. __: https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html.
|
||||||
|
.. __: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
============
|
||||||
|
|
||||||
|
You can clone the repository from opendev.org::
|
||||||
|
|
||||||
|
git clone https://opendev.org/openstack/python-openstackclient
|
||||||
|
cd python-openstackclient
|
||||||
|
|
||||||
|
OpenStack Client uses the same contributor process as other OpenStack projects.
|
||||||
|
For information on this process, including help on setting up you Gerrit
|
||||||
|
account and an overview of the CI process, refer to the `OpenStack Contributors
|
||||||
|
Guide`__.
|
||||||
|
|
||||||
|
For more information on contributing to OpenStack Client itself, including
|
||||||
|
guidance on how to design new commands and how to report bugs, refer to the
|
||||||
|
`Contributors Guide`__.
|
||||||
|
|
||||||
|
.. __: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html
|
||||||
|
.. __: https://docs.opendev.org/opendev/infra-manual/latest/developers.html
|
||||||
|
|
||||||
|
Links
|
||||||
|
-----
|
||||||
|
|
||||||
|
* `Issue Tracker <https://bugs.launchpad.net/python-openstackclient>`_
|
||||||
|
* `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_
|
||||||
|
* `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_
|
||||||
|
* `PyPi <https://pypi.org/project/python-openstackclient>`_
|
||||||
|
* `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_
|
||||||
|
* `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_
|
||||||
|
* `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@ libffi-dev [compile test platform:dpkg]
|
||||||
libssl-dev [compile test platform:dpkg]
|
libssl-dev [compile test platform:dpkg]
|
||||||
python3-dev [compile test platform:dpkg]
|
python3-dev [compile test platform:dpkg]
|
||||||
python3-devel [compile test platform:rpm]
|
python3-devel [compile test platform:rpm]
|
||||||
|
libpcre3-dev [test platform:dpkg]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ image
|
||||||
=====
|
=====
|
||||||
|
|
||||||
.. NOTE(efried): This page is hidden from the main TOC; it's here so links in
|
.. NOTE(efried): This page is hidden from the main TOC; it's here so links in
|
||||||
the wild redirect somewhere sane, because previously identity v2 and v3 were
|
the wild redirect somewhere sane, because previously image v2 and v3 were
|
||||||
combined in a single page.
|
combined in a single page.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,15 @@ or, using environment variables:
|
||||||
|
|
||||||
$ TOKEN=$(openstack token issue -f value -c id)
|
$ TOKEN=$(openstack token issue -f value -c id)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The above examples assume you require a project-scoped token. You can omit
|
||||||
|
the project-related configuration if your user has a default project ID set.
|
||||||
|
Conversely, if requesting domain-scoped or system-scoped, you should update
|
||||||
|
these examples accordingly. If the user does not have a default project
|
||||||
|
configured and no scoping information is provided, the resulting token will
|
||||||
|
be unscoped.
|
||||||
|
|
||||||
``v3totp``
|
``v3totp``
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,53 +9,11 @@ rule comprises of a service type, a request path, and a request method. Access
|
||||||
rules may only be created as attributes of application credentials, but they may
|
rules may only be created as attributes of application credentials, but they may
|
||||||
be viewed and deleted independently.
|
be viewed and deleted independently.
|
||||||
|
|
||||||
|
.. autoprogram-cliff:: openstack.identity.v3
|
||||||
|
:command: access rule delete
|
||||||
|
|
||||||
access rule delete
|
.. autoprogram-cliff:: openstack.identity.v3
|
||||||
------------------
|
:command: access rule list
|
||||||
|
|
||||||
Delete access rule(s)
|
.. autoprogram-cliff:: openstack.identity.v3
|
||||||
|
:command: access rule show
|
||||||
.. program:: access rule delete
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack access rule delete <access-rule> [<access-rule> ...]
|
|
||||||
|
|
||||||
.. describe:: <access-rule>
|
|
||||||
|
|
||||||
Access rule(s) to delete (ID)
|
|
||||||
|
|
||||||
access rule list
|
|
||||||
----------------
|
|
||||||
|
|
||||||
List access rules
|
|
||||||
|
|
||||||
.. program:: access rule list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack access rule list
|
|
||||||
[--user <user>]
|
|
||||||
[--user-domain <user-domain>]
|
|
||||||
|
|
||||||
.. option:: --user
|
|
||||||
|
|
||||||
User whose access rules to list (name or ID). If not provided, looks up the
|
|
||||||
current user's access rules.
|
|
||||||
|
|
||||||
.. option:: --user-domain
|
|
||||||
|
|
||||||
Domain the user belongs to (name or ID). This can be
|
|
||||||
used in case collisions between user names exist.
|
|
||||||
|
|
||||||
access rule show
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Display access rule details
|
|
||||||
|
|
||||||
.. program:: access rule show
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack access rule show <access-rule>
|
|
||||||
|
|
||||||
.. describe:: <access-rule>
|
|
||||||
|
|
||||||
Access rule to display (ID)
|
|
||||||
|
|
|
||||||
|
|
@ -2,95 +2,16 @@
|
||||||
consistency group snapshot
|
consistency group snapshot
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Block Storage v2
|
Block Storage v2, v3
|
||||||
|
|
||||||
consistency group snapshot create
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
---------------------------------
|
:command: consistency group snapshot create
|
||||||
|
|
||||||
Create new consistency group snapshot.
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: consistency group snapshot delete
|
||||||
|
|
||||||
.. program:: consistency group snapshot create
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
.. code:: bash
|
:command: consistency group snapshot list
|
||||||
|
|
||||||
openstack consistency group snapshot create
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
[--consistency-group <consistency-group>]
|
:command: consistency group snapshot show
|
||||||
[--description <description>]
|
|
||||||
[<snapshot-name>]
|
|
||||||
|
|
||||||
.. option:: --consistency-group <consistency-group>
|
|
||||||
|
|
||||||
Consistency group to snapshot (name or ID)
|
|
||||||
(default to be the same as <snapshot-name>)
|
|
||||||
|
|
||||||
.. option:: --description <description>
|
|
||||||
|
|
||||||
Description of this consistency group snapshot
|
|
||||||
|
|
||||||
.. _consistency_group_snapshot_create-snapshot-name:
|
|
||||||
.. describe:: <snapshot-name>
|
|
||||||
|
|
||||||
Name of new consistency group snapshot (default to None)
|
|
||||||
|
|
||||||
consistency group snapshot delete
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Delete consistency group snapshot(s)
|
|
||||||
|
|
||||||
.. program:: consistency group snapshot delete
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group snapshot delete
|
|
||||||
<consistency-group-snapshot> [<consistency-group-snapshot> ...]
|
|
||||||
|
|
||||||
.. _consistency_group_snapshot_delete-consistency-group-snapshot:
|
|
||||||
.. describe:: <consistency-group-snapshot>
|
|
||||||
|
|
||||||
Consistency group snapshot(s) to delete (name or ID)
|
|
||||||
|
|
||||||
consistency group snapshot list
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
List consistency group snapshots.
|
|
||||||
|
|
||||||
.. program:: consistency group snapshot list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group snapshot list
|
|
||||||
[--all-projects]
|
|
||||||
[--long]
|
|
||||||
[--status <status>]
|
|
||||||
[--consistency-group <consistency-group>]
|
|
||||||
|
|
||||||
.. option:: --all-projects
|
|
||||||
|
|
||||||
Show detail for all projects. Admin only.
|
|
||||||
(defaults to False)
|
|
||||||
|
|
||||||
.. option:: --long
|
|
||||||
|
|
||||||
List additional fields in output
|
|
||||||
|
|
||||||
.. option:: --status <status>
|
|
||||||
|
|
||||||
Filters results by a status
|
|
||||||
("available", "error", "creating", "deleting" or "error_deleting")
|
|
||||||
|
|
||||||
.. option:: --consistency-group <consistency-group>
|
|
||||||
|
|
||||||
Filters results by a consistency group (name or ID)
|
|
||||||
|
|
||||||
consistency group snapshot show
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
Display consistency group snapshot details.
|
|
||||||
|
|
||||||
.. program:: consistency group snapshot show
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group snapshot show
|
|
||||||
<consistency-group-snapshot>
|
|
||||||
|
|
||||||
.. _consistency_group_snapshot_show-consistency-group-snapshot:
|
|
||||||
.. describe:: <consistency-group-snapshot>
|
|
||||||
|
|
||||||
Consistency group snapshot to display (name or ID)
|
|
||||||
|
|
|
||||||
|
|
@ -2,172 +2,25 @@
|
||||||
consistency group
|
consistency group
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Block Storage v2
|
Block Storage v2, v3
|
||||||
|
|
||||||
consistency group add volume
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
----------------------------
|
:command: consistency group add volume
|
||||||
|
|
||||||
Add volume(s) to consistency group.
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: consistency group create
|
||||||
|
|
||||||
.. program:: consistency group add volume
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
.. code:: bash
|
:command: consistency group delete
|
||||||
|
|
||||||
openstack consistency group add volume
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
<consistency-group>
|
:command: consistency group list
|
||||||
<volume> [<volume> ...]
|
|
||||||
|
|
||||||
.. _consistency_group_add_volume:
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
.. describe:: <consistency-group>
|
:command: consistency group remove volume
|
||||||
|
|
||||||
Consistency group to contain <volume> (name or ID)
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: consistency group set
|
||||||
|
|
||||||
.. describe:: <volume>
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: consistency group show
|
||||||
Volume(s) to add to <consistency-group> (name or ID)
|
|
||||||
(repeat option to add multiple volumes)
|
|
||||||
|
|
||||||
consistency group create
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Create new consistency group.
|
|
||||||
|
|
||||||
.. program:: consistency group create
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group create
|
|
||||||
--volume-type <volume-type> | --consistency-group-source <consistency-group> | --consistency-group-snapshot <consistency-group-snapshot>
|
|
||||||
[--description <description>]
|
|
||||||
[--availability-zone <availability-zone>]
|
|
||||||
[<name>]
|
|
||||||
|
|
||||||
.. option:: --volume-type <volume-type>
|
|
||||||
|
|
||||||
Volume type of this consistency group (name or ID)
|
|
||||||
|
|
||||||
.. option:: --consistency-group-source <consistency-group>
|
|
||||||
|
|
||||||
Existing consistency group (name or ID)
|
|
||||||
|
|
||||||
.. option:: --consistency-group-snapshot <consistency-group-snapshot>
|
|
||||||
|
|
||||||
Existing consistency group snapshot (name or ID)
|
|
||||||
|
|
||||||
.. option:: --description <description>
|
|
||||||
|
|
||||||
Description of this consistency group
|
|
||||||
|
|
||||||
.. option:: --availability-zone <availability-zone>
|
|
||||||
|
|
||||||
Availability zone for this consistency group
|
|
||||||
(not available if creating consistency group from source)
|
|
||||||
|
|
||||||
.. _consistency_group_create-name:
|
|
||||||
.. describe:: <name>
|
|
||||||
|
|
||||||
Name of new consistency group (default to None)
|
|
||||||
|
|
||||||
consistency group delete
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Delete consistency group(s).
|
|
||||||
|
|
||||||
.. program:: consistency group delete
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group delete
|
|
||||||
[--force]
|
|
||||||
<consistency-group> [<consistency-group> ...]
|
|
||||||
|
|
||||||
.. option:: --force
|
|
||||||
|
|
||||||
Allow delete in state other than error or available
|
|
||||||
|
|
||||||
.. _consistency_group_delete-consistency-group:
|
|
||||||
.. describe:: <consistency-group>
|
|
||||||
|
|
||||||
Consistency group(s) to delete (name or ID)
|
|
||||||
|
|
||||||
consistency group list
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
List consistency groups.
|
|
||||||
|
|
||||||
.. program:: consistency group list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group list
|
|
||||||
[--all-projects]
|
|
||||||
[--long]
|
|
||||||
|
|
||||||
.. option:: --all-projects
|
|
||||||
|
|
||||||
Show detail for all projects. Admin only.
|
|
||||||
(defaults to False)
|
|
||||||
|
|
||||||
.. option:: --long
|
|
||||||
|
|
||||||
List additional fields in output
|
|
||||||
|
|
||||||
consistency group remove volume
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
Remove volume(s) from consistency group.
|
|
||||||
|
|
||||||
.. program:: consistency group remove volume
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group remove volume
|
|
||||||
<consistency-group>
|
|
||||||
<volume> [<volume> ...]
|
|
||||||
|
|
||||||
.. _consistency_group_remove_volume:
|
|
||||||
.. describe:: <consistency-group>
|
|
||||||
|
|
||||||
Consistency group containing <volume> (name or ID)
|
|
||||||
|
|
||||||
.. describe:: <volume>
|
|
||||||
|
|
||||||
Volume(s) to remove from <consistency-group> (name or ID)
|
|
||||||
(repeat option to remove multiple volumes)
|
|
||||||
|
|
||||||
consistency group set
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Set consistency group properties.
|
|
||||||
|
|
||||||
.. program:: consistency group set
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group set
|
|
||||||
[--name <name>]
|
|
||||||
[--description <description>]
|
|
||||||
<consistency-group>
|
|
||||||
|
|
||||||
.. option:: --name <name>
|
|
||||||
|
|
||||||
New consistency group name
|
|
||||||
|
|
||||||
.. option:: --description <description>
|
|
||||||
|
|
||||||
New consistency group description
|
|
||||||
|
|
||||||
.. _consistency_group_set-consistency-group:
|
|
||||||
.. describe:: <consistency-group>
|
|
||||||
|
|
||||||
Consistency group to modify (name or ID)
|
|
||||||
|
|
||||||
consistency group show
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Display consistency group details.
|
|
||||||
|
|
||||||
.. program:: consistency group show
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack consistency group show
|
|
||||||
<consistency-group>
|
|
||||||
|
|
||||||
.. _consistency_group_show-consistency-group:
|
|
||||||
.. describe:: <consistency-group>
|
|
||||||
|
|
||||||
Consistency group to display (name or ID)
|
|
||||||
|
|
|
||||||
10
doc/source/cli/command-objects/console-connection.rst
Normal file
10
doc/source/cli/command-objects/console-connection.rst
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
==================
|
||||||
|
console connection
|
||||||
|
==================
|
||||||
|
|
||||||
|
Server console connection information
|
||||||
|
|
||||||
|
Compute v2
|
||||||
|
|
||||||
|
.. autoprogram-cliff:: openstack.compute.v2
|
||||||
|
:command: console connection show
|
||||||
|
|
@ -4,7 +4,7 @@ limits
|
||||||
|
|
||||||
The Compute and Block Storage APIs have resource usage limits.
|
The Compute and Block Storage APIs have resource usage limits.
|
||||||
|
|
||||||
Compute v2, Block Storage v1
|
Block Storage v2, v3; Compute v2
|
||||||
|
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.common
|
.. autoprogram-cliff:: openstack.common
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ quota
|
||||||
Resource quotas appear in multiple APIs, OpenStackClient presents them as a
|
Resource quotas appear in multiple APIs, OpenStackClient presents them as a
|
||||||
single object with multiple properties.
|
single object with multiple properties.
|
||||||
|
|
||||||
Block Storage v1, v2, Compute v2, Network v2
|
Block Storage v1, v3; Compute v2; Network v2
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.common
|
.. autoprogram-cliff:: openstack.common
|
||||||
:command: quota *
|
:command: quota *
|
||||||
|
|
|
||||||
|
|
@ -4,103 +4,5 @@ role assignment
|
||||||
|
|
||||||
Identity v2, v3
|
Identity v2, v3
|
||||||
|
|
||||||
role assignment list
|
.. autoprogram-cliff:: openstack.identity.v3
|
||||||
--------------------
|
:command: role assignment list
|
||||||
|
|
||||||
List role assignments
|
|
||||||
|
|
||||||
.. program:: role assignment list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack role assignment list
|
|
||||||
[--role <role>]
|
|
||||||
[--role-domain <role-domain>]
|
|
||||||
[--user <user>]
|
|
||||||
[--user-domain <user-domain>]
|
|
||||||
[--group <group>]
|
|
||||||
[--group-domain <group-domain>]
|
|
||||||
[--domain <domain>]
|
|
||||||
[--project <project>]
|
|
||||||
[--project-domain <project-domain>]
|
|
||||||
[--effective]
|
|
||||||
[--inherited]
|
|
||||||
[--names]
|
|
||||||
|
|
||||||
.. option:: --role <role>
|
|
||||||
|
|
||||||
Role to filter (name or ID)
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --role-domain <role-domain>
|
|
||||||
|
|
||||||
Domain the role belongs to (name or ID).
|
|
||||||
This can be used in case collisions between role names exist.
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --user <user>
|
|
||||||
|
|
||||||
User to filter (name or ID)
|
|
||||||
|
|
||||||
.. option:: --user-domain <user-domain>
|
|
||||||
|
|
||||||
Domain the user belongs to (name or ID).
|
|
||||||
This can be used in case collisions between user names exist.
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --group <group>
|
|
||||||
|
|
||||||
Group to filter (name or ID)
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --group-domain <group-domain>
|
|
||||||
|
|
||||||
Domain the group belongs to (name or ID).
|
|
||||||
This can be used in case collisions between group names exist.
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --domain <domain>
|
|
||||||
|
|
||||||
Domain to filter (name or ID)
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --project <project>
|
|
||||||
|
|
||||||
Project to filter (name or ID)
|
|
||||||
|
|
||||||
.. option:: --project-domain <project-domain>
|
|
||||||
|
|
||||||
Domain the project belongs to (name or ID).
|
|
||||||
This can be used in case collisions between project names exist.
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --effective
|
|
||||||
|
|
||||||
Returns only effective role assignments (defaults to False)
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --inherited
|
|
||||||
|
|
||||||
Specifies if the role grant is inheritable to the sub projects
|
|
||||||
|
|
||||||
.. versionadded:: 3
|
|
||||||
|
|
||||||
.. option:: --names
|
|
||||||
|
|
||||||
Returns role assignments with names instead of IDs
|
|
||||||
|
|
||||||
.. option:: --auth-user
|
|
||||||
|
|
||||||
Returns role assignments for the authenticated user.
|
|
||||||
|
|
||||||
.. option:: --auth-project
|
|
||||||
|
|
||||||
Returns role assignments for the project to which the authenticated user
|
|
||||||
is scoped.
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
volume backup
|
volume backup
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Block Storage v1, v2, v3
|
Block Storage v2, v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume backup *
|
:command: volume backup *
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
volume qos
|
volume qos
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Block Storage v1, v2, v3
|
Block Storage v2, v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume qos *
|
:command: volume qos *
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
volume service
|
volume service
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Block Storage v1, v2, v3
|
Block Storage v2, v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume service *
|
:command: volume service *
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
volume snapshot
|
volume snapshot
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Block Storage v1, v2, v3
|
Block Storage v2, v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume snapshot *
|
:command: volume snapshot *
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
volume transfer request
|
volume transfer request
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Block Storage v1, v2, v3
|
Block Storage v2, v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume transfer request *
|
:command: volume transfer request *
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
volume type
|
volume type
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Block Storage v1, v2, v3
|
Block Storage v2, v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume type *
|
:command: volume type *
|
||||||
|
|
|
||||||
|
|
@ -2,397 +2,33 @@
|
||||||
volume
|
volume
|
||||||
======
|
======
|
||||||
|
|
||||||
Block Storage v1, v2
|
Block Storage v2, v3
|
||||||
|
|
||||||
volume create
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
-------------
|
:command: volume create
|
||||||
|
|
||||||
Create new volume
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: volume delete
|
||||||
|
|
||||||
.. program:: volume create
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
.. code:: bash
|
:command: volume list
|
||||||
|
|
||||||
openstack volume create
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
[--size <size>]
|
:command: volume migrate
|
||||||
[--type <volume-type>]
|
|
||||||
[--image <image> | --snapshot <snapshot> | --source <volume> ]
|
|
||||||
[--description <description>]
|
|
||||||
[--availability-zone <availability-zone>]
|
|
||||||
[--consistency-group <consistency-group>]
|
|
||||||
[--property <key=value> [...] ]
|
|
||||||
[--hint <key=value> [...] ]
|
|
||||||
[--bootable | --non-bootable]
|
|
||||||
[--read-only | --read-write]
|
|
||||||
<name>
|
|
||||||
|
|
||||||
.. option:: --size <size>
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: volume set
|
||||||
|
|
||||||
Volume size in GB
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
(Required unless --snapshot or --source is specified)
|
:command: volume show
|
||||||
|
|
||||||
.. option:: --type <volume-type>
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
|
:command: volume unset
|
||||||
Set the type of volume
|
|
||||||
|
|
||||||
Select ``<volume-type>`` from the available types as shown
|
|
||||||
by ``volume type list``.
|
|
||||||
|
|
||||||
.. option:: --image <image>
|
|
||||||
|
|
||||||
Use ``<image>`` as source of volume (name or ID)
|
|
||||||
|
|
||||||
This is commonly used to create a boot volume for a server.
|
|
||||||
|
|
||||||
.. option:: --snapshot <snapshot>
|
|
||||||
|
|
||||||
Use ``<snapshot>`` as source of volume (name or ID)
|
|
||||||
|
|
||||||
.. option:: --source <volume>
|
|
||||||
|
|
||||||
Volume to clone (name or ID)
|
|
||||||
|
|
||||||
.. option:: --description <description>
|
|
||||||
|
|
||||||
Volume description
|
|
||||||
|
|
||||||
.. option:: --availability-zone <availability-zone>
|
|
||||||
|
|
||||||
Create volume in ``<availability-zone>``
|
|
||||||
|
|
||||||
.. option:: --consistency-group <consistency-group>
|
|
||||||
|
|
||||||
Consistency group where the new volume belongs to
|
|
||||||
|
|
||||||
.. option:: --property <key=value>
|
|
||||||
|
|
||||||
Set a property on this volume (repeat option to set multiple properties)
|
|
||||||
|
|
||||||
.. option:: --hint <key=value>
|
|
||||||
|
|
||||||
Arbitrary scheduler hint key-value pairs to help boot an instance
|
|
||||||
(repeat option to set multiple hints)
|
|
||||||
|
|
||||||
.. option:: --bootable
|
|
||||||
|
|
||||||
Mark volume as bootable
|
|
||||||
|
|
||||||
.. option:: --non-bootable
|
|
||||||
|
|
||||||
Mark volume as non-bootable (default)
|
|
||||||
|
|
||||||
.. option:: --read-only
|
|
||||||
|
|
||||||
Set volume to read-only access mode
|
|
||||||
|
|
||||||
.. option:: --read-write
|
|
||||||
|
|
||||||
Set volume to read-write access mode (default)
|
|
||||||
|
|
||||||
.. _volume_create-name:
|
|
||||||
.. describe:: <name>
|
|
||||||
|
|
||||||
Volume name
|
|
||||||
|
|
||||||
volume delete
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Delete volume(s)
|
|
||||||
|
|
||||||
.. program:: volume delete
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack volume delete
|
|
||||||
[--force | --purge]
|
|
||||||
<volume> [<volume> ...]
|
|
||||||
|
|
||||||
.. option:: --force
|
|
||||||
|
|
||||||
Attempt forced removal of volume(s), regardless of state (defaults to False)
|
|
||||||
|
|
||||||
.. option:: --purge
|
|
||||||
|
|
||||||
Remove any snapshots along with volume(s) (defaults to False)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. _volume_delete-volume:
|
|
||||||
.. describe:: <volume>
|
|
||||||
|
|
||||||
Volume(s) to delete (name or ID)
|
|
||||||
|
|
||||||
volume list
|
|
||||||
-----------
|
|
||||||
|
|
||||||
List volumes
|
|
||||||
|
|
||||||
.. program:: volume list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack volume list
|
|
||||||
[--project <project> [--project-domain <project-domain>]]
|
|
||||||
[--user <user> [--user-domain <user-domain>]]
|
|
||||||
[--name <name>]
|
|
||||||
[--status <status>]
|
|
||||||
[--all-projects]
|
|
||||||
[--long]
|
|
||||||
[--limit <num-volumes>]
|
|
||||||
[--marker <volume>]
|
|
||||||
|
|
||||||
.. option:: --project <project>
|
|
||||||
|
|
||||||
Filter results by ``<project>`` (name or ID) (admin only)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --project-domain <project-domain>
|
|
||||||
|
|
||||||
Domain the project belongs to (name or ID).
|
|
||||||
|
|
||||||
This can be used in case collisions between project names exist.
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --user <user>
|
|
||||||
|
|
||||||
Filter results by ``<user>`` (name or ID) (admin only)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --user-domain <user-domain>
|
|
||||||
|
|
||||||
Domain the user belongs to (name or ID).
|
|
||||||
|
|
||||||
This can be used in case collisions between user names exist.
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --name <name>
|
|
||||||
|
|
||||||
Filter results by volume name
|
|
||||||
|
|
||||||
.. option:: --status <status>
|
|
||||||
|
|
||||||
Filter results by status
|
|
||||||
|
|
||||||
.. option:: --all-projects
|
|
||||||
|
|
||||||
Include all projects (admin only)
|
|
||||||
|
|
||||||
.. option:: --long
|
|
||||||
|
|
||||||
List additional fields in output
|
|
||||||
|
|
||||||
.. option:: --limit <num-volumes>
|
|
||||||
|
|
||||||
Maximum number of volumes to display
|
|
||||||
|
|
||||||
.. option:: --marker <volume>
|
|
||||||
|
|
||||||
The last volume ID of the previous page
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
volume migrate
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Migrate volume to a new host
|
|
||||||
|
|
||||||
.. program:: volume migrate
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack volume migrate
|
|
||||||
--host <host>
|
|
||||||
[--force-host-copy]
|
|
||||||
[--lock-volume]
|
|
||||||
<volume>
|
|
||||||
|
|
||||||
.. option:: --host <host>
|
|
||||||
|
|
||||||
Destination host (takes the form: host@backend-name#pool) (required)
|
|
||||||
|
|
||||||
.. option:: --force-host-copy
|
|
||||||
|
|
||||||
Enable generic host-based force-migration,
|
|
||||||
which bypasses driver optimizations
|
|
||||||
|
|
||||||
.. option:: --lock-volume
|
|
||||||
|
|
||||||
If specified, the volume state will be locked and will not allow
|
|
||||||
a migration to be aborted (possibly by another operation)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. _volume_migrate-volume:
|
|
||||||
.. describe:: <volume>
|
|
||||||
|
|
||||||
Volume to migrate (name or ID)
|
|
||||||
|
|
||||||
volume set
|
|
||||||
----------
|
|
||||||
|
|
||||||
Set volume properties
|
|
||||||
|
|
||||||
.. program:: volume set
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack volume set
|
|
||||||
[--name <name>]
|
|
||||||
[--size <size>]
|
|
||||||
[--description <description>]
|
|
||||||
[--no-property]
|
|
||||||
[--property <key=value> [...] ]
|
|
||||||
[--image-property <key=value> [...] ]
|
|
||||||
[--state <state>]
|
|
||||||
[--attached | --detached ]
|
|
||||||
[--type <volume-type>]
|
|
||||||
[--retype-policy <retype-policy>]
|
|
||||||
[--bootable | --non-bootable]
|
|
||||||
[--read-only | --read-write]
|
|
||||||
<volume>
|
|
||||||
|
|
||||||
.. option:: --name <name>
|
|
||||||
|
|
||||||
New volume name
|
|
||||||
|
|
||||||
.. option:: --size <size>
|
|
||||||
|
|
||||||
Extend volume size in GB
|
|
||||||
|
|
||||||
.. option:: --description <description>
|
|
||||||
|
|
||||||
New volume description
|
|
||||||
|
|
||||||
.. option:: --no-property
|
|
||||||
|
|
||||||
Remove all properties from :ref:`\<volume\> <volume_set-volume>`
|
|
||||||
(specify both :option:`--no-property` and :option:`--property` to
|
|
||||||
remove the current properties before setting new properties.)
|
|
||||||
|
|
||||||
.. option:: --property <key=value>
|
|
||||||
|
|
||||||
Set a property on this volume (repeat option to set multiple properties)
|
|
||||||
|
|
||||||
.. option:: --type <volume-type>
|
|
||||||
|
|
||||||
New volume type (name or ID)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --retype-policy <retype-policy>
|
|
||||||
|
|
||||||
Migration policy while re-typing volume
|
|
||||||
("never" or "on-demand", default is "never" )
|
|
||||||
(available only when :option:`--type` option is specified)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --bootable
|
|
||||||
|
|
||||||
Mark volume as bootable
|
|
||||||
|
|
||||||
.. option:: --non-bootable
|
|
||||||
|
|
||||||
Mark volume as non-bootable
|
|
||||||
|
|
||||||
.. option:: --read-only
|
|
||||||
|
|
||||||
Set volume to read-only access mode
|
|
||||||
|
|
||||||
.. option:: --read-write
|
|
||||||
|
|
||||||
Set volume to read-write access mode
|
|
||||||
|
|
||||||
.. option:: --image-property <key=value>
|
|
||||||
|
|
||||||
Set an image property on this volume
|
|
||||||
(repeat option to set multiple image properties)
|
|
||||||
|
|
||||||
Image properties are copied along with the image when creating a volume
|
|
||||||
using ``--image``. Note that these properties are immutable on the image
|
|
||||||
itself, this option updates the copy attached to this volume.
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --state <state>
|
|
||||||
|
|
||||||
New volume state
|
|
||||||
("available", "error", "creating", "deleting", "in-use",
|
|
||||||
"attaching", "detaching", "error_deleting" or "maintenance") (admin only)
|
|
||||||
(This option simply changes the state of the volume in the database with
|
|
||||||
no regard to actual status, exercise caution when using)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --attached
|
|
||||||
|
|
||||||
Set volume attachment status to "attached" (admin only)
|
|
||||||
(This option simply changes the state of the volume in the database with
|
|
||||||
no regard to actual status, exercise caution when using)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. option:: --deattach
|
|
||||||
|
|
||||||
Set volume attachment status to "detached" (admin only)
|
|
||||||
(This option simply changes the state of the volume in the database with
|
|
||||||
no regard to actual status, exercise caution when using)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. _volume_set-volume:
|
|
||||||
.. describe:: <volume>
|
|
||||||
|
|
||||||
Volume to modify (name or ID)
|
|
||||||
|
|
||||||
volume show
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Show volume details
|
|
||||||
|
|
||||||
.. program:: volume show
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack volume show
|
|
||||||
<volume>
|
|
||||||
|
|
||||||
.. _volume_show-volume:
|
|
||||||
.. describe:: <volume>
|
|
||||||
|
|
||||||
Volume to display (name or ID)
|
|
||||||
|
|
||||||
volume unset
|
|
||||||
------------
|
|
||||||
|
|
||||||
Unset volume properties
|
|
||||||
|
|
||||||
.. program:: volume unset
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack volume unset
|
|
||||||
[--property <key>]
|
|
||||||
[--image-property <key>]
|
|
||||||
<volume>
|
|
||||||
|
|
||||||
.. option:: --property <key>
|
|
||||||
|
|
||||||
Remove a property from volume (repeat option to remove multiple properties)
|
|
||||||
|
|
||||||
.. option:: --image-property <key>
|
|
||||||
|
|
||||||
Remove an image property from volume
|
|
||||||
(repeat option to remove multiple image properties)
|
|
||||||
|
|
||||||
*Volume version 2 only*
|
|
||||||
|
|
||||||
.. _volume_unset-volume:
|
|
||||||
.. describe:: <volume>
|
|
||||||
|
|
||||||
Volume to modify (name or ID)
|
|
||||||
|
|
||||||
Block Storage v3
|
Block Storage v3
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume summary
|
:command: volume summary
|
||||||
|
|
||||||
.. autoprogram-cliff:: openstack.volume.v3
|
.. autoprogram-cliff:: openstack.volume.v3
|
||||||
:command: volume revert
|
:command: volume revert
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ location-delete,,Remove locations (and related metadata) from an image.
|
||||||
location-update,,Update metadata of an image's location.
|
location-update,,Update metadata of an image's location.
|
||||||
md-namespace-create,image metadef namespace create,Create a new metadata definitions namespace.
|
md-namespace-create,image metadef namespace create,Create a new metadata definitions namespace.
|
||||||
md-namespace-delete,image metadef namespace delete,Delete specified metadata definitions namespace with its contents.
|
md-namespace-delete,image metadef namespace delete,Delete specified metadata definitions namespace with its contents.
|
||||||
md-namespace-import,,Import a metadata definitions namespace from file or standard input.
|
md-namespace-import,WONTFIX,Import a metadata definitions namespace from file or standard input.
|
||||||
md-namespace-list,image metadef namespace list,List metadata definitions namespaces.
|
md-namespace-list,image metadef namespace list,List metadata definitions namespaces.
|
||||||
md-namespace-objects-delete,,Delete all metadata definitions objects inside a specific namespace.
|
md-namespace-objects-delete,image metadef object delete,Delete all metadata definitions objects inside a specific namespace.
|
||||||
md-namespace-properties-delete,,Delete all metadata definitions property inside a specific namespace.
|
md-namespace-properties-delete,image metadef property delete,Delete all metadata definitions property inside a specific namespace.
|
||||||
md-namespace-resource-type-list,image metadef resource type association list,List resource types associated to specific namespace.
|
md-namespace-resource-type-list,image metadef resource type association list,List resource types associated to specific namespace.
|
||||||
md-namespace-show,image metadef namespace show,Describe a specific metadata definitions namespace.
|
md-namespace-show,image metadef namespace show,Describe a specific metadata definitions namespace.
|
||||||
md-namespace-tags-delete,,Delete all metadata definitions tags inside a specific namespace.
|
md-namespace-tags-delete,,Delete all metadata definitions tags inside a specific namespace.
|
||||||
|
|
|
||||||
|
|
|
@ -25,10 +25,3 @@ Plugin Commands
|
||||||
watcher
|
watcher
|
||||||
zaqar
|
zaqar
|
||||||
zun
|
zun
|
||||||
|
|
||||||
.. TODO(efried): Make pages for the following once they're fixed.
|
|
||||||
|
|
||||||
.. cue
|
|
||||||
.. # cueclient is not in global-requirements
|
|
||||||
.. # list-plugins:: openstack.mb.v1
|
|
||||||
.. # :detailed:
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ opened.
|
||||||
## ...
|
## ...
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
public_key = parsed_args.public_key
|
public_key = parsed_args.public_key
|
||||||
if public_key:
|
if public_key:
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
command-wrappers
|
command-wrappers
|
||||||
command-errors
|
command-errors
|
||||||
command-logs
|
command-logs
|
||||||
specs/commands
|
|
||||||
plugins
|
plugins
|
||||||
humaninterfaceguide
|
humaninterfaceguide
|
||||||
api/modules
|
api/modules
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
=======
|
|
||||||
example
|
|
||||||
=======
|
|
||||||
|
|
||||||
This is a specification for the ``example`` command object. It is not intended
|
|
||||||
to be a complete template for new commands since other actions, options
|
|
||||||
and/or arguments may be used. You can include general specification information
|
|
||||||
before the commands below. This information could include links to related material
|
|
||||||
or descriptions of similar commands.
|
|
||||||
|
|
||||||
[example API name] [example API version]
|
|
||||||
|
|
||||||
example create
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Create new example
|
|
||||||
|
|
||||||
.. program:: example create
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack example create
|
|
||||||
<name>
|
|
||||||
|
|
||||||
.. describe:: <name>
|
|
||||||
|
|
||||||
New example name
|
|
||||||
|
|
||||||
example delete
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Delete example(s)
|
|
||||||
|
|
||||||
.. program:: example delete
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack example delete
|
|
||||||
<example> [<example> ...]
|
|
||||||
|
|
||||||
.. describe:: <example>
|
|
||||||
|
|
||||||
Example(s) to delete (name or ID)
|
|
||||||
|
|
||||||
example list
|
|
||||||
------------
|
|
||||||
|
|
||||||
List examples
|
|
||||||
|
|
||||||
.. program:: example list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack example list
|
|
||||||
|
|
||||||
example set
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Set example properties
|
|
||||||
|
|
||||||
.. program:: example set
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack example set
|
|
||||||
[--name <new-name>]
|
|
||||||
<example>
|
|
||||||
|
|
||||||
.. option:: --name <new-name>
|
|
||||||
|
|
||||||
New example name
|
|
||||||
|
|
||||||
.. describe:: <example>
|
|
||||||
|
|
||||||
Example to modify (name or ID)
|
|
||||||
|
|
||||||
example show
|
|
||||||
------------
|
|
||||||
|
|
||||||
Display example details
|
|
||||||
|
|
||||||
.. program:: example show
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack example show
|
|
||||||
<example>
|
|
||||||
|
|
||||||
.. describe:: <example>
|
|
||||||
|
|
||||||
Example to display (name or ID)
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
=============
|
|
||||||
Command Specs
|
|
||||||
=============
|
|
||||||
|
|
||||||
Specifications for new commands, objects and actions are listed below.
|
|
||||||
These specifications have not been implemented. See
|
|
||||||
:ref:`command-list` for implemented commands and
|
|
||||||
:ref:`command-structure` for implemented objects and actions.
|
|
||||||
|
|
||||||
It is optional to propose a specifications patch for new commands,
|
|
||||||
objects and actions here before submitting the implementation. Once your
|
|
||||||
specifications patch merges then you may proceed with the implementation.
|
|
||||||
Your implementation patches should move applicable portions of the
|
|
||||||
specifications patch to the official :ref:`command-list`
|
|
||||||
and :ref:`command-structure` documentation.
|
|
||||||
|
|
||||||
Objects Specs
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Add specifications for new objects based on the ``example`` object.
|
|
||||||
|
|
||||||
Actions Specs
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Add specifications for new actions based on the ``example`` action.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
network-topology
|
|
||||||
|
|
||||||
Commands Specs
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Add specifications for new commands based on the commands for the
|
|
||||||
``example`` object. The ``example`` commands are not intended to
|
|
||||||
be a complete template for new commands since other actions, options
|
|
||||||
and/or arguments may be used.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:glob:
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
command-objects/*
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
================
|
|
||||||
network topology
|
|
||||||
================
|
|
||||||
|
|
||||||
A **network topology** shows a topological graph about
|
|
||||||
devices which connect to the specific network. Also, it
|
|
||||||
will return availability information for each individual
|
|
||||||
device within the network as well. One other thing to note
|
|
||||||
is that it is the intention for OSC to collect data from
|
|
||||||
existing REST APIs
|
|
||||||
|
|
||||||
Network v2
|
|
||||||
|
|
||||||
network topology list
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
List network topologies
|
|
||||||
|
|
||||||
.. program:: network topology list
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack network topology list
|
|
||||||
[--project <project>]
|
|
||||||
|
|
||||||
.. option:: --project <project>
|
|
||||||
|
|
||||||
List network topologies for given project
|
|
||||||
(name or ID)
|
|
||||||
|
|
||||||
network topology show
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Show network topology details
|
|
||||||
|
|
||||||
.. program:: network topology show
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
openstack network topology show
|
|
||||||
<network>
|
|
||||||
|
|
||||||
.. _network_topology_show-network:
|
|
||||||
.. describe:: <network>
|
|
||||||
|
|
||||||
Show network topology for a specific network (name or ID)
|
|
||||||
|
|
@ -94,7 +94,7 @@ def run(opts):
|
||||||
c_list = obj_api.container_list()
|
c_list = obj_api.container_list()
|
||||||
print("Name\tCount\tBytes")
|
print("Name\tCount\tBytes")
|
||||||
for c in c_list:
|
for c in c_list:
|
||||||
print("%s\t%d\t%d" % (c['name'], c['count'], c['bytes']))
|
print(f"{c['name']}\t{c['count']}\t{c['bytes']}")
|
||||||
|
|
||||||
if len(c_list) > 0:
|
if len(c_list) > 0:
|
||||||
# See what is in the first container
|
# See what is in the first container
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ def run(opts):
|
||||||
c_list = client_manager.object_store.container_list()
|
c_list = client_manager.object_store.container_list()
|
||||||
print("Name\tCount\tBytes")
|
print("Name\tCount\tBytes")
|
||||||
for c in c_list:
|
for c in c_list:
|
||||||
print("%s\t%d\t%d" % (c['name'], c['count'], c['bytes']))
|
print(f"{c['name']}\t{c['count']}\t{c['bytes']}")
|
||||||
|
|
||||||
if len(c_list) > 0:
|
if len(c_list) > 0:
|
||||||
# See what is in the first container
|
# See what is in the first container
|
||||||
|
|
|
||||||
179
hacking/checks.py
Normal file
179
hacking/checks.py
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from hacking import core
|
||||||
|
|
||||||
|
"""
|
||||||
|
Guidelines for writing new hacking checks
|
||||||
|
|
||||||
|
- Use only for python-openstackclient specific tests. OpenStack general tests
|
||||||
|
should be submitted to the common 'hacking' module.
|
||||||
|
- Pick numbers in the range O4xx. Find the current test with the highest
|
||||||
|
allocated number and then pick the next value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@core.flake8ext
|
||||||
|
def assert_no_oslo(logical_line):
|
||||||
|
"""Check for use of oslo libraries.
|
||||||
|
|
||||||
|
O400
|
||||||
|
"""
|
||||||
|
if re.match(r'(from|import) oslo_.*', logical_line):
|
||||||
|
yield (0, "0400: oslo libraries should not be used in SDK projects")
|
||||||
|
|
||||||
|
|
||||||
|
@core.flake8ext
|
||||||
|
def assert_no_duplicated_setup(logical_line, filename):
|
||||||
|
"""Check for use of various unnecessary test duplications.
|
||||||
|
|
||||||
|
O401
|
||||||
|
"""
|
||||||
|
if os.path.join('openstackclient', 'tests', 'unit') not in filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
if re.match(r'self.app = .*\(self.app, self.namespace\)', logical_line):
|
||||||
|
yield (
|
||||||
|
0,
|
||||||
|
'O401: It is not necessary to create dummy Namespace objects',
|
||||||
|
)
|
||||||
|
|
||||||
|
if os.path.basename(filename) != 'fakes.py':
|
||||||
|
if re.match(
|
||||||
|
r'self.[a-z_]+_client = self.app.client_manager.*', logical_line
|
||||||
|
):
|
||||||
|
yield (
|
||||||
|
0,
|
||||||
|
"O401: Aliases for mocks of the service client are already "
|
||||||
|
"provided by the respective service's FakeClientMixin class",
|
||||||
|
)
|
||||||
|
|
||||||
|
if match := re.match(
|
||||||
|
r'self.app.client_manager.([a-z_]+) = mock.Mock', logical_line
|
||||||
|
):
|
||||||
|
service = match.group(1)
|
||||||
|
if service == 'auth_ref':
|
||||||
|
return
|
||||||
|
yield (
|
||||||
|
0,
|
||||||
|
f"O401: client_manager.{service} mocks are already provided "
|
||||||
|
f"by the {service} service's FakeClientMixin class",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@core.flake8ext
|
||||||
|
def assert_use_of_client_aliases(logical_line):
|
||||||
|
"""Ensure we use $service_client instead of $sdk_connection.service.
|
||||||
|
|
||||||
|
O402
|
||||||
|
"""
|
||||||
|
# we should expand the list of services as we drop legacy clients
|
||||||
|
if match := re.match(
|
||||||
|
r'self\.app\.client_manager\.sdk_connnection\.(compute|network|image)',
|
||||||
|
logical_line,
|
||||||
|
):
|
||||||
|
service = match.group(1)
|
||||||
|
yield (0, f"0402: prefer {service}_client to sdk_connection.{service}")
|
||||||
|
|
||||||
|
if match := re.match(
|
||||||
|
r'(self\.app\.client_manager\.(compute|network|image)+\.[a-z_]+) = mock.Mock', # noqa: E501
|
||||||
|
logical_line,
|
||||||
|
):
|
||||||
|
yield (
|
||||||
|
0,
|
||||||
|
f"O402: {match.group(1)} is already a mock: there's no need to "
|
||||||
|
f"assign a new mock.Mock instance.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if match := re.match(
|
||||||
|
r'(self\.(compute|network|image)_client\.[a-z_]+) = mock.Mock',
|
||||||
|
logical_line,
|
||||||
|
):
|
||||||
|
yield (
|
||||||
|
0,
|
||||||
|
f"O402: {match.group(1)} is already a mock: there's no need to "
|
||||||
|
f"assign a new mock.Mock instance.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SDKProxyFindChecker(ast.NodeVisitor):
|
||||||
|
"""NodeVisitor to find ``*_client.find_*`` statements."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.error = False
|
||||||
|
|
||||||
|
def visit_Call(self, node):
|
||||||
|
# No need to keep visiting the AST if we already found something.
|
||||||
|
if self.error:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.generic_visit(node)
|
||||||
|
|
||||||
|
if not (
|
||||||
|
isinstance(node.func, ast.Attribute)
|
||||||
|
and node.func.attr.startswith('find_') # and
|
||||||
|
# isinstance(node.func.value, ast.Attribute) and
|
||||||
|
# node.func.value.attr.endswith('_client')
|
||||||
|
):
|
||||||
|
# print(f'skipping: got {node.func}')
|
||||||
|
return
|
||||||
|
|
||||||
|
if not (
|
||||||
|
(
|
||||||
|
# handle calls like 'identity_client.find_project'
|
||||||
|
isinstance(node.func.value, ast.Name)
|
||||||
|
and node.func.value.id.endswith('client')
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
# handle calls like 'self.app.client_manager.image.find_image'
|
||||||
|
isinstance(node.func.value, ast.Attribute)
|
||||||
|
and node.func.value.attr
|
||||||
|
in ('identity', 'network', 'image', 'compute')
|
||||||
|
)
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not any(kw.arg == 'ignore_missing' for kw in node.keywords):
|
||||||
|
self.error = True
|
||||||
|
|
||||||
|
|
||||||
|
@core.flake8ext
|
||||||
|
def assert_find_ignore_missing_kwargs(logical_line, filename):
|
||||||
|
"""Ensure ignore_missing is always used for ``find_*`` SDK proxy calls.
|
||||||
|
|
||||||
|
Okay: self.compute_client.find_server(foo, ignore_missing=True)
|
||||||
|
Okay: self.image_client.find_server(foo, ignore_missing=False)
|
||||||
|
Okay: self.volume_client.volumes.find(name='foo')
|
||||||
|
O403: self.network_client.find_network(parsed_args.network)
|
||||||
|
O403: self.compute_client.find_flavor(flavor_id, get_extra_specs=True)
|
||||||
|
"""
|
||||||
|
if 'tests' in filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
checker = SDKProxyFindChecker()
|
||||||
|
try:
|
||||||
|
parsed_logical_line = ast.parse(logical_line)
|
||||||
|
except SyntaxError:
|
||||||
|
# let flake8 catch this itself
|
||||||
|
# https://github.com/PyCQA/flake8/issues/1948
|
||||||
|
return
|
||||||
|
checker.visit(parsed_logical_line)
|
||||||
|
if checker.error:
|
||||||
|
yield (
|
||||||
|
0,
|
||||||
|
'O403: Calls to find_* proxy methods must explicitly set '
|
||||||
|
'ignore_missing',
|
||||||
|
)
|
||||||
|
|
@ -64,7 +64,7 @@ def list_security_groups(compute_client, all_projects=None):
|
||||||
|
|
||||||
|
|
||||||
def find_security_group(compute_client, name_or_id):
|
def find_security_group(compute_client, name_or_id):
|
||||||
"""Find the name for a given security group name or ID
|
"""Find the security group for a given name or ID
|
||||||
|
|
||||||
https://docs.openstack.org/api-ref/compute/#show-security-group-details
|
https://docs.openstack.org/api-ref/compute/#show-security-group-details
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ def list_networks(compute_client):
|
||||||
|
|
||||||
|
|
||||||
def find_network(compute_client, name_or_id):
|
def find_network(compute_client, name_or_id):
|
||||||
"""Find the ID for a given network name or ID
|
"""Find the network for a given name or ID
|
||||||
|
|
||||||
https://docs.openstack.org/api-ref/compute/#show-network-details
|
https://docs.openstack.org/api-ref/compute/#show-network-details
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,10 @@ class APIv1(api.BaseAPI):
|
||||||
# object's name in the container.
|
# object's name in the container.
|
||||||
object_name_str = name if name else object
|
object_name_str = name if name else object
|
||||||
|
|
||||||
full_url = f"{urllib.parse.quote(container)}/{urllib.parse.quote(object_name_str)}"
|
full_url = (
|
||||||
|
f"{urllib.parse.quote(container)}/"
|
||||||
|
f"{urllib.parse.quote(object_name_str)}"
|
||||||
|
)
|
||||||
with open(object, 'rb') as f:
|
with open(object, 'rb') as f:
|
||||||
response = self.create(
|
response = self.create(
|
||||||
full_url,
|
full_url,
|
||||||
|
|
|
||||||
60
openstackclient/api/volume_v2.py
Normal file
60
openstackclient/api/volume_v2.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Volume v2 API Library
|
||||||
|
|
||||||
|
A collection of wrappers for deprecated Block Storage v2 APIs that are not
|
||||||
|
intentionally supported by SDK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import http
|
||||||
|
|
||||||
|
from openstack import exceptions as sdk_exceptions
|
||||||
|
from osc_lib import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
# consistency groups
|
||||||
|
|
||||||
|
|
||||||
|
def find_consistency_group(compute_client, name_or_id):
|
||||||
|
"""Find the consistency group for a given name or ID
|
||||||
|
|
||||||
|
https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details
|
||||||
|
|
||||||
|
:param volume_client: A volume client
|
||||||
|
:param name_or_id: The name or ID of the consistency group to look up
|
||||||
|
:returns: A consistency group object
|
||||||
|
:raises exception.NotFound: If a matching consistency group could not be
|
||||||
|
found or more than one match was found
|
||||||
|
"""
|
||||||
|
response = compute_client.get(f'/consistencygroups/{name_or_id}')
|
||||||
|
if response.status_code != http.HTTPStatus.NOT_FOUND:
|
||||||
|
# there might be other, non-404 errors
|
||||||
|
sdk_exceptions.raise_from_response(response)
|
||||||
|
return response.json()['consistencygroup']
|
||||||
|
|
||||||
|
response = compute_client.get('/consistencygroups')
|
||||||
|
sdk_exceptions.raise_from_response(response)
|
||||||
|
found = None
|
||||||
|
consistency_groups = response.json()['consistencygroups']
|
||||||
|
for consistency_group in consistency_groups:
|
||||||
|
if consistency_group['name'] == name_or_id:
|
||||||
|
if found:
|
||||||
|
raise exceptions.NotFound(
|
||||||
|
f'multiple matches found for {name_or_id}'
|
||||||
|
)
|
||||||
|
found = consistency_group
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
raise exceptions.NotFound(f'{name_or_id} not found')
|
||||||
|
|
||||||
|
return found
|
||||||
60
openstackclient/api/volume_v3.py
Normal file
60
openstackclient/api/volume_v3.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Volume v3 API Library
|
||||||
|
|
||||||
|
A collection of wrappers for deprecated Block Storage v3 APIs that are not
|
||||||
|
intentionally supported by SDK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import http
|
||||||
|
|
||||||
|
from openstack import exceptions as sdk_exceptions
|
||||||
|
from osc_lib import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
# consistency groups
|
||||||
|
|
||||||
|
|
||||||
|
def find_consistency_group(compute_client, name_or_id):
|
||||||
|
"""Find the consistency group for a given name or ID
|
||||||
|
|
||||||
|
https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details
|
||||||
|
|
||||||
|
:param volume_client: A volume client
|
||||||
|
:param name_or_id: The name or ID of the consistency group to look up
|
||||||
|
:returns: A consistency group object
|
||||||
|
:raises exception.NotFound: If a matching consistency group could not be
|
||||||
|
found or more than one match was found
|
||||||
|
"""
|
||||||
|
response = compute_client.get(f'/consistencygroups/{name_or_id}')
|
||||||
|
if response.status_code != http.HTTPStatus.NOT_FOUND:
|
||||||
|
# there might be other, non-404 errors
|
||||||
|
sdk_exceptions.raise_from_response(response)
|
||||||
|
return response.json()['consistencygroup']
|
||||||
|
|
||||||
|
response = compute_client.get('/consistencygroups')
|
||||||
|
sdk_exceptions.raise_from_response(response)
|
||||||
|
found = None
|
||||||
|
consistency_groups = response.json()['consistencygroups']
|
||||||
|
for consistency_group in consistency_groups:
|
||||||
|
if consistency_group['name'] == name_or_id:
|
||||||
|
if found:
|
||||||
|
raise exceptions.NotFound(
|
||||||
|
f'multiple matches found for {name_or_id}'
|
||||||
|
)
|
||||||
|
found = consistency_group
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
raise exceptions.NotFound(f'{name_or_id} not found')
|
||||||
|
|
||||||
|
return found
|
||||||
27
openstackclient/command.py
Normal file
27
openstackclient/command.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from cliff import lister
|
||||||
|
from cliff import show
|
||||||
|
from osc_lib.command import command
|
||||||
|
|
||||||
|
from openstackclient import shell
|
||||||
|
|
||||||
|
|
||||||
|
class Command(command.Command):
|
||||||
|
app: shell.OpenStackShell
|
||||||
|
|
||||||
|
|
||||||
|
class Lister(Command, lister.Lister): ...
|
||||||
|
|
||||||
|
|
||||||
|
class ShowOne(Command, show.ShowOne): ...
|
||||||
|
|
@ -17,9 +17,9 @@ import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstack import exceptions as sdk_exceptions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ class ListAvailabilityZone(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _get_compute_availability_zones(self, parsed_args):
|
def _get_compute_availability_zones(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
try:
|
try:
|
||||||
data = list(compute_client.availability_zones(details=True))
|
data = list(compute_client.availability_zones(details=True))
|
||||||
except sdk_exceptions.ForbiddenException: # policy doesn't allow
|
except sdk_exceptions.ForbiddenException: # policy doesn't allow
|
||||||
|
|
@ -172,17 +172,14 @@ class ListAvailabilityZone(command.Lister):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
columns: tuple[str, ...] = ('Zone Name', 'Zone Status')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = (
|
columns += (
|
||||||
'Zone Name',
|
|
||||||
'Zone Status',
|
|
||||||
'Zone Resource',
|
'Zone Resource',
|
||||||
'Host Name',
|
'Host Name',
|
||||||
'Service Name',
|
'Service Name',
|
||||||
'Service Status',
|
'Service Status',
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
columns = ('Zone Name', 'Zone Status')
|
|
||||||
|
|
||||||
# Show everything by default.
|
# Show everything by default.
|
||||||
show_all = (
|
show_all = (
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,29 @@
|
||||||
|
|
||||||
"""Manage access to the clients, including authenticating when needed."""
|
"""Manage access to the clients, including authenticating when needed."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from collections.abc import Callable
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
|
from osc_lib.cli import client_config
|
||||||
from osc_lib import clientmanager
|
from osc_lib import clientmanager
|
||||||
from osc_lib import shell
|
from osc_lib import shell
|
||||||
import stevedore
|
import stevedore
|
||||||
|
|
||||||
|
if ty.TYPE_CHECKING:
|
||||||
|
from keystoneauth1 import access as ksa_access
|
||||||
|
from openstack.compute.v2 import _proxy as compute_proxy
|
||||||
|
from openstack.image.v2 import _proxy as image_proxy
|
||||||
|
from openstack.network.v2 import _proxy as network_proxy
|
||||||
|
|
||||||
|
from openstackclient.api import object_store_v1
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLUGIN_MODULES = []
|
PLUGIN_MODULES: list[ty.Any] = []
|
||||||
|
|
||||||
USER_AGENT = 'python-openstackclient'
|
USER_AGENT = 'python-openstackclient'
|
||||||
|
|
||||||
|
|
@ -39,11 +50,23 @@ class ClientManager(clientmanager.ClientManager):
|
||||||
in osc-lib so we need to maintain a transition period.
|
in osc-lib so we need to maintain a transition period.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# A simple incrementing version for the plugin to know what is available
|
if ty.TYPE_CHECKING:
|
||||||
PLUGIN_INTERFACE_VERSION = "2"
|
# we know this will be set by us and will not be nullable
|
||||||
|
auth_ref: ksa_access.AccessInfo
|
||||||
|
|
||||||
# Let the commands set this
|
# this is a hack to keep mypy happy: the actual attributes are set in
|
||||||
_auth_required = False
|
# get_plugin_modules below
|
||||||
|
# TODO(stephenfin): Change the types of identity and volume once we've
|
||||||
|
# migrated everything to SDK. Hopefully by then we'll have figured out
|
||||||
|
# how to statically distinguish between the v2 and v3 versions of both
|
||||||
|
# services...
|
||||||
|
# TODO(stephenfin): We also need to migrate object storage...
|
||||||
|
compute: compute_proxy.Proxy
|
||||||
|
identity: ty.Any
|
||||||
|
image: image_proxy.Proxy
|
||||||
|
network: network_proxy.Proxy
|
||||||
|
object_store: object_store_v1.APIv1
|
||||||
|
volume: ty.Any
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
@ -80,6 +103,12 @@ class ClientManager(clientmanager.ClientManager):
|
||||||
self._auth_required
|
self._auth_required
|
||||||
and self._cli_options._openstack_config is not None
|
and self._cli_options._openstack_config is not None
|
||||||
):
|
):
|
||||||
|
if not isinstance(
|
||||||
|
self._cli_options._openstack_config, client_config.OSC_Config
|
||||||
|
):
|
||||||
|
# programmer error
|
||||||
|
raise TypeError('unexpected type for _openstack_config')
|
||||||
|
|
||||||
self._cli_options._openstack_config._pw_callback = (
|
self._cli_options._openstack_config._pw_callback = (
|
||||||
shell.prompt_for_password
|
shell.prompt_for_password
|
||||||
)
|
)
|
||||||
|
|
@ -106,6 +135,13 @@ class ClientManager(clientmanager.ClientManager):
|
||||||
self._cli_options.config['auth_type'] = self._original_auth_type
|
self._cli_options.config['auth_type'] = self._original_auth_type
|
||||||
del self._cli_options.config['auth']['token']
|
del self._cli_options.config['auth']['token']
|
||||||
del self._cli_options.config['auth']['endpoint']
|
del self._cli_options.config['auth']['endpoint']
|
||||||
|
|
||||||
|
if not isinstance(
|
||||||
|
self._cli_options._openstack_config, client_config.OSC_Config
|
||||||
|
):
|
||||||
|
# programmer error
|
||||||
|
raise TypeError('unexpected type for _openstack_config')
|
||||||
|
|
||||||
self._cli_options._auth = (
|
self._cli_options._auth = (
|
||||||
self._cli_options._openstack_config.load_auth_plugin(
|
self._cli_options._openstack_config.load_auth_plugin(
|
||||||
self._cli_options.config,
|
self._cli_options.config,
|
||||||
|
|
@ -129,20 +165,39 @@ class ClientManager(clientmanager.ClientManager):
|
||||||
# TODO(stephenfin): Drop volume_client argument in OSC 8.0 or later.
|
# TODO(stephenfin): Drop volume_client argument in OSC 8.0 or later.
|
||||||
def is_volume_endpoint_enabled(self, volume_client=None):
|
def is_volume_endpoint_enabled(self, volume_client=None):
|
||||||
"""Check if volume endpoint is enabled"""
|
"""Check if volume endpoint is enabled"""
|
||||||
|
# We check against the service type and all aliases defined by the
|
||||||
|
# Service Types Authority
|
||||||
|
# https://service-types.openstack.org/service-types.json
|
||||||
return (
|
return (
|
||||||
self.is_service_available('volume') is not False
|
self.is_service_available('block-storage') is not False
|
||||||
|
or self.is_service_available('volume') is not False
|
||||||
or self.is_service_available('volumev3') is not False
|
or self.is_service_available('volumev3') is not False
|
||||||
or self.is_service_available('volumev2') is not False
|
or self.is_service_available('volumev2') is not False
|
||||||
|
or self.is_service_available('block-store') is not False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Plugin Support
|
# Plugin Support
|
||||||
|
|
||||||
|
ArgumentParserT = ty.TypeVar('ArgumentParserT', bound=argparse.ArgumentParser)
|
||||||
|
|
||||||
|
|
||||||
|
@ty.runtime_checkable # Optional: allows usage with isinstance()
|
||||||
|
class PluginModule(ty.Protocol):
|
||||||
|
DEFAULT_API_VERSION: str
|
||||||
|
API_VERSION_OPTION: str
|
||||||
|
API_NAME: str
|
||||||
|
API_VERSIONS: tuple[str]
|
||||||
|
|
||||||
|
make_client: Callable[..., ty.Any]
|
||||||
|
build_option_parser: Callable[[ArgumentParserT], ArgumentParserT]
|
||||||
|
check_api_version: Callable[[str], bool]
|
||||||
|
|
||||||
|
|
||||||
def _on_load_failure_callback(
|
def _on_load_failure_callback(
|
||||||
manager: stevedore.ExtensionManager,
|
manager: stevedore.ExtensionManager,
|
||||||
ep: importlib.metadata.EntryPoint,
|
ep: importlib.metadata.EntryPoint,
|
||||||
err: Exception,
|
err: BaseException,
|
||||||
) -> None:
|
) -> None:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n"
|
f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n"
|
||||||
|
|
@ -152,36 +207,25 @@ def _on_load_failure_callback(
|
||||||
def get_plugin_modules(group):
|
def get_plugin_modules(group):
|
||||||
"""Find plugin entry points"""
|
"""Find plugin entry points"""
|
||||||
mod_list = []
|
mod_list = []
|
||||||
|
mgr: stevedore.ExtensionManager[PluginModule]
|
||||||
mgr = stevedore.ExtensionManager(
|
mgr = stevedore.ExtensionManager(
|
||||||
group, on_load_failure_callback=_on_load_failure_callback
|
group, on_load_failure_callback=_on_load_failure_callback
|
||||||
)
|
)
|
||||||
for ep in mgr:
|
for ep in mgr:
|
||||||
LOG.debug('Found plugin %s', ep.name)
|
LOG.debug('Found plugin %s', ep.name)
|
||||||
|
|
||||||
# Different versions of stevedore use different
|
module_name = ep.entry_point.module
|
||||||
# implementations of EntryPoint from other libraries, which
|
|
||||||
# are not API-compatible.
|
|
||||||
try:
|
|
||||||
module_name = ep.entry_point.module_name
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
module_name = ep.entry_point.module
|
|
||||||
except AttributeError:
|
|
||||||
module_name = ep.entry_point.value
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(module_name)
|
module = importlib.import_module(module_name)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
f"WARNING: Failed to import plugin {ep.group}:{ep.name}: "
|
f"WARNING: Failed to import plugin "
|
||||||
f"{err}.\n"
|
f"{ep.module_name}:{ep.name}: {err}.\n"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
mod_list.append(module)
|
mod_list.append(module)
|
||||||
init_func = getattr(module, 'Initialize', None)
|
|
||||||
if init_func:
|
|
||||||
init_func('x')
|
|
||||||
|
|
||||||
# Add the plugin to the ClientManager
|
# Add the plugin to the ClientManager
|
||||||
setattr(
|
setattr(
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
"""Configuration action implementations"""
|
"""Configuration action implementations"""
|
||||||
|
|
||||||
from keystoneauth1.loading import base
|
from keystoneauth1.loading import base
|
||||||
from osc_lib.command import command
|
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
REDACTED = "<redacted>"
|
REDACTED = "<redacted>"
|
||||||
|
|
|
||||||
57
openstackclient/common/envvars.py
Normal file
57
openstackclient/common/envvars.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
def bool_from_str(value, strict=False):
|
||||||
|
true_strings = ('1', 't', 'true', 'on', 'y', 'yes')
|
||||||
|
false_strings = ('0', 'f', 'false', 'off', 'n', 'no')
|
||||||
|
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
|
||||||
|
lowered = value.strip().lower()
|
||||||
|
if lowered in true_strings:
|
||||||
|
return True
|
||||||
|
elif lowered in false_strings or not strict:
|
||||||
|
return False
|
||||||
|
|
||||||
|
msg = _(
|
||||||
|
"Unrecognized value '%(value)s'; acceptable values are: %(valid)s"
|
||||||
|
) % {
|
||||||
|
'value': value,
|
||||||
|
'valid': ', '.join(
|
||||||
|
f"'{s}'" for s in sorted(true_strings + false_strings)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def boolenv(*vars, default=False):
|
||||||
|
"""Search for the first defined of possibly many bool-like env vars.
|
||||||
|
|
||||||
|
Returns the first environment variable defined in vars, or returns the
|
||||||
|
default.
|
||||||
|
|
||||||
|
:param vars: Arbitrary strings to search for. Case sensitive.
|
||||||
|
:param default: The default to return if no value found.
|
||||||
|
:returns: A boolean corresponding to the value found, else the default if
|
||||||
|
no value found.
|
||||||
|
"""
|
||||||
|
for v in vars:
|
||||||
|
value = os.environ.get(v, None)
|
||||||
|
if value:
|
||||||
|
return bool_from_str(value)
|
||||||
|
return default
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
@ -73,17 +73,9 @@ class ListExtension(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
columns: tuple[str, ...] = ('Name', 'Alias', 'Description')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = (
|
columns += ('Namespace', 'Updated At', 'Links')
|
||||||
'Name',
|
|
||||||
'Alias',
|
|
||||||
'Description',
|
|
||||||
'Namespace',
|
|
||||||
'Updated At',
|
|
||||||
'Links',
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
columns = ('Name', 'Alias', 'Description')
|
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
|
|
@ -106,7 +98,7 @@ class ListExtension(command.Lister):
|
||||||
LOG.warning(message)
|
LOG.warning(message)
|
||||||
|
|
||||||
if parsed_args.compute or show_all:
|
if parsed_args.compute or show_all:
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
try:
|
try:
|
||||||
data += compute_client.extensions()
|
data += compute_client.extensions()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as identity_common
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ class ShowLimits(command.Lister):
|
||||||
volume_limits = None
|
volume_limits = None
|
||||||
|
|
||||||
if self.app.client_manager.is_compute_endpoint_enabled():
|
if self.app.client_manager.is_compute_endpoint_enabled():
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
compute_limits = compute_client.get_limits(
|
compute_limits = compute_client.get_limits(
|
||||||
reserved=parsed_args.is_reserved, tenant_id=project_id
|
reserved=parsed_args.is_reserved, tenant_id=project_id
|
||||||
)
|
)
|
||||||
|
|
@ -130,7 +130,7 @@ class ShowLimits(command.Lister):
|
||||||
if self.app.client_manager.is_volume_endpoint_enabled():
|
if self.app.client_manager.is_volume_endpoint_enabled():
|
||||||
volume_client = self.app.client_manager.sdk_connection.volume
|
volume_client = self.app.client_manager.sdk_connection.volume
|
||||||
volume_limits = volume_client.get_limits(
|
volume_limits = volume_client.get_limits(
|
||||||
project_id=project_id,
|
project=project_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.is_absolute:
|
if parsed_args.is_absolute:
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,7 +48,9 @@ class ListCommand(command.Lister):
|
||||||
columns = ('Command Group', 'Commands')
|
columns = ('Command Group', 'Commands')
|
||||||
|
|
||||||
if parsed_args.group:
|
if parsed_args.group:
|
||||||
groups = (group for group in groups if parsed_args.group in group)
|
groups = sorted(
|
||||||
|
group for group in groups if parsed_args.group in group
|
||||||
|
)
|
||||||
|
|
||||||
commands = []
|
commands = []
|
||||||
for group in groups:
|
for group in groups:
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,11 @@ import getpass
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
from cliff.formatters import table
|
from cliff.formatters import table
|
||||||
from osc_lib.command import command
|
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as identity_common
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ def ask_user_yesno(msg):
|
||||||
:return bool: User choice
|
:return bool: User choice
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
answer = getpass._raw_input('{} [{}]: '.format(msg, 'y/n'))
|
answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n'))
|
||||||
if answer in ('y', 'Y', 'yes'):
|
if answer in ('y', 'Y', 'yes'):
|
||||||
return True
|
return True
|
||||||
elif answer in ('n', 'N', 'no'):
|
elif answer in ('n', 'N', 'no'):
|
||||||
|
|
@ -89,18 +90,20 @@ class ProjectCleanup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
sdk = self.app.client_manager.sdk_connection
|
connection = self.app.client_manager.sdk_connection
|
||||||
|
|
||||||
if parsed_args.auth_project:
|
if parsed_args.auth_project:
|
||||||
project_connect = sdk
|
# is we've got a project already configured, use the connection
|
||||||
|
# as-is
|
||||||
|
pass
|
||||||
elif parsed_args.project:
|
elif parsed_args.project:
|
||||||
project = sdk.identity.find_project(
|
project = connection.identity.find_project(
|
||||||
name_or_id=parsed_args.project, ignore_missing=False
|
name_or_id=parsed_args.project, ignore_missing=False
|
||||||
)
|
)
|
||||||
project_connect = sdk.connect_as_project(project)
|
connection = connection.connect_as_project(project)
|
||||||
|
|
||||||
if project_connect:
|
if connection:
|
||||||
status_queue = queue.Queue()
|
status_queue: queue.Queue[ty.Any] = queue.Queue()
|
||||||
parsed_args.max_width = int(
|
parsed_args.max_width = int(
|
||||||
os.environ.get('CLIFF_MAX_TERM_WIDTH', 0)
|
os.environ.get('CLIFF_MAX_TERM_WIDTH', 0)
|
||||||
)
|
)
|
||||||
|
|
@ -119,7 +122,7 @@ class ProjectCleanup(command.Command):
|
||||||
if parsed_args.updated_before:
|
if parsed_args.updated_before:
|
||||||
filters['updated_at'] = parsed_args.updated_before
|
filters['updated_at'] = parsed_args.updated_before
|
||||||
|
|
||||||
project_connect.project_cleanup(
|
connection.project_cleanup(
|
||||||
dry_run=True,
|
dry_run=True,
|
||||||
status_queue=status_queue,
|
status_queue=status_queue,
|
||||||
filters=filters,
|
filters=filters,
|
||||||
|
|
@ -149,7 +152,7 @@ class ProjectCleanup(command.Command):
|
||||||
|
|
||||||
self.log.warning(_('Deleting resources'))
|
self.log.warning(_('Deleting resources'))
|
||||||
|
|
||||||
project_connect.project_cleanup(
|
connection.project_cleanup(
|
||||||
dry_run=False,
|
dry_run=False,
|
||||||
status_queue=status_queue,
|
status_queue=status_queue,
|
||||||
filters=filters,
|
filters=filters,
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,13 @@ import argparse
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstack import exceptions as sdk_exceptions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.network import common
|
from openstackclient.network import common
|
||||||
|
|
||||||
|
|
@ -132,7 +133,7 @@ def get_compute_quotas(
|
||||||
default=False,
|
default=False,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
client = app.client_manager.sdk_connection.compute
|
client = app.client_manager.compute
|
||||||
if default:
|
if default:
|
||||||
quota = client.get_quota_set_defaults(project_id)
|
quota = client.get_quota_set_defaults(project_id)
|
||||||
else:
|
else:
|
||||||
|
|
@ -176,25 +177,37 @@ def get_network_quotas(
|
||||||
default=False,
|
default=False,
|
||||||
):
|
):
|
||||||
def _network_quota_to_dict(network_quota, detail=False):
|
def _network_quota_to_dict(network_quota, detail=False):
|
||||||
if not isinstance(network_quota, dict):
|
dict_quota = network_quota.to_dict(computed=False)
|
||||||
dict_quota = network_quota.to_dict()
|
|
||||||
else:
|
|
||||||
dict_quota = network_quota
|
|
||||||
|
|
||||||
result = {}
|
if not detail:
|
||||||
|
return dict_quota
|
||||||
|
|
||||||
|
# Neutron returns quota details in dict which is in format like:
|
||||||
|
# {'resource_name': {'in_use': X, 'limit': Y, 'reserved': Z},
|
||||||
|
# 'resource_name_2': {'in_use': X2, 'limit': Y2, 'reserved': Z2}}
|
||||||
|
#
|
||||||
|
# but Nova and Cinder returns quota in different format, like:
|
||||||
|
# {'resource_name': X,
|
||||||
|
# 'resource_name_2': X2,
|
||||||
|
# 'usage': {
|
||||||
|
# 'resource_name': Y,
|
||||||
|
# 'resource_name_2': Y2
|
||||||
|
# },
|
||||||
|
# 'reserved': {
|
||||||
|
# 'resource_name': Z,
|
||||||
|
# 'resource_name_2': Z2
|
||||||
|
# }}
|
||||||
|
#
|
||||||
|
# so we need to make conversion to have data in same format from
|
||||||
|
# all of the services
|
||||||
|
result: dict[str, ty.Any] = {"usage": {}, "reservation": {}}
|
||||||
for key, values in dict_quota.items():
|
for key, values in dict_quota.items():
|
||||||
if values is None:
|
if values is None:
|
||||||
continue
|
continue
|
||||||
|
if isinstance(values, dict):
|
||||||
# NOTE(slaweq): Neutron returns values with key "used" but Nova for
|
result[key] = values['limit']
|
||||||
# example returns same data with key "in_use" instead. Because of
|
result["reservation"][key] = values['reserved']
|
||||||
# that we need to convert Neutron key to the same as is returned
|
result["usage"][key] = values['used']
|
||||||
# from Nova to make result more consistent
|
|
||||||
if isinstance(values, dict) and 'used' in values:
|
|
||||||
values['in_use'] = values.pop("used")
|
|
||||||
|
|
||||||
result[key] = values
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -243,15 +256,20 @@ class ListQuota(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _list_quota_compute(self, parsed_args, project_ids):
|
def _list_quota_compute(self, parsed_args, project_ids):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for project_id in project_ids:
|
for project_id in project_ids:
|
||||||
try:
|
try:
|
||||||
project_data = compute_client.get_quota_set(project_id)
|
project_data = compute_client.get_quota_set(project_id)
|
||||||
|
# NOTE(stephenfin): Unfortunately, Nova raises a HTTP 400 (Bad
|
||||||
|
# Request) if the project ID is invalid, even though the project
|
||||||
|
# ID is actually the resource's identifier which would normally
|
||||||
|
# lead us to expect a HTTP 404 (Not Found).
|
||||||
except (
|
except (
|
||||||
sdk_exceptions.NotFoundException,
|
sdk_exceptions.BadRequestException,
|
||||||
sdk_exceptions.ForbiddenException,
|
sdk_exceptions.ForbiddenException,
|
||||||
|
sdk_exceptions.NotFoundException,
|
||||||
) as exc:
|
) as exc:
|
||||||
# Project not found, move on to next one
|
# Project not found, move on to next one
|
||||||
LOG.warning(f"Project {project_id} not found: {exc}")
|
LOG.warning(f"Project {project_id} not found: {exc}")
|
||||||
|
|
@ -273,7 +291,7 @@ class ListQuota(command.Lister):
|
||||||
if default_result != project_result:
|
if default_result != project_result:
|
||||||
result += project_result
|
result += project_result
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'id',
|
'id',
|
||||||
'cores',
|
'cores',
|
||||||
'injected_files',
|
'injected_files',
|
||||||
|
|
@ -286,7 +304,7 @@ class ListQuota(command.Lister):
|
||||||
'server_groups',
|
'server_groups',
|
||||||
'server_group_members',
|
'server_group_members',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'Project ID',
|
'Project ID',
|
||||||
'Cores',
|
'Cores',
|
||||||
'Injected Files',
|
'Injected Files',
|
||||||
|
|
@ -312,8 +330,8 @@ class ListQuota(command.Lister):
|
||||||
try:
|
try:
|
||||||
project_data = volume_client.get_quota_set(project_id)
|
project_data = volume_client.get_quota_set(project_id)
|
||||||
except (
|
except (
|
||||||
sdk_exceptions.NotFoundException,
|
|
||||||
sdk_exceptions.ForbiddenException,
|
sdk_exceptions.ForbiddenException,
|
||||||
|
sdk_exceptions.NotFoundException,
|
||||||
) as exc:
|
) as exc:
|
||||||
# Project not found, move on to next one
|
# Project not found, move on to next one
|
||||||
LOG.warning(f"Project {project_id} not found: {exc}")
|
LOG.warning(f"Project {project_id} not found: {exc}")
|
||||||
|
|
@ -335,7 +353,7 @@ class ListQuota(command.Lister):
|
||||||
if default_result != project_result:
|
if default_result != project_result:
|
||||||
result += project_result
|
result += project_result
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'id',
|
'id',
|
||||||
'backups',
|
'backups',
|
||||||
'backup_gigabytes',
|
'backup_gigabytes',
|
||||||
|
|
@ -344,7 +362,7 @@ class ListQuota(command.Lister):
|
||||||
'snapshots',
|
'snapshots',
|
||||||
'volumes',
|
'volumes',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'Project ID',
|
'Project ID',
|
||||||
'Backups',
|
'Backups',
|
||||||
'Backup Gigabytes',
|
'Backup Gigabytes',
|
||||||
|
|
@ -390,7 +408,7 @@ class ListQuota(command.Lister):
|
||||||
if default_result != project_result:
|
if default_result != project_result:
|
||||||
result += project_result
|
result += project_result
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'id',
|
'id',
|
||||||
'floating_ips',
|
'floating_ips',
|
||||||
'networks',
|
'networks',
|
||||||
|
|
@ -402,7 +420,7 @@ class ListQuota(command.Lister):
|
||||||
'subnets',
|
'subnets',
|
||||||
'subnet_pools',
|
'subnet_pools',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'Project ID',
|
'Project ID',
|
||||||
'Floating IPs',
|
'Floating IPs',
|
||||||
'Networks',
|
'Networks',
|
||||||
|
|
@ -570,7 +588,7 @@ class SetQuota(common.NetDetectionMixin, command.Command):
|
||||||
network_kwargs = {}
|
network_kwargs = {}
|
||||||
|
|
||||||
if self.app.client_manager.is_compute_endpoint_enabled():
|
if self.app.client_manager.is_compute_endpoint_enabled():
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
for k, v in COMPUTE_QUOTAS.items():
|
for k, v in COMPUTE_QUOTAS.items():
|
||||||
value = getattr(parsed_args, k, None)
|
value = getattr(parsed_args, k, None)
|
||||||
|
|
@ -728,21 +746,32 @@ and ``server-group-members`` output for a given quota class."""
|
||||||
# values if the project or class does not exist. This is expected
|
# values if the project or class does not exist. This is expected
|
||||||
# behavior. However, we have already checked for the presence of the
|
# behavior. However, we have already checked for the presence of the
|
||||||
# project above so it shouldn't be an issue.
|
# project above so it shouldn't be an issue.
|
||||||
if parsed_args.service in {'all', 'compute'}:
|
if parsed_args.service == 'compute' or (
|
||||||
|
parsed_args.service == 'all'
|
||||||
|
and self.app.client_manager.is_compute_endpoint_enabled()
|
||||||
|
):
|
||||||
compute_quota_info = get_compute_quotas(
|
compute_quota_info = get_compute_quotas(
|
||||||
self.app,
|
self.app,
|
||||||
project,
|
project,
|
||||||
detail=parsed_args.usage,
|
detail=parsed_args.usage,
|
||||||
default=parsed_args.default,
|
default=parsed_args.default,
|
||||||
)
|
)
|
||||||
if parsed_args.service in {'all', 'volume'}:
|
|
||||||
|
if parsed_args.service == 'volume' or (
|
||||||
|
parsed_args.service == 'all'
|
||||||
|
and self.app.client_manager.is_volume_endpoint_enabled()
|
||||||
|
):
|
||||||
volume_quota_info = get_volume_quotas(
|
volume_quota_info = get_volume_quotas(
|
||||||
self.app,
|
self.app,
|
||||||
project,
|
project,
|
||||||
detail=parsed_args.usage,
|
detail=parsed_args.usage,
|
||||||
default=parsed_args.default,
|
default=parsed_args.default,
|
||||||
)
|
)
|
||||||
if parsed_args.service in {'all', 'network'}:
|
|
||||||
|
if parsed_args.service == 'network' or (
|
||||||
|
parsed_args.service == 'all'
|
||||||
|
and self.app.client_manager.is_network_endpoint_enabled()
|
||||||
|
):
|
||||||
network_quota_info = get_network_quotas(
|
network_quota_info = get_network_quotas(
|
||||||
self.app,
|
self.app,
|
||||||
project,
|
project,
|
||||||
|
|
@ -751,24 +780,42 @@ and ``server-group-members`` output for a given quota class."""
|
||||||
)
|
)
|
||||||
|
|
||||||
info = {}
|
info = {}
|
||||||
|
if parsed_args.usage:
|
||||||
|
info["reservation"] = compute_quota_info.pop("reservation", {})
|
||||||
|
info["reservation"].update(
|
||||||
|
volume_quota_info.pop("reservation", {})
|
||||||
|
)
|
||||||
|
info["reservation"].update(
|
||||||
|
network_quota_info.pop("reservation", {})
|
||||||
|
)
|
||||||
|
|
||||||
|
info["usage"] = compute_quota_info.pop("usage", {})
|
||||||
|
info["usage"].update(volume_quota_info.pop("usage", {}))
|
||||||
|
info["usage"].update(network_quota_info.pop("usage", {}))
|
||||||
|
|
||||||
info.update(compute_quota_info)
|
info.update(compute_quota_info)
|
||||||
info.update(volume_quota_info)
|
info.update(volume_quota_info)
|
||||||
info.update(network_quota_info)
|
info.update(network_quota_info)
|
||||||
|
|
||||||
# Map the internal quota names to the external ones
|
def _normalize_names(section: dict) -> None:
|
||||||
# COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips,
|
# Map the internal quota names to the external ones
|
||||||
# secgroup-rules and secgroups as dict value, so when
|
# COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips,
|
||||||
# neutron is enabled, quotas of these three resources
|
# secgroup-rules and secgroups as dict value, so when
|
||||||
# in nova will be replaced by neutron's.
|
# neutron is enabled, quotas of these three resources
|
||||||
for k, v in itertools.chain(
|
# in nova will be replaced by neutron's.
|
||||||
COMPUTE_QUOTAS.items(),
|
for k, v in itertools.chain(
|
||||||
NOVA_NETWORK_QUOTAS.items(),
|
COMPUTE_QUOTAS.items(),
|
||||||
VOLUME_QUOTAS.items(),
|
NOVA_NETWORK_QUOTAS.items(),
|
||||||
NETWORK_QUOTAS.items(),
|
VOLUME_QUOTAS.items(),
|
||||||
):
|
NETWORK_QUOTAS.items(),
|
||||||
if not k == v and info.get(k) is not None:
|
):
|
||||||
info[v] = info[k]
|
if not k == v and section.get(k) is not None:
|
||||||
info.pop(k)
|
section[v] = section.pop(k)
|
||||||
|
|
||||||
|
_normalize_names(info)
|
||||||
|
if parsed_args.usage:
|
||||||
|
_normalize_names(info["reservation"])
|
||||||
|
_normalize_names(info["usage"])
|
||||||
|
|
||||||
# Remove the 'id' field since it's not very useful
|
# Remove the 'id' field since it's not very useful
|
||||||
if 'id' in info:
|
if 'id' in info:
|
||||||
|
|
@ -793,11 +840,11 @@ and ``server-group-members`` output for a given quota class."""
|
||||||
if k not in ('usage', 'reservation')
|
if k not in ('usage', 'reservation')
|
||||||
]
|
]
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'resource',
|
'resource',
|
||||||
'limit',
|
'limit',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'Resource',
|
'Resource',
|
||||||
'Limit',
|
'Limit',
|
||||||
)
|
)
|
||||||
|
|
@ -875,12 +922,18 @@ class DeleteQuota(command.Command):
|
||||||
)
|
)
|
||||||
|
|
||||||
# compute quotas
|
# compute quotas
|
||||||
if parsed_args.service in {'all', 'compute'}:
|
if parsed_args.service == 'compute' or (
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
parsed_args.service == 'all'
|
||||||
|
and self.app.client_manager.is_compute_endpoint_enabled()
|
||||||
|
):
|
||||||
|
compute_client = self.app.client_manager.compute
|
||||||
compute_client.revert_quota_set(project.id)
|
compute_client.revert_quota_set(project.id)
|
||||||
|
|
||||||
# volume quotas
|
# volume quotas
|
||||||
if parsed_args.service in {'all', 'volume'}:
|
if parsed_args.service == 'volume' or (
|
||||||
|
parsed_args.service == 'all'
|
||||||
|
and self.app.client_manager.is_volume_endpoint_enabled()
|
||||||
|
):
|
||||||
volume_client = self.app.client_manager.sdk_connection.volume
|
volume_client = self.app.client_manager.sdk_connection.volume
|
||||||
volume_client.revert_quota_set(project.id)
|
volume_client.revert_quota_set(project.id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@
|
||||||
|
|
||||||
"""Versions Action Implementation"""
|
"""Versions Action Implementation"""
|
||||||
|
|
||||||
from osc_lib.command import command
|
from openstackclient import command
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
@ -21,13 +20,11 @@ from openstackclient.i18n import _
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# global variables used when building the shell
|
||||||
DEFAULT_API_VERSION = '2.1'
|
DEFAULT_API_VERSION = '2.1'
|
||||||
API_VERSION_OPTION = 'os_compute_api_version'
|
API_VERSION_OPTION = 'os_compute_api_version'
|
||||||
API_NAME = 'compute'
|
API_NAME = 'compute'
|
||||||
API_VERSIONS = {
|
API_VERSIONS = ('2', '2.1')
|
||||||
'2': 'openstack.connection.Connection',
|
|
||||||
'2.1': 'openstack.connection.Connection',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def make_client(instance):
|
def make_client(instance):
|
||||||
|
|
@ -49,3 +46,8 @@ def build_option_parser(parser):
|
||||||
% DEFAULT_API_VERSION,
|
% DEFAULT_API_VERSION,
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def check_api_version(check_version):
|
||||||
|
# SDK supports auto-negotiation for us: always return True
|
||||||
|
return True
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstack import exceptions as sdk_exceptions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ class CreateAgent(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
# doing this since openstacksdk has decided not to support this
|
# doing this since openstacksdk has decided not to support this
|
||||||
# deprecated command
|
# deprecated command
|
||||||
|
|
@ -95,7 +95,7 @@ class DeleteAgent(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
result = 0
|
result = 0
|
||||||
for id in parsed_args.id:
|
for id in parsed_args.id:
|
||||||
try:
|
try:
|
||||||
|
|
@ -114,7 +114,7 @@ class DeleteAgent(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.id)
|
total = len(parsed_args.id)
|
||||||
msg = _("%(result)s of %(total)s agents failed " "to delete.") % {
|
msg = _("%(result)s of %(total)s agents failed to delete.") % {
|
||||||
'result': result,
|
'result': result,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ class ListAgent(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
columns = (
|
columns = (
|
||||||
"Agent ID",
|
"Agent ID",
|
||||||
"Hypervisor",
|
"Hypervisor",
|
||||||
|
|
@ -194,7 +194,7 @@ class SetAgent(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
response = compute_client.get('/os-agents', microversion='2.1')
|
response = compute_client.get('/os-agents', microversion='2.1')
|
||||||
sdk_exceptions.raise_from_response(response)
|
sdk_exceptions.raise_from_response(response)
|
||||||
|
|
|
||||||
|
|
@ -17,21 +17,23 @@
|
||||||
"""Compute v2 Aggregate action implementations"""
|
"""Compute v2 Aggregate action implementations"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
|
from cliff import columns
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
_aggregate_formatters = {
|
_aggregate_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = {
|
||||||
'Hosts': format_columns.ListColumn,
|
'Hosts': format_columns.ListColumn,
|
||||||
'Metadata': format_columns.DictColumn,
|
'Metadata': format_columns.DictColumn,
|
||||||
'hosts': format_columns.ListColumn,
|
'hosts': format_columns.ListColumn,
|
||||||
|
|
@ -67,7 +69,7 @@ class AddAggregateHost(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
aggregate = compute_client.find_aggregate(
|
aggregate = compute_client.find_aggregate(
|
||||||
parsed_args.aggregate, ignore_missing=False
|
parsed_args.aggregate, ignore_missing=False
|
||||||
|
|
@ -110,7 +112,7 @@ class CreateAggregate(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
attrs = {'name': parsed_args.name}
|
attrs = {'name': parsed_args.name}
|
||||||
|
|
||||||
|
|
@ -146,7 +148,7 @@ class DeleteAggregate(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
result = 0
|
result = 0
|
||||||
for a in parsed_args.aggregate:
|
for a in parsed_args.aggregate:
|
||||||
try:
|
try:
|
||||||
|
|
@ -168,9 +170,10 @@ class DeleteAggregate(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.aggregate)
|
total = len(parsed_args.aggregate)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s aggregates failed to delete.") % {
|
||||||
"%(result)s of %(total)s aggregates failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -188,13 +191,13 @@ class ListAggregate(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
aggregates = list(compute_client.aggregates())
|
aggregates = list(compute_client.aggregates())
|
||||||
|
|
||||||
if sdk_utils.supports_microversion(compute_client, '2.41'):
|
if sdk_utils.supports_microversion(compute_client, '2.41'):
|
||||||
column_headers = ("ID", "UUID")
|
column_headers: tuple[str, ...] = ("ID", "UUID")
|
||||||
columns = ("id", "uuid")
|
columns: tuple[str, ...] = ("id", "uuid")
|
||||||
else:
|
else:
|
||||||
column_headers = ("ID",)
|
column_headers = ("ID",)
|
||||||
columns = ("id",)
|
columns = ("id",)
|
||||||
|
|
@ -250,7 +253,7 @@ class RemoveAggregateHost(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
aggregate = compute_client.find_aggregate(
|
aggregate = compute_client.find_aggregate(
|
||||||
parsed_args.aggregate, ignore_missing=False
|
parsed_args.aggregate, ignore_missing=False
|
||||||
|
|
@ -307,7 +310,7 @@ class SetAggregate(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
aggregate = compute_client.find_aggregate(
|
aggregate = compute_client.find_aggregate(
|
||||||
parsed_args.aggregate, ignore_missing=False
|
parsed_args.aggregate, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -320,7 +323,7 @@ class SetAggregate(command.Command):
|
||||||
if kwargs:
|
if kwargs:
|
||||||
compute_client.update_aggregate(aggregate.id, **kwargs)
|
compute_client.update_aggregate(aggregate.id, **kwargs)
|
||||||
|
|
||||||
properties = {}
|
properties: dict[str, ty.Any] = {}
|
||||||
if parsed_args.no_property:
|
if parsed_args.no_property:
|
||||||
# NOTE(RuiChen): "availability_zone" can not be unset from
|
# NOTE(RuiChen): "availability_zone" can not be unset from
|
||||||
# properties. It is already excluded from show and create output.
|
# properties. It is already excluded from show and create output.
|
||||||
|
|
@ -352,7 +355,7 @@ class ShowAggregate(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
aggregate = compute_client.find_aggregate(
|
aggregate = compute_client.find_aggregate(
|
||||||
parsed_args.aggregate, ignore_missing=False
|
parsed_args.aggregate, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -392,7 +395,7 @@ class UnsetAggregate(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
aggregate = compute_client.find_aggregate(
|
aggregate = compute_client.find_aggregate(
|
||||||
parsed_args.aggregate, ignore_missing=False
|
parsed_args.aggregate, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -427,7 +430,7 @@ class CacheImageForAggregate(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.81'):
|
if not sdk_utils.supports_microversion(compute_client, '2.81'):
|
||||||
msg = _(
|
msg = _(
|
||||||
|
|
@ -436,15 +439,15 @@ class CacheImageForAggregate(command.Command):
|
||||||
)
|
)
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
image_client = self.app.client_manager.sdk_connection.image
|
||||||
|
|
||||||
aggregate = compute_client.find_aggregate(
|
aggregate = compute_client.find_aggregate(
|
||||||
parsed_args.aggregate, ignore_missing=False
|
parsed_args.aggregate, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
||||||
images = []
|
images = []
|
||||||
for img in parsed_args.image:
|
for img in parsed_args.image:
|
||||||
image = self.app.client_manager.sdk_connection.image.find_image(
|
image = image_client.find_image(img, ignore_missing=False)
|
||||||
img, ignore_missing=False
|
|
||||||
)
|
|
||||||
images.append(image.id)
|
images.append(image.id)
|
||||||
|
|
||||||
compute_client.aggregate_precache_images(aggregate.id, images)
|
compute_client.aggregate_precache_images(aggregate.id, images)
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,18 @@
|
||||||
"""Compute v2 Console action implementations"""
|
"""Compute v2 Console action implementations"""
|
||||||
|
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
def _get_console_columns(item):
|
def _get_console_columns(item):
|
||||||
# To maintain backwards compatibility we need to rename sdk props to
|
# To maintain backwards compatibility we need to rename sdk props to
|
||||||
# whatever OSC was using before
|
# whatever OSC was using before
|
||||||
column_map = {}
|
|
||||||
hidden_columns = ['id', 'links', 'location', 'name']
|
hidden_columns = ['id', 'links', 'location', 'name']
|
||||||
return utils.get_osc_show_columns_for_sdk_resource(
|
return utils.get_osc_show_columns_for_sdk_resource(
|
||||||
item, column_map, hidden_columns
|
item, {}, hidden_columns
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -56,7 +55,7 @@ class ShowConsoleLog(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
name_or_id=parsed_args.server, ignore_missing=False
|
name_or_id=parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -65,13 +64,15 @@ class ShowConsoleLog(command.Command):
|
||||||
output = compute_client.get_server_console_output(
|
output = compute_client.get_server_console_output(
|
||||||
server.id, length=parsed_args.lines
|
server.id, length=parsed_args.lines
|
||||||
)
|
)
|
||||||
data = None
|
data: str | None = None
|
||||||
if output:
|
if output:
|
||||||
data = output.get('output', None)
|
data = output.get('output', None)
|
||||||
|
|
||||||
if data and data[-1] != '\n':
|
if data and data[-1] != '\n':
|
||||||
data += '\n'
|
data += '\n'
|
||||||
self.app.stdout.write(data)
|
|
||||||
|
if data:
|
||||||
|
self.app.stdout.write(data)
|
||||||
|
|
||||||
|
|
||||||
class ShowConsoleURL(command.ShowOne):
|
class ShowConsoleURL(command.ShowOne):
|
||||||
|
|
@ -107,6 +108,13 @@ class ShowConsoleURL(command.ShowOne):
|
||||||
const='spice-html5',
|
const='spice-html5',
|
||||||
help=_("Show SPICE console URL"),
|
help=_("Show SPICE console URL"),
|
||||||
)
|
)
|
||||||
|
type_group.add_argument(
|
||||||
|
'--spice-direct',
|
||||||
|
dest='url_type',
|
||||||
|
action='store_const',
|
||||||
|
const='spice-direct',
|
||||||
|
help=_("Show SPICE direct protocol native console URL"),
|
||||||
|
)
|
||||||
type_group.add_argument(
|
type_group.add_argument(
|
||||||
'--rdp',
|
'--rdp',
|
||||||
dest='url_type',
|
dest='url_type',
|
||||||
|
|
@ -131,7 +139,7 @@ class ShowConsoleURL(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
|
||||||
48
openstackclient/compute/v2/console_connection.py
Normal file
48
openstackclient/compute/v2/console_connection.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Compute v2 Console auth token implementations."""
|
||||||
|
|
||||||
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
def _get_console_connection_columns(item):
|
||||||
|
column_map: dict[str, str] = {}
|
||||||
|
hidden_columns = ['id', 'location', 'name']
|
||||||
|
return utils.get_osc_show_columns_for_sdk_resource(
|
||||||
|
item, column_map, hidden_columns
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ShowConsoleConnectionInformation(command.ShowOne):
|
||||||
|
_description = _("Show server's remote console connection information")
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super().get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'token',
|
||||||
|
metavar='<token>',
|
||||||
|
help=_("Nova console token to lookup"),
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
compute_client = self.app.client_manager.compute
|
||||||
|
data = compute_client.validate_console_auth_token(parsed_args.token)
|
||||||
|
display_columns, columns = _get_console_connection_columns(data)
|
||||||
|
data = utils.get_dict_properties(data, columns)
|
||||||
|
|
||||||
|
return (display_columns, data)
|
||||||
|
|
@ -21,10 +21,10 @@ from openstack import exceptions as sdk_exceptions
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as identity_common
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
@ -149,19 +149,32 @@ class CreateFlavor(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
if parsed_args.project and parsed_args.public:
|
if parsed_args.project and parsed_args.public:
|
||||||
msg = _("--project is only allowed with --private")
|
msg = _("--project is only allowed with --private")
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
flavor_id = parsed_args.id
|
||||||
|
if parsed_args.id == 'auto':
|
||||||
|
# novaclient aliased 'auto' to mean "generate a UUID for me": we
|
||||||
|
# do the same to avoid breaking existing users
|
||||||
|
flavor_id = None
|
||||||
|
|
||||||
|
msg = _(
|
||||||
|
"Passing '--id auto' is deprecated. Nova will automatically "
|
||||||
|
"assign a UUID-like ID if no ID is provided. Omit the '--id' "
|
||||||
|
"parameter instead."
|
||||||
|
)
|
||||||
|
self.log.warning(msg)
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
'name': parsed_args.name,
|
'name': parsed_args.name,
|
||||||
'ram': parsed_args.ram,
|
'ram': parsed_args.ram,
|
||||||
'vcpus': parsed_args.vcpus,
|
'vcpus': parsed_args.vcpus,
|
||||||
'disk': parsed_args.disk,
|
'disk': parsed_args.disk,
|
||||||
'id': parsed_args.id,
|
'id': flavor_id,
|
||||||
'ephemeral': parsed_args.ephemeral,
|
'ephemeral': parsed_args.ephemeral,
|
||||||
'swap': parsed_args.swap,
|
'swap': parsed_args.swap,
|
||||||
'rxtx_factor': parsed_args.rxtx_factor,
|
'rxtx_factor': parsed_args.rxtx_factor,
|
||||||
|
|
@ -190,8 +203,7 @@ class CreateFlavor(command.ShowOne):
|
||||||
compute_client.flavor_add_tenant_access(flavor.id, project_id)
|
compute_client.flavor_add_tenant_access(flavor.id, project_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = _(
|
msg = _(
|
||||||
"Failed to add project %(project)s access to "
|
"Failed to add project %(project)s access to flavor: %(e)s"
|
||||||
"flavor: %(e)s"
|
|
||||||
)
|
)
|
||||||
LOG.error(msg, {'project': parsed_args.project, 'e': e})
|
LOG.error(msg, {'project': parsed_args.project, 'e': e})
|
||||||
if parsed_args.properties:
|
if parsed_args.properties:
|
||||||
|
|
@ -224,7 +236,7 @@ class DeleteFlavor(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
result = 0
|
result = 0
|
||||||
for f in parsed_args.flavor:
|
for f in parsed_args.flavor:
|
||||||
try:
|
try:
|
||||||
|
|
@ -242,7 +254,7 @@ class DeleteFlavor(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.flavor)
|
total = len(parsed_args.flavor)
|
||||||
msg = _("%(result)s of %(total)s flavors failed " "to delete.") % {
|
msg = _("%(result)s of %(total)s flavors failed to delete.") % {
|
||||||
'result': result,
|
'result': result,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +309,7 @@ class ListFlavor(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
# is_public is ternary - None means give all flavors,
|
# is_public is ternary - None means give all flavors,
|
||||||
# True is public only and False is private only
|
# True is public only and False is private only
|
||||||
# By default Nova assumes True and gives admins public flavors
|
# By default Nova assumes True and gives admins public flavors
|
||||||
|
|
@ -330,7 +342,7 @@ class ListFlavor(command.Lister):
|
||||||
if parsed_args.long and not f.extra_specs:
|
if parsed_args.long and not f.extra_specs:
|
||||||
compute_client.fetch_flavor_extra_specs(f)
|
compute_client.fetch_flavor_extra_specs(f)
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
"id",
|
"id",
|
||||||
"name",
|
"name",
|
||||||
"ram",
|
"ram",
|
||||||
|
|
@ -346,7 +358,7 @@ class ListFlavor(command.Lister):
|
||||||
"extra_specs",
|
"extra_specs",
|
||||||
)
|
)
|
||||||
|
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
"ID",
|
"ID",
|
||||||
"Name",
|
"Name",
|
||||||
"RAM",
|
"RAM",
|
||||||
|
|
@ -404,9 +416,7 @@ class SetFlavor(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--project',
|
'--project',
|
||||||
metavar='<project>',
|
metavar='<project>',
|
||||||
help=_(
|
help=_('Set flavor access to project (name or ID) (admin only)'),
|
||||||
'Set flavor access to project (name or ID) ' '(admin only)'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
identity_common.add_project_domain_option_to_parser(parser)
|
identity_common.add_project_domain_option_to_parser(parser)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -421,7 +431,7 @@ class SetFlavor(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -483,7 +493,7 @@ class SetFlavor(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
raise exceptions.CommandError(
|
raise exceptions.CommandError(
|
||||||
_("Command Failed: One or more of" " the operations failed")
|
_("Command Failed: One or more of the operations failed")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -500,7 +510,7 @@ class ShowFlavor(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
flavor = compute_client.find_flavor(
|
flavor = compute_client.find_flavor(
|
||||||
parsed_args.flavor, get_extra_specs=True, ignore_missing=False
|
parsed_args.flavor, get_extra_specs=True, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -560,8 +570,7 @@ class UnsetFlavor(command.Command):
|
||||||
'--project',
|
'--project',
|
||||||
metavar='<project>',
|
metavar='<project>',
|
||||||
help=_(
|
help=_(
|
||||||
'Remove flavor access from project (name or ID) '
|
'Remove flavor access from project (name or ID) (admin only)'
|
||||||
'(admin only)'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
identity_common.add_project_domain_option_to_parser(parser)
|
identity_common.add_project_domain_option_to_parser(parser)
|
||||||
|
|
@ -569,7 +578,7 @@ class UnsetFlavor(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -612,5 +621,5 @@ class UnsetFlavor(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
raise exceptions.CommandError(
|
raise exceptions.CommandError(
|
||||||
_("Command Failed: One or more of" " the operations failed")
|
_("Command Failed: One or more of the operations failed")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
"""Host action implementations"""
|
"""Host action implementations"""
|
||||||
|
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstack import exceptions as sdk_exceptions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ class ListHost(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"API has been deprecated; "
|
"API has been deprecated; "
|
||||||
|
|
@ -83,7 +83,7 @@ class SetHost(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"API has been deprecated; "
|
"API has been deprecated; "
|
||||||
|
|
@ -121,7 +121,7 @@ class ShowHost(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"API has been deprecated; "
|
"API has been deprecated; "
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import re
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstack import exceptions as sdk_exceptions
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ class ListHypervisor(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
list_opts = {}
|
list_opts = {}
|
||||||
|
|
||||||
|
|
@ -119,14 +119,20 @@ class ListHypervisor(command.Lister):
|
||||||
if parsed_args.matching:
|
if parsed_args.matching:
|
||||||
list_opts['hypervisor_hostname_pattern'] = parsed_args.matching
|
list_opts['hypervisor_hostname_pattern'] = parsed_args.matching
|
||||||
|
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
"ID",
|
"ID",
|
||||||
"Hypervisor Hostname",
|
"Hypervisor Hostname",
|
||||||
"Hypervisor Type",
|
"Hypervisor Type",
|
||||||
"Host IP",
|
"Host IP",
|
||||||
"State",
|
"State",
|
||||||
)
|
)
|
||||||
columns = ('id', 'name', 'hypervisor_type', 'host_ip', 'state')
|
columns: tuple[str, ...] = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'hypervisor_type',
|
||||||
|
'host_ip',
|
||||||
|
'state',
|
||||||
|
)
|
||||||
|
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.88'):
|
if not sdk_utils.supports_microversion(compute_client, '2.88'):
|
||||||
|
|
@ -164,7 +170,7 @@ class ShowHypervisor(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
hypervisor_id = compute_client.find_hypervisor(
|
hypervisor_id = compute_client.find_hypervisor(
|
||||||
parsed_args.hypervisor, ignore_missing=False, details=False
|
parsed_args.hypervisor, ignore_missing=False, details=False
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@
|
||||||
|
|
||||||
"""Hypervisor Stats action implementations"""
|
"""Hypervisor Stats action implementations"""
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ class ShowHypervisorStats(command.ShowOne):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
# The command is deprecated since it is being dropped in Nova.
|
# The command is deprecated since it is being dropped in Nova.
|
||||||
self.log.warning(_("This command is deprecated."))
|
self.log.warning(_("This command is deprecated."))
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
# We do API request directly cause this deprecated method is not and
|
# We do API request directly cause this deprecated method is not and
|
||||||
# will not be supported by OpenStackSDK.
|
# will not be supported by OpenStackSDK.
|
||||||
response = compute_client.get(
|
response = compute_client.get(
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ import os
|
||||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as identity_common
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
@ -61,14 +61,13 @@ def _generate_keypair():
|
||||||
def _get_keypair_columns(item, hide_pub_key=False, hide_priv_key=False):
|
def _get_keypair_columns(item, hide_pub_key=False, hide_priv_key=False):
|
||||||
# To maintain backwards compatibility we need to rename sdk props to
|
# To maintain backwards compatibility we need to rename sdk props to
|
||||||
# whatever OSC was using before
|
# whatever OSC was using before
|
||||||
column_map = {}
|
|
||||||
hidden_columns = ['links', 'location']
|
hidden_columns = ['links', 'location']
|
||||||
if hide_pub_key:
|
if hide_pub_key:
|
||||||
hidden_columns.append('public_key')
|
hidden_columns.append('public_key')
|
||||||
if hide_priv_key:
|
if hide_priv_key:
|
||||||
hidden_columns.append('private_key')
|
hidden_columns.append('private_key')
|
||||||
return utils.get_osc_show_columns_for_sdk_resource(
|
return utils.get_osc_show_columns_for_sdk_resource(
|
||||||
item, column_map, hidden_columns
|
item, {}, hidden_columns
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -121,13 +120,12 @@ class CreateKeypair(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
kwargs = {'name': parsed_args.name}
|
kwargs = {'name': parsed_args.name}
|
||||||
|
|
||||||
if parsed_args.public_key:
|
if parsed_args.public_key:
|
||||||
generated_keypair = None
|
|
||||||
try:
|
try:
|
||||||
with open(os.path.expanduser(parsed_args.public_key)) as p:
|
with open(os.path.expanduser(parsed_args.public_key)) as p:
|
||||||
public_key = p.read()
|
public_key = p.read()
|
||||||
|
|
@ -230,7 +228,7 @@ class DeleteKeypair(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
@ -258,13 +256,13 @@ class DeleteKeypair(command.Command):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
_("Failed to delete key with name " "'%(name)s': %(e)s"),
|
_("Failed to delete key with name '%(name)s': %(e)s"),
|
||||||
{'name': n, 'e': e},
|
{'name': n, 'e': e},
|
||||||
)
|
)
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.name)
|
total = len(parsed_args.name)
|
||||||
msg = _("%(result)s of %(total)s keys failed " "to delete.") % {
|
msg = _("%(result)s of %(total)s keys failed to delete.") % {
|
||||||
'result': result,
|
'result': result,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -300,8 +298,9 @@ class ListKeypair(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
identity_sdk_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
|
|
@ -347,11 +346,17 @@ class ListKeypair(command.Lister):
|
||||||
parsed_args.project,
|
parsed_args.project,
|
||||||
parsed_args.project_domain,
|
parsed_args.project_domain,
|
||||||
).id
|
).id
|
||||||
users = identity_client.users.list(tenant_id=project)
|
assignments = identity_sdk_client.role_assignments(
|
||||||
|
scope_project_id=project
|
||||||
|
)
|
||||||
|
user_ids = set()
|
||||||
|
for assignment in assignments:
|
||||||
|
if assignment.user:
|
||||||
|
user_ids.add(assignment.user['id'])
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for user in users:
|
for user_id in user_ids:
|
||||||
kwargs['user_id'] = user.id
|
kwargs['user_id'] = user_id
|
||||||
data.extend(compute_client.keypairs(**kwargs))
|
data.extend(compute_client.keypairs(**kwargs))
|
||||||
elif parsed_args.user:
|
elif parsed_args.user:
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.10'):
|
if not sdk_utils.supports_microversion(compute_client, '2.10'):
|
||||||
|
|
@ -372,7 +377,7 @@ class ListKeypair(command.Lister):
|
||||||
else:
|
else:
|
||||||
data = compute_client.keypairs(**kwargs)
|
data = compute_client.keypairs(**kwargs)
|
||||||
|
|
||||||
columns = ("Name", "Fingerprint")
|
columns: tuple[str, ...] = ("Name", "Fingerprint")
|
||||||
|
|
||||||
if sdk_utils.supports_microversion(compute_client, '2.2'):
|
if sdk_utils.supports_microversion(compute_client, '2.2'):
|
||||||
columns += ("Type",)
|
columns += ("Type",)
|
||||||
|
|
@ -411,7 +416,7 @@ class ShowKeypair(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,12 @@ from openstack import exceptions as sdk_exceptions
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
from openstackclient.api import compute_v2
|
from openstackclient.api import compute_v2
|
||||||
|
from openstackclient import command
|
||||||
|
from openstackclient.common import envvars
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as identity_common
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
@ -44,7 +45,7 @@ LOG = logging.getLogger(__name__)
|
||||||
IMAGE_STRING_FOR_BFV = 'N/A (booted from volume)'
|
IMAGE_STRING_FOR_BFV = 'N/A (booted from volume)'
|
||||||
|
|
||||||
|
|
||||||
class PowerStateColumn(cliff_columns.FormattableColumn):
|
class PowerStateColumn(cliff_columns.FormattableColumn[int]):
|
||||||
"""Generate a formatted string of a server's power state."""
|
"""Generate a formatted string of a server's power state."""
|
||||||
|
|
||||||
power_states = [
|
power_states = [
|
||||||
|
|
@ -65,7 +66,7 @@ class PowerStateColumn(cliff_columns.FormattableColumn):
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
|
|
||||||
|
|
||||||
class AddressesColumn(cliff_columns.FormattableColumn):
|
class AddressesColumn(cliff_columns.FormattableColumn[ty.Any]):
|
||||||
"""Generate a formatted string of a server's addresses."""
|
"""Generate a formatted string of a server's addresses."""
|
||||||
|
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
|
|
@ -86,7 +87,7 @@ class AddressesColumn(cliff_columns.FormattableColumn):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class HostColumn(cliff_columns.FormattableColumn):
|
class HostColumn(cliff_columns.FormattableColumn[str | None]):
|
||||||
"""Generate a formatted string of a hostname."""
|
"""Generate a formatted string of a hostname."""
|
||||||
|
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
|
|
@ -183,8 +184,17 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
||||||
'updated_at': 'updated',
|
'updated_at': 'updated',
|
||||||
'user_data': 'OS-EXT-SRV-ATTR:user_data',
|
'user_data': 'OS-EXT-SRV-ATTR:user_data',
|
||||||
'vm_state': 'OS-EXT-STS:vm_state',
|
'vm_state': 'OS-EXT-STS:vm_state',
|
||||||
'pinned_availability_zone': 'pinned_availability_zone',
|
|
||||||
}
|
}
|
||||||
|
# NOTE(ratailor): microversion 2.96 introduces
|
||||||
|
# pinned_availability_zone support
|
||||||
|
if sdk_utils.supports_microversion(compute_client, '2.96'):
|
||||||
|
column_map['pinned_availability_zone'] = 'pinned_availability_zone'
|
||||||
|
|
||||||
|
# NOTE(ratailor): microversion 2.100 introduces
|
||||||
|
# scheduler_hints support
|
||||||
|
if sdk_utils.supports_microversion(compute_client, '2.100'):
|
||||||
|
column_map['scheduler_hints'] = 'scheduler_hints'
|
||||||
|
|
||||||
# Some columns returned by openstacksdk should not be shown because they're
|
# Some columns returned by openstacksdk should not be shown because they're
|
||||||
# either irrelevant or duplicates
|
# either irrelevant or duplicates
|
||||||
ignored_columns = {
|
ignored_columns = {
|
||||||
|
|
@ -204,7 +214,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
||||||
'min_count',
|
'min_count',
|
||||||
'networks',
|
'networks',
|
||||||
'personality',
|
'personality',
|
||||||
'scheduler_hints',
|
|
||||||
# aliases
|
# aliases
|
||||||
'volumes',
|
'volumes',
|
||||||
# unnecessary
|
# unnecessary
|
||||||
|
|
@ -235,6 +244,16 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
||||||
|
|
||||||
info = data
|
info = data
|
||||||
|
|
||||||
|
# NOTE(dviroel): microversion 2.100 is now retrieving scheduler_hints
|
||||||
|
# content from request_spec on detailed responses
|
||||||
|
if not sdk_utils.supports_microversion(compute_client, '2.100'):
|
||||||
|
info.pop('scheduler_hints', None)
|
||||||
|
|
||||||
|
# NOTE(ratailor): microversion 2.96 introduces
|
||||||
|
# pinned_availability_zone support
|
||||||
|
if not sdk_utils.supports_microversion(compute_client, '2.96'):
|
||||||
|
info.pop('pinned_availability_zone', None)
|
||||||
|
|
||||||
# Convert the image blob to a name
|
# Convert the image blob to a name
|
||||||
image_info = info.get('image', {})
|
image_info = info.get('image', {})
|
||||||
if image_info and any(image_info.values()):
|
if image_info and any(image_info.values()):
|
||||||
|
|
@ -321,51 +340,15 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
||||||
info['OS-EXT-STS:power_state']
|
info['OS-EXT-STS:power_state']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if sdk_utils.supports_microversion(compute_client, '2.100'):
|
||||||
|
if 'scheduler_hints' in info:
|
||||||
|
info['scheduler_hints'] = format_columns.DictListColumn(
|
||||||
|
info.pop('scheduler_hints', {}),
|
||||||
|
)
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def bool_from_str(value, strict=False):
|
|
||||||
true_strings = ('1', 't', 'true', 'on', 'y', 'yes')
|
|
||||||
false_strings = ('0', 'f', 'false', 'off', 'n', 'no')
|
|
||||||
|
|
||||||
if isinstance(value, bool):
|
|
||||||
return value
|
|
||||||
|
|
||||||
lowered = value.strip().lower()
|
|
||||||
if lowered in true_strings:
|
|
||||||
return True
|
|
||||||
elif lowered in false_strings or not strict:
|
|
||||||
return False
|
|
||||||
|
|
||||||
msg = _(
|
|
||||||
"Unrecognized value '%(value)s'; acceptable values are: %(valid)s"
|
|
||||||
) % {
|
|
||||||
'value': value,
|
|
||||||
'valid': ', '.join(
|
|
||||||
f"'{s}'" for s in sorted(true_strings + false_strings)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def boolenv(*vars, default=False):
|
|
||||||
"""Search for the first defined of possibly many bool-like env vars.
|
|
||||||
|
|
||||||
Returns the first environment variable defined in vars, or returns the
|
|
||||||
default.
|
|
||||||
|
|
||||||
:param vars: Arbitrary strings to search for. Case sensitive.
|
|
||||||
:param default: The default to return if no value found.
|
|
||||||
:returns: A boolean corresponding to the value found, else the default if
|
|
||||||
no value found.
|
|
||||||
"""
|
|
||||||
for v in vars:
|
|
||||||
value = os.environ.get(v, None)
|
|
||||||
if value:
|
|
||||||
return bool_from_str(value)
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
class AddFixedIP(command.ShowOne):
|
class AddFixedIP(command.ShowOne):
|
||||||
_description = _("Add fixed IP address to server")
|
_description = _("Add fixed IP address to server")
|
||||||
|
|
||||||
|
|
@ -399,7 +382,7 @@ class AddFixedIP(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -430,7 +413,7 @@ class AddFixedIP(command.ShowOne):
|
||||||
|
|
||||||
interface = compute_client.create_server_interface(server.id, **kwargs)
|
interface = compute_client.create_server_interface(server.id, **kwargs)
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'port_id',
|
'port_id',
|
||||||
'server_id',
|
'server_id',
|
||||||
'net_id',
|
'net_id',
|
||||||
|
|
@ -438,7 +421,7 @@ class AddFixedIP(command.ShowOne):
|
||||||
'port_state',
|
'port_state',
|
||||||
'fixed_ips',
|
'fixed_ips',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'Port ID',
|
'Port ID',
|
||||||
'Server ID',
|
'Server ID',
|
||||||
'Network ID',
|
'Network ID',
|
||||||
|
|
@ -493,7 +476,7 @@ class AddFloatingIP(network_common.NetworkAndComputeCommand):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action_network(self, client, parsed_args):
|
def take_action_network(self, client, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
attrs = {}
|
attrs = {}
|
||||||
obj = client.find_ip(
|
obj = client.find_ip(
|
||||||
|
|
@ -586,7 +569,7 @@ class AddPort(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -640,7 +623,7 @@ class AddNetwork(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -691,7 +674,7 @@ class AddServerSecurityGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -714,7 +697,8 @@ class AddServerSecurityGroup(command.Command):
|
||||||
for security_group in security_groups:
|
for security_group in security_groups:
|
||||||
try:
|
try:
|
||||||
compute_client.add_security_group_to_server(
|
compute_client.add_security_group_to_server(
|
||||||
server, security_group
|
server,
|
||||||
|
{'name': security_group},
|
||||||
)
|
)
|
||||||
except sdk_exceptions.HttpException as e:
|
except sdk_exceptions.HttpException as e:
|
||||||
errors += 1
|
errors += 1
|
||||||
|
|
@ -792,7 +776,7 @@ with status ``SHELVED`` or ``SHELVED_OFFLOADED``."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
volume_client = self.app.client_manager.sdk_connection.volume
|
volume_client = self.app.client_manager.sdk_connection.volume
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
|
|
@ -841,8 +825,13 @@ with status ``SHELVED`` or ``SHELVED_OFFLOADED``."""
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
columns = ('id', 'server id', 'volume id', 'device')
|
columns: tuple[str, ...] = ('id', 'server id', 'volume id', 'device')
|
||||||
column_headers = ('ID', 'Server ID', 'Volume ID', 'Device')
|
column_headers: tuple[str, ...] = (
|
||||||
|
'ID',
|
||||||
|
'Server ID',
|
||||||
|
'Volume ID',
|
||||||
|
'Device',
|
||||||
|
)
|
||||||
if sdk_utils.supports_microversion(compute_client, '2.49'):
|
if sdk_utils.supports_microversion(compute_client, '2.49'):
|
||||||
columns += ('tag',)
|
columns += ('tag',)
|
||||||
column_headers += ('Tag',)
|
column_headers += ('Tag',)
|
||||||
|
|
@ -1026,7 +1015,6 @@ class BDMLegacyAction(argparse.Action):
|
||||||
|
|
||||||
class BDMAction(parseractions.MultiKeyValueAction):
|
class BDMAction(parseractions.MultiKeyValueAction):
|
||||||
def __init__(self, option_strings, dest, **kwargs):
|
def __init__(self, option_strings, dest, **kwargs):
|
||||||
required_keys = []
|
|
||||||
optional_keys = [
|
optional_keys = [
|
||||||
'uuid',
|
'uuid',
|
||||||
'source_type',
|
'source_type',
|
||||||
|
|
@ -1044,7 +1032,7 @@ class BDMAction(parseractions.MultiKeyValueAction):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
option_strings,
|
option_strings,
|
||||||
dest,
|
dest,
|
||||||
required_keys=required_keys,
|
required_keys=[],
|
||||||
optional_keys=optional_keys,
|
optional_keys=optional_keys,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
@ -1356,14 +1344,26 @@ class CreateServer(command.ShowOne):
|
||||||
'This option requires cloud support.'
|
'This option requires cloud support.'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
secgroups = parser.add_mutually_exclusive_group()
|
||||||
|
secgroups.add_argument(
|
||||||
|
'--no-security-group',
|
||||||
|
dest='security_groups',
|
||||||
|
action='store_const',
|
||||||
|
const=[],
|
||||||
|
help=_(
|
||||||
|
'Do not associate a security group with ports attached to '
|
||||||
|
'this server. This does not affect the security groups '
|
||||||
|
'associated with pre-existing ports.'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
secgroups.add_argument(
|
||||||
'--security-group',
|
'--security-group',
|
||||||
metavar='<security-group>',
|
metavar='<security-group>',
|
||||||
action='append',
|
action='append',
|
||||||
default=[],
|
|
||||||
dest='security_groups',
|
dest='security_groups',
|
||||||
help=_(
|
help=_(
|
||||||
'Security group to assign to this server (name or ID) '
|
'Security group to associate with ports attached to this '
|
||||||
|
'server (name or ID) '
|
||||||
'(repeat option to set multiple groups)'
|
'(repeat option to set multiple groups)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -1389,7 +1389,7 @@ class CreateServer(command.ShowOne):
|
||||||
default=[],
|
default=[],
|
||||||
help=_(
|
help=_(
|
||||||
'File(s) to inject into image before boot '
|
'File(s) to inject into image before boot '
|
||||||
'(repeat option to set multiple files)'
|
'(repeat option to set multiple files) '
|
||||||
'(supported by --os-compute-api-version 2.57 or below)'
|
'(supported by --os-compute-api-version 2.57 or below)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -1541,7 +1541,7 @@ class CreateServer(command.ShowOne):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
volume_client = self.app.client_manager.volume
|
volume_client = self.app.client_manager.volume
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
|
|
@ -1604,8 +1604,7 @@ class CreateServer(command.ShowOne):
|
||||||
image = images[0]
|
image = images[0]
|
||||||
else:
|
else:
|
||||||
msg = _(
|
msg = _(
|
||||||
'No images match the property expected by '
|
'No images match the property expected by --image-property'
|
||||||
'--image-property'
|
|
||||||
)
|
)
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -1860,7 +1859,7 @@ class CreateServer(command.ShowOne):
|
||||||
|
|
||||||
if 'delete_on_termination' in mapping:
|
if 'delete_on_termination' in mapping:
|
||||||
try:
|
try:
|
||||||
value = bool_from_str(
|
value = envvars.bool_from_str(
|
||||||
mapping['delete_on_termination'],
|
mapping['delete_on_termination'],
|
||||||
strict=True,
|
strict=True,
|
||||||
)
|
)
|
||||||
|
|
@ -1889,7 +1888,7 @@ class CreateServer(command.ShowOne):
|
||||||
|
|
||||||
# Default to empty list if nothing was specified and let nova
|
# Default to empty list if nothing was specified and let nova
|
||||||
# decide the default behavior.
|
# decide the default behavior.
|
||||||
networks: ty.Union[str, ty.List[ty.Dict[str, str]], None] = []
|
networks: str | list[dict[str, str]] | None = []
|
||||||
|
|
||||||
if 'auto' in parsed_args.nics or 'none' in parsed_args.nics:
|
if 'auto' in parsed_args.nics or 'none' in parsed_args.nics:
|
||||||
if len(parsed_args.nics) > 1:
|
if len(parsed_args.nics) > 1:
|
||||||
|
|
@ -1954,7 +1953,7 @@ class CreateServer(command.ShowOne):
|
||||||
|
|
||||||
# convert from the novaclient-derived "NIC" view to the actual
|
# convert from the novaclient-derived "NIC" view to the actual
|
||||||
# "network" view
|
# "network" view
|
||||||
network = {}
|
network: dict[str, str] = {}
|
||||||
|
|
||||||
if nic['net-id']:
|
if nic['net-id']:
|
||||||
network['uuid'] = nic['net-id']
|
network['uuid'] = nic['net-id']
|
||||||
|
|
@ -1963,14 +1962,14 @@ class CreateServer(command.ShowOne):
|
||||||
network['port'] = nic['port-id']
|
network['port'] = nic['port-id']
|
||||||
|
|
||||||
if nic['v4-fixed-ip']:
|
if nic['v4-fixed-ip']:
|
||||||
network['fixed'] = nic['v4-fixed-ip']
|
network['fixed_ip'] = nic['v4-fixed-ip']
|
||||||
elif nic['v6-fixed-ip']:
|
elif nic['v6-fixed-ip']:
|
||||||
network['fixed'] = nic['v6-fixed-ip']
|
network['fixed_ip'] = nic['v6-fixed-ip']
|
||||||
|
|
||||||
if nic.get('tag'): # tags are optional
|
if nic.get('tag'): # tags are optional
|
||||||
network['tag'] = nic['tag']
|
network['tag'] = nic['tag']
|
||||||
|
|
||||||
networks.append(network)
|
networks.append(network) # type: ignore[union-attr]
|
||||||
|
|
||||||
if not parsed_args.nics and sdk_utils.supports_microversion(
|
if not parsed_args.nics and sdk_utils.supports_microversion(
|
||||||
compute_client, '2.37'
|
compute_client, '2.37'
|
||||||
|
|
@ -1980,22 +1979,24 @@ class CreateServer(command.ShowOne):
|
||||||
networks = 'auto'
|
networks = 'auto'
|
||||||
|
|
||||||
# Check security group(s) exist and convert ID to name
|
# Check security group(s) exist and convert ID to name
|
||||||
security_groups = []
|
security_groups = None
|
||||||
if self.app.client_manager.is_network_endpoint_enabled():
|
if parsed_args.security_groups is not None:
|
||||||
network_client = self.app.client_manager.network
|
security_groups = []
|
||||||
for security_group in parsed_args.security_groups:
|
if self.app.client_manager.is_network_endpoint_enabled():
|
||||||
sg = network_client.find_security_group(
|
network_client = self.app.client_manager.network
|
||||||
security_group, ignore_missing=False
|
for security_group in parsed_args.security_groups:
|
||||||
)
|
sg = network_client.find_security_group(
|
||||||
# Use security group ID to avoid multiple security group have
|
security_group, ignore_missing=False
|
||||||
# same name in neutron networking backend
|
)
|
||||||
security_groups.append({'name': sg.id})
|
# Use security group ID to avoid multiple security group
|
||||||
else: # nova-network
|
# have same name in neutron networking backend
|
||||||
for security_group in parsed_args.security_groups:
|
security_groups.append({'name': sg.id})
|
||||||
sg = compute_v2.find_security_group(
|
else: # nova-network
|
||||||
compute_client, security_group
|
for security_group in parsed_args.security_groups:
|
||||||
)
|
sg = compute_v2.find_security_group(
|
||||||
security_groups.append({'name': sg['name']})
|
compute_client, security_group
|
||||||
|
)
|
||||||
|
security_groups.append({'name': sg['name']})
|
||||||
|
|
||||||
hints = {}
|
hints = {}
|
||||||
for key, values in parsed_args.hints.items():
|
for key, values in parsed_args.hints.items():
|
||||||
|
|
@ -2058,7 +2059,7 @@ class CreateServer(command.ShowOne):
|
||||||
if files:
|
if files:
|
||||||
kwargs['personality'] = files
|
kwargs['personality'] = files
|
||||||
|
|
||||||
if security_groups:
|
if security_groups is not None:
|
||||||
kwargs['security_groups'] = security_groups
|
kwargs['security_groups'] = security_groups
|
||||||
|
|
||||||
if block_device_mapping_v2:
|
if block_device_mapping_v2:
|
||||||
|
|
@ -2143,13 +2144,11 @@ class CreateServer(command.ShowOne):
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
if parsed_args.wait:
|
if parsed_args.wait:
|
||||||
if utils.wait_for_status(
|
if not utils.wait_for_status(
|
||||||
compute_client.get_server,
|
compute_client.get_server,
|
||||||
server.id,
|
server.id,
|
||||||
callback=_show_progress,
|
callback=_show_progress,
|
||||||
):
|
):
|
||||||
self.app.stdout.write('\n')
|
|
||||||
else:
|
|
||||||
msg = _('Error creating server: %s') % parsed_args.server_name
|
msg = _('Error creating server: %s') % parsed_args.server_name
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -2180,9 +2179,11 @@ class CreateServerDump(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for name_or_id in parsed_args.server:
|
for name_or_id in parsed_args.server:
|
||||||
server = compute_client.find_server(name_or_id)
|
server = compute_client.find_server(
|
||||||
|
name_or_id, ignore_missing=False
|
||||||
|
)
|
||||||
server.trigger_crash_dump(compute_client)
|
server.trigger_crash_dump(compute_client)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2205,7 +2206,7 @@ class DeleteServer(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--all-projects',
|
'--all-projects',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=boolenv('ALL_PROJECTS'),
|
default=envvars.boolenv('ALL_PROJECTS'),
|
||||||
help=_(
|
help=_(
|
||||||
'Delete server(s) in another project by name (admin only)'
|
'Delete server(s) in another project by name (admin only)'
|
||||||
'(can be specified using the ALL_PROJECTS envvar)'
|
'(can be specified using the ALL_PROJECTS envvar)'
|
||||||
|
|
@ -2224,25 +2225,50 @@ class DeleteServer(command.Command):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
|
deleted_servers = []
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_obj = compute_client.find_server(
|
try:
|
||||||
server,
|
server_obj = compute_client.find_server(
|
||||||
ignore_missing=False,
|
server,
|
||||||
all_projects=parsed_args.all_projects,
|
ignore_missing=False,
|
||||||
)
|
all_projects=parsed_args.all_projects,
|
||||||
|
)
|
||||||
|
|
||||||
compute_client.delete_server(server_obj, force=parsed_args.force)
|
compute_client.delete_server(
|
||||||
|
server_obj, force=parsed_args.force
|
||||||
|
)
|
||||||
|
deleted_servers.append(server_obj)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(
|
||||||
|
_(
|
||||||
|
"Failed to delete server with "
|
||||||
|
"name or ID '%(server)s': %(e)s"
|
||||||
|
),
|
||||||
|
{'server': server, 'e': e},
|
||||||
|
)
|
||||||
|
|
||||||
if parsed_args.wait:
|
if parsed_args.wait:
|
||||||
|
for server_obj in deleted_servers:
|
||||||
try:
|
try:
|
||||||
compute_client.wait_for_delete(
|
compute_client.wait_for_delete(
|
||||||
server_obj, callback=_show_progress
|
server_obj, callback=_show_progress
|
||||||
)
|
)
|
||||||
except sdk_exceptions.ResourceTimeout:
|
except sdk_exceptions.ResourceTimeout:
|
||||||
msg = _('Error deleting server: %s') % server_obj.id
|
msg = _('Error deleting server: %s') % server_obj.id
|
||||||
|
deleted_servers.remove(server_obj)
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
fails = len(parsed_args.server) - len(deleted_servers)
|
||||||
|
if fails > 0:
|
||||||
|
total = len(parsed_args.server)
|
||||||
|
msg = _("%(fails)s of %(total)s servers failed to delete.") % {
|
||||||
|
'fails': fails,
|
||||||
|
'total': total,
|
||||||
|
}
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
class PercentAction(argparse.Action):
|
class PercentAction(argparse.Action):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -2371,7 +2397,7 @@ class ListServer(command.Lister):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--all-projects',
|
'--all-projects',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=boolenv('ALL_PROJECTS'),
|
default=envvars.boolenv('ALL_PROJECTS'),
|
||||||
help=_(
|
help=_(
|
||||||
'Include all projects (admin only) '
|
'Include all projects (admin only) '
|
||||||
'(can be specified using the ALL_PROJECTS envvar)'
|
'(can be specified using the ALL_PROJECTS envvar)'
|
||||||
|
|
@ -2409,8 +2435,7 @@ class ListServer(command.Lister):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--key-name',
|
'--key-name',
|
||||||
help=_(
|
help=_(
|
||||||
'Search by keypair name '
|
'Search by keypair name (admin only before microversion 2.83)'
|
||||||
'(admin only before microversion 2.83)'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
config_drive_group = parser.add_mutually_exclusive_group()
|
config_drive_group = parser.add_mutually_exclusive_group()
|
||||||
|
|
@ -2633,7 +2658,7 @@ class ListServer(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
|
|
@ -2789,12 +2814,12 @@ class ListServer(command.Lister):
|
||||||
msg % search_opts['changes-since']
|
msg % search_opts['changes-since']
|
||||||
)
|
)
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
'status',
|
'status',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'ID',
|
'ID',
|
||||||
'Name',
|
'Name',
|
||||||
'Status',
|
'Status',
|
||||||
|
|
@ -2855,16 +2880,25 @@ class ListServer(command.Lister):
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns += (
|
columns += (
|
||||||
'availability_zone',
|
'availability_zone',
|
||||||
'pinned_availability_zone',
|
|
||||||
'hypervisor_hostname',
|
'hypervisor_hostname',
|
||||||
'metadata',
|
'metadata',
|
||||||
)
|
)
|
||||||
column_headers += (
|
column_headers += (
|
||||||
'Availability Zone',
|
'Availability Zone',
|
||||||
'Pinned Availability Zone',
|
|
||||||
'Host',
|
'Host',
|
||||||
'Properties',
|
'Properties',
|
||||||
)
|
)
|
||||||
|
if sdk_utils.supports_microversion(compute_client, '2.96'):
|
||||||
|
columns += ('pinned_availability_zone',)
|
||||||
|
column_headers += ('Pinned Availability Zone',)
|
||||||
|
|
||||||
|
if sdk_utils.supports_microversion(compute_client, '2.100'):
|
||||||
|
columns += ('scheduler_hints',)
|
||||||
|
column_headers += ('Scheduler Hints',)
|
||||||
|
|
||||||
|
if parsed_args.all_projects:
|
||||||
|
columns += ('project_id',)
|
||||||
|
column_headers += ('Project ID',)
|
||||||
|
|
||||||
# support for additional columns
|
# support for additional columns
|
||||||
if parsed_args.columns:
|
if parsed_args.columns:
|
||||||
|
|
@ -2898,16 +2932,26 @@ class ListServer(command.Lister):
|
||||||
column_headers += ('Availability Zone',)
|
column_headers += ('Availability Zone',)
|
||||||
if c in (
|
if c in (
|
||||||
'pinned_availability_zone',
|
'pinned_availability_zone',
|
||||||
"Pinned Availability Zone",
|
'Pinned Availability Zone',
|
||||||
):
|
):
|
||||||
columns += ('Pinned Availability Zone',)
|
if sdk_utils.supports_microversion(compute_client, '2.96'):
|
||||||
column_headers += ('Pinned Availability Zone',)
|
columns += ('pinned_availability_zone',)
|
||||||
|
column_headers += ('Pinned Availability Zone',)
|
||||||
if c in ('Host', "host"):
|
if c in ('Host', "host"):
|
||||||
columns += ('hypervisor_hostname',)
|
columns += ('hypervisor_hostname',)
|
||||||
column_headers += ('Host',)
|
column_headers += ('Host',)
|
||||||
if c in ('Properties', "properties"):
|
if c in ('Properties', "properties"):
|
||||||
columns += ('Metadata',)
|
columns += ('Metadata',)
|
||||||
column_headers += ('Properties',)
|
column_headers += ('Properties',)
|
||||||
|
if c in (
|
||||||
|
'scheduler_hints',
|
||||||
|
"Scheduler Hints",
|
||||||
|
):
|
||||||
|
if sdk_utils.supports_microversion(
|
||||||
|
compute_client, '2.100'
|
||||||
|
):
|
||||||
|
columns += ('scheduler_hints',)
|
||||||
|
column_headers += ('Scheduler Hints',)
|
||||||
|
|
||||||
# remove duplicates
|
# remove duplicates
|
||||||
column_headers = tuple(dict.fromkeys(column_headers))
|
column_headers = tuple(dict.fromkeys(column_headers))
|
||||||
|
|
@ -3010,7 +3054,7 @@ class ListServer(command.Lister):
|
||||||
# infrastructure failure situations.
|
# infrastructure failure situations.
|
||||||
# For those servers with partial constructs we just skip the
|
# For those servers with partial constructs we just skip the
|
||||||
# processing of the image and flavor information.
|
# processing of the image and flavor information.
|
||||||
if not hasattr(s, 'image') or not hasattr(s, 'flavor'):
|
if getattr(s, 'status') == 'UNKNOWN':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if 'id' in s.image and s.image.id is not None:
|
if 'id' in s.image and s.image.id is not None:
|
||||||
|
|
@ -3074,6 +3118,7 @@ class ListServer(command.Lister):
|
||||||
'metadata': format_columns.DictColumn,
|
'metadata': format_columns.DictColumn,
|
||||||
'security_groups_name': format_columns.ListColumn,
|
'security_groups_name': format_columns.ListColumn,
|
||||||
'hypervisor_hostname': HostColumn,
|
'hypervisor_hostname': HostColumn,
|
||||||
|
'scheduler_hints': format_columns.DictListColumn,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
for s in data
|
for s in data
|
||||||
|
|
@ -3109,7 +3154,7 @@ A non-admin user will not be able to execute actions."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.reason:
|
if parsed_args.reason:
|
||||||
|
|
@ -3207,7 +3252,7 @@ revert to release the new server and restart the old one."""
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=None,
|
default=None,
|
||||||
help=_(
|
help=_(
|
||||||
'Allow disk over-commit on the destination host'
|
'Allow disk over-commit on the destination host '
|
||||||
'(supported with --os-compute-api-version 2.24 or below)'
|
'(supported with --os-compute-api-version 2.24 or below)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -3216,7 +3261,7 @@ revert to release the new server and restart the old one."""
|
||||||
dest='disk_overcommit',
|
dest='disk_overcommit',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
help=_(
|
help=_(
|
||||||
'Do not over-commit disk on the destination host (default)'
|
'Do not over-commit disk on the destination host (default) '
|
||||||
'(supported with --os-compute-api-version 2.24 or below)'
|
'(supported with --os-compute-api-version 2.24 or below)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -3233,7 +3278,7 @@ revert to release the new server and restart the old one."""
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -3336,7 +3381,7 @@ class PauseServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -3385,7 +3430,7 @@ class RebootServer(command.Command):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
parsed_args.server,
|
parsed_args.server,
|
||||||
ignore_missing=False,
|
ignore_missing=False,
|
||||||
|
|
@ -3419,7 +3464,7 @@ class RebuildServer(command.ShowOne):
|
||||||
'--image',
|
'--image',
|
||||||
metavar='<image>',
|
metavar='<image>',
|
||||||
help=_(
|
help=_(
|
||||||
'Recreate server from the specified image (name or ID).'
|
'Recreate server from the specified image (name or ID). '
|
||||||
'Defaults to the currently used one.'
|
'Defaults to the currently used one.'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -3590,7 +3635,7 @@ class RebuildServer(command.ShowOne):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
|
|
@ -3851,7 +3896,7 @@ host."""
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
if parsed_args.host:
|
if parsed_args.host:
|
||||||
|
|
@ -3884,9 +3929,15 @@ host."""
|
||||||
compute_client.evacuate_server(server, **kwargs)
|
compute_client.evacuate_server(server, **kwargs)
|
||||||
|
|
||||||
if parsed_args.wait:
|
if parsed_args.wait:
|
||||||
|
orig_status = server.status
|
||||||
|
success = ['ACTIVE']
|
||||||
|
if orig_status == 'SHUTOFF':
|
||||||
|
success.append('SHUTOFF')
|
||||||
|
|
||||||
if utils.wait_for_status(
|
if utils.wait_for_status(
|
||||||
compute_client.get_server,
|
compute_client.get_server,
|
||||||
server.id,
|
server.id,
|
||||||
|
success_status=success,
|
||||||
callback=_show_progress,
|
callback=_show_progress,
|
||||||
):
|
):
|
||||||
self.app.stdout.write(_('Complete\n'))
|
self.app.stdout.write(_('Complete\n'))
|
||||||
|
|
@ -3916,7 +3967,7 @@ class RemoveFixedIP(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -3945,14 +3996,12 @@ class RemoveFloatingIP(network_common.NetworkAndComputeCommand):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action_network(self, client, parsed_args):
|
def take_action_network(self, client, parsed_args):
|
||||||
attrs = {}
|
|
||||||
obj = client.find_ip(
|
obj = client.find_ip(
|
||||||
parsed_args.ip_address,
|
parsed_args.ip_address,
|
||||||
ignore_missing=False,
|
ignore_missing=False,
|
||||||
)
|
)
|
||||||
attrs['port_id'] = None
|
|
||||||
|
|
||||||
client.update_ip(obj, **attrs)
|
client.update_ip(obj, port_id=None)
|
||||||
|
|
||||||
def take_action_compute(self, client, parsed_args):
|
def take_action_compute(self, client, parsed_args):
|
||||||
server = client.find_server(parsed_args.server, ignore_missing=False)
|
server = client.find_server(parsed_args.server, ignore_missing=False)
|
||||||
|
|
@ -3977,7 +4026,7 @@ class RemovePort(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -4016,7 +4065,7 @@ class RemoveNetwork(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -4060,7 +4109,7 @@ class RemoveServerSecurityGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -4083,7 +4132,8 @@ class RemoveServerSecurityGroup(command.Command):
|
||||||
for security_group in security_groups:
|
for security_group in security_groups:
|
||||||
try:
|
try:
|
||||||
compute_client.remove_security_group_from_server(
|
compute_client.remove_security_group_from_server(
|
||||||
server, security_group
|
server,
|
||||||
|
{'name': security_group},
|
||||||
)
|
)
|
||||||
except sdk_exceptions.HttpException as e:
|
except sdk_exceptions.HttpException as e:
|
||||||
errors += 1
|
errors += 1
|
||||||
|
|
@ -4129,7 +4179,7 @@ volume from a server with status ``SHELVED`` or ``SHELVED_OFFLOADED``."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
volume_client = self.app.client_manager.sdk_connection.volume
|
volume_client = self.app.client_manager.sdk_connection.volume
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
|
|
@ -4182,7 +4232,7 @@ server booted from a volume."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
image_ref = None
|
image_ref = None
|
||||||
|
|
@ -4236,7 +4286,7 @@ release the new server and restart the old one."""
|
||||||
'--revert',
|
'--revert',
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help=_(
|
help=_(
|
||||||
'**Deprecated** Restore server state before resize'
|
'**Deprecated** Restore server state before resize. '
|
||||||
"Replaced by the 'openstack server resize revert' and "
|
"Replaced by the 'openstack server resize revert' and "
|
||||||
"'openstack server migration revert' commands"
|
"'openstack server migration revert' commands"
|
||||||
),
|
),
|
||||||
|
|
@ -4254,7 +4304,7 @@ release the new server and restart the old one."""
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -4316,7 +4366,7 @@ Confirm (verify) success of resize operation and release the old server."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -4364,7 +4414,7 @@ one."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -4408,7 +4458,7 @@ class RestoreServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -4431,7 +4481,7 @@ class ResumeServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -4459,8 +4509,7 @@ class SetServer(command.Command):
|
||||||
password_group.add_argument(
|
password_group.add_argument(
|
||||||
'--password',
|
'--password',
|
||||||
help=_(
|
help=_(
|
||||||
'Set the server password. '
|
'Set the server password. This option requires cloud support.'
|
||||||
'This option requires cloud support.'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
password_group.add_argument(
|
password_group.add_argument(
|
||||||
|
|
@ -4488,15 +4537,27 @@ class SetServer(command.Command):
|
||||||
'(repeat option to set multiple properties)'
|
'(repeat option to set multiple properties)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--auto-approve',
|
||||||
|
action='store_true',
|
||||||
|
help=_(
|
||||||
|
"Allow server state override without asking for confirmation"
|
||||||
|
),
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--state',
|
'--state',
|
||||||
metavar='<state>',
|
metavar='<state>',
|
||||||
choices=['active', 'error'],
|
choices=['active', 'error'],
|
||||||
help=_(
|
help=_(
|
||||||
'New server state '
|
'New server state.'
|
||||||
'**WARNING** This can result in instances that are no longer '
|
'**WARNING** Resetting the state is intended to work around '
|
||||||
'usable and should be used with caution '
|
'servers stuck in an intermediate state, such as deleting. '
|
||||||
'(admin only)'
|
'If the server is in an error state then this is almost '
|
||||||
|
'never the correct command to run and you should prefer hard '
|
||||||
|
'reboot where possible. In particular, if the server is in '
|
||||||
|
'an error state due to a move operation, setting the state '
|
||||||
|
'can result in instances that are no longer usable. Proceed '
|
||||||
|
'with caution. (admin only)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -4531,8 +4592,22 @@ class SetServer(command.Command):
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ask_user_yesno(msg):
|
||||||
|
"""Ask user Y/N question
|
||||||
|
|
||||||
|
:param str msg: question text
|
||||||
|
:return bool: User choice
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n'))
|
||||||
|
if answer in ('y', 'Y', 'yes'):
|
||||||
|
return True
|
||||||
|
elif answer in ('n', 'N', 'no'):
|
||||||
|
return False
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -4581,6 +4656,17 @@ class SetServer(command.Command):
|
||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.state:
|
if parsed_args.state:
|
||||||
|
if not parsed_args.auto_approve:
|
||||||
|
if not self.ask_user_yesno(
|
||||||
|
_(
|
||||||
|
"Resetting the server state can make it much harder "
|
||||||
|
"to recover a server from an error state. If the "
|
||||||
|
"server is in error status due to a failed move "
|
||||||
|
"operation then this is likely not the correct "
|
||||||
|
"approach to fix the problem. Do you wish to continue?"
|
||||||
|
)
|
||||||
|
):
|
||||||
|
return
|
||||||
compute_client.reset_server_state(server, state=parsed_args.state)
|
compute_client.reset_server_state(server, state=parsed_args.state)
|
||||||
|
|
||||||
if parsed_args.root_password:
|
if parsed_args.root_password:
|
||||||
|
|
@ -4650,7 +4736,7 @@ class ShelveServer(command.Command):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server_ids = []
|
server_ids = []
|
||||||
|
|
||||||
for server in parsed_args.servers:
|
for server in parsed_args.servers:
|
||||||
|
|
@ -4745,7 +4831,7 @@ information for the server."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
|
|
@ -4875,7 +4961,7 @@ class SshServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -4952,7 +5038,7 @@ class StartServer(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--all-projects',
|
'--all-projects',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=boolenv('ALL_PROJECTS'),
|
default=envvars.boolenv('ALL_PROJECTS'),
|
||||||
help=_(
|
help=_(
|
||||||
'Start server(s) in another project by name (admin only) '
|
'Start server(s) in another project by name (admin only) '
|
||||||
'(can be specified using the ALL_PROJECTS envvar)'
|
'(can be specified using the ALL_PROJECTS envvar)'
|
||||||
|
|
@ -4961,7 +5047,7 @@ class StartServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -4987,16 +5073,16 @@ class StopServer(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--all-projects',
|
'--all-projects',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=boolenv('ALL_PROJECTS'),
|
default=envvars.boolenv('ALL_PROJECTS'),
|
||||||
help=_(
|
help=_(
|
||||||
'Stop server(s) in another project by name (admin only)'
|
'Stop server(s) in another project by name (admin only) '
|
||||||
'(can be specified using the ALL_PROJECTS envvar)'
|
'(can be specified using the ALL_PROJECTS envvar)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -5021,7 +5107,7 @@ class SuspendServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -5044,7 +5130,7 @@ class UnlockServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -5067,7 +5153,7 @@ class UnpauseServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
for server in parsed_args.server:
|
for server in parsed_args.server:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
server,
|
server,
|
||||||
|
|
@ -5089,7 +5175,7 @@ class UnrescueServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
@ -5156,7 +5242,7 @@ class UnsetServer(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
@ -5247,7 +5333,7 @@ class UnshelveServer(command.Command):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
if parsed_args.availability_zone:
|
if parsed_args.availability_zone:
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ class CreateServerBackup(command.ShowOne):
|
||||||
self.app.stderr.write(f'\rProgress: {progress}')
|
self.app.stderr.write(f'\rProgress: {progress}')
|
||||||
self.app.stderr.flush()
|
self.app.stderr.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server, ignore_missing=False
|
parsed_args.server, ignore_missing=False
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ from cliff import columns
|
||||||
import iso8601
|
import iso8601
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstack import exceptions as sdk_exceptions
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
@ -65,17 +65,11 @@ class ServerActionEventColumn(columns.FormattableColumn):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _format_event(self, event):
|
def _format_event(self, event):
|
||||||
column_map = {}
|
|
||||||
hidden_columns = ['id', 'name', 'location']
|
hidden_columns = ['id', 'name', 'location']
|
||||||
_, columns = utils.get_osc_show_columns_for_sdk_resource(
|
_, columns = utils.get_osc_show_columns_for_sdk_resource(
|
||||||
event,
|
event, {}, hidden_columns
|
||||||
column_map,
|
|
||||||
hidden_columns,
|
|
||||||
)
|
|
||||||
data = utils.get_item_properties(
|
|
||||||
event,
|
|
||||||
columns,
|
|
||||||
)
|
)
|
||||||
|
data = utils.get_item_properties(event, columns)
|
||||||
return dict(zip(columns, data))
|
return dict(zip(columns, data))
|
||||||
|
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
|
|
@ -88,17 +82,14 @@ class ServerActionEventColumn(columns.FormattableColumn):
|
||||||
|
|
||||||
|
|
||||||
def _get_server_event_columns(item, client):
|
def _get_server_event_columns(item, client):
|
||||||
column_map = {}
|
hidden_columns = ['name', 'server_id', 'links', 'location', 'finish_time']
|
||||||
hidden_columns = ['name', 'server_id', 'links', 'location']
|
|
||||||
|
|
||||||
if not sdk_utils.supports_microversion(client, '2.58'):
|
if not sdk_utils.supports_microversion(client, '2.58'):
|
||||||
# updated_at was introduced in 2.58
|
# updated_at was introduced in 2.58
|
||||||
hidden_columns.append('updated_at')
|
hidden_columns.append('updated_at')
|
||||||
|
|
||||||
return utils.get_osc_show_columns_for_sdk_resource(
|
return utils.get_osc_show_columns_for_sdk_resource(
|
||||||
item,
|
item, {}, hidden_columns
|
||||||
column_map,
|
|
||||||
hidden_columns,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -148,7 +139,7 @@ class ListServerEvent(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
|
|
@ -220,13 +211,13 @@ class ListServerEvent(command.Lister):
|
||||||
|
|
||||||
data = compute_client.server_actions(server_id, **kwargs)
|
data = compute_client.server_actions(server_id, **kwargs)
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'request_id',
|
'request_id',
|
||||||
'server_id',
|
'server_id',
|
||||||
'action',
|
'action',
|
||||||
'start_time',
|
'start_time',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'Request ID',
|
'Request ID',
|
||||||
'Server ID',
|
'Server ID',
|
||||||
'Action',
|
'Action',
|
||||||
|
|
@ -275,7 +266,7 @@ class ShowServerEvent(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server_id = compute_client.find_server(
|
server_id = compute_client.find_server(
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,23 @@
|
||||||
"""Compute v2 Server Group action implementations"""
|
"""Compute v2 Server Group action implementations"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
|
from cliff import columns
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
_formatters = {
|
_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = {
|
||||||
'member_ids': format_columns.ListColumn,
|
'member_ids': format_columns.ListColumn,
|
||||||
'policies': format_columns.ListColumn,
|
'policies': format_columns.ListColumn,
|
||||||
'rules': format_columns.DictColumn,
|
'rules': format_columns.DictColumn,
|
||||||
|
|
@ -93,7 +95,7 @@ class CreateServerGroup(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
if parsed_args.policy in ('soft-affinity', 'soft-anti-affinity'):
|
if parsed_args.policy in ('soft-affinity', 'soft-anti-affinity'):
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.15'):
|
if not sdk_utils.supports_microversion(compute_client, '2.15'):
|
||||||
|
|
@ -153,7 +155,7 @@ class DeleteServerGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
result = 0
|
result = 0
|
||||||
for group in parsed_args.server_group:
|
for group in parsed_args.server_group:
|
||||||
try:
|
try:
|
||||||
|
|
@ -197,7 +199,7 @@ class ListServerGroup(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
|
|
@ -216,12 +218,12 @@ class ListServerGroup(command.Lister):
|
||||||
if sdk_utils.supports_microversion(compute_client, '2.64'):
|
if sdk_utils.supports_microversion(compute_client, '2.64'):
|
||||||
policy_key = 'Policy'
|
policy_key = 'Policy'
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
policy_key.lower(),
|
policy_key.lower(),
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'ID',
|
'ID',
|
||||||
'Name',
|
'Name',
|
||||||
policy_key,
|
policy_key,
|
||||||
|
|
@ -264,7 +266,7 @@ class ShowServerGroup(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
group = compute_client.find_server_group(
|
group = compute_client.find_server_group(
|
||||||
parsed_args.server_group, ignore_missing=False
|
parsed_args.server_group, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ import importlib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ class CreateServerImage(command.ShowOne):
|
||||||
self.app.stdout.write(f'\rProgress: {progress}')
|
self.app.stdout.write(f'\rProgress: {progress}')
|
||||||
self.app.stdout.flush()
|
self.app.stdout.flush()
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
image_client = self.app.client_manager.image
|
image_client = self.app.client_manager.image
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.common import pagination
|
from openstackclient.common import pagination
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as identity_common
|
from openstackclient.identity import common as identity_common
|
||||||
|
|
@ -154,7 +154,7 @@ class ListMigration(command.Lister):
|
||||||
)
|
)
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
search_opts = {}
|
search_opts = {}
|
||||||
|
|
@ -289,7 +289,7 @@ class ShowMigration(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.24'):
|
if not sdk_utils.supports_microversion(compute_client, '2.24'):
|
||||||
msg = _(
|
msg = _(
|
||||||
|
|
@ -333,7 +333,7 @@ class ShowMigration(command.ShowOne):
|
||||||
ignore_missing=False,
|
ignore_missing=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
'ID',
|
'ID',
|
||||||
'Server UUID',
|
'Server UUID',
|
||||||
'Status',
|
'Status',
|
||||||
|
|
@ -352,7 +352,7 @@ class ShowMigration(command.ShowOne):
|
||||||
'Updated At',
|
'Updated At',
|
||||||
)
|
)
|
||||||
|
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
'id',
|
'id',
|
||||||
'server_id',
|
'server_id',
|
||||||
'status',
|
'status',
|
||||||
|
|
@ -404,7 +404,7 @@ class AbortMigration(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.24'):
|
if not sdk_utils.supports_microversion(compute_client, '2.24'):
|
||||||
msg = _(
|
msg = _(
|
||||||
|
|
@ -469,7 +469,7 @@ class ForceCompleteMigration(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.22'):
|
if not sdk_utils.supports_microversion(compute_client, '2.22'):
|
||||||
msg = _(
|
msg = _(
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@
|
||||||
"""Compute v2 Server action implementations"""
|
"""Compute v2 Server action implementations"""
|
||||||
|
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ class ListServerVolume(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
server = compute_client.find_server(
|
server = compute_client.find_server(
|
||||||
parsed_args.server,
|
parsed_args.server,
|
||||||
|
|
@ -42,8 +42,8 @@ class ListServerVolume(command.Lister):
|
||||||
)
|
)
|
||||||
volumes = compute_client.volume_attachments(server)
|
volumes = compute_client.volume_attachments(server)
|
||||||
|
|
||||||
columns = ()
|
columns: tuple[str, ...] = ()
|
||||||
column_headers = ()
|
column_headers: tuple[str, ...] = ()
|
||||||
|
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.89'):
|
if not sdk_utils.supports_microversion(compute_client, '2.89'):
|
||||||
columns += ('id',)
|
columns += ('id',)
|
||||||
|
|
@ -114,7 +114,7 @@ class SetServerVolume(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
volume_client = self.app.client_manager.sdk_connection.volume
|
volume_client = self.app.client_manager.sdk_connection.volume
|
||||||
|
|
||||||
if parsed_args.delete_on_termination is not None:
|
if parsed_args.delete_on_termination is not None:
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openstack import utils as sdk_utils
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ class DeleteService(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
result = 0
|
result = 0
|
||||||
for s in parsed_args.service:
|
for s in parsed_args.service:
|
||||||
try:
|
try:
|
||||||
|
|
@ -70,7 +70,7 @@ class DeleteService(command.Command):
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.service)
|
total = len(parsed_args.service)
|
||||||
msg = _(
|
msg = _(
|
||||||
"%(result)s of %(total)s compute services failed " "to delete."
|
"%(result)s of %(total)s compute services failed to delete."
|
||||||
) % {'result': result, 'total': total}
|
) % {'result': result, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -108,8 +108,8 @@ deployment."""
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
columns = (
|
columns: tuple[str, ...] = (
|
||||||
"id",
|
"id",
|
||||||
"binary",
|
"binary",
|
||||||
"host",
|
"host",
|
||||||
|
|
@ -118,7 +118,7 @@ deployment."""
|
||||||
"state",
|
"state",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers: tuple[str, ...] = (
|
||||||
"ID",
|
"ID",
|
||||||
"Binary",
|
"Binary",
|
||||||
"Host",
|
"Host",
|
||||||
|
|
@ -153,8 +153,7 @@ class SetService(command.Command):
|
||||||
"service",
|
"service",
|
||||||
metavar="<service>",
|
metavar="<service>",
|
||||||
help=_(
|
help=_(
|
||||||
"Name of service (Binary name), for example "
|
"Name of service (Binary name), for example ``nova-compute``"
|
||||||
"``nova-compute``"
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
enabled_group = parser.add_mutually_exclusive_group()
|
enabled_group = parser.add_mutually_exclusive_group()
|
||||||
|
|
@ -222,7 +221,7 @@ class SetService(command.Command):
|
||||||
return services[0]
|
return services[0]
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
|
|
||||||
if (
|
if (
|
||||||
parsed_args.enable or not parsed_args.disable
|
parsed_args.enable or not parsed_args.disable
|
||||||
|
|
@ -281,9 +280,7 @@ class SetService(command.Command):
|
||||||
force_down = False
|
force_down = False
|
||||||
if force_down is not None:
|
if force_down is not None:
|
||||||
if not sdk_utils.supports_microversion(compute_client, '2.11'):
|
if not sdk_utils.supports_microversion(compute_client, '2.11'):
|
||||||
msg = _(
|
msg = _('--os-compute-api-version 2.11 or later is required')
|
||||||
'--os-compute-api-version 2.11 or later is ' 'required'
|
|
||||||
)
|
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
try:
|
try:
|
||||||
compute_client.update_service_forced_down(
|
compute_client.update_service_forced_down(
|
||||||
|
|
@ -299,7 +296,6 @@ class SetService(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
msg = _(
|
msg = _(
|
||||||
"Compute service %(service)s of host %(host)s failed to "
|
"Compute service %(service)s of host %(host)s failed to set."
|
||||||
"set."
|
|
||||||
) % {"service": parsed_args.service, "host": parsed_args.host}
|
) % {"service": parsed_args.service, "host": parsed_args.host}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,21 @@
|
||||||
|
|
||||||
"""Usage action implementations"""
|
"""Usage action implementations"""
|
||||||
|
|
||||||
|
from collections.abc import Collection
|
||||||
import datetime
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
from cliff import columns as cliff_columns
|
from cliff import columns as cliff_columns
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
# TODO(stephenfin): This exists in a couple of places and should be moved to a
|
# TODO(stephenfin): This exists in a couple of places and should be moved to a
|
||||||
# common module
|
# common module
|
||||||
class ProjectColumn(cliff_columns.FormattableColumn):
|
class ProjectColumn(cliff_columns.FormattableColumn[str]):
|
||||||
"""Formattable column for project column.
|
"""Formattable column for project column.
|
||||||
|
|
||||||
Unlike the parent FormattableColumn class, the initializer of the class
|
Unlike the parent FormattableColumn class, the initializer of the class
|
||||||
|
|
@ -53,12 +55,12 @@ class ProjectColumn(cliff_columns.FormattableColumn):
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
|
||||||
class CountColumn(cliff_columns.FormattableColumn):
|
class CountColumn(cliff_columns.FormattableColumn[Collection[ty.Any]]):
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
return len(self._value) if self._value is not None else None
|
return len(self._value) if self._value is not None else None
|
||||||
|
|
||||||
|
|
||||||
class FloatColumn(cliff_columns.FormattableColumn):
|
class FloatColumn(cliff_columns.FormattableColumn[float]):
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
return float(f"{self._value:.2f}")
|
return float(f"{self._value:.2f}")
|
||||||
|
|
||||||
|
|
@ -115,8 +117,7 @@ class ListUsage(command.Lister):
|
||||||
metavar="<start>",
|
metavar="<start>",
|
||||||
default=None,
|
default=None,
|
||||||
help=_(
|
help=_(
|
||||||
"Usage range start date, ex 2012-01-20"
|
"Usage range start date, ex 2012-01-20 (default: 4 weeks ago)"
|
||||||
" (default: 4 weeks ago)"
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -136,7 +137,7 @@ class ListUsage(command.Lister):
|
||||||
else:
|
else:
|
||||||
return project
|
return project
|
||||||
|
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
columns = (
|
columns = (
|
||||||
"project_id",
|
"project_id",
|
||||||
"server_usages",
|
"server_usages",
|
||||||
|
|
@ -153,7 +154,7 @@ class ListUsage(command.Lister):
|
||||||
)
|
)
|
||||||
|
|
||||||
date_cli_format = "%Y-%m-%d"
|
date_cli_format = "%Y-%m-%d"
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
||||||
|
|
||||||
if parsed_args.start:
|
if parsed_args.start:
|
||||||
start = datetime.datetime.strptime(
|
start = datetime.datetime.strptime(
|
||||||
|
|
@ -222,8 +223,7 @@ class ShowUsage(command.ShowOne):
|
||||||
metavar="<start>",
|
metavar="<start>",
|
||||||
default=None,
|
default=None,
|
||||||
help=_(
|
help=_(
|
||||||
"Usage range start date, ex 2012-01-20"
|
"Usage range start date, ex 2012-01-20 (default: 4 weeks ago)"
|
||||||
" (default: 4 weeks ago)"
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -236,9 +236,9 @@ class ShowUsage(command.ShowOne):
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
compute_client = self.app.client_manager.sdk_connection.compute
|
compute_client = self.app.client_manager.compute
|
||||||
date_cli_format = "%Y-%m-%d"
|
date_cli_format = "%Y-%m-%d"
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
||||||
|
|
||||||
if parsed_args.start:
|
if parsed_args.start:
|
||||||
start = datetime.datetime.strptime(
|
start = datetime.datetime.strptime(
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
@ -20,9 +19,9 @@ from osc_lib import utils
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# global variables used when building the shell
|
||||||
DEFAULT_API_VERSION = '3'
|
DEFAULT_API_VERSION = '3'
|
||||||
API_VERSION_OPTION = 'os_identity_api_version'
|
API_VERSION_OPTION = 'os_identity_api_version'
|
||||||
API_NAME = 'identity'
|
API_NAME = 'identity'
|
||||||
|
|
@ -64,8 +63,7 @@ def build_option_parser(parser):
|
||||||
metavar='<identity-api-version>',
|
metavar='<identity-api-version>',
|
||||||
default=utils.env('OS_IDENTITY_API_VERSION'),
|
default=utils.env('OS_IDENTITY_API_VERSION'),
|
||||||
help=_(
|
help=_(
|
||||||
'Identity API version, default=%s '
|
'Identity API version, default=%s (Env: OS_IDENTITY_API_VERSION)'
|
||||||
'(Env: OS_IDENTITY_API_VERSION)'
|
|
||||||
)
|
)
|
||||||
% DEFAULT_API_VERSION,
|
% DEFAULT_API_VERSION,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -88,23 +88,23 @@ def find_service_sdk(identity_client, name_type_or_id):
|
||||||
raise exceptions.CommandError(e.message)
|
raise exceptions.CommandError(e.message)
|
||||||
|
|
||||||
# search for service type
|
# search for service type
|
||||||
services = identity_client.services()
|
services = identity_client.services(type=name_type_or_id)
|
||||||
result = None
|
try:
|
||||||
for service in services:
|
service = next(services)
|
||||||
if name_type_or_id == service.type:
|
except StopIteration:
|
||||||
if result:
|
msg = _(
|
||||||
msg = _(
|
"No service with a type, name or ID of '%(query)s' exists."
|
||||||
"Multiple service matches found for '%s', "
|
) % {"query": name_type_or_id}
|
||||||
"use an ID or name to be more specific."
|
raise exceptions.CommandError(msg)
|
||||||
)
|
|
||||||
raise exceptions.CommandError(msg % name_type_or_id)
|
|
||||||
result = service
|
|
||||||
|
|
||||||
if result is None:
|
if next(services, None):
|
||||||
msg = _("No service with a type, name or ID of '%s' exists.")
|
msg = _(
|
||||||
raise exceptions.CommandError(msg % name_type_or_id)
|
"Multiple service matches found for '%(query)s', "
|
||||||
|
"use an ID to be more specific."
|
||||||
|
) % {"query": name_type_or_id}
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
return result
|
return service
|
||||||
|
|
||||||
|
|
||||||
def get_resource(manager, name_type_or_id):
|
def get_resource(manager, name_type_or_id):
|
||||||
|
|
@ -184,62 +184,113 @@ def _get_token_resource(client, resource, parsed_name, parsed_domain=None):
|
||||||
return parsed_name
|
return parsed_name
|
||||||
|
|
||||||
|
|
||||||
def _get_domain_id_if_requested(identity_client, domain_name_or_id):
|
|
||||||
if not domain_name_or_id:
|
|
||||||
return None
|
|
||||||
domain = find_domain(identity_client, domain_name_or_id)
|
|
||||||
return domain.id
|
|
||||||
|
|
||||||
|
|
||||||
def find_domain(identity_client, name_or_id):
|
def find_domain(identity_client, name_or_id):
|
||||||
return _find_identity_resource(
|
return _find_identity_resource(
|
||||||
identity_client.domains, name_or_id, domains.Domain
|
identity_client.domains, name_or_id, domains.Domain
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def find_domain_id_sdk(
|
||||||
|
identity_client, name_or_id, *, validate_actor_existence=True
|
||||||
|
):
|
||||||
|
return _find_sdk_id(
|
||||||
|
identity_client.find_domain,
|
||||||
|
name_or_id=name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def find_group(identity_client, name_or_id, domain_name_or_id=None):
|
def find_group(identity_client, name_or_id, domain_name_or_id=None):
|
||||||
domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id)
|
if domain_name_or_id is None:
|
||||||
if not domain_id:
|
|
||||||
return _find_identity_resource(
|
return _find_identity_resource(
|
||||||
identity_client.groups, name_or_id, groups.Group
|
identity_client.groups, name_or_id, groups.Group
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
domain_id = find_domain(identity_client, domain_id).id
|
domain_id = find_domain(identity_client, domain_name_or_id).id
|
||||||
return _find_identity_resource(
|
return _find_identity_resource(
|
||||||
identity_client.groups,
|
identity_client.groups,
|
||||||
name_or_id,
|
name_or_id,
|
||||||
groups.Group,
|
groups.Group,
|
||||||
domain_id=domain_id,
|
domain_id=domain_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def find_group_id_sdk(
|
||||||
|
identity_client,
|
||||||
|
name_or_id,
|
||||||
|
domain_name_or_id=None,
|
||||||
|
*,
|
||||||
|
validate_actor_existence=True,
|
||||||
|
):
|
||||||
|
if domain_name_or_id is None:
|
||||||
|
return _find_sdk_id(
|
||||||
|
identity_client.find_group,
|
||||||
|
name_or_id=name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
domain_id = find_domain_id_sdk(
|
||||||
|
identity_client,
|
||||||
|
name_or_id=domain_name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
)
|
||||||
|
return _find_sdk_id(
|
||||||
|
identity_client.find_group,
|
||||||
|
name_or_id=name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
domain_id=domain_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def find_project(identity_client, name_or_id, domain_name_or_id=None):
|
def find_project(identity_client, name_or_id, domain_name_or_id=None):
|
||||||
domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id)
|
if domain_name_or_id is None:
|
||||||
if not domain_id:
|
|
||||||
return _find_identity_resource(
|
return _find_identity_resource(
|
||||||
identity_client.projects, name_or_id, projects.Project
|
identity_client.projects, name_or_id, projects.Project
|
||||||
)
|
)
|
||||||
else:
|
domain_id = find_domain(identity_client, domain_name_or_id).id
|
||||||
domain_id = find_domain(identity_client, domain_id).id
|
return _find_identity_resource(
|
||||||
return _find_identity_resource(
|
identity_client.projects,
|
||||||
identity_client.projects,
|
name_or_id,
|
||||||
name_or_id,
|
projects.Project,
|
||||||
projects.Project,
|
domain_id=domain_id,
|
||||||
domain_id=domain_id,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def find_user(identity_client, name_or_id, domain_name_or_id=None):
|
def find_user(identity_client, name_or_id, domain_name_or_id=None):
|
||||||
domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id)
|
if domain_name_or_id is None:
|
||||||
if not domain_id:
|
|
||||||
return _find_identity_resource(
|
return _find_identity_resource(
|
||||||
identity_client.users, name_or_id, users.User
|
identity_client.users, name_or_id, users.User
|
||||||
)
|
)
|
||||||
else:
|
domain_id = find_domain(identity_client, domain_name_or_id).id
|
||||||
domain_id = find_domain(identity_client, domain_id).id
|
return _find_identity_resource(
|
||||||
return _find_identity_resource(
|
identity_client.users, name_or_id, users.User, domain_id=domain_id
|
||||||
identity_client.users, name_or_id, users.User, domain_id=domain_id
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def find_user_id_sdk(
|
||||||
|
identity_client,
|
||||||
|
name_or_id,
|
||||||
|
domain_name_or_id=None,
|
||||||
|
*,
|
||||||
|
validate_actor_existence=True,
|
||||||
|
):
|
||||||
|
if domain_name_or_id is None:
|
||||||
|
return _find_sdk_id(
|
||||||
|
identity_client.find_user,
|
||||||
|
name_or_id=name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
)
|
)
|
||||||
|
domain_id = find_domain_id_sdk(
|
||||||
|
identity_client,
|
||||||
|
name_or_id=domain_name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
)
|
||||||
|
return _find_sdk_id(
|
||||||
|
identity_client.find_user,
|
||||||
|
name_or_id=name_or_id,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
domain_id=domain_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _find_identity_resource(
|
def _find_identity_resource(
|
||||||
|
|
@ -282,13 +333,20 @@ def _find_identity_resource(
|
||||||
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
|
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
|
||||||
|
|
||||||
|
|
||||||
def get_immutable_options(parsed_args):
|
def _find_sdk_id(
|
||||||
options = {}
|
find_command, name_or_id, *, validate_actor_existence=True, **kwargs
|
||||||
if parsed_args.immutable:
|
):
|
||||||
options['immutable'] = True
|
try:
|
||||||
if parsed_args.no_immutable:
|
resource = find_command(
|
||||||
options['immutable'] = False
|
name_or_id=name_or_id, ignore_missing=False, **kwargs
|
||||||
return options
|
)
|
||||||
|
except sdk_exceptions.ForbiddenException:
|
||||||
|
return name_or_id
|
||||||
|
except sdk_exceptions.ResourceNotFound as exc:
|
||||||
|
if not validate_actor_existence:
|
||||||
|
return name_or_id
|
||||||
|
raise exceptions.CommandError from exc
|
||||||
|
return resource.id
|
||||||
|
|
||||||
|
|
||||||
def add_user_domain_option_to_parser(parser):
|
def add_user_domain_option_to_parser(parser):
|
||||||
|
|
@ -347,23 +405,27 @@ def add_inherited_option_to_parser(parser):
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=_(
|
help=_(
|
||||||
'Specifies if the role grant is inheritable to the sub ' 'projects'
|
'Specifies if the role grant is inheritable to the sub projects'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_resource_option_to_parser(parser):
|
def add_resource_option_to_parser(parser):
|
||||||
enable_group = parser.add_mutually_exclusive_group()
|
immutable_group = parser.add_mutually_exclusive_group()
|
||||||
enable_group.add_argument(
|
immutable_group.add_argument(
|
||||||
'--immutable',
|
'--immutable',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
dest='immutable',
|
||||||
|
default=None,
|
||||||
help=_(
|
help=_(
|
||||||
'Make resource immutable. An immutable project may not '
|
'Make resource immutable. An immutable project may not '
|
||||||
'be deleted or modified except to remove the immutable flag'
|
'be deleted or modified except to remove the immutable flag'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
enable_group.add_argument(
|
immutable_group.add_argument(
|
||||||
'--no-immutable',
|
'--no-immutable',
|
||||||
action='store_true',
|
action='store_false',
|
||||||
|
dest='immutable',
|
||||||
|
default=None,
|
||||||
help=_('Make resource mutable (default)'),
|
help=_('Make resource mutable (default)'),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,20 @@
|
||||||
"""Identity v2 Service Catalog action implementations"""
|
"""Identity v2 Service Catalog action implementations"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
from cliff import columns as cliff_columns
|
from cliff import columns as cliff_columns
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EndpointsColumn(cliff_columns.FormattableColumn):
|
class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]):
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
if not self._value:
|
if not self._value:
|
||||||
return ""
|
return ""
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -128,9 +128,10 @@ class DeleteEC2Creds(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.access_keys)
|
total = len(parsed_args.access_keys)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s EC2 keys failed to delete.") % {
|
||||||
"%(result)s of %(total)s EC2 keys failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -111,9 +111,10 @@ class DeleteEndpoint(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.endpoints)
|
total = len(parsed_args.endpoints)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s endpoints failed to delete.") % {
|
||||||
"%(result)s of %(total)s endpoints failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -132,18 +133,19 @@ class ListEndpoint(command.Lister):
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
|
columns: tuple[str, ...] = (
|
||||||
|
'ID',
|
||||||
|
'Region',
|
||||||
|
'Service Name',
|
||||||
|
'Service Type',
|
||||||
|
)
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = (
|
columns += (
|
||||||
'ID',
|
|
||||||
'Region',
|
|
||||||
'Service Name',
|
|
||||||
'Service Type',
|
|
||||||
'PublicURL',
|
'PublicURL',
|
||||||
'AdminURL',
|
'AdminURL',
|
||||||
'InternalURL',
|
'InternalURL',
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
columns = ('ID', 'Region', 'Service Name', 'Service Type')
|
|
||||||
data = identity_client.endpoints.list()
|
data = identity_client.endpoints.list()
|
||||||
|
|
||||||
for ep in data:
|
for ep in data:
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ import logging
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -59,6 +59,7 @@ class CreateProject(command.ShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--property',
|
'--property',
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
|
dest='properties',
|
||||||
action=parseractions.KeyValueAction,
|
action=parseractions.KeyValueAction,
|
||||||
help=_(
|
help=_(
|
||||||
'Add a property to <name> '
|
'Add a property to <name> '
|
||||||
|
|
@ -79,8 +80,8 @@ class CreateProject(command.ShowOne):
|
||||||
if parsed_args.disable:
|
if parsed_args.disable:
|
||||||
enabled = False
|
enabled = False
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.property:
|
if parsed_args.properties:
|
||||||
kwargs = parsed_args.property.copy()
|
kwargs.update(parsed_args.properties)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
project = identity_client.tenants.create(
|
project = identity_client.tenants.create(
|
||||||
|
|
@ -105,7 +106,14 @@ class CreateProject(command.ShowOne):
|
||||||
|
|
||||||
|
|
||||||
class DeleteProject(command.Command):
|
class DeleteProject(command.Command):
|
||||||
_description = _("Delete project(s)")
|
_description = _(
|
||||||
|
"Delete project(s). This command will remove specified "
|
||||||
|
"existing project(s) if an active user is authorized to do "
|
||||||
|
"this. If there are resources managed by other services "
|
||||||
|
"(for example, Nova, Neutron, Cinder) associated with "
|
||||||
|
"specified project(s), delete operation will proceed "
|
||||||
|
"regardless."
|
||||||
|
)
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super().get_parser(prog_name)
|
parser = super().get_parser(prog_name)
|
||||||
|
|
@ -140,9 +148,10 @@ class DeleteProject(command.Command):
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.projects)
|
total = len(parsed_args.projects)
|
||||||
msg = _(
|
msg = _("%(errors)s of %(total)s projects failed to delete.") % {
|
||||||
"%(errors)s of %(total)s projects failed " "to delete."
|
'errors': errors,
|
||||||
) % {'errors': errors, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -169,10 +178,9 @@ class ListProject(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
columns: tuple[str, ...] = ('ID', 'Name')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Name', 'Description', 'Enabled')
|
columns += ('Description', 'Enabled')
|
||||||
else:
|
|
||||||
columns = ('ID', 'Name')
|
|
||||||
data = self.app.client_manager.identity.tenants.list()
|
data = self.app.client_manager.identity.tenants.list()
|
||||||
if parsed_args.sort:
|
if parsed_args.sort:
|
||||||
data = utils.sort_items(data, parsed_args.sort)
|
data = utils.sort_items(data, parsed_args.sort)
|
||||||
|
|
@ -223,6 +231,7 @@ class SetProject(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--property',
|
'--property',
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
|
dest='properties',
|
||||||
action=parseractions.KeyValueAction,
|
action=parseractions.KeyValueAction,
|
||||||
help=_(
|
help=_(
|
||||||
'Set a project property '
|
'Set a project property '
|
||||||
|
|
@ -248,8 +257,8 @@ class SetProject(command.Command):
|
||||||
kwargs['enabled'] = True
|
kwargs['enabled'] = True
|
||||||
if parsed_args.disable:
|
if parsed_args.disable:
|
||||||
kwargs['enabled'] = False
|
kwargs['enabled'] = False
|
||||||
if parsed_args.property:
|
if parsed_args.properties:
|
||||||
kwargs.update(parsed_args.property)
|
kwargs.update(parsed_args.properties)
|
||||||
if 'id' in kwargs:
|
if 'id' in kwargs:
|
||||||
del kwargs['id']
|
del kwargs['id']
|
||||||
if 'name' in kwargs:
|
if 'name' in kwargs:
|
||||||
|
|
@ -331,6 +340,7 @@ class UnsetProject(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--property',
|
'--property',
|
||||||
metavar='<key>',
|
metavar='<key>',
|
||||||
|
dest='properties',
|
||||||
action='append',
|
action='append',
|
||||||
default=[],
|
default=[],
|
||||||
help=_(
|
help=_(
|
||||||
|
|
@ -347,7 +357,7 @@ class UnsetProject(command.Command):
|
||||||
parsed_args.project,
|
parsed_args.project,
|
||||||
)
|
)
|
||||||
kwargs = project._info
|
kwargs = project._info
|
||||||
for key in parsed_args.property:
|
for key in parsed_args.properties:
|
||||||
if key in kwargs:
|
if key in kwargs:
|
||||||
kwargs[key] = None
|
kwargs[key] = None
|
||||||
identity_client.tenants.update(project.id, **kwargs)
|
identity_client.tenants.update(project.id, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ class DeleteRole(command.Command):
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.roles)
|
total = len(parsed_args.roles)
|
||||||
msg = _("%(errors)s of %(total)s roles failed " "to delete.") % {
|
msg = _("%(errors)s of %(total)s roles failed to delete.") % {
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@
|
||||||
|
|
||||||
"""Identity v2 Assignment action implementations"""
|
"""Identity v2 Assignment action implementations"""
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _ # noqa
|
from openstackclient.i18n import _ # noqa
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ class ListRoleAssignment(command.Lister):
|
||||||
parsed_args.user,
|
parsed_args.user,
|
||||||
)
|
)
|
||||||
elif parsed_args.authuser:
|
elif parsed_args.authuser:
|
||||||
if auth_ref:
|
if auth_ref and auth_ref.user_id:
|
||||||
user = utils.find_resource(
|
user = utils.find_resource(
|
||||||
identity_client.users, auth_ref.user_id
|
identity_client.users, auth_ref.user_id
|
||||||
)
|
)
|
||||||
|
|
@ -80,7 +80,7 @@ class ListRoleAssignment(command.Lister):
|
||||||
parsed_args.project,
|
parsed_args.project,
|
||||||
)
|
)
|
||||||
elif parsed_args.authproject:
|
elif parsed_args.authproject:
|
||||||
if auth_ref:
|
if auth_ref and auth_ref.project_id:
|
||||||
project = utils.find_resource(
|
project = utils.find_resource(
|
||||||
identity_client.projects, auth_ref.project_id
|
identity_client.projects, auth_ref.project_id
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -100,9 +100,10 @@ class DeleteService(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.services)
|
total = len(parsed_args.services)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s services failed to delete.") % {
|
||||||
"%(result)s of %(total)s services failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -120,10 +121,9 @@ class ListService(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
columns: tuple[str, ...] = ('ID', 'Name', 'Type')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Name', 'Type', 'Description')
|
columns += ('Description',)
|
||||||
else:
|
|
||||||
columns = ('ID', 'Name', 'Type')
|
|
||||||
data = self.app.client_manager.identity.services.list()
|
data = self.app.client_manager.identity.services.list()
|
||||||
return (
|
return (
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -164,7 +164,7 @@ class ShowService(command.ShowOne):
|
||||||
return zip(*sorted(info.items()))
|
return zip(*sorted(info.items()))
|
||||||
|
|
||||||
msg = _(
|
msg = _(
|
||||||
"No service catalog with a type, name or ID of '%s' " "exists."
|
"No service catalog with a type, name or ID of '%s' exists."
|
||||||
) % (parsed_args.service)
|
) % (parsed_args.service)
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@
|
||||||
|
|
||||||
"""Identity v2 Token action implementations"""
|
"""Identity v2 Token action implementations"""
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,17 @@ import logging
|
||||||
|
|
||||||
from cliff import columns as cliff_columns
|
from cliff import columns as cliff_columns
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ProjectColumn(cliff_columns.FormattableColumn):
|
class ProjectColumn(cliff_columns.FormattableColumn[str]):
|
||||||
"""Formattable column for project column.
|
"""Formattable column for project column.
|
||||||
|
|
||||||
Unlike the parent FormattableColumn class, the initializer of the
|
Unlike the parent FormattableColumn class, the initializer of the
|
||||||
|
|
@ -194,7 +194,7 @@ class DeleteUser(command.Command):
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.users)
|
total = len(parsed_args.users)
|
||||||
msg = _("%(errors)s of %(total)s users failed " "to delete.") % {
|
msg = _("%(errors)s of %(total)s users failed to delete.") % {
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -230,21 +230,11 @@ class ListUser(command.Lister):
|
||||||
)
|
)
|
||||||
project = project.id
|
project = project.id
|
||||||
|
|
||||||
|
columns: tuple[str, ...] = ('id', 'name')
|
||||||
|
column_headers: tuple[str, ...] = ('ID', 'Name')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = (
|
columns += ('tenantId', 'email', 'enabled')
|
||||||
'ID',
|
column_headers += ('Project', 'Email', 'Enabled')
|
||||||
'Name',
|
|
||||||
'tenantId',
|
|
||||||
'Email',
|
|
||||||
'Enabled',
|
|
||||||
)
|
|
||||||
column_headers = (
|
|
||||||
'ID',
|
|
||||||
'Name',
|
|
||||||
'Project',
|
|
||||||
'Email',
|
|
||||||
'Enabled',
|
|
||||||
)
|
|
||||||
# Cache the project list
|
# Cache the project list
|
||||||
project_cache = {}
|
project_cache = {}
|
||||||
try:
|
try:
|
||||||
|
|
@ -256,15 +246,10 @@ class ListUser(command.Lister):
|
||||||
formatters['tenantId'] = functools.partial(
|
formatters['tenantId'] = functools.partial(
|
||||||
ProjectColumn, project_cache=project_cache
|
ProjectColumn, project_cache=project_cache
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
columns = column_headers = ('ID', 'Name')
|
|
||||||
data = identity_client.users.list(tenant_id=project)
|
data = identity_client.users.list(tenant_id=project)
|
||||||
|
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
d = {}
|
data = {s.id: s for s in data}.values()
|
||||||
for s in data:
|
|
||||||
d[s.id] = s
|
|
||||||
data = d.values()
|
|
||||||
|
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
# FIXME(dtroyer): Sometimes user objects have 'tenant_id' instead
|
# FIXME(dtroyer): Sometimes user objects have 'tenant_id' instead
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -44,7 +44,11 @@ class DeleteAccessRule(command.Command):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
errors = 0
|
errors = 0
|
||||||
for ac in parsed_args.access_rule:
|
for ac in parsed_args.access_rule:
|
||||||
|
|
@ -54,17 +58,14 @@ class DeleteAccessRule(command.Command):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errors += 1
|
errors += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
_(
|
_("Failed to delete access rule with ID '%(ac)s': %(e)s"),
|
||||||
"Failed to delete access rule with "
|
|
||||||
"ID '%(ac)s': %(e)s"
|
|
||||||
),
|
|
||||||
{'ac': ac, 'e': e},
|
{'ac': ac, 'e': e},
|
||||||
)
|
)
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.access_rule)
|
total = len(parsed_args.access_rule)
|
||||||
msg = _(
|
msg = _(
|
||||||
"%(errors)s of %(total)s access rules failed " "to delete."
|
"%(errors)s of %(total)s access rules failed to delete."
|
||||||
) % {'errors': errors, 'total': total}
|
) % {'errors': errors, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -90,7 +91,11 @@ class ListAccessRule(command.Lister):
|
||||||
).id
|
).id
|
||||||
else:
|
else:
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
columns = ('ID', 'Service', 'Method', 'Path')
|
columns = ('ID', 'Service', 'Method', 'Path')
|
||||||
data = identity_client.access_rules(user=user_id)
|
data = identity_client.access_rules(user=user_id)
|
||||||
|
|
@ -122,7 +127,11 @@ class ShowAccessRule(command.ShowOne):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
access_rule = identity_client.get_access_rule(
|
access_rule = identity_client.get_access_rule(
|
||||||
user_id, parsed_args.access_rule
|
user_id, parsed_args.access_rule
|
||||||
|
|
|
||||||
|
|
@ -18,19 +18,95 @@
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import typing as ty
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from osc_lib.command import command
|
from cliff import columns as cliff_columns
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class RolesColumn(cliff_columns.FormattableColumn[ty.Any]):
|
||||||
|
"""Generate a formatted string of role names."""
|
||||||
|
|
||||||
|
def human_readable(self):
|
||||||
|
return utils.format_list(list(r['name'] for r in self._value))
|
||||||
|
|
||||||
|
|
||||||
|
def _format_application_credential(
|
||||||
|
application_credential, *, include_secret=False
|
||||||
|
):
|
||||||
|
column_headers: tuple[str, ...] = (
|
||||||
|
'ID',
|
||||||
|
'Name',
|
||||||
|
'Description',
|
||||||
|
'Project ID',
|
||||||
|
'Roles',
|
||||||
|
'Unrestricted',
|
||||||
|
'Access Rules',
|
||||||
|
'Expires At',
|
||||||
|
)
|
||||||
|
columns: tuple[str, ...] = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'project_id',
|
||||||
|
'roles',
|
||||||
|
'unrestricted',
|
||||||
|
'access_rules',
|
||||||
|
'expires_at',
|
||||||
|
)
|
||||||
|
if include_secret:
|
||||||
|
column_headers += ('Secret',)
|
||||||
|
columns += ('secret',)
|
||||||
|
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
utils.get_item_properties(
|
||||||
|
application_credential, columns, formatters={'roles': RolesColumn}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_application_credentials(application_credentials):
|
||||||
|
column_headers = (
|
||||||
|
'ID',
|
||||||
|
'Name',
|
||||||
|
'Description',
|
||||||
|
'Project ID',
|
||||||
|
'Roles',
|
||||||
|
'Unrestricted',
|
||||||
|
'Access Rules',
|
||||||
|
'Expires At',
|
||||||
|
)
|
||||||
|
columns = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'project_id',
|
||||||
|
'roles',
|
||||||
|
'unrestricted',
|
||||||
|
'access_rules',
|
||||||
|
'expires_at',
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
(
|
||||||
|
utils.get_item_properties(
|
||||||
|
x, columns, formatters={'roles': RolesColumn}
|
||||||
|
)
|
||||||
|
for x in application_credentials
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO(stephenfin): Move this to osc_lib since it's useful elsewhere
|
# TODO(stephenfin): Move this to osc_lib since it's useful elsewhere
|
||||||
def is_uuid_like(value) -> bool:
|
def is_uuid_like(value) -> bool:
|
||||||
"""Returns validation of a value as a UUID.
|
"""Returns validation of a value as a UUID.
|
||||||
|
|
@ -38,9 +114,6 @@ def is_uuid_like(value) -> bool:
|
||||||
:param val: Value to verify
|
:param val: Value to verify
|
||||||
:type val: string
|
:type val: string
|
||||||
:returns: bool
|
:returns: bool
|
||||||
|
|
||||||
.. versionchanged:: 1.1.1
|
|
||||||
Support non-lowercase UUIDs.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
formatted_value = (
|
formatted_value = (
|
||||||
|
|
@ -76,6 +149,7 @@ class CreateApplicationCredential(command.ShowOne):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--role',
|
'--role',
|
||||||
metavar='<role>',
|
metavar='<role>',
|
||||||
|
dest='roles',
|
||||||
action='append',
|
action='append',
|
||||||
default=[],
|
default=[],
|
||||||
help=_(
|
help=_(
|
||||||
|
|
@ -132,10 +206,15 @@ class CreateApplicationCredential(command.ShowOne):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
role_ids = []
|
role_ids = []
|
||||||
for role in parsed_args.role:
|
for role in parsed_args.roles:
|
||||||
if is_uuid_like(role):
|
if is_uuid_like(role):
|
||||||
role_ids.append({'id': role})
|
role_ids.append({'id': role})
|
||||||
else:
|
else:
|
||||||
|
|
@ -179,31 +258,8 @@ class CreateApplicationCredential(command.ShowOne):
|
||||||
access_rules=access_rules,
|
access_rules=access_rules,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Format roles into something sensible
|
return _format_application_credential(
|
||||||
if application_credential['roles']:
|
application_credential, include_secret=True
|
||||||
roles = application_credential['roles']
|
|
||||||
msg = ' '.join(r['name'] for r in roles)
|
|
||||||
application_credential['roles'] = msg
|
|
||||||
|
|
||||||
columns = (
|
|
||||||
'id',
|
|
||||||
'name',
|
|
||||||
'description',
|
|
||||||
'project_id',
|
|
||||||
'roles',
|
|
||||||
'unrestricted',
|
|
||||||
'access_rules',
|
|
||||||
'expires_at',
|
|
||||||
'secret',
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
columns,
|
|
||||||
(
|
|
||||||
utils.get_dict_properties(
|
|
||||||
application_credential,
|
|
||||||
columns,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -223,13 +279,18 @@ class DeleteApplicationCredential(command.Command):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
errors = 0
|
errors = 0
|
||||||
for ac in parsed_args.application_credential:
|
for ac in parsed_args.application_credential:
|
||||||
try:
|
try:
|
||||||
app_cred = identity_client.find_application_credential(
|
app_cred = identity_client.find_application_credential(
|
||||||
user_id, ac
|
user_id, ac, ignore_missing=False
|
||||||
)
|
)
|
||||||
identity_client.delete_application_credential(
|
identity_client.delete_application_credential(
|
||||||
user_id, app_cred.id
|
user_id, app_cred.id
|
||||||
|
|
@ -252,6 +313,8 @@ class DeleteApplicationCredential(command.Command):
|
||||||
) % {'errors': errors, 'total': total}
|
) % {'errors': errors, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class ListApplicationCredential(command.Lister):
|
class ListApplicationCredential(command.Lister):
|
||||||
_description = _("List application credentials")
|
_description = _("List application credentials")
|
||||||
|
|
@ -269,46 +332,23 @@ class ListApplicationCredential(command.Lister):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
if parsed_args.user:
|
if parsed_args.user:
|
||||||
user_id = common.find_user(
|
user_id = common.find_user_id_sdk(
|
||||||
identity_client, parsed_args.user, parsed_args.user_domain
|
identity_client, parsed_args.user, parsed_args.user_domain
|
||||||
).id
|
)
|
||||||
else:
|
else:
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
data = identity_client.application_credentials(user=user_id)
|
application_credentials = identity_client.application_credentials(
|
||||||
|
user=user_id
|
||||||
data_formatted = []
|
|
||||||
for ac in data:
|
|
||||||
# Format roles into something sensible
|
|
||||||
roles = ac['roles']
|
|
||||||
msg = ' '.join(r['name'] for r in roles)
|
|
||||||
ac['roles'] = msg
|
|
||||||
|
|
||||||
data_formatted.append(ac)
|
|
||||||
|
|
||||||
columns = (
|
|
||||||
'ID',
|
|
||||||
'Name',
|
|
||||||
'Description',
|
|
||||||
'Project ID',
|
|
||||||
'Roles',
|
|
||||||
'Unrestricted',
|
|
||||||
'Access Rules',
|
|
||||||
'Expires At',
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
columns,
|
|
||||||
(
|
|
||||||
utils.get_item_properties(
|
|
||||||
s,
|
|
||||||
columns,
|
|
||||||
formatters={},
|
|
||||||
)
|
|
||||||
for s in data_formatted
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return _format_application_credentials(application_credentials)
|
||||||
|
|
||||||
|
|
||||||
class ShowApplicationCredential(command.ShowOne):
|
class ShowApplicationCredential(command.ShowOne):
|
||||||
_description = _("Display application credential details")
|
_description = _("Display application credential details")
|
||||||
|
|
@ -325,33 +365,14 @@ class ShowApplicationCredential(command.ShowOne):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
conn = self.app.client_manager.sdk_connection
|
conn = self.app.client_manager.sdk_connection
|
||||||
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
auth = conn.config.get_auth()
|
||||||
|
if auth is None:
|
||||||
|
# this will never happen
|
||||||
|
raise exceptions.CommandError('invalid authentication info')
|
||||||
|
user_id = auth.get_user_id(conn.identity)
|
||||||
|
|
||||||
app_cred = identity_client.find_application_credential(
|
application_credential = identity_client.find_application_credential(
|
||||||
user_id, parsed_args.application_credential
|
user_id, parsed_args.application_credential, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# Format roles into something sensible
|
return _format_application_credential(application_credential)
|
||||||
roles = app_cred['roles']
|
|
||||||
msg = ' '.join(r['name'] for r in roles)
|
|
||||||
app_cred['roles'] = msg
|
|
||||||
|
|
||||||
columns = (
|
|
||||||
'id',
|
|
||||||
'name',
|
|
||||||
'description',
|
|
||||||
'project_id',
|
|
||||||
'roles',
|
|
||||||
'unrestricted',
|
|
||||||
'access_rules',
|
|
||||||
'expires_at',
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
columns,
|
|
||||||
(
|
|
||||||
utils.get_dict_properties(
|
|
||||||
app_cred,
|
|
||||||
columns,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -9,24 +9,24 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
|
|
||||||
"""Identity v3 Service Catalog action implementations"""
|
"""Identity v3 Service Catalog action implementations"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import typing as ty
|
||||||
|
|
||||||
from cliff import columns as cliff_columns
|
from cliff import columns as cliff_columns
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EndpointsColumn(cliff_columns.FormattableColumn):
|
class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]):
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
if not self._value:
|
if not self._value:
|
||||||
return ""
|
return ""
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -82,9 +82,10 @@ class DeleteConsumer(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.consumer)
|
total = len(parsed_args.consumer)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s consumers failed to delete.") % {
|
||||||
"%(result)s of %(total)s consumers failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -28,6 +28,23 @@ from openstackclient.identity import common
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_credential(credential):
|
||||||
|
columns = (
|
||||||
|
'blob',
|
||||||
|
'id',
|
||||||
|
'project_id',
|
||||||
|
'type',
|
||||||
|
'user_id',
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
columns,
|
||||||
|
utils.get_item_properties(
|
||||||
|
credential,
|
||||||
|
columns,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateCredential(command.ShowOne):
|
class CreateCredential(command.ShowOne):
|
||||||
_description = _("Create new credential")
|
_description = _("Create new credential")
|
||||||
|
|
||||||
|
|
@ -53,32 +70,30 @@ class CreateCredential(command.ShowOne):
|
||||||
'--project',
|
'--project',
|
||||||
metavar='<project>',
|
metavar='<project>',
|
||||||
help=_(
|
help=_(
|
||||||
'Project which limits the scope of '
|
'Project which limits the scope of the credential (name or ID)'
|
||||||
'the credential (name or ID)'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
user_id = utils.find_resource(
|
user_id = identity_client.find_user(
|
||||||
identity_client.users, parsed_args.user
|
parsed_args.user, ignore_missing=False
|
||||||
).id
|
).id
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
project = utils.find_resource(
|
project = identity_client.find_project(
|
||||||
identity_client.projects, parsed_args.project
|
parsed_args.project, ignore_missing=False
|
||||||
).id
|
).id
|
||||||
else:
|
else:
|
||||||
project = None
|
project = None
|
||||||
credential = identity_client.credentials.create(
|
credential = identity_client.create_credential(
|
||||||
user=user_id,
|
user_id=user_id,
|
||||||
type=parsed_args.type,
|
type=parsed_args.type,
|
||||||
blob=parsed_args.data,
|
blob=parsed_args.data,
|
||||||
project=project,
|
project_id=project,
|
||||||
)
|
)
|
||||||
|
|
||||||
credential._info.pop('links')
|
return _format_credential(credential)
|
||||||
return zip(*sorted(credential._info.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteCredential(command.Command):
|
class DeleteCredential(command.Command):
|
||||||
|
|
@ -95,11 +110,11 @@ class DeleteCredential(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.credential:
|
for i in parsed_args.credential:
|
||||||
try:
|
try:
|
||||||
identity_client.credentials.delete(i)
|
identity_client.delete_credential(i)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|
@ -112,9 +127,10 @@ class DeleteCredential(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.credential)
|
total = len(parsed_args.credential)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s credential failed to delete.") % {
|
||||||
"%(result)s of %(total)s credential failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -137,14 +153,17 @@ class ListCredential(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.user:
|
if parsed_args.user:
|
||||||
user_id = common.find_user(
|
domain_id = None
|
||||||
identity_client,
|
if parsed_args.user_domain:
|
||||||
parsed_args.user,
|
domain_id = identity_client.find_domain(
|
||||||
parsed_args.user_domain,
|
parsed_args.user_domain, ignore_missing=False
|
||||||
|
)
|
||||||
|
user_id = identity_client.find_user(
|
||||||
|
parsed_args.user, domain_id=domain_id, ignore_missing=False
|
||||||
).id
|
).id
|
||||||
kwargs["user_id"] = user_id
|
kwargs["user_id"] = user_id
|
||||||
|
|
||||||
|
|
@ -153,7 +172,8 @@ class ListCredential(command.Lister):
|
||||||
|
|
||||||
columns = ('ID', 'Type', 'User ID', 'Blob', 'Project ID')
|
columns = ('ID', 'Type', 'User ID', 'Blob', 'Project ID')
|
||||||
column_headers = ('ID', 'Type', 'User ID', 'Data', 'Project ID')
|
column_headers = ('ID', 'Type', 'User ID', 'Data', 'Project ID')
|
||||||
data = self.app.client_manager.identity.credentials.list(**kwargs)
|
data = identity_client.credentials(**kwargs)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
column_headers,
|
column_headers,
|
||||||
(
|
(
|
||||||
|
|
@ -199,27 +219,26 @@ class SetCredential(command.Command):
|
||||||
'--project',
|
'--project',
|
||||||
metavar='<project>',
|
metavar='<project>',
|
||||||
help=_(
|
help=_(
|
||||||
'Project which limits the scope of '
|
'Project which limits the scope of the credential (name or ID)'
|
||||||
'the credential (name or ID)'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
user_id = utils.find_resource(
|
user_id = identity_client.find_user(
|
||||||
identity_client.users, parsed_args.user
|
parsed_args.user, ignore_missing=False
|
||||||
).id
|
).id
|
||||||
|
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
project = utils.find_resource(
|
project = identity_client.find_project(
|
||||||
identity_client.projects, parsed_args.project
|
parsed_args.project, ignore_missing=False
|
||||||
).id
|
).id
|
||||||
else:
|
else:
|
||||||
project = None
|
project = None
|
||||||
|
|
||||||
identity_client.credentials.update(
|
identity_client.update_credential(
|
||||||
parsed_args.credential,
|
parsed_args.credential,
|
||||||
user=user_id,
|
user=user_id,
|
||||||
type=parsed_args.type,
|
type=parsed_args.type,
|
||||||
|
|
@ -241,10 +260,7 @@ class ShowCredential(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
credential = utils.find_resource(
|
credential = identity_client.get_credential(parsed_args.credential)
|
||||||
identity_client.credentials, parsed_args.credential
|
|
||||||
)
|
|
||||||
|
|
||||||
credential._info.pop('links')
|
return _format_credential(credential)
|
||||||
return zip(*sorted(credential._info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from openstack import exceptions as sdk_exceptions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -29,6 +29,31 @@ from openstackclient.identity import common
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_domain(domain):
|
||||||
|
columns = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'is_enabled',
|
||||||
|
'description',
|
||||||
|
'options',
|
||||||
|
)
|
||||||
|
column_headers = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'enabled',
|
||||||
|
'description',
|
||||||
|
'options',
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
utils.get_item_properties(
|
||||||
|
domain,
|
||||||
|
columns,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateDomain(command.ShowOne):
|
class CreateDomain(command.ShowOne):
|
||||||
_description = _("Create new domain")
|
_description = _("Create new domain")
|
||||||
|
|
||||||
|
|
@ -47,12 +72,15 @@ class CreateDomain(command.ShowOne):
|
||||||
enable_group = parser.add_mutually_exclusive_group()
|
enable_group = parser.add_mutually_exclusive_group()
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--enable',
|
'--enable',
|
||||||
|
dest='is_enabled',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
default=True,
|
||||||
help=_('Enable domain (default)'),
|
help=_('Enable domain (default)'),
|
||||||
)
|
)
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--disable',
|
'--disable',
|
||||||
action='store_true',
|
dest='is_enabled',
|
||||||
|
action='store_false',
|
||||||
help=_('Disable domain'),
|
help=_('Disable domain'),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -64,32 +92,29 @@ class CreateDomain(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
enabled = True
|
options = {}
|
||||||
if parsed_args.disable:
|
if parsed_args.immutable is not None:
|
||||||
enabled = False
|
options['immutable'] = parsed_args.immutable
|
||||||
|
|
||||||
options = common.get_immutable_options(parsed_args)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
domain = identity_client.domains.create(
|
domain = identity_client.create_domain(
|
||||||
name=parsed_args.name,
|
name=parsed_args.name,
|
||||||
description=parsed_args.description,
|
description=parsed_args.description,
|
||||||
options=options,
|
options=options,
|
||||||
enabled=enabled,
|
is_enabled=parsed_args.is_enabled,
|
||||||
)
|
)
|
||||||
except ks_exc.Conflict:
|
except sdk_exceptions.ConflictException:
|
||||||
if parsed_args.or_show:
|
if parsed_args.or_show:
|
||||||
domain = utils.find_resource(
|
domain = identity_client.find_domain(
|
||||||
identity_client.domains, parsed_args.name
|
parsed_args.name, ignore_missing=False
|
||||||
)
|
)
|
||||||
LOG.info(_('Returning existing domain %s'), domain.name)
|
LOG.info(_('Returning existing domain %s'), domain.name)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
domain._info.pop('links')
|
return _format_domain(domain)
|
||||||
return zip(*sorted(domain._info.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteDomain(command.Command):
|
class DeleteDomain(command.Command):
|
||||||
|
|
@ -106,12 +131,12 @@ class DeleteDomain(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.domain:
|
for i in parsed_args.domain:
|
||||||
try:
|
try:
|
||||||
domain = utils.find_resource(identity_client.domains, i)
|
domain = identity_client.find_domain(i, ignore_missing=False)
|
||||||
identity_client.domains.delete(domain.id)
|
identity_client.delete_domain(domain.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|
@ -124,7 +149,7 @@ class DeleteDomain(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.domain)
|
total = len(parsed_args.domain)
|
||||||
msg = _("%(result)s of %(total)s domains failed " "to delete.") % {
|
msg = _("%(result)s of %(total)s domains failed to delete.") % {
|
||||||
'result': result,
|
'result': result,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +168,7 @@ class ListDomain(command.Lister):
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--enabled',
|
'--enabled',
|
||||||
dest='enabled',
|
dest='is_enabled',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help=_('The domains that are enabled will be returned'),
|
help=_('The domains that are enabled will be returned'),
|
||||||
)
|
)
|
||||||
|
|
@ -153,13 +178,17 @@ class ListDomain(command.Lister):
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.name:
|
if parsed_args.name:
|
||||||
kwargs['name'] = parsed_args.name
|
kwargs['name'] = parsed_args.name
|
||||||
if parsed_args.enabled:
|
if parsed_args.is_enabled:
|
||||||
kwargs['enabled'] = True
|
kwargs['is_enabled'] = True
|
||||||
|
|
||||||
|
columns = ('id', 'name', 'is_enabled', 'description')
|
||||||
|
column_headers = ('ID', 'Name', 'Enabled', 'Description')
|
||||||
|
data = self.app.client_manager.sdk_connection.identity.domains(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
columns = ('ID', 'Name', 'Enabled', 'Description')
|
|
||||||
data = self.app.client_manager.identity.domains.list(**kwargs)
|
|
||||||
return (
|
return (
|
||||||
columns,
|
column_headers,
|
||||||
(
|
(
|
||||||
utils.get_item_properties(
|
utils.get_item_properties(
|
||||||
s,
|
s,
|
||||||
|
|
@ -194,38 +223,37 @@ class SetDomain(command.Command):
|
||||||
enable_group = parser.add_mutually_exclusive_group()
|
enable_group = parser.add_mutually_exclusive_group()
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--enable',
|
'--enable',
|
||||||
|
dest='is_enabled',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
default=None,
|
||||||
help=_('Enable domain'),
|
help=_('Enable domain'),
|
||||||
)
|
)
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--disable',
|
'--disable',
|
||||||
action='store_true',
|
dest='is_enabled',
|
||||||
|
action='store_false',
|
||||||
|
default=None,
|
||||||
help=_('Disable domain'),
|
help=_('Disable domain'),
|
||||||
)
|
)
|
||||||
common.add_resource_option_to_parser(parser)
|
common.add_resource_option_to_parser(parser)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
domain = utils.find_resource(
|
domain = identity_client.find_domain(
|
||||||
identity_client.domains, parsed_args.domain
|
parsed_args.domain, ignore_missing=False
|
||||||
)
|
)
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.name:
|
if parsed_args.name:
|
||||||
kwargs['name'] = parsed_args.name
|
kwargs['name'] = parsed_args.name
|
||||||
if parsed_args.description:
|
if parsed_args.description:
|
||||||
kwargs['description'] = parsed_args.description
|
kwargs['description'] = parsed_args.description
|
||||||
|
if parsed_args.is_enabled is not None:
|
||||||
|
kwargs['is_enabled'] = parsed_args.is_enabled
|
||||||
|
if parsed_args.immutable is not None:
|
||||||
|
kwargs['options'] = {'immutable': parsed_args.immutable}
|
||||||
|
|
||||||
if parsed_args.enable:
|
identity_client.update_domain(domain.id, **kwargs)
|
||||||
kwargs['enabled'] = True
|
|
||||||
if parsed_args.disable:
|
|
||||||
kwargs['enabled'] = False
|
|
||||||
|
|
||||||
options = common.get_immutable_options(parsed_args)
|
|
||||||
if options:
|
|
||||||
kwargs['options'] = options
|
|
||||||
|
|
||||||
identity_client.domains.update(domain.id, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ShowDomain(command.ShowOne):
|
class ShowDomain(command.ShowOne):
|
||||||
|
|
@ -241,13 +269,9 @@ class ShowDomain(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
domain = identity_client.find_domain(
|
||||||
domain_str = common._get_token_resource(
|
parsed_args.domain, ignore_missing=False
|
||||||
identity_client, 'domain', parsed_args.domain
|
|
||||||
)
|
)
|
||||||
|
|
||||||
domain = utils.find_resource(identity_client.domains, domain_str)
|
return _format_domain(domain)
|
||||||
|
|
||||||
domain._info.pop('links')
|
|
||||||
return zip(*sorted(domain._info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -156,9 +156,10 @@ class DeleteEC2Creds(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.access_key)
|
total = len(parsed_args.access_key)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s EC2 keys failed to delete.") % {
|
||||||
"%(result)s of %(total)s EC2 keys failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -28,11 +28,31 @@ from openstackclient.identity import common
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_service_name(service):
|
def _format_endpoint(endpoint, service):
|
||||||
if hasattr(service, 'name'):
|
columns = (
|
||||||
return service.name
|
'is_enabled',
|
||||||
else:
|
'id',
|
||||||
return ''
|
'interface',
|
||||||
|
'region_id',
|
||||||
|
'region_id',
|
||||||
|
'service_id',
|
||||||
|
'url',
|
||||||
|
)
|
||||||
|
column_headers = (
|
||||||
|
'enabled',
|
||||||
|
'id',
|
||||||
|
'interface',
|
||||||
|
'region',
|
||||||
|
'region_id',
|
||||||
|
'service_id',
|
||||||
|
'url',
|
||||||
|
'service_name',
|
||||||
|
'service_type',
|
||||||
|
)
|
||||||
|
|
||||||
|
data = utils.get_item_properties(endpoint, columns)
|
||||||
|
data += (getattr(service, 'name', ''), service.type)
|
||||||
|
return column_headers, data
|
||||||
|
|
||||||
|
|
||||||
class AddProjectToEndpoint(command.Command):
|
class AddProjectToEndpoint(command.Command):
|
||||||
|
|
@ -44,15 +64,13 @@ class AddProjectToEndpoint(command.Command):
|
||||||
'endpoint',
|
'endpoint',
|
||||||
metavar='<endpoint>',
|
metavar='<endpoint>',
|
||||||
help=_(
|
help=_(
|
||||||
'Endpoint to associate with ' 'specified project (name or ID)'
|
'Endpoint to associate with specified project (name or ID)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'project',
|
'project',
|
||||||
metavar='<project>',
|
metavar='<project>',
|
||||||
help=_(
|
help=_('Project to associate with specified endpoint name or ID)'),
|
||||||
'Project to associate with ' 'specified endpoint name or ID)'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
common.add_project_domain_option_to_parser(parser)
|
common.add_project_domain_option_to_parser(parser)
|
||||||
return parser
|
return parser
|
||||||
|
|
@ -114,23 +132,23 @@ class CreateEndpoint(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
service = common.find_service(identity_client, parsed_args.service)
|
service = common.find_service_sdk(identity_client, parsed_args.service)
|
||||||
|
|
||||||
endpoint = identity_client.endpoints.create(
|
kwargs = {}
|
||||||
service=service.id,
|
|
||||||
url=parsed_args.url,
|
|
||||||
interface=parsed_args.interface,
|
|
||||||
region=parsed_args.region,
|
|
||||||
enabled=parsed_args.enabled,
|
|
||||||
)
|
|
||||||
|
|
||||||
info = {}
|
kwargs['service_id'] = service.id
|
||||||
endpoint._info.pop('links')
|
kwargs['url'] = parsed_args.url
|
||||||
info.update(endpoint._info)
|
kwargs['interface'] = parsed_args.interface
|
||||||
info['service_name'] = get_service_name(service)
|
kwargs['is_enabled'] = parsed_args.enabled
|
||||||
info['service_type'] = service.type
|
|
||||||
return zip(*sorted(info.items()))
|
if parsed_args.region:
|
||||||
|
region = identity_client.get_region(parsed_args.region)
|
||||||
|
kwargs['region_id'] = region.id
|
||||||
|
|
||||||
|
endpoint = identity_client.create_endpoint(**kwargs)
|
||||||
|
|
||||||
|
return _format_endpoint(endpoint, service=service)
|
||||||
|
|
||||||
|
|
||||||
class DeleteEndpoint(command.Command):
|
class DeleteEndpoint(command.Command):
|
||||||
|
|
@ -147,14 +165,14 @@ class DeleteEndpoint(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.endpoint:
|
for i in parsed_args.endpoint:
|
||||||
try:
|
try:
|
||||||
endpoint_id = utils.find_resource(
|
endpoint_id = identity_client.find_endpoint(
|
||||||
identity_client.endpoints, i
|
i, ignore_missing=False
|
||||||
).id
|
).id
|
||||||
identity_client.endpoints.delete(endpoint_id)
|
identity_client.delete_endpoint(endpoint_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|
@ -167,9 +185,10 @@ class DeleteEndpoint(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.endpoint)
|
total = len(parsed_args.endpoint)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s endpoints failed to delete.") % {
|
||||||
"%(result)s of %(total)s endpoints failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -209,28 +228,37 @@ class ListEndpoint(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
endpoint = None
|
endpoint = None
|
||||||
if parsed_args.endpoint:
|
if parsed_args.endpoint:
|
||||||
endpoint = utils.find_resource(
|
endpoint = identity_client.find_endpoint(
|
||||||
identity_client.endpoints, parsed_args.endpoint
|
parsed_args.endpoint, ignore_missing=False
|
||||||
)
|
)
|
||||||
project = None
|
|
||||||
|
project_domain_id = None
|
||||||
|
if parsed_args.project_domain:
|
||||||
|
project_domain_id = common._find_sdk_id(
|
||||||
|
identity_client.find_domain,
|
||||||
|
name_or_id=parsed_args.project_domain,
|
||||||
|
)
|
||||||
|
|
||||||
|
project_id = None
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
project = common.find_project(
|
project_id = common._find_sdk_id(
|
||||||
identity_client,
|
identity_client.find_project,
|
||||||
parsed_args.project,
|
name_or_id=common._get_token_resource(
|
||||||
parsed_args.project_domain,
|
identity_client, 'project', parsed_args.project
|
||||||
|
),
|
||||||
|
domain_id=project_domain_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if endpoint:
|
if endpoint:
|
||||||
columns = ('ID', 'Name')
|
column_headers: tuple[str, ...] = ('ID', 'Name')
|
||||||
data = identity_client.endpoint_filter.list_projects_for_endpoint(
|
columns: tuple[str, ...] = ('id', 'name')
|
||||||
endpoint=endpoint.id
|
data = identity_client.endpoint_projects(endpoint=endpoint.id)
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
columns = (
|
column_headers = (
|
||||||
'ID',
|
'ID',
|
||||||
'Region',
|
'Region',
|
||||||
'Service Name',
|
'Service Name',
|
||||||
|
|
@ -239,37 +267,43 @@ class ListEndpoint(command.Lister):
|
||||||
'Interface',
|
'Interface',
|
||||||
'URL',
|
'URL',
|
||||||
)
|
)
|
||||||
|
columns = (
|
||||||
|
'id',
|
||||||
|
'region_id',
|
||||||
|
'service_name',
|
||||||
|
'service_type',
|
||||||
|
'is_enabled',
|
||||||
|
'interface',
|
||||||
|
'url',
|
||||||
|
)
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.service:
|
if parsed_args.service:
|
||||||
service = common.find_service(
|
service = common.find_service_sdk(
|
||||||
identity_client, parsed_args.service
|
identity_client, parsed_args.service
|
||||||
)
|
)
|
||||||
kwargs['service'] = service.id
|
kwargs['service_id'] = service.id
|
||||||
if parsed_args.interface:
|
if parsed_args.interface:
|
||||||
kwargs['interface'] = parsed_args.interface
|
kwargs['interface'] = parsed_args.interface
|
||||||
if parsed_args.region:
|
if parsed_args.region:
|
||||||
kwargs['region'] = parsed_args.region
|
region = identity_client.get_region(parsed_args.region)
|
||||||
|
kwargs['region_id'] = region.id
|
||||||
|
|
||||||
if project:
|
if project_id:
|
||||||
data = (
|
data = list(
|
||||||
identity_client.endpoint_filter.list_endpoints_for_project(
|
identity_client.project_endpoints(project=project_id)
|
||||||
project=project.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
data = identity_client.endpoints.list(**kwargs)
|
data = list(identity_client.endpoints(**kwargs))
|
||||||
|
|
||||||
service_list = identity_client.services.list()
|
|
||||||
|
|
||||||
for ep in data:
|
for ep in data:
|
||||||
service = common.find_service_in_list(
|
service = identity_client.find_service(
|
||||||
service_list, ep.service_id
|
ep.service_id, ignore_missing=False
|
||||||
)
|
)
|
||||||
ep.service_name = get_service_name(service)
|
ep.service_name = getattr(service, 'name', '')
|
||||||
ep.service_type = service.type
|
ep.service_type = service.type
|
||||||
|
|
||||||
return (
|
return (
|
||||||
columns,
|
column_headers,
|
||||||
(
|
(
|
||||||
utils.get_item_properties(
|
utils.get_item_properties(
|
||||||
s,
|
s,
|
||||||
|
|
@ -290,14 +324,14 @@ class RemoveProjectFromEndpoint(command.Command):
|
||||||
'endpoint',
|
'endpoint',
|
||||||
metavar='<endpoint>',
|
metavar='<endpoint>',
|
||||||
help=_(
|
help=_(
|
||||||
'Endpoint to dissociate from ' 'specified project (name or ID)'
|
'Endpoint to dissociate from specified project (name or ID)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'project',
|
'project',
|
||||||
metavar='<project>',
|
metavar='<project>',
|
||||||
help=_(
|
help=_(
|
||||||
'Project to dissociate from ' 'specified endpoint name or ID)'
|
'Project to dissociate from specified endpoint name or ID)'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
common.add_project_domain_option_to_parser(parser)
|
common.add_project_domain_option_to_parser(parser)
|
||||||
|
|
@ -364,28 +398,36 @@ class SetEndpoint(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
endpoint = utils.find_resource(
|
endpoint = identity_client.find_endpoint(
|
||||||
identity_client.endpoints, parsed_args.endpoint
|
parsed_args.endpoint, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
||||||
service_id = None
|
kwargs = {}
|
||||||
if parsed_args.service:
|
|
||||||
service = common.find_service(identity_client, parsed_args.service)
|
|
||||||
service_id = service.id
|
|
||||||
enabled = None
|
|
||||||
if parsed_args.enabled:
|
|
||||||
enabled = True
|
|
||||||
if parsed_args.disabled:
|
|
||||||
enabled = False
|
|
||||||
|
|
||||||
identity_client.endpoints.update(
|
if parsed_args.service:
|
||||||
|
service = common.find_service_sdk(
|
||||||
|
identity_client, parsed_args.service
|
||||||
|
)
|
||||||
|
kwargs['service_id'] = service.id
|
||||||
|
|
||||||
|
if parsed_args.enabled:
|
||||||
|
kwargs['is_enabled'] = True
|
||||||
|
if parsed_args.disabled:
|
||||||
|
kwargs['is_enabled'] = False
|
||||||
|
|
||||||
|
if parsed_args.url:
|
||||||
|
kwargs['url'] = parsed_args.url
|
||||||
|
|
||||||
|
if parsed_args.interface:
|
||||||
|
kwargs['interface'] = parsed_args.interface
|
||||||
|
|
||||||
|
if parsed_args.region:
|
||||||
|
kwargs['region_id'] = parsed_args.region
|
||||||
|
|
||||||
|
identity_client.update_endpoint(
|
||||||
endpoint.id,
|
endpoint.id,
|
||||||
service=service_id,
|
**kwargs,
|
||||||
url=parsed_args.url,
|
|
||||||
interface=parsed_args.interface,
|
|
||||||
region=parsed_args.region,
|
|
||||||
enabled=enabled,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -405,16 +447,11 @@ class ShowEndpoint(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
endpoint = utils.find_resource(
|
endpoint = identity_client.find_endpoint(
|
||||||
identity_client.endpoints, parsed_args.endpoint
|
parsed_args.endpoint, ignore_missing=False
|
||||||
)
|
)
|
||||||
|
|
||||||
service = common.find_service(identity_client, endpoint.service_id)
|
service = common.find_service_sdk(identity_client, endpoint.service_id)
|
||||||
|
|
||||||
info = {}
|
return _format_endpoint(endpoint, service)
|
||||||
endpoint._info.pop('links')
|
|
||||||
info.update(endpoint._info)
|
|
||||||
info['service_name'] = get_service_name(service)
|
|
||||||
info['service_type'] = service.type
|
|
||||||
return zip(*sorted(info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@ class DeleteEndpointGroup(command.Command):
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.endpointgroup)
|
total = len(parsed_args.endpointgroup)
|
||||||
msg = _(
|
msg = _(
|
||||||
"%(result)s of %(total)s endpointgroups failed " "to delete."
|
"%(result)s of %(total)s endpointgroups failed to delete."
|
||||||
) % {'result': result, 'total': total}
|
) % {'result': result, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from openstack import exceptions as sdk_exc
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -29,6 +29,25 @@ from openstackclient.identity import common
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_group(group):
|
||||||
|
columns = (
|
||||||
|
'description',
|
||||||
|
'domain_id',
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
)
|
||||||
|
column_headers = (
|
||||||
|
'description',
|
||||||
|
'domain_id',
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
utils.get_item_properties(group, columns),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AddUserToGroup(command.Command):
|
class AddUserToGroup(command.Command):
|
||||||
_description = _("Add user to group")
|
_description = _("Add user to group")
|
||||||
|
|
||||||
|
|
@ -53,19 +72,19 @@ class AddUserToGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
group_id = common.find_group(
|
group_id = common.find_group_id_sdk(
|
||||||
identity_client, parsed_args.group, parsed_args.group_domain
|
identity_client, parsed_args.group, parsed_args.group_domain
|
||||||
).id
|
)
|
||||||
|
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.user:
|
for i in parsed_args.user:
|
||||||
try:
|
try:
|
||||||
user_id = common.find_user(
|
user_id = common.find_user_id_sdk(
|
||||||
identity_client, i, parsed_args.user_domain
|
identity_client, i, parsed_args.user_domain
|
||||||
).id
|
)
|
||||||
identity_client.users.add_to_group(user_id, group_id)
|
identity_client.add_user_to_group(user_id, group_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
msg = _("%(user)s not added to group %(group)s: %(e)s") % {
|
msg = _("%(user)s not added to group %(group)s: %(e)s") % {
|
||||||
|
|
@ -109,32 +128,41 @@ class CheckUserInGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
user_id = common.find_user(
|
user_id = common.find_user_id_sdk(
|
||||||
identity_client, parsed_args.user, parsed_args.user_domain
|
identity_client,
|
||||||
).id
|
parsed_args.user,
|
||||||
group_id = common.find_group(
|
parsed_args.user_domain,
|
||||||
identity_client, parsed_args.group, parsed_args.group_domain
|
validate_actor_existence=False,
|
||||||
).id
|
)
|
||||||
|
group_id = common.find_group_id_sdk(
|
||||||
|
identity_client,
|
||||||
|
parsed_args.group,
|
||||||
|
parsed_args.group_domain,
|
||||||
|
validate_actor_existence=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
user_in_group = False
|
||||||
try:
|
try:
|
||||||
identity_client.users.check_in_group(user_id, group_id)
|
user_in_group = identity_client.check_user_in_group(
|
||||||
except ks_exc.http.HTTPClientError as e:
|
user_id, group_id
|
||||||
if e.http_status == 403 or e.http_status == 404:
|
)
|
||||||
msg = _("%(user)s not in group %(group)s\n") % {
|
except sdk_exc.ForbiddenException:
|
||||||
'user': parsed_args.user,
|
# Assume False if forbidden
|
||||||
'group': parsed_args.group,
|
pass
|
||||||
}
|
if user_in_group:
|
||||||
self.app.stderr.write(msg)
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
else:
|
|
||||||
msg = _("%(user)s in group %(group)s\n") % {
|
msg = _("%(user)s in group %(group)s\n") % {
|
||||||
'user': parsed_args.user,
|
'user': parsed_args.user,
|
||||||
'group': parsed_args.group,
|
'group': parsed_args.group,
|
||||||
}
|
}
|
||||||
self.app.stdout.write(msg)
|
self.app.stdout.write(msg)
|
||||||
|
else:
|
||||||
|
msg = _("%(user)s not in group %(group)s\n") % {
|
||||||
|
'user': parsed_args.user,
|
||||||
|
'group': parsed_args.group,
|
||||||
|
}
|
||||||
|
self.app.stderr.write(msg)
|
||||||
|
|
||||||
|
|
||||||
class CreateGroup(command.ShowOne):
|
class CreateGroup(command.ShowOne):
|
||||||
|
|
@ -165,29 +193,38 @@ class CreateGroup(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
domain = None
|
kwargs = {}
|
||||||
|
if parsed_args.name:
|
||||||
|
kwargs['name'] = parsed_args.name
|
||||||
|
if parsed_args.description:
|
||||||
|
kwargs['description'] = parsed_args.description
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain = common.find_domain(identity_client, parsed_args.domain).id
|
kwargs['domain_id'] = common.find_domain_id_sdk(
|
||||||
|
identity_client, parsed_args.domain
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
group = identity_client.groups.create(
|
group = identity_client.create_group(**kwargs)
|
||||||
name=parsed_args.name,
|
except sdk_exc.ConflictException:
|
||||||
domain=domain,
|
|
||||||
description=parsed_args.description,
|
|
||||||
)
|
|
||||||
except ks_exc.Conflict:
|
|
||||||
if parsed_args.or_show:
|
if parsed_args.or_show:
|
||||||
group = utils.find_resource(
|
if parsed_args.domain:
|
||||||
identity_client.groups, parsed_args.name, domain_id=domain
|
group = identity_client.find_group(
|
||||||
)
|
parsed_args.name,
|
||||||
|
domain_id=parsed_args.domain,
|
||||||
|
ignore_missing=False,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
group = identity_client.find_group(
|
||||||
|
parsed_args.name,
|
||||||
|
ignore_missing=False,
|
||||||
|
)
|
||||||
LOG.info(_('Returning existing group %s'), group.name)
|
LOG.info(_('Returning existing group %s'), group.name)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
group._info.pop('links')
|
return _format_group(group)
|
||||||
return zip(*sorted(group._info.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteGroup(command.Command):
|
class DeleteGroup(command.Command):
|
||||||
|
|
@ -209,15 +246,15 @@ class DeleteGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
errors = 0
|
errors = 0
|
||||||
for group in parsed_args.groups:
|
for group in parsed_args.groups:
|
||||||
try:
|
try:
|
||||||
group_obj = common.find_group(
|
group_id = common.find_group_id_sdk(
|
||||||
identity_client, group, parsed_args.domain
|
identity_client, group, parsed_args.domain
|
||||||
)
|
)
|
||||||
identity_client.groups.delete(group_obj.id)
|
identity_client.delete_group(group_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errors += 1
|
errors += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|
@ -230,7 +267,7 @@ class DeleteGroup(command.Command):
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.groups)
|
total = len(parsed_args.groups)
|
||||||
msg = _("%(errors)s of %(total)s groups failed " "to delete.") % {
|
msg = _("%(errors)s of %(total)s groups failed to delete.") % {
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -262,30 +299,38 @@ class ListGroup(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
domain = None
|
domain = None
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain = common.find_domain(identity_client, parsed_args.domain).id
|
domain = common.find_domain_id_sdk(
|
||||||
|
identity_client, parsed_args.domain
|
||||||
|
)
|
||||||
|
|
||||||
|
data = []
|
||||||
if parsed_args.user:
|
if parsed_args.user:
|
||||||
user = common.find_user(
|
user = common.find_user_id_sdk(
|
||||||
identity_client,
|
identity_client,
|
||||||
parsed_args.user,
|
parsed_args.user,
|
||||||
parsed_args.user_domain,
|
parsed_args.user_domain,
|
||||||
).id
|
)
|
||||||
|
if domain:
|
||||||
|
# NOTE(0weng): The API doesn't actually support filtering
|
||||||
|
# additionally by domain_id, so this doesn't really do
|
||||||
|
# anything.
|
||||||
|
data = identity_client.user_groups(user, domain_id=domain)
|
||||||
|
else:
|
||||||
|
data = identity_client.user_groups(user)
|
||||||
else:
|
else:
|
||||||
user = None
|
if domain:
|
||||||
|
data = identity_client.groups(domain_id=domain)
|
||||||
|
else:
|
||||||
|
data = identity_client.groups()
|
||||||
|
|
||||||
# List groups
|
# List groups
|
||||||
|
columns: tuple[str, ...] = ('ID', 'Name')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Name', 'Domain ID', 'Description')
|
columns += ('Domain ID', 'Description')
|
||||||
else:
|
|
||||||
columns = ('ID', 'Name')
|
|
||||||
data = identity_client.groups.list(
|
|
||||||
domain=domain,
|
|
||||||
user=user,
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -324,19 +369,19 @@ class RemoveUserFromGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
group_id = common.find_group(
|
group_id = common.find_group_id_sdk(
|
||||||
identity_client, parsed_args.group, parsed_args.group_domain
|
identity_client, parsed_args.group, parsed_args.group_domain
|
||||||
).id
|
)
|
||||||
|
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.user:
|
for i in parsed_args.user:
|
||||||
try:
|
try:
|
||||||
user_id = common.find_user(
|
user_id = common.find_user_id_sdk(
|
||||||
identity_client, i, parsed_args.user_domain
|
identity_client, i, parsed_args.user_domain
|
||||||
).id
|
)
|
||||||
identity_client.users.remove_from_group(user_id, group_id)
|
identity_client.remove_user_from_group(user_id, group_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
msg = _("%(user)s not removed from group %(group)s: %(e)s") % {
|
msg = _("%(user)s not removed from group %(group)s: %(e)s") % {
|
||||||
|
|
@ -388,8 +433,8 @@ class SetGroup(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
group = common.find_group(
|
group = common.find_group_id_sdk(
|
||||||
identity_client, parsed_args.group, parsed_args.domain
|
identity_client, parsed_args.group, parsed_args.domain
|
||||||
)
|
)
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
@ -398,7 +443,7 @@ class SetGroup(command.Command):
|
||||||
if parsed_args.description:
|
if parsed_args.description:
|
||||||
kwargs['description'] = parsed_args.description
|
kwargs['description'] = parsed_args.description
|
||||||
|
|
||||||
identity_client.groups.update(group.id, **kwargs)
|
identity_client.update_group(group, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ShowGroup(command.ShowOne):
|
class ShowGroup(command.ShowOne):
|
||||||
|
|
@ -419,13 +464,18 @@ class ShowGroup(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
group = common.find_group(
|
if parsed_args.domain:
|
||||||
identity_client,
|
domain = common.find_domain_id_sdk(
|
||||||
parsed_args.group,
|
identity_client, parsed_args.domain
|
||||||
domain_name_or_id=parsed_args.domain,
|
)
|
||||||
)
|
group = identity_client.find_group(
|
||||||
|
parsed_args.group, domain_id=domain, ignore_missing=False
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
group = identity_client.find_group(
|
||||||
|
parsed_args.group, ignore_missing=False
|
||||||
|
)
|
||||||
|
|
||||||
group._info.pop('links')
|
return _format_group(group)
|
||||||
return zip(*sorted(group._info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -41,6 +41,7 @@ class CreateIdentityProvider(command.ShowOne):
|
||||||
identity_remote_id_provider.add_argument(
|
identity_remote_id_provider.add_argument(
|
||||||
'--remote-id',
|
'--remote-id',
|
||||||
metavar='<remote-id>',
|
metavar='<remote-id>',
|
||||||
|
dest='remote_ids',
|
||||||
action='append',
|
action='append',
|
||||||
help=_(
|
help=_(
|
||||||
'Remote IDs to associate with the Identity Provider '
|
'Remote IDs to associate with the Identity Provider '
|
||||||
|
|
@ -99,16 +100,15 @@ class CreateIdentityProvider(command.ShowOne):
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
remote_ids: list[str] | None = None
|
||||||
if parsed_args.remote_id_file:
|
if parsed_args.remote_id_file:
|
||||||
file_content = utils.read_blob_file_contents(
|
file_content = utils.read_blob_file_contents(
|
||||||
parsed_args.remote_id_file
|
parsed_args.remote_id_file
|
||||||
)
|
)
|
||||||
remote_ids = file_content.splitlines()
|
remote_ids = file_content.splitlines()
|
||||||
remote_ids = list(map(str.strip, remote_ids))
|
remote_ids = list(map(str.strip, remote_ids))
|
||||||
else:
|
elif parsed_args.remote_ids:
|
||||||
remote_ids = (
|
remote_ids = parsed_args.remote_ids
|
||||||
parsed_args.remote_id if parsed_args.remote_id else None
|
|
||||||
)
|
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
|
|
@ -137,8 +137,9 @@ class CreateIdentityProvider(command.ShowOne):
|
||||||
)
|
)
|
||||||
|
|
||||||
idp._info.pop('links', None)
|
idp._info.pop('links', None)
|
||||||
remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', []))
|
idp._info['remote_ids'] = format_columns.ListColumn(
|
||||||
idp._info['remote_ids'] = remote_ids
|
idp._info.pop('remote_ids', [])
|
||||||
|
)
|
||||||
return zip(*sorted(idp._info.items()))
|
return zip(*sorted(idp._info.items()))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -174,8 +175,7 @@ class DeleteIdentityProvider(command.Command):
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.identity_provider)
|
total = len(parsed_args.identity_provider)
|
||||||
msg = _(
|
msg = _(
|
||||||
"%(result)s of %(total)s identity providers failed"
|
"%(result)s of %(total)s identity providers failed to delete."
|
||||||
" to delete."
|
|
||||||
) % {'result': result, 'total': total}
|
) % {'result': result, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -241,6 +241,7 @@ class SetIdentityProvider(command.Command):
|
||||||
identity_remote_id_provider.add_argument(
|
identity_remote_id_provider.add_argument(
|
||||||
'--remote-id',
|
'--remote-id',
|
||||||
metavar='<remote-id>',
|
metavar='<remote-id>',
|
||||||
|
dest='remote_ids',
|
||||||
action='append',
|
action='append',
|
||||||
help=_(
|
help=_(
|
||||||
'Remote IDs to associate with the Identity Provider '
|
'Remote IDs to associate with the Identity Provider '
|
||||||
|
|
@ -288,8 +289,8 @@ class SetIdentityProvider(command.Command):
|
||||||
)
|
)
|
||||||
remote_ids = file_content.splitlines()
|
remote_ids = file_content.splitlines()
|
||||||
remote_ids = list(map(str.strip, remote_ids))
|
remote_ids = list(map(str.strip, remote_ids))
|
||||||
elif parsed_args.remote_id:
|
elif parsed_args.remote_ids:
|
||||||
remote_ids = parsed_args.remote_id
|
remote_ids = parsed_args.remote_ids
|
||||||
|
|
||||||
# Setup keyword args for the client
|
# Setup keyword args for the client
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
@ -299,7 +300,7 @@ class SetIdentityProvider(command.Command):
|
||||||
kwargs['enabled'] = True
|
kwargs['enabled'] = True
|
||||||
if parsed_args.disable:
|
if parsed_args.disable:
|
||||||
kwargs['enabled'] = False
|
kwargs['enabled'] = False
|
||||||
if parsed_args.remote_id_file or parsed_args.remote_id:
|
if parsed_args.remote_id_file or parsed_args.remote_ids:
|
||||||
kwargs['remote_ids'] = remote_ids
|
kwargs['remote_ids'] = remote_ids
|
||||||
|
|
||||||
# TODO(pas-ha) actually check for 3.14 microversion
|
# TODO(pas-ha) actually check for 3.14 microversion
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as common_utils
|
from openstackclient.identity import common as common_utils
|
||||||
|
|
||||||
|
|
@ -77,8 +77,7 @@ class CreateLimit(command.ShowOne):
|
||||||
)
|
)
|
||||||
region = None
|
region = None
|
||||||
if parsed_args.region:
|
if parsed_args.region:
|
||||||
val = getattr(parsed_args, 'region', None)
|
if 'None' not in parsed_args.region:
|
||||||
if 'None' not in val:
|
|
||||||
# NOTE (vishakha): Due to bug #1799153 and for any another
|
# NOTE (vishakha): Due to bug #1799153 and for any another
|
||||||
# related case where GET resource API does not support the
|
# related case where GET resource API does not support the
|
||||||
# filter by name, osc_lib.utils.find_resource() method cannot
|
# filter by name, osc_lib.utils.find_resource() method cannot
|
||||||
|
|
@ -90,6 +89,13 @@ class CreateLimit(command.ShowOne):
|
||||||
region = common_utils.get_resource(
|
region = common_utils.get_resource(
|
||||||
identity_client.regions, parsed_args.region
|
identity_client.regions, parsed_args.region
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.log.warning(
|
||||||
|
_(
|
||||||
|
"Passing 'None' to indicate no region is deprecated. "
|
||||||
|
"Instead, don't pass --region."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
limit = identity_client.limits.create(
|
limit = identity_client.limits.create(
|
||||||
project,
|
project,
|
||||||
|
|
@ -142,11 +148,7 @@ class ListLimit(command.Lister):
|
||||||
)
|
)
|
||||||
region = None
|
region = None
|
||||||
if parsed_args.region:
|
if parsed_args.region:
|
||||||
region = utils.find_resource(
|
if 'None' not in parsed_args.region:
|
||||||
identity_client.regions, parsed_args.region
|
|
||||||
)
|
|
||||||
val = getattr(parsed_args, 'region', None)
|
|
||||||
if 'None' not in val:
|
|
||||||
# NOTE (vishakha): Due to bug #1799153 and for any another
|
# NOTE (vishakha): Due to bug #1799153 and for any another
|
||||||
# related case where GET resource API does not support the
|
# related case where GET resource API does not support the
|
||||||
# filter by name, osc_lib.utils.find_resource() method cannot
|
# filter by name, osc_lib.utils.find_resource() method cannot
|
||||||
|
|
@ -158,6 +160,14 @@ class ListLimit(command.Lister):
|
||||||
region = common_utils.get_resource(
|
region = common_utils.get_resource(
|
||||||
identity_client.regions, parsed_args.region
|
identity_client.regions, parsed_args.region
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.log.warning(
|
||||||
|
_(
|
||||||
|
"Passing 'None' to indicate no region is deprecated. "
|
||||||
|
"Instead, don't pass --region."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
project = None
|
project = None
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
project = utils.find_resource(
|
project = utils.find_resource(
|
||||||
|
|
@ -266,13 +276,13 @@ class DeleteLimit(command.Command):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errors += 1
|
errors += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
_("Failed to delete limit with ID " "'%(id)s': %(e)s"),
|
_("Failed to delete limit with ID '%(id)s': %(e)s"),
|
||||||
{'id': limit_id, 'e': e},
|
{'id': limit_id, 'e': e},
|
||||||
)
|
)
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.limit_id)
|
total = len(parsed_args.limit_id)
|
||||||
msg = _("%(errors)s of %(total)s limits failed to " "delete.") % {
|
msg = _("%(errors)s of %(total)s limits failed to delete.") % {
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -161,9 +161,10 @@ class DeleteMapping(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.mapping)
|
total = len(parsed_args.mapping)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s mappings failed to delete.") % {
|
||||||
"%(result)s of %(total)s mappings failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -92,9 +92,7 @@ class DeletePolicy(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.policy)
|
total = len(parsed_args.policy)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s policies failed to delete.") % {
|
||||||
"%(result)s of %(total)s policies failed " "to delete."
|
|
||||||
) % {
|
|
||||||
'result': result,
|
'result': result,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -115,12 +113,11 @@ class ListPolicy(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
columns: tuple[str, ...] = ('ID', 'Type')
|
||||||
|
column_headers: tuple[str, ...] = columns
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Type', 'Blob')
|
columns += ('Blob',)
|
||||||
column_headers = ('ID', 'Type', 'Rules')
|
column_headers += ('Rules',)
|
||||||
else:
|
|
||||||
columns = ('ID', 'Type')
|
|
||||||
column_headers = columns
|
|
||||||
data = self.app.client_manager.identity.policies.list()
|
data = self.app.client_manager.identity.policies.list()
|
||||||
return (
|
return (
|
||||||
column_headers,
|
column_headers,
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ import logging
|
||||||
|
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
from openstackclient.identity.v3 import tag
|
from openstackclient.identity.v3 import tag
|
||||||
|
|
@ -59,16 +59,21 @@ class CreateProject(command.ShowOne):
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--enable',
|
'--enable',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
dest='enabled',
|
||||||
|
default=True,
|
||||||
help=_('Enable project'),
|
help=_('Enable project'),
|
||||||
)
|
)
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--disable',
|
'--disable',
|
||||||
action='store_true',
|
action='store_false',
|
||||||
|
dest='enabled',
|
||||||
|
default=True,
|
||||||
help=_('Disable project'),
|
help=_('Disable project'),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--property',
|
'--property',
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
|
dest='properties',
|
||||||
action=parseractions.KeyValueAction,
|
action=parseractions.KeyValueAction,
|
||||||
help=_(
|
help=_(
|
||||||
'Add a property to <name> '
|
'Add a property to <name> '
|
||||||
|
|
@ -98,15 +103,9 @@ class CreateProject(command.ShowOne):
|
||||||
parsed_args.parent,
|
parsed_args.parent,
|
||||||
).id
|
).id
|
||||||
|
|
||||||
enabled = True
|
|
||||||
if parsed_args.disable:
|
|
||||||
enabled = False
|
|
||||||
|
|
||||||
options = common.get_immutable_options(parsed_args)
|
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.property:
|
if parsed_args.properties:
|
||||||
kwargs = parsed_args.property.copy()
|
kwargs = parsed_args.properties.copy()
|
||||||
if 'is_domain' in kwargs.keys():
|
if 'is_domain' in kwargs.keys():
|
||||||
if kwargs['is_domain'].lower() == "true":
|
if kwargs['is_domain'].lower() == "true":
|
||||||
kwargs['is_domain'] = True
|
kwargs['is_domain'] = True
|
||||||
|
|
@ -117,13 +116,17 @@ class CreateProject(command.ShowOne):
|
||||||
|
|
||||||
kwargs['tags'] = list(set(parsed_args.tags))
|
kwargs['tags'] = list(set(parsed_args.tags))
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
if parsed_args.immutable is not None:
|
||||||
|
options['immutable'] = parsed_args.immutable
|
||||||
|
|
||||||
try:
|
try:
|
||||||
project = identity_client.projects.create(
|
project = identity_client.projects.create(
|
||||||
name=parsed_args.name,
|
name=parsed_args.name,
|
||||||
domain=domain,
|
domain=domain,
|
||||||
parent=parent,
|
parent=parent,
|
||||||
description=parsed_args.description,
|
description=parsed_args.description,
|
||||||
enabled=enabled,
|
enabled=parsed_args.enabled,
|
||||||
options=options,
|
options=options,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
@ -143,7 +146,14 @@ class CreateProject(command.ShowOne):
|
||||||
|
|
||||||
|
|
||||||
class DeleteProject(command.Command):
|
class DeleteProject(command.Command):
|
||||||
_description = _("Delete project(s)")
|
_description = _(
|
||||||
|
"Delete project(s). This command will remove specified "
|
||||||
|
"existing project(s) if an active user is authorized to do "
|
||||||
|
"this. If there are resources managed by other services "
|
||||||
|
"(for example, Nova, Neutron, Cinder) associated with "
|
||||||
|
"specified project(s), delete operation will proceed "
|
||||||
|
"regardless."
|
||||||
|
)
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super().get_parser(prog_name)
|
parser = super().get_parser(prog_name)
|
||||||
|
|
@ -190,9 +200,10 @@ class DeleteProject(command.Command):
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.projects)
|
total = len(parsed_args.projects)
|
||||||
msg = _(
|
msg = _("%(errors)s of %(total)s projects failed to delete.") % {
|
||||||
"%(errors)s of %(total)s projects failed " "to delete."
|
'errors': errors,
|
||||||
) % {'errors': errors, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -239,15 +250,28 @@ class ListProject(command.Lister):
|
||||||
'keys and directions.'
|
'keys and directions.'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--enabled',
|
||||||
|
action='store_true',
|
||||||
|
dest='is_enabled',
|
||||||
|
default=None,
|
||||||
|
help=_('List only enabled projects'),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--disabled',
|
||||||
|
action='store_false',
|
||||||
|
dest='is_enabled',
|
||||||
|
default=None,
|
||||||
|
help=_('List only disabled projects'),
|
||||||
|
)
|
||||||
tag.add_tag_filtering_option_to_parser(parser, _('projects'))
|
tag.add_tag_filtering_option_to_parser(parser, _('projects'))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
columns: tuple[str, ...] = ('ID', 'Name')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('ID', 'Name', 'Domain ID', 'Description', 'Enabled')
|
columns += ('Domain ID', 'Description', 'Enabled')
|
||||||
else:
|
|
||||||
columns = ('ID', 'Name')
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
|
|
@ -277,6 +301,9 @@ class ListProject(command.Lister):
|
||||||
|
|
||||||
kwargs['user'] = user_id
|
kwargs['user'] = user_id
|
||||||
|
|
||||||
|
if parsed_args.is_enabled is not None:
|
||||||
|
kwargs['is_enabled'] = parsed_args.is_enabled
|
||||||
|
|
||||||
tag.get_tag_filtering_args(parsed_args, kwargs)
|
tag.get_tag_filtering_args(parsed_args, kwargs)
|
||||||
|
|
||||||
if parsed_args.my_projects:
|
if parsed_args.my_projects:
|
||||||
|
|
@ -339,16 +366,21 @@ class SetProject(command.Command):
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--enable',
|
'--enable',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
dest='enabled',
|
||||||
|
default=None,
|
||||||
help=_('Enable project'),
|
help=_('Enable project'),
|
||||||
)
|
)
|
||||||
enable_group.add_argument(
|
enable_group.add_argument(
|
||||||
'--disable',
|
'--disable',
|
||||||
action='store_true',
|
action='store_false',
|
||||||
|
dest='enabled',
|
||||||
|
default=None,
|
||||||
help=_('Disable project'),
|
help=_('Disable project'),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--property',
|
'--property',
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
|
dest='properties',
|
||||||
action=parseractions.KeyValueAction,
|
action=parseractions.KeyValueAction,
|
||||||
help=_(
|
help=_(
|
||||||
'Set a property on <project> '
|
'Set a property on <project> '
|
||||||
|
|
@ -371,15 +403,12 @@ class SetProject(command.Command):
|
||||||
kwargs['name'] = parsed_args.name
|
kwargs['name'] = parsed_args.name
|
||||||
if parsed_args.description:
|
if parsed_args.description:
|
||||||
kwargs['description'] = parsed_args.description
|
kwargs['description'] = parsed_args.description
|
||||||
if parsed_args.enable:
|
if parsed_args.enabled is not None:
|
||||||
kwargs['enabled'] = True
|
kwargs['enabled'] = parsed_args.enabled
|
||||||
if parsed_args.disable:
|
if parsed_args.immutable is not None:
|
||||||
kwargs['enabled'] = False
|
kwargs['options'] = {'immutable': parsed_args.immutable}
|
||||||
options = common.get_immutable_options(parsed_args)
|
if parsed_args.properties:
|
||||||
if options:
|
kwargs.update(parsed_args.properties)
|
||||||
kwargs['options'] = options
|
|
||||||
if parsed_args.property:
|
|
||||||
kwargs.update(parsed_args.property)
|
|
||||||
tag.update_tags_in_args(parsed_args, project, kwargs)
|
tag.update_tags_in_args(parsed_args, project, kwargs)
|
||||||
|
|
||||||
identity_client.projects.update(project.id, **kwargs)
|
identity_client.projects.update(project.id, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,25 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_region(region):
|
||||||
|
columns = ('id', 'description', 'parent_region_id')
|
||||||
|
column_headers = ('region', 'description', 'parent_region')
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
utils.get_item_properties(region, columns),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateRegion(command.ShowOne):
|
class CreateRegion(command.ShowOne):
|
||||||
_description = _("Create new region")
|
_description = _("Create new region")
|
||||||
|
|
||||||
|
|
@ -50,18 +59,15 @@ class CreateRegion(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
region = identity_client.regions.create(
|
region = identity_client.create_region(
|
||||||
id=parsed_args.region,
|
id=parsed_args.region,
|
||||||
parent_region=parsed_args.parent_region,
|
parent_region_id=parsed_args.parent_region,
|
||||||
description=parsed_args.description,
|
description=parsed_args.description,
|
||||||
)
|
)
|
||||||
|
|
||||||
region._info['region'] = region._info.pop('id')
|
return _format_region(region)
|
||||||
region._info['parent_region'] = region._info.pop('parent_region_id')
|
|
||||||
region._info.pop('links', None)
|
|
||||||
return zip(*sorted(region._info.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteRegion(command.Command):
|
class DeleteRegion(command.Command):
|
||||||
|
|
@ -78,24 +84,21 @@ class DeleteRegion(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.region:
|
for i in parsed_args.region:
|
||||||
try:
|
try:
|
||||||
identity_client.regions.delete(i)
|
identity_client.delete_region(i)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
_(
|
_("Failed to delete region with ID '%(region)s': %(e)s"),
|
||||||
"Failed to delete region with "
|
|
||||||
"ID '%(region)s': %(e)s"
|
|
||||||
),
|
|
||||||
{'region': i, 'e': e},
|
{'region': i, 'e': e},
|
||||||
)
|
)
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.region)
|
total = len(parsed_args.region)
|
||||||
msg = _("%(result)s of %(total)s regions failed " "to delete.") % {
|
msg = _("%(result)s of %(total)s regions failed to delete.") % {
|
||||||
'result': result,
|
'result': result,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +118,7 @@ class ListRegion(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.parent_region:
|
if parsed_args.parent_region:
|
||||||
|
|
@ -124,7 +127,7 @@ class ListRegion(command.Lister):
|
||||||
columns_headers = ('Region', 'Parent Region', 'Description')
|
columns_headers = ('Region', 'Parent Region', 'Description')
|
||||||
columns = ('ID', 'Parent Region Id', 'Description')
|
columns = ('ID', 'Parent Region Id', 'Description')
|
||||||
|
|
||||||
data = identity_client.regions.list(**kwargs)
|
data = identity_client.regions(**kwargs)
|
||||||
return (
|
return (
|
||||||
columns_headers,
|
columns_headers,
|
||||||
(
|
(
|
||||||
|
|
@ -161,15 +164,15 @@ class SetRegion(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.description:
|
if parsed_args.description:
|
||||||
kwargs['description'] = parsed_args.description
|
kwargs['description'] = parsed_args.description
|
||||||
if parsed_args.parent_region:
|
if parsed_args.parent_region:
|
||||||
kwargs['parent_region'] = parsed_args.parent_region
|
kwargs['parent_region_id'] = parsed_args.parent_region
|
||||||
|
|
||||||
identity_client.regions.update(parsed_args.region, **kwargs)
|
identity_client.update_region(parsed_args.region, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ShowRegion(command.ShowOne):
|
class ShowRegion(command.ShowOne):
|
||||||
|
|
@ -185,13 +188,8 @@ class ShowRegion(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
region = utils.find_resource(
|
region = identity_client.get_region(parsed_args.region)
|
||||||
identity_client.regions, parsed_args.region
|
|
||||||
)
|
|
||||||
|
|
||||||
region._info['region'] = region._info.pop('id')
|
return _format_region(region)
|
||||||
region._info['parent_region'] = region._info.pop('parent_region_id')
|
|
||||||
region._info.pop('links', None)
|
|
||||||
return zip(*sorted(region._info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common as common_utils
|
from openstackclient.identity import common as common_utils
|
||||||
|
|
||||||
|
|
@ -44,7 +44,10 @@ class CreateRegisteredLimit(command.ShowOne):
|
||||||
'--service',
|
'--service',
|
||||||
metavar='<service>',
|
metavar='<service>',
|
||||||
required=True,
|
required=True,
|
||||||
help=_('Service responsible for the resource to limit (required)'),
|
help=_(
|
||||||
|
'Service responsible for the resource to limit (required) '
|
||||||
|
'(name or ID)'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--default-limit',
|
'--default-limit',
|
||||||
|
|
@ -68,8 +71,7 @@ class CreateRegisteredLimit(command.ShowOne):
|
||||||
)
|
)
|
||||||
region = None
|
region = None
|
||||||
if parsed_args.region:
|
if parsed_args.region:
|
||||||
val = getattr(parsed_args, 'region', None)
|
if 'None' not in parsed_args.region:
|
||||||
if 'None' not in val:
|
|
||||||
# NOTE (vishakha): Due to bug #1799153 and for any another
|
# NOTE (vishakha): Due to bug #1799153 and for any another
|
||||||
# related case where GET resource API does not support the
|
# related case where GET resource API does not support the
|
||||||
# filter by name, osc_lib.utils.find_resource() method cannot
|
# filter by name, osc_lib.utils.find_resource() method cannot
|
||||||
|
|
@ -81,6 +83,13 @@ class CreateRegisteredLimit(command.ShowOne):
|
||||||
region = common_utils.get_resource(
|
region = common_utils.get_resource(
|
||||||
identity_client.regions, parsed_args.region
|
identity_client.regions, parsed_args.region
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.log.warning(
|
||||||
|
_(
|
||||||
|
"Passing 'None' to indicate no region is deprecated. "
|
||||||
|
"Instead, don't pass --region."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
registered_limit = identity_client.registered_limits.create(
|
registered_limit = identity_client.registered_limits.create(
|
||||||
service,
|
service,
|
||||||
|
|
@ -100,10 +109,10 @@ class DeleteRegisteredLimit(command.Command):
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super().get_parser(prog_name)
|
parser = super().get_parser(prog_name)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'registered_limit_id',
|
'registered_limits',
|
||||||
metavar='<registered-limit-id>',
|
metavar='<registered-limits>',
|
||||||
nargs="+",
|
nargs="+",
|
||||||
help=_('Registered limit to delete (ID)'),
|
help=_('Registered limit(s) to delete (ID)'),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
@ -111,7 +120,7 @@ class DeleteRegisteredLimit(command.Command):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
errors = 0
|
errors = 0
|
||||||
for registered_limit_id in parsed_args.registered_limit_id:
|
for registered_limit_id in parsed_args.registered_limits:
|
||||||
try:
|
try:
|
||||||
identity_client.registered_limits.delete(registered_limit_id)
|
identity_client.registered_limits.delete(registered_limit_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -128,10 +137,9 @@ class DeleteRegisteredLimit(command.Command):
|
||||||
)
|
)
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.registered_limit_id)
|
total = len(parsed_args.registered_limits)
|
||||||
msg = _(
|
msg = _(
|
||||||
"%(errors)s of %(total)s registered limits failed to "
|
"%(errors)s of %(total)s registered limits failed to delete."
|
||||||
"delete."
|
|
||||||
) % {'errors': errors, 'total': total}
|
) % {'errors': errors, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -144,7 +152,9 @@ class ListRegisteredLimit(command.Lister):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--service',
|
'--service',
|
||||||
metavar='<service>',
|
metavar='<service>',
|
||||||
help=_('Service responsible for the resource to limit'),
|
help=_(
|
||||||
|
'Service responsible for the resource to limit (name or ID)'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--resource-name',
|
'--resource-name',
|
||||||
|
|
@ -169,8 +179,7 @@ class ListRegisteredLimit(command.Lister):
|
||||||
)
|
)
|
||||||
region = None
|
region = None
|
||||||
if parsed_args.region:
|
if parsed_args.region:
|
||||||
val = getattr(parsed_args, 'region', None)
|
if 'None' not in parsed_args.region:
|
||||||
if 'None' not in val:
|
|
||||||
# NOTE (vishakha): Due to bug #1799153 and for any another
|
# NOTE (vishakha): Due to bug #1799153 and for any another
|
||||||
# related case where GET resource API does not support the
|
# related case where GET resource API does not support the
|
||||||
# filter by name, osc_lib.utils.find_resource() method cannot
|
# filter by name, osc_lib.utils.find_resource() method cannot
|
||||||
|
|
@ -182,6 +191,13 @@ class ListRegisteredLimit(command.Lister):
|
||||||
region = common_utils.get_resource(
|
region = common_utils.get_resource(
|
||||||
identity_client.regions, parsed_args.region
|
identity_client.regions, parsed_args.region
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.log.warning(
|
||||||
|
_(
|
||||||
|
"Passing 'None' to indicate no region is deprecated. "
|
||||||
|
"Instead, don't pass --region."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
registered_limits = identity_client.registered_limits.list(
|
registered_limits = identity_client.registered_limits.list(
|
||||||
service=service,
|
service=service,
|
||||||
|
|
@ -217,9 +233,9 @@ class SetRegisteredLimit(command.ShowOne):
|
||||||
'--service',
|
'--service',
|
||||||
metavar='<service>',
|
metavar='<service>',
|
||||||
help=_(
|
help=_(
|
||||||
'Service to be updated responsible for the resource to '
|
'Service to be updated responsible for the resource to limit '
|
||||||
'limit. Either --service, --resource-name or --region must '
|
'(name or ID). Either --service, --resource-name or --region '
|
||||||
'be different than existing value otherwise it will be '
|
'must be different than existing value otherwise it will be '
|
||||||
'duplicate entry'
|
'duplicate entry'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -267,8 +283,7 @@ class SetRegisteredLimit(command.ShowOne):
|
||||||
|
|
||||||
region = None
|
region = None
|
||||||
if parsed_args.region:
|
if parsed_args.region:
|
||||||
val = getattr(parsed_args, 'region', None)
|
if 'None' not in parsed_args.region:
|
||||||
if 'None' not in val:
|
|
||||||
# NOTE (vishakha): Due to bug #1799153 and for any another
|
# NOTE (vishakha): Due to bug #1799153 and for any another
|
||||||
# related case where GET resource API does not support the
|
# related case where GET resource API does not support the
|
||||||
# filter by name, osc_lib.utils.find_resource() method cannot
|
# filter by name, osc_lib.utils.find_resource() method cannot
|
||||||
|
|
@ -280,6 +295,10 @@ class SetRegisteredLimit(command.ShowOne):
|
||||||
region = common_utils.get_resource(
|
region = common_utils.get_resource(
|
||||||
identity_client.regions, parsed_args.region
|
identity_client.regions, parsed_args.region
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
self.log.warning(
|
||||||
|
_("Passing 'None' to indicate no region is deprecated.")
|
||||||
|
)
|
||||||
|
|
||||||
registered_limit = identity_client.registered_limits.update(
|
registered_limit = identity_client.registered_limits.update(
|
||||||
parsed_args.registered_limit_id,
|
parsed_args.registered_limit_id,
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from openstack import exceptions as sdk_exc
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -29,6 +29,25 @@ from openstackclient.identity import common
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_role(role):
|
||||||
|
columns = (
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"domain_id",
|
||||||
|
"description",
|
||||||
|
)
|
||||||
|
column_headers = (
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"domain_id",
|
||||||
|
"description",
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
utils.get_item_properties(role, columns),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _add_identity_and_resource_options_to_parser(parser):
|
def _add_identity_and_resource_options_to_parser(parser):
|
||||||
system_or_domain_or_project = parser.add_mutually_exclusive_group()
|
system_or_domain_or_project = parser.add_mutually_exclusive_group()
|
||||||
system_or_domain_or_project.add_argument(
|
system_or_domain_or_project.add_argument(
|
||||||
|
|
@ -64,31 +83,58 @@ def _add_identity_and_resource_options_to_parser(parser):
|
||||||
|
|
||||||
|
|
||||||
def _process_identity_and_resource_options(
|
def _process_identity_and_resource_options(
|
||||||
parsed_args, identity_client_manager, validate_actor_existence=True
|
parsed_args, identity_client, validate_actor_existence=True
|
||||||
):
|
):
|
||||||
def _find_user():
|
def _find_user():
|
||||||
try:
|
domain_id = (
|
||||||
return common.find_user(
|
common._find_sdk_id(
|
||||||
identity_client_manager,
|
identity_client.find_domain,
|
||||||
parsed_args.user,
|
name_or_id=parsed_args.user_domain,
|
||||||
parsed_args.user_domain,
|
validate_actor_existence=validate_actor_existence,
|
||||||
).id
|
)
|
||||||
except exceptions.CommandError:
|
if parsed_args.user_domain
|
||||||
if not validate_actor_existence:
|
else None
|
||||||
return parsed_args.user
|
)
|
||||||
raise
|
return common._find_sdk_id(
|
||||||
|
identity_client.find_user,
|
||||||
|
name_or_id=parsed_args.user,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
domain_id=domain_id,
|
||||||
|
)
|
||||||
|
|
||||||
def _find_group():
|
def _find_group():
|
||||||
try:
|
domain_id = (
|
||||||
return common.find_group(
|
common._find_sdk_id(
|
||||||
identity_client_manager,
|
identity_client.find_domain,
|
||||||
parsed_args.group,
|
name_or_id=parsed_args.group_domain,
|
||||||
parsed_args.group_domain,
|
validate_actor_existence=validate_actor_existence,
|
||||||
).id
|
)
|
||||||
except exceptions.CommandError:
|
if parsed_args.group_domain
|
||||||
if not validate_actor_existence:
|
else None
|
||||||
return parsed_args.group
|
)
|
||||||
raise
|
return common._find_sdk_id(
|
||||||
|
identity_client.find_group,
|
||||||
|
name_or_id=parsed_args.group,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
domain_id=domain_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _find_project():
|
||||||
|
domain_id = (
|
||||||
|
common._find_sdk_id(
|
||||||
|
identity_client.find_domain,
|
||||||
|
name_or_id=parsed_args.project_domain,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
)
|
||||||
|
if parsed_args.project_domain
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
return common._find_sdk_id(
|
||||||
|
identity_client.find_project,
|
||||||
|
name_or_id=parsed_args.project,
|
||||||
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
domain_id=domain_id,
|
||||||
|
)
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.user and parsed_args.system:
|
if parsed_args.user and parsed_args.system:
|
||||||
|
|
@ -96,34 +142,35 @@ def _process_identity_and_resource_options(
|
||||||
kwargs['system'] = parsed_args.system
|
kwargs['system'] = parsed_args.system
|
||||||
elif parsed_args.user and parsed_args.domain:
|
elif parsed_args.user and parsed_args.domain:
|
||||||
kwargs['user'] = _find_user()
|
kwargs['user'] = _find_user()
|
||||||
kwargs['domain'] = common.find_domain(
|
kwargs['domain'] = common._find_sdk_id(
|
||||||
identity_client_manager,
|
identity_client.find_domain,
|
||||||
parsed_args.domain,
|
name_or_id=parsed_args.domain,
|
||||||
).id
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
)
|
||||||
elif parsed_args.user and parsed_args.project:
|
elif parsed_args.user and parsed_args.project:
|
||||||
kwargs['user'] = _find_user()
|
kwargs['user'] = _find_user()
|
||||||
kwargs['project'] = common.find_project(
|
kwargs['project'] = _find_project()
|
||||||
identity_client_manager,
|
|
||||||
parsed_args.project,
|
|
||||||
parsed_args.project_domain,
|
|
||||||
).id
|
|
||||||
elif parsed_args.group and parsed_args.system:
|
elif parsed_args.group and parsed_args.system:
|
||||||
kwargs['group'] = _find_group()
|
kwargs['group'] = _find_group()
|
||||||
kwargs['system'] = parsed_args.system
|
kwargs['system'] = parsed_args.system
|
||||||
elif parsed_args.group and parsed_args.domain:
|
elif parsed_args.group and parsed_args.domain:
|
||||||
kwargs['group'] = _find_group()
|
kwargs['group'] = _find_group()
|
||||||
kwargs['domain'] = common.find_domain(
|
kwargs['domain'] = common._find_sdk_id(
|
||||||
identity_client_manager,
|
identity_client.find_domain,
|
||||||
parsed_args.domain,
|
name_or_id=parsed_args.domain,
|
||||||
).id
|
validate_actor_existence=validate_actor_existence,
|
||||||
|
)
|
||||||
elif parsed_args.group and parsed_args.project:
|
elif parsed_args.group and parsed_args.project:
|
||||||
kwargs['group'] = _find_group()
|
kwargs['group'] = _find_group()
|
||||||
kwargs['project'] = common.find_project(
|
kwargs['project'] = _find_project()
|
||||||
identity_client_manager,
|
else:
|
||||||
parsed_args.project,
|
msg = _(
|
||||||
parsed_args.project_domain,
|
"Role not added, incorrect set of arguments "
|
||||||
).id
|
"provided. See openstack --help for more details"
|
||||||
kwargs['os_inherit_extension_inherited'] = parsed_args.inherited
|
)
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
kwargs['inherited'] = parsed_args.inherited
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -145,7 +192,7 @@ class AddRole(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not parsed_args.user
|
not parsed_args.user
|
||||||
|
|
@ -161,18 +208,71 @@ class AddRole(command.Command):
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.role_domain:
|
if parsed_args.role_domain:
|
||||||
domain_id = common.find_domain(
|
domain_id = common._find_sdk_id(
|
||||||
identity_client, parsed_args.role_domain
|
identity_client.find_domain, name_or_id=parsed_args.role_domain
|
||||||
).id
|
)
|
||||||
role = utils.find_resource(
|
role = common._find_sdk_id(
|
||||||
identity_client.roles, parsed_args.role, domain_id=domain_id
|
identity_client.find_role,
|
||||||
|
name_or_id=parsed_args.role,
|
||||||
|
domain_id=domain_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
kwargs = _process_identity_and_resource_options(
|
add_kwargs = _process_identity_and_resource_options(
|
||||||
parsed_args, self.app.client_manager.identity
|
parsed_args, identity_client
|
||||||
)
|
)
|
||||||
|
|
||||||
identity_client.roles.grant(role.id, **kwargs)
|
if add_kwargs.get("domain"):
|
||||||
|
if add_kwargs.get("user"):
|
||||||
|
identity_client.assign_domain_role_to_user(
|
||||||
|
domain=add_kwargs["domain"],
|
||||||
|
user=add_kwargs["user"],
|
||||||
|
role=role,
|
||||||
|
inherited=add_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
if add_kwargs.get("group"):
|
||||||
|
identity_client.assign_domain_role_to_group(
|
||||||
|
domain=add_kwargs["domain"],
|
||||||
|
group=add_kwargs["group"],
|
||||||
|
role=role,
|
||||||
|
inherited=add_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
elif add_kwargs.get("project"):
|
||||||
|
if add_kwargs.get("user"):
|
||||||
|
identity_client.assign_project_role_to_user(
|
||||||
|
project=add_kwargs["project"],
|
||||||
|
user=add_kwargs["user"],
|
||||||
|
role=role,
|
||||||
|
inherited=add_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
if add_kwargs.get("group"):
|
||||||
|
identity_client.assign_project_role_to_group(
|
||||||
|
project=add_kwargs["project"],
|
||||||
|
group=add_kwargs["group"],
|
||||||
|
role=role,
|
||||||
|
inherited=add_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
elif add_kwargs.get("system"):
|
||||||
|
if add_kwargs["inherited"]:
|
||||||
|
LOG.warning(
|
||||||
|
_(
|
||||||
|
"'--inherited' was given, which is not supported "
|
||||||
|
"when adding a system role. This will be an error "
|
||||||
|
"in a future release."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# TODO(0weng): This should be an error in a future release
|
||||||
|
if add_kwargs.get("user"):
|
||||||
|
identity_client.assign_system_role_to_user(
|
||||||
|
system=add_kwargs["system"],
|
||||||
|
user=add_kwargs["user"],
|
||||||
|
role=role,
|
||||||
|
)
|
||||||
|
if add_kwargs.get("group"):
|
||||||
|
identity_client.assign_system_role_to_group(
|
||||||
|
system=add_kwargs["system"],
|
||||||
|
group=add_kwargs["group"],
|
||||||
|
role=role,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateRole(command.ShowOne):
|
class CreateRole(command.ShowOne):
|
||||||
|
|
@ -204,37 +304,38 @@ class CreateRole(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
domain_id = None
|
create_kwargs = {}
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain_id = common.find_domain(
|
create_kwargs['domain_id'] = common._find_sdk_id(
|
||||||
identity_client, parsed_args.domain
|
identity_client.find_domain, name_or_id=parsed_args.domain
|
||||||
).id
|
|
||||||
|
|
||||||
options = common.get_immutable_options(parsed_args)
|
|
||||||
|
|
||||||
try:
|
|
||||||
role = identity_client.roles.create(
|
|
||||||
name=parsed_args.name,
|
|
||||||
domain=domain_id,
|
|
||||||
description=parsed_args.description,
|
|
||||||
options=options,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
except ks_exc.Conflict:
|
if parsed_args.name:
|
||||||
|
create_kwargs['name'] = parsed_args.name
|
||||||
|
|
||||||
|
if parsed_args.description:
|
||||||
|
create_kwargs['description'] = parsed_args.description
|
||||||
|
|
||||||
|
if parsed_args.immutable is not None:
|
||||||
|
create_kwargs['options'] = {"immutable": parsed_args.immutable}
|
||||||
|
|
||||||
|
try:
|
||||||
|
role = identity_client.create_role(**create_kwargs)
|
||||||
|
|
||||||
|
except sdk_exc.ConflictException:
|
||||||
if parsed_args.or_show:
|
if parsed_args.or_show:
|
||||||
role = utils.find_resource(
|
role = identity_client.find_role(
|
||||||
identity_client.roles,
|
name_or_id=parsed_args.name,
|
||||||
parsed_args.name,
|
domain_id=parsed_args.domain,
|
||||||
domain_id=domain_id,
|
ignore_missing=False,
|
||||||
)
|
)
|
||||||
LOG.info(_('Returning existing role %s'), role.name)
|
LOG.info(_('Returning existing role %s'), role.name)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
role._info.pop('links')
|
return _format_role(role)
|
||||||
return zip(*sorted(role._info.items()))
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteRole(command.Command):
|
class DeleteRole(command.Command):
|
||||||
|
|
@ -245,7 +346,7 @@ class DeleteRole(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'roles',
|
'roles',
|
||||||
metavar='<role>',
|
metavar='<role>',
|
||||||
nargs="+",
|
nargs='+',
|
||||||
help=_('Role(s) to delete (name or ID)'),
|
help=_('Role(s) to delete (name or ID)'),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -256,20 +357,22 @@ class DeleteRole(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain_id = common.find_domain(
|
domain_id = common._find_sdk_id(
|
||||||
identity_client, parsed_args.domain
|
identity_client.find_domain, parsed_args.domain
|
||||||
).id
|
)
|
||||||
errors = 0
|
errors = 0
|
||||||
for role in parsed_args.roles:
|
for role in parsed_args.roles:
|
||||||
try:
|
try:
|
||||||
role_obj = utils.find_resource(
|
role_id = common._find_sdk_id(
|
||||||
identity_client.roles, role, domain_id=domain_id
|
identity_client.find_role,
|
||||||
|
name_or_id=role,
|
||||||
|
domain_id=domain_id,
|
||||||
)
|
)
|
||||||
identity_client.roles.delete(role_obj.id)
|
identity_client.delete_role(role=role_id, ignore_missing=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errors += 1
|
errors += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|
@ -282,7 +385,7 @@ class DeleteRole(command.Command):
|
||||||
|
|
||||||
if errors > 0:
|
if errors > 0:
|
||||||
total = len(parsed_args.roles)
|
total = len(parsed_args.roles)
|
||||||
msg = _("%(errors)s of %(total)s roles failed " "to delete.") % {
|
msg = _("%(errors)s of %(total)s roles failed to delete.") % {
|
||||||
'errors': errors,
|
'errors': errors,
|
||||||
'total': total,
|
'total': total,
|
||||||
}
|
}
|
||||||
|
|
@ -302,37 +405,34 @@ class ListRole(command.Lister):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain = common.find_domain(
|
domain = identity_client.find_domain(
|
||||||
identity_client,
|
name_or_id=parsed_args.domain,
|
||||||
parsed_args.domain,
|
ignore_missing=False,
|
||||||
|
)
|
||||||
|
data = identity_client.roles(domain_id=domain.id)
|
||||||
|
return (
|
||||||
|
('ID', 'Name', 'Domain'),
|
||||||
|
(
|
||||||
|
utils.get_item_properties(s, ('id', 'name'))
|
||||||
|
+ (domain.name,)
|
||||||
|
for s in data
|
||||||
|
),
|
||||||
)
|
)
|
||||||
columns = ('ID', 'Name', 'Domain')
|
|
||||||
data = identity_client.roles.list(domain_id=domain.id)
|
|
||||||
for role in data:
|
|
||||||
role.domain = domain.name
|
|
||||||
else:
|
|
||||||
columns = ('ID', 'Name')
|
|
||||||
data = identity_client.roles.list()
|
|
||||||
|
|
||||||
return (
|
else:
|
||||||
columns,
|
data = identity_client.roles()
|
||||||
(
|
return (
|
||||||
utils.get_item_properties(
|
('ID', 'Name'),
|
||||||
s,
|
(utils.get_item_properties(s, ('id', 'name')) for s in data),
|
||||||
columns,
|
)
|
||||||
formatters={},
|
|
||||||
)
|
|
||||||
for s in data
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveRole(command.Command):
|
class RemoveRole(command.Command):
|
||||||
_description = _(
|
_description = _(
|
||||||
"Removes a role assignment from system/domain/project : " "user/group"
|
"Removes a role assignment from system/domain/project : user/group"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
|
|
@ -348,8 +448,7 @@ class RemoveRole(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not parsed_args.user
|
not parsed_args.user
|
||||||
and not parsed_args.domain
|
and not parsed_args.domain
|
||||||
|
|
@ -364,19 +463,65 @@ class RemoveRole(command.Command):
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.role_domain:
|
if parsed_args.role_domain:
|
||||||
domain_id = common.find_domain(
|
domain_id = common._find_sdk_id(
|
||||||
identity_client, parsed_args.role_domain
|
identity_client.find_domain,
|
||||||
).id
|
name_or_id=parsed_args.role_domain,
|
||||||
role = utils.find_resource(
|
)
|
||||||
identity_client.roles, parsed_args.role, domain_id=domain_id
|
role = common._find_sdk_id(
|
||||||
|
identity_client.find_role,
|
||||||
|
name_or_id=parsed_args.role,
|
||||||
|
domain_id=domain_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
kwargs = _process_identity_and_resource_options(
|
remove_kwargs = _process_identity_and_resource_options(
|
||||||
parsed_args,
|
parsed_args,
|
||||||
self.app.client_manager.identity,
|
identity_client,
|
||||||
validate_actor_existence=False,
|
validate_actor_existence=False,
|
||||||
)
|
)
|
||||||
identity_client.roles.revoke(role.id, **kwargs)
|
|
||||||
|
if remove_kwargs.get("domain"):
|
||||||
|
if remove_kwargs.get("user"):
|
||||||
|
identity_client.unassign_domain_role_from_user(
|
||||||
|
domain=remove_kwargs["domain"],
|
||||||
|
user=remove_kwargs["user"],
|
||||||
|
role=role,
|
||||||
|
inherited=remove_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
if remove_kwargs.get("group"):
|
||||||
|
identity_client.unassign_domain_role_from_group(
|
||||||
|
domain=remove_kwargs["domain"],
|
||||||
|
group=remove_kwargs["group"],
|
||||||
|
role=role,
|
||||||
|
inherited=remove_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
elif remove_kwargs.get("project"):
|
||||||
|
if remove_kwargs.get("user"):
|
||||||
|
identity_client.unassign_project_role_from_user(
|
||||||
|
project=remove_kwargs["project"],
|
||||||
|
user=remove_kwargs["user"],
|
||||||
|
role=role,
|
||||||
|
inherited=remove_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
if remove_kwargs.get("group"):
|
||||||
|
identity_client.unassign_project_role_from_group(
|
||||||
|
project=remove_kwargs["project"],
|
||||||
|
group=remove_kwargs["group"],
|
||||||
|
role=role,
|
||||||
|
inherited=remove_kwargs["inherited"],
|
||||||
|
)
|
||||||
|
elif remove_kwargs.get("system"):
|
||||||
|
if remove_kwargs.get("user"):
|
||||||
|
identity_client.unassign_system_role_from_user(
|
||||||
|
system=remove_kwargs["system"],
|
||||||
|
user=remove_kwargs["user"],
|
||||||
|
role=role,
|
||||||
|
)
|
||||||
|
if remove_kwargs.get("group"):
|
||||||
|
identity_client.unassign_system_role_from_group(
|
||||||
|
system=remove_kwargs["system"],
|
||||||
|
group=remove_kwargs["group"],
|
||||||
|
role=role,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SetRole(command.Command):
|
class SetRole(command.Command):
|
||||||
|
|
@ -408,25 +553,33 @@ class SetRole(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
|
update_kwargs = {}
|
||||||
|
if parsed_args.description:
|
||||||
|
update_kwargs["description"] = parsed_args.description
|
||||||
|
if parsed_args.name:
|
||||||
|
update_kwargs["name"] = parsed_args.name
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain_id = common.find_domain(
|
domain_id = common._find_sdk_id(
|
||||||
identity_client, parsed_args.domain
|
identity_client.find_domain,
|
||||||
).id
|
name_or_id=parsed_args.domain,
|
||||||
|
)
|
||||||
|
update_kwargs["domain_id"] = domain_id
|
||||||
|
|
||||||
options = common.get_immutable_options(parsed_args)
|
if parsed_args.immutable is not None:
|
||||||
role = utils.find_resource(
|
update_kwargs["options"] = {"immutable": parsed_args.immutable}
|
||||||
identity_client.roles, parsed_args.role, domain_id=domain_id
|
|
||||||
)
|
|
||||||
|
|
||||||
identity_client.roles.update(
|
role = common._find_sdk_id(
|
||||||
role.id,
|
identity_client.find_role,
|
||||||
name=parsed_args.name,
|
name_or_id=parsed_args.role,
|
||||||
description=parsed_args.description,
|
domain_id=domain_id,
|
||||||
options=options,
|
|
||||||
)
|
)
|
||||||
|
update_kwargs["role"] = role
|
||||||
|
|
||||||
|
identity_client.update_role(**update_kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ShowRole(command.ShowOne):
|
class ShowRole(command.ShowOne):
|
||||||
|
|
@ -447,17 +600,19 @@ class ShowRole(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain_id = common.find_domain(
|
domain_id = common._find_sdk_id(
|
||||||
identity_client, parsed_args.domain
|
identity_client.find_domain,
|
||||||
).id
|
name_or_id=parsed_args.domain,
|
||||||
|
)
|
||||||
|
|
||||||
role = utils.find_resource(
|
role = identity_client.find_role(
|
||||||
identity_client.roles, parsed_args.role, domain_id=domain_id
|
name_or_id=parsed_args.role,
|
||||||
|
domain_id=domain_id,
|
||||||
|
ignore_missing=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
role._info.pop('links')
|
return _format_role(role)
|
||||||
return zip(*sorted(role._info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,7 @@
|
||||||
|
|
||||||
"""Identity v3 Assignment action implementations"""
|
"""Identity v3 Assignment action implementations"""
|
||||||
|
|
||||||
from openstack import exceptions as sdk_exceptions
|
from openstackclient import command
|
||||||
from osc_lib.command import command
|
|
||||||
|
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -51,15 +49,6 @@ def _format_role_assignment_(assignment, include_names):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _find_sdk_id(find_command, name_or_id, **kwargs):
|
|
||||||
try:
|
|
||||||
return find_command(
|
|
||||||
name_or_id=name_or_id, ignore_missing=False, **kwargs
|
|
||||||
).id
|
|
||||||
except sdk_exceptions.ForbiddenException:
|
|
||||||
return name_or_id
|
|
||||||
|
|
||||||
|
|
||||||
class ListRoleAssignment(command.Lister):
|
class ListRoleAssignment(command.Lister):
|
||||||
_description = _("List role assignments")
|
_description = _("List role assignments")
|
||||||
|
|
||||||
|
|
@ -135,12 +124,12 @@ class ListRoleAssignment(command.Lister):
|
||||||
role_id = None
|
role_id = None
|
||||||
role_domain_id = None
|
role_domain_id = None
|
||||||
if parsed_args.role_domain:
|
if parsed_args.role_domain:
|
||||||
role_domain_id = _find_sdk_id(
|
role_domain_id = common._find_sdk_id(
|
||||||
identity_client.find_domain,
|
identity_client.find_domain,
|
||||||
name_or_id=parsed_args.role_domain,
|
name_or_id=parsed_args.role_domain,
|
||||||
)
|
)
|
||||||
if parsed_args.role:
|
if parsed_args.role:
|
||||||
role_id = _find_sdk_id(
|
role_id = common._find_sdk_id(
|
||||||
identity_client.find_role,
|
identity_client.find_role,
|
||||||
name_or_id=parsed_args.role,
|
name_or_id=parsed_args.role,
|
||||||
domain_id=role_domain_id,
|
domain_id=role_domain_id,
|
||||||
|
|
@ -148,21 +137,21 @@ class ListRoleAssignment(command.Lister):
|
||||||
|
|
||||||
user_domain_id = None
|
user_domain_id = None
|
||||||
if parsed_args.user_domain:
|
if parsed_args.user_domain:
|
||||||
project_domain_id = _find_sdk_id(
|
user_domain_id = common._find_sdk_id(
|
||||||
identity_client.find_domain,
|
identity_client.find_domain,
|
||||||
name_or_id=parsed_args.user_domain,
|
name_or_id=parsed_args.user_domain,
|
||||||
)
|
)
|
||||||
|
|
||||||
user_id = None
|
user_id = None
|
||||||
if parsed_args.user:
|
if parsed_args.user:
|
||||||
user_id = _find_sdk_id(
|
user_id = common._find_sdk_id(
|
||||||
identity_client.find_user,
|
identity_client.find_user,
|
||||||
name_or_id=parsed_args.user,
|
name_or_id=parsed_args.user,
|
||||||
domain_id=user_domain_id,
|
domain_id=user_domain_id,
|
||||||
)
|
)
|
||||||
elif parsed_args.authuser:
|
elif parsed_args.authuser:
|
||||||
if auth_ref:
|
if auth_ref:
|
||||||
user_id = _find_sdk_id(
|
user_id = common._find_sdk_id(
|
||||||
identity_client.find_user,
|
identity_client.find_user,
|
||||||
name_or_id=auth_ref.user_id,
|
name_or_id=auth_ref.user_id,
|
||||||
)
|
)
|
||||||
|
|
@ -173,21 +162,21 @@ class ListRoleAssignment(command.Lister):
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if parsed_args.domain:
|
if parsed_args.domain:
|
||||||
domain_id = _find_sdk_id(
|
domain_id = common._find_sdk_id(
|
||||||
identity_client.find_domain,
|
identity_client.find_domain,
|
||||||
name_or_id=parsed_args.domain,
|
name_or_id=parsed_args.domain,
|
||||||
)
|
)
|
||||||
|
|
||||||
project_domain_id = None
|
project_domain_id = None
|
||||||
if parsed_args.project_domain:
|
if parsed_args.project_domain:
|
||||||
project_domain_id = _find_sdk_id(
|
project_domain_id = common._find_sdk_id(
|
||||||
identity_client.find_domain,
|
identity_client.find_domain,
|
||||||
name_or_id=parsed_args.project_domain,
|
name_or_id=parsed_args.project_domain,
|
||||||
)
|
)
|
||||||
|
|
||||||
project_id = None
|
project_id = None
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
project_id = _find_sdk_id(
|
project_id = common._find_sdk_id(
|
||||||
identity_client.find_project,
|
identity_client.find_project,
|
||||||
name_or_id=common._get_token_resource(
|
name_or_id=common._get_token_resource(
|
||||||
identity_client, 'project', parsed_args.project
|
identity_client, 'project', parsed_args.project
|
||||||
|
|
@ -196,21 +185,21 @@ class ListRoleAssignment(command.Lister):
|
||||||
)
|
)
|
||||||
elif parsed_args.authproject:
|
elif parsed_args.authproject:
|
||||||
if auth_ref:
|
if auth_ref:
|
||||||
project_id = _find_sdk_id(
|
project_id = common._find_sdk_id(
|
||||||
identity_client.find_project,
|
identity_client.find_project,
|
||||||
name_or_id=auth_ref.project_id,
|
name_or_id=auth_ref.project_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
group_domain_id = None
|
group_domain_id = None
|
||||||
if parsed_args.group_domain:
|
if parsed_args.group_domain:
|
||||||
group_domain_id = _find_sdk_id(
|
group_domain_id = common._find_sdk_id(
|
||||||
identity_client.find_domain,
|
identity_client.find_domain,
|
||||||
name_or_id=parsed_args.group_domain,
|
name_or_id=parsed_args.group_domain,
|
||||||
)
|
)
|
||||||
|
|
||||||
group_id = None
|
group_id = None
|
||||||
if parsed_args.group:
|
if parsed_args.group:
|
||||||
group_id = _find_sdk_id(
|
group_id = common._find_sdk_id(
|
||||||
identity_client.find_group,
|
identity_client.find_group,
|
||||||
name_or_id=parsed_args.group,
|
name_or_id=parsed_args.group,
|
||||||
domain_id=group_domain_id,
|
domain_id=group_domain_id,
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
from openstackclient.identity import common
|
from openstackclient.identity import common
|
||||||
|
|
||||||
|
|
@ -135,9 +135,10 @@ class DeleteService(command.Command):
|
||||||
|
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.service)
|
total = len(parsed_args.service)
|
||||||
msg = _(
|
msg = _("%(result)s of %(total)s services failed to delete.") % {
|
||||||
"%(result)s of %(total)s services failed " "to delete."
|
'result': result,
|
||||||
) % {'result': result, 'total': total}
|
'total': total,
|
||||||
|
}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -157,12 +158,11 @@ class ListService(command.Lister):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
identity_client = self.app.client_manager.sdk_connection.identity
|
identity_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
|
columns: tuple[str, ...] = ('id', 'name', 'type')
|
||||||
|
column_headers: tuple[str, ...] = ('ID', 'Name', 'Type')
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('id', 'name', 'type', 'description', 'is_enabled')
|
columns += ('description', 'is_enabled')
|
||||||
column_headers = ('ID', 'Name', 'Type', 'Description', 'Enabled')
|
column_headers += ('Description', 'Enabled')
|
||||||
else:
|
|
||||||
columns = ('id', 'name', 'type')
|
|
||||||
column_headers = ('ID', 'Name', 'Type')
|
|
||||||
|
|
||||||
data = identity_client.services()
|
data = identity_client.services()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,39 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from osc_lib.command import command
|
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient import command
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_service_provider(sp):
|
||||||
|
column_headers = (
|
||||||
|
'id',
|
||||||
|
'enabled',
|
||||||
|
'description',
|
||||||
|
'auth_url',
|
||||||
|
'sp_url',
|
||||||
|
'relay_state_prefix',
|
||||||
|
)
|
||||||
|
columns = (
|
||||||
|
'id',
|
||||||
|
'is_enabled',
|
||||||
|
'description',
|
||||||
|
'auth_url',
|
||||||
|
'sp_url',
|
||||||
|
'relay_state_prefix',
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
column_headers,
|
||||||
|
utils.get_item_properties(sp, columns),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateServiceProvider(command.ShowOne):
|
class CreateServiceProvider(command.ShowOne):
|
||||||
_description = _("Create new service provider")
|
_description = _("Create new service provider")
|
||||||
|
|
||||||
|
|
@ -54,22 +77,21 @@ class CreateServiceProvider(command.ShowOne):
|
||||||
metavar='<sp-url>',
|
metavar='<sp-url>',
|
||||||
required=True,
|
required=True,
|
||||||
help=_(
|
help=_(
|
||||||
'A service URL where SAML assertions are being sent '
|
'A service URL where SAML assertions are being sent (required)'
|
||||||
'(required)'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
enable_service_provider = parser.add_mutually_exclusive_group()
|
enable_service_provider = parser.add_mutually_exclusive_group()
|
||||||
enable_service_provider.add_argument(
|
enable_service_provider.add_argument(
|
||||||
'--enable',
|
'--enable',
|
||||||
dest='enabled',
|
dest='is_enabled',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=True,
|
default=True,
|
||||||
help=_('Enable the service provider (default)'),
|
help=_('Enable the service provider (default)'),
|
||||||
)
|
)
|
||||||
enable_service_provider.add_argument(
|
enable_service_provider.add_argument(
|
||||||
'--disable',
|
'--disable',
|
||||||
dest='enabled',
|
dest='is_enabled',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
help=_('Disable the service provider'),
|
help=_('Disable the service provider'),
|
||||||
)
|
)
|
||||||
|
|
@ -77,17 +99,27 @@ class CreateServiceProvider(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
service_client = self.app.client_manager.identity
|
service_client = self.app.client_manager.sdk_connection.identity
|
||||||
sp = service_client.federation.service_providers.create(
|
|
||||||
id=parsed_args.service_provider_id,
|
|
||||||
auth_url=parsed_args.auth_url,
|
|
||||||
description=parsed_args.description,
|
|
||||||
enabled=parsed_args.enabled,
|
|
||||||
sp_url=parsed_args.service_provider_url,
|
|
||||||
)
|
|
||||||
|
|
||||||
sp._info.pop('links', None)
|
kwargs = {}
|
||||||
return zip(*sorted(sp._info.items()))
|
|
||||||
|
kwargs = {'id': parsed_args.service_provider_id}
|
||||||
|
|
||||||
|
if parsed_args.is_enabled is not None:
|
||||||
|
kwargs['is_enabled'] = parsed_args.is_enabled
|
||||||
|
|
||||||
|
if parsed_args.description:
|
||||||
|
kwargs['description'] = parsed_args.description
|
||||||
|
|
||||||
|
if parsed_args.auth_url:
|
||||||
|
kwargs['auth_url'] = parsed_args.auth_url
|
||||||
|
|
||||||
|
if parsed_args.service_provider_url:
|
||||||
|
kwargs['sp_url'] = parsed_args.service_provider_url
|
||||||
|
|
||||||
|
sp = service_client.create_service_provider(**kwargs)
|
||||||
|
|
||||||
|
return _format_service_provider(sp)
|
||||||
|
|
||||||
|
|
||||||
class DeleteServiceProvider(command.Command):
|
class DeleteServiceProvider(command.Command):
|
||||||
|
|
@ -104,11 +136,11 @@ class DeleteServiceProvider(command.Command):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
service_client = self.app.client_manager.identity
|
service_client = self.app.client_manager.sdk_connection.identity
|
||||||
result = 0
|
result = 0
|
||||||
for i in parsed_args.service_provider:
|
for i in parsed_args.service_provider:
|
||||||
try:
|
try:
|
||||||
service_client.federation.service_providers.delete(i)
|
service_client.delete_service_provider(i)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(
|
LOG.error(
|
||||||
|
|
@ -122,8 +154,7 @@ class DeleteServiceProvider(command.Command):
|
||||||
if result > 0:
|
if result > 0:
|
||||||
total = len(parsed_args.service_provider)
|
total = len(parsed_args.service_provider)
|
||||||
msg = _(
|
msg = _(
|
||||||
"%(result)s of %(total)s service providers failed"
|
"%(result)s of %(total)s service providers failed to delete."
|
||||||
" to delete."
|
|
||||||
) % {'result': result, 'total': total}
|
) % {'result': result, 'total': total}
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
|
@ -132,24 +163,32 @@ class ListServiceProvider(command.Lister):
|
||||||
_description = _("List service providers")
|
_description = _("List service providers")
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
service_client = self.app.client_manager.identity
|
service_client = self.app.client_manager.sdk_connection.identity
|
||||||
data = service_client.federation.service_providers.list()
|
data = service_client.service_providers()
|
||||||
|
|
||||||
column_headers = ('ID', 'Enabled', 'Description', 'Auth URL')
|
column_headers = (
|
||||||
|
'ID',
|
||||||
|
'Enabled',
|
||||||
|
'Description',
|
||||||
|
'Auth URL',
|
||||||
|
'Service Provider URL',
|
||||||
|
'Relay State Prefix',
|
||||||
|
)
|
||||||
|
columns = (
|
||||||
|
'id',
|
||||||
|
'is_enabled',
|
||||||
|
'description',
|
||||||
|
'auth_url',
|
||||||
|
'sp_url',
|
||||||
|
'relay_state_prefix',
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
column_headers,
|
column_headers,
|
||||||
(
|
(utils.get_item_properties(s, columns) for s in data),
|
||||||
utils.get_item_properties(
|
|
||||||
s,
|
|
||||||
column_headers,
|
|
||||||
formatters={},
|
|
||||||
)
|
|
||||||
for s in data
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SetServiceProvider(command.Command):
|
class SetServiceProvider(command.ShowOne):
|
||||||
_description = _("Set service provider properties")
|
_description = _("Set service provider properties")
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
|
|
@ -163,8 +202,7 @@ class SetServiceProvider(command.Command):
|
||||||
'--auth-url',
|
'--auth-url',
|
||||||
metavar='<auth-url>',
|
metavar='<auth-url>',
|
||||||
help=_(
|
help=_(
|
||||||
'New Authentication URL of remote '
|
'New Authentication URL of remote federated service provider'
|
||||||
'federated service provider'
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -181,33 +219,44 @@ class SetServiceProvider(command.Command):
|
||||||
enable_service_provider = parser.add_mutually_exclusive_group()
|
enable_service_provider = parser.add_mutually_exclusive_group()
|
||||||
enable_service_provider.add_argument(
|
enable_service_provider.add_argument(
|
||||||
'--enable',
|
'--enable',
|
||||||
|
dest='is_enabled',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
default=None,
|
||||||
help=_('Enable the service provider'),
|
help=_('Enable the service provider'),
|
||||||
)
|
)
|
||||||
enable_service_provider.add_argument(
|
enable_service_provider.add_argument(
|
||||||
'--disable',
|
'--disable',
|
||||||
action='store_true',
|
dest='is_enabled',
|
||||||
|
action='store_false',
|
||||||
|
default=None,
|
||||||
help=_('Disable the service provider'),
|
help=_('Disable the service provider'),
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
federation_client = self.app.client_manager.identity.federation
|
service_client = self.app.client_manager.sdk_connection.identity
|
||||||
|
|
||||||
enabled = None
|
kwargs = {}
|
||||||
if parsed_args.enable is True:
|
|
||||||
enabled = True
|
|
||||||
elif parsed_args.disable is True:
|
|
||||||
enabled = False
|
|
||||||
|
|
||||||
federation_client.service_providers.update(
|
if parsed_args.is_enabled is not None:
|
||||||
|
kwargs['is_enabled'] = parsed_args.is_enabled
|
||||||
|
|
||||||
|
if parsed_args.description:
|
||||||
|
kwargs['description'] = parsed_args.description
|
||||||
|
|
||||||
|
if parsed_args.auth_url:
|
||||||
|
kwargs['auth_url'] = parsed_args.auth_url
|
||||||
|
|
||||||
|
if parsed_args.service_provider_url:
|
||||||
|
kwargs['sp_url'] = parsed_args.service_provider_url
|
||||||
|
|
||||||
|
service_provider = service_client.update_service_provider(
|
||||||
parsed_args.service_provider,
|
parsed_args.service_provider,
|
||||||
enabled=enabled,
|
**kwargs,
|
||||||
description=parsed_args.description,
|
|
||||||
auth_url=parsed_args.auth_url,
|
|
||||||
sp_url=parsed_args.service_provider_url,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return _format_service_provider(service_provider)
|
||||||
|
|
||||||
|
|
||||||
class ShowServiceProvider(command.ShowOne):
|
class ShowServiceProvider(command.ShowOne):
|
||||||
_description = _("Display service provider details")
|
_description = _("Display service provider details")
|
||||||
|
|
@ -222,12 +271,10 @@ class ShowServiceProvider(command.ShowOne):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
service_client = self.app.client_manager.identity
|
service_client = self.app.client_manager.sdk_connection.identity
|
||||||
service_provider = utils.find_resource(
|
service_provider = service_client.find_service_provider(
|
||||||
service_client.federation.service_providers,
|
|
||||||
parsed_args.service_provider,
|
parsed_args.service_provider,
|
||||||
id=parsed_args.service_provider,
|
ignore_missing=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
service_provider._info.pop('links', None)
|
return _format_service_provider(service_provider)
|
||||||
return zip(*sorted(service_provider._info.items()))
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ def add_tag_option_to_parser_for_create(parser, resource_name):
|
||||||
metavar='<tag>',
|
metavar='<tag>',
|
||||||
default=[],
|
default=[],
|
||||||
help=_(
|
help=_(
|
||||||
'Tag to be added to the %s ' '(repeat option to set multiple tags)'
|
'Tag to be added to the %s (repeat option to set multiple tags)'
|
||||||
)
|
)
|
||||||
% resource_name,
|
% resource_name,
|
||||||
)
|
)
|
||||||
|
|
@ -97,7 +97,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name):
|
||||||
metavar='<tag>',
|
metavar='<tag>',
|
||||||
default=[],
|
default=[],
|
||||||
help=_(
|
help=_(
|
||||||
'Tag to be added to the %s ' '(repeat option to set multiple tags)'
|
'Tag to be added to the %s (repeat option to set multiple tags)'
|
||||||
)
|
)
|
||||||
% resource_name,
|
% resource_name,
|
||||||
)
|
)
|
||||||
|
|
@ -114,6 +114,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--remove-tag',
|
'--remove-tag',
|
||||||
action='append',
|
action='append',
|
||||||
|
dest='remove_tags',
|
||||||
metavar='<tag>',
|
metavar='<tag>',
|
||||||
default=[],
|
default=[],
|
||||||
help=_(
|
help=_(
|
||||||
|
|
@ -128,8 +129,8 @@ def update_tags_in_args(parsed_args, obj, args):
|
||||||
if parsed_args.clear_tags:
|
if parsed_args.clear_tags:
|
||||||
args['tags'] = []
|
args['tags'] = []
|
||||||
obj.tags = []
|
obj.tags = []
|
||||||
if parsed_args.remove_tag:
|
if parsed_args.remove_tags:
|
||||||
args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tag))
|
args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tags))
|
||||||
return
|
return
|
||||||
if parsed_args.tags:
|
if parsed_args.tags:
|
||||||
args['tags'] = sorted(set(obj.tags).union(set(parsed_args.tags)))
|
args['tags'] = sorted(set(obj.tags).union(set(parsed_args.tags)))
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue