summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Bryant <corey.bryant@canonical.com>2019-09-25 14:23:09 -0400
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2019-09-26 00:18:33 +0000
commitfddfc0e68940906cce3341f47a97af27d99e4b47 (patch)
tree0b906d0c52205f5ff17f3b2aa67c1b41a19c4796
parent1488118c4720744447e6fb460944d5a0221ab44a (diff)
Imported using git-ubuntu import.
Notes
Notes: * New upstream release for OpenStack Train. * d/control: Align (Build-)Depends with upstream.
-rw-r--r--.zuul.yaml3
-rw-r--r--AUTHORS5
-rw-r--r--ChangeLog57
-rw-r--r--PKG-INFO19
-rw-r--r--README.rst16
-rw-r--r--debian/changelog7
-rw-r--r--debian/control12
-rw-r--r--doc/source/cli/authentication.rst11
-rw-r--r--doc/source/cli/backwards-incompatible.rst129
-rw-r--r--doc/source/cli/command-objects/backup.rst137
-rw-r--r--doc/source/cli/command-objects/compute-service.rst18
-rw-r--r--doc/source/cli/command-objects/endpoint.rst2
-rw-r--r--doc/source/cli/command-objects/floating-ip-pool.rst2
-rw-r--r--doc/source/cli/command-objects/floating-ip-port-forwarding.rst173
-rw-r--r--doc/source/cli/command-objects/floating-ip.rst2
-rw-r--r--doc/source/cli/command-objects/ip-fixed.rst47
-rw-r--r--doc/source/cli/command-objects/role.rst58
-rw-r--r--doc/source/cli/command-objects/security-group-rule.rst19
-rw-r--r--doc/source/cli/command-objects/snapshot.rst176
-rw-r--r--doc/source/cli/command-objects/usage.rst3
-rw-r--r--doc/source/cli/command-objects/user-role.rst27
-rw-r--r--doc/source/cli/commands.rst9
-rw-r--r--doc/source/cli/data/glance.csv48
-rw-r--r--doc/source/index.rst3
-rw-r--r--lower-constraints.txt13
-rw-r--r--openstackclient/api/auth_plugin.py61
-rw-r--r--openstackclient/api/compute_v2.py38
-rw-r--r--openstackclient/common/client_config.py71
-rw-r--r--openstackclient/common/configuration.py20
-rw-r--r--openstackclient/compute/v2/aggregate.py46
-rw-r--r--openstackclient/compute/v2/fixedip.py100
-rw-r--r--openstackclient/compute/v2/flavor.py2
-rw-r--r--openstackclient/compute/v2/floatingip.py97
-rw-r--r--openstackclient/compute/v2/hypervisor.py2
-rw-r--r--openstackclient/compute/v2/server.py321
-rw-r--r--openstackclient/compute/v2/server_event.py12
-rw-r--r--openstackclient/compute/v2/service.py86
-rw-r--r--openstackclient/identity/v2_0/catalog.py34
-rw-r--r--openstackclient/identity/v2_0/project.py3
-rw-r--r--openstackclient/identity/v2_0/role.py148
-rw-r--r--openstackclient/identity/v2_0/service.py30
-rw-r--r--openstackclient/identity/v2_0/user.py41
-rw-r--r--openstackclient/identity/v3/catalog.py24
-rw-r--r--openstackclient/identity/v3/identity_provider.py5
-rw-r--r--openstackclient/identity/v3/role.py125
-rw-r--r--openstackclient/image/v1/image.py63
-rw-r--r--openstackclient/image/v2/image.py56
-rw-r--r--openstackclient/network/v2/address_scope.py10
-rw-r--r--openstackclient/network/v2/floating_ip.py2
-rw-r--r--openstackclient/network/v2/floating_ip_port_forwarding.py371
-rw-r--r--openstackclient/network/v2/ip_availability.py5
-rw-r--r--openstackclient/network/v2/network.py31
-rw-r--r--openstackclient/network/v2/network_agent.py24
-rw-r--r--openstackclient/network/v2/network_auto_allocated_topology.py17
-rw-r--r--openstackclient/network/v2/network_flavor.py10
-rw-r--r--openstackclient/network/v2/network_flavor_profile.py10
-rw-r--r--openstackclient/network/v2/network_meter.py10
-rw-r--r--openstackclient/network/v2/network_meter_rule.py10
-rw-r--r--openstackclient/network/v2/network_qos_policy.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule_type.py8
-rw-r--r--openstackclient/network/v2/network_rbac.py10
-rw-r--r--openstackclient/network/v2/network_segment.py10
-rw-r--r--openstackclient/network/v2/network_segment_range.py11
-rw-r--r--openstackclient/network/v2/port.py97
-rw-r--r--openstackclient/network/v2/router.py67
-rw-r--r--openstackclient/network/v2/security_group.py18
-rw-r--r--openstackclient/network/v2/security_group_rule.py134
-rw-r--r--openstackclient/network/v2/subnet.py31
-rw-r--r--openstackclient/network/v2/subnet_pool.py6
-rw-r--r--openstackclient/object/v1/account.py5
-rw-r--r--openstackclient/object/v1/container.py3
-rw-r--r--openstackclient/object/v1/object.py3
-rw-r--r--openstackclient/shell.py42
-rw-r--r--openstackclient/tests/functional/base.py30
-rw-r--r--openstackclient/tests/functional/common/test_args.py79
-rw-r--r--openstackclient/tests/functional/common/test_configuration.py27
-rw-r--r--openstackclient/tests/functional/compute/v2/test_aggregate.py57
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server.py165
-rw-r--r--openstackclient/tests/functional/identity/v2/test_role.py32
-rw-r--r--openstackclient/tests/functional/identity/v3/test_role.py41
-rw-r--r--openstackclient/tests/functional/image/v1/test_image.py2
-rw-r--r--openstackclient/tests/functional/image/v2/test_image.py21
-rw-r--r--openstackclient/tests/functional/network/v2/common.py14
-rw-r--r--openstackclient/tests/functional/network/v2/test_floating_ip.py120
-rw-r--r--openstackclient/tests/functional/network/v2/test_network.py18
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_agent.py4
-rw-r--r--openstackclient/tests/functional/network/v2/test_port.py29
-rw-r--r--openstackclient/tests/functional/network/v2/test_router.py6
-rw-r--r--openstackclient/tests/functional/network/v2/test_subnet.py4
-rw-r--r--openstackclient/tests/functional/network/v2/test_subnet_pool.py16
-rw-r--r--openstackclient/tests/functional/volume/v1/test_qos.py4
-rw-r--r--openstackclient/tests/functional/volume/v1/test_snapshot.py9
-rw-r--r--openstackclient/tests/functional/volume/v1/test_volume.py6
-rw-r--r--openstackclient/tests/functional/volume/v1/test_volume_type.py61
-rw-r--r--openstackclient/tests/functional/volume/v2/test_qos.py6
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume.py6
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_backup.py (renamed from openstackclient/tests/functional/volume/v2/test_backup.py)4
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_snapshot.py (renamed from openstackclient/tests/functional/volume/v2/test_snapshot.py)8
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_type.py74
-rw-r--r--openstackclient/tests/functional/volume/v3/test_volume_snapshot.py (renamed from openstackclient/tests/functional/volume/v3/test_snapshot.py)2
-rw-r--r--openstackclient/tests/unit/api/test_object_store_v1.py2
-rw-r--r--openstackclient/tests/unit/common/test_clientmanager.py8
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py2
-rw-r--r--openstackclient/tests/unit/compute/v2/test_aggregate.py41
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py615
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_backup.py9
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_image.py9
-rw-r--r--openstackclient/tests/unit/compute/v2/test_service.py104
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_catalog.py55
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_project.py5
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_role.py133
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_service.py45
-rw-r--r--openstackclient/tests/unit/identity/v2_0/test_user.py12
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py3
-rw-r--r--openstackclient/tests/unit/identity/v3/test_catalog.py25
-rw-r--r--openstackclient/tests/unit/identity/v3/test_identity_provider.py20
-rw-r--r--openstackclient/tests/unit/identity/v3/test_role.py232
-rw-r--r--openstackclient/tests/unit/image/v1/test_image.py22
-rw-r--r--openstackclient/tests/unit/image/v2/fakes.py6
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py78
-rw-r--r--openstackclient/tests/unit/integ/cli/test_project.py32
-rw-r--r--openstackclient/tests/unit/integ/cli/test_shell.py105
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py81
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py502
-rw-r--r--openstackclient/tests/unit/network/v2/test_ip_availability.py12
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py97
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_agent.py26
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py129
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py122
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_compute.py15
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_network.py20
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py87
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule_network.py49
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py92
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet_pool.py57
-rw-r--r--openstackclient/tests/unit/test_shell.py22
-rw-r--r--openstackclient/tests/unit/utils.py17
-rw-r--r--openstackclient/tests/unit/volume/v1/test_qos_specs.py28
-rw-r--r--openstackclient/tests/unit/volume/v1/test_snapshot.py580
-rw-r--r--openstackclient/tests/unit/volume/v1/test_transfer_request.py20
-rw-r--r--openstackclient/tests/unit/volume/v1/test_type.py59
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume.py79
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume_backup.py (renamed from openstackclient/tests/unit/volume/v1/test_backup.py)24
-rw-r--r--openstackclient/tests/unit/volume/v2/fakes.py5
-rw-r--r--openstackclient/tests/unit/volume/v2/test_consistency_group.py17
-rw-r--r--openstackclient/tests/unit/volume/v2/test_qos_specs.py28
-rw-r--r--openstackclient/tests/unit/volume/v2/test_snapshot.py741
-rw-r--r--openstackclient/tests/unit/volume/v2/test_transfer_request.py30
-rw-r--r--openstackclient/tests/unit/volume/v2/test_type.py75
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py160
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume_backup.py (renamed from openstackclient/tests/unit/volume/v2/test_backup.py)20
-rw-r--r--openstackclient/volume/client.py2
-rw-r--r--openstackclient/volume/v1/qos_specs.py13
-rw-r--r--openstackclient/volume/v1/snapshot.py318
-rw-r--r--openstackclient/volume/v1/volume.py62
-rw-r--r--openstackclient/volume/v1/volume_backup.py (renamed from openstackclient/volume/v1/backup.py)130
-rw-r--r--openstackclient/volume/v1/volume_snapshot.py54
-rw-r--r--openstackclient/volume/v1/volume_transfer_request.py22
-rw-r--r--openstackclient/volume/v1/volume_type.py60
-rw-r--r--openstackclient/volume/v2/consistency_group.py3
-rw-r--r--openstackclient/volume/v2/qos_specs.py13
-rw-r--r--openstackclient/volume/v2/snapshot.py351
-rw-r--r--openstackclient/volume/v2/volume.py98
-rw-r--r--openstackclient/volume/v2/volume_backup.py (renamed from openstackclient/volume/v2/backup.py)130
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py54
-rw-r--r--openstackclient/volume/v2/volume_transfer_request.py23
-rw-r--r--openstackclient/volume/v2/volume_type.py63
-rw-r--r--python_openstackclient.egg-info/PKG-INFO19
-rw-r--r--python_openstackclient.egg-info/SOURCES.txt50
-rw-r--r--python_openstackclient.egg-info/entry_points.txt71
-rw-r--r--python_openstackclient.egg-info/pbr.json2
-rw-r--r--python_openstackclient.egg-info/requires.txt4
-rw-r--r--releasenotes/notes/add-fip-portforwarding-commands-6e4d8ace698ee308.yaml9
-rw-r--r--releasenotes/notes/add-host-and-hypervisor-hostname-flag-to-create-server-cb8b39a9f9311d42.yaml6
-rw-r--r--releasenotes/notes/add-server-migrate-with-host-4884a71903c5c8a9.yaml8
-rw-r--r--releasenotes/notes/add-server-resize-confirm-revert-commands-98854ca98965432a.yaml12
-rw-r--r--releasenotes/notes/allow-port-create-with-extra-dhcp-options-c2c40e4002b52e2a.yaml6
-rw-r--r--releasenotes/notes/better-ipv6-address-suppport-security-group-rules-95272847349982e5.yaml14
-rw-r--r--releasenotes/notes/bp-add-locked-reason-425efd2def1144f1.yaml13
-rw-r--r--releasenotes/notes/bp-support-specifying-az-when-restore-shelved-server-16e864223d51b50a.yaml7
-rw-r--r--releasenotes/notes/bug-1716789-abfae897b7e61246.yaml10
-rw-r--r--releasenotes/notes/config-show-00512dc60882e5c0.yaml6
-rw-r--r--releasenotes/notes/osc4-compute-09246008eff260cb.yaml8
-rw-r--r--releasenotes/notes/osc4-identity-6564257c67d43106.yaml12
-rw-r--r--releasenotes/notes/osc4-image-e922ee6f8e028648.yaml5
-rw-r--r--releasenotes/notes/osc4-network-db2aed696d964ca6.yaml12
-rw-r--r--releasenotes/notes/osc4-volume-470422e5a453310e.yaml14
-rw-r--r--releasenotes/notes/story-2005349-compute-service-set-2.53-3d2db875154e633a.yaml6
-rw-r--r--releasenotes/notes/story-2006302-add-boot-from-volume-cd411b1ca776bb3c.yaml8
-rw-r--r--releasenotes/notes/story-2006302-support-bdm-type-image-0becfb63bd4fb969.yaml8
-rw-r--r--releasenotes/notes/usage-list-all-49ca7a62c50e71d3.yaml6
-rw-r--r--releasenotes/notes/volume-v3-default-0ffa9bebb43b5057.yaml7
-rw-r--r--releasenotes/source/index.rst2
-rw-r--r--requirements.txt4
-rw-r--r--setup.cfg79
-rw-r--r--test-requirements.txt5
-rw-r--r--tox.ini15
198 files changed, 4973 insertions, 5799 deletions
diff --git a/.zuul.yaml b/.zuul.yaml
index 95a6890..5747346 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -193,8 +193,7 @@
- openstack-cover-jobs
- openstack-lower-constraints-jobs
- openstack-python-jobs
- - openstack-python36-jobs
- - openstack-python37-jobs
+ - openstack-python3-train-jobs
- publish-openstack-docs-pti
- check-requirements
- release-notes-jobs-python3
diff --git a/AUTHORS b/AUTHORS
index ec6105d..3299917 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -65,6 +65,7 @@ Clark Boylan <clark.boylan@gmail.com>
Colleen Murphy <colleen@gazlene.net>
Colleen Murphy <colleen@puppetlabs.com>
Colleen Murphy <comurphy@suse.com>
+Corey Bryant <corey.bryant@canonical.com>
Cyril Roelandt <cyril.roelandt@enovance.com>
Dag Stenstad <dag@stenstad.net>
Daisuke Fujita <fuzita.daisuke@jp.fujitsu.com>
@@ -198,6 +199,7 @@ Marcos Fermin Lobo <marcos.fermin.lobo@cern.ch>
Marek Aufart <maufart@redhat.com>
Marek Denis <marek.denis@cern.ch>
Mark Vanderwiel <vanderwl@us.ibm.com>
+Martin Chlumsky <martin.chlumsky@gmail.com>
Martin Schuppert <mschuppert@redhat.com>
Masayuki Igawa <masayuki@igawa.io>
Matt Fischer <matt@mattfischer.com>
@@ -373,6 +375,7 @@ melanie witt <melwittt@gmail.com>
melissaml <ma.lei@99cloud.net>
nidhimittalhada <nidhimittal19@gmail.com>
npraveen35 <npraveen35@gmail.com>
+pengyuesheng <pengyuesheng@gohighsec.com>
phil-hopkins-a <phil.hopkins@rackspace.com>
qinchunhua <qin.chunhua@zte.com.cn>
qingszhao <zhao.daqing@99cloud.net>
@@ -415,6 +418,8 @@ zheng yin <yin.zheng@easystack.cn>
zhengsenyan <zhengsy_sean@126.com>
zhiyong.dai <zhiyong.dai@easystack.cn>
zhiyuan_cai <luckyvega.g@gmail.com>
+zhouhenglc <zhouhenglc@inspur.com>
zhouqi <qi.zhou@easystack.cn>
+zhu.boxiang <zhu.boxiang@99cloud.net>
zhufl <zhu.fanglei@zte.com.cn>
zhurong <aaronzhu1121@gmail.com>
diff --git a/ChangeLog b/ChangeLog
index 3c6c98d..5d2ff98 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,21 +1,78 @@
CHANGES
=======
+4.0.0
+-----
+
+* Update release table for Train and 4.0.0
+* More aggregate functional race chasing
+* Add doc and relnote for review 639652
+* Default to Cinder v3 API
+* Clean up app initialization and config
+* Follow-up: fix the invalid releasenote link
+* Microversion 2.77: Support Specifying AZ to unshelve
+* Bump lower constraint of python-zunclient
+* Remove races in floating ip functional tests
+* Bump min osc-lib to 1.14.0
+* Format location columns in network commands
+* Add floating IP Port Forwarding commands
+* Bump hacking version
+* Remove token\_endpoint auth type
+* Format aggregate command fields and de-race functional tests
+* Add CLI argument tests before making changes
+* Fix functional.base.TestCase.openstack() to optionally omit --os-auth-type
+* Document 2.53 behavior for compute service list/delete
+* Add openstack server create --boot-from-volume option
+* Fix <id> description for --block-device-mapping
+* Support type=image with --block-device-mapping option
+* Fix compute service set handling for 2.53+
+* Add 'openstack server resize (confirm|revert)' commands
+* Make configuration show not require auth
+* Fix typo: "to and endpoint"
+* Fix module paths for volumev3 volume backup commands
+* openstack port create support --extra-dhcp-option
+* Mention compute service set --up|--down requires 2.11 or greater
+* Update api-ref location
+* Add host and hypervisor\_hostname to create servers
+* Support IPv6 addresses better
+* Add Python 3 Train unit tests
+* docs: clarify compute service --service option
+* Update the constraints url
+* Add Python 3 Train unit tests
+* Fix BFV server list handling with --name-lookup-one-by-one
+* Use cliff formattable columns in volume v2 commands
+* Use cliff formattable columns in volume v1 commands
+* Use cliff formattable columns in object storage commands
+* Use cliff formattable columns in image commands
+* Use cliff formattable columns in identity commands
+* Microversion 2.73: Support adding the reason behind a server lock
+* Allow "server migrate" (not live) to take "--host" option
+* Add server add/remove volume description for microversion 2.20
+* Add server event command documentation for compute API 2.21
+
3.19.0
------
* Add changes-before attribute to server list
* Deprecate openstack server migrate --host option
+* Remove deprecated volume commands and args
* Batch up minor cleanups for release
+* Remove deprecated network options
+* Remove deprecated image commands
+* Remove deprecated identity commands and args
+* Remove deprecated compute commands
* Serialize more aggregate functional tests
* Compute: Add description support for server
* Remove code migrated to osc-lib long ago
* Aggregate functional test tweak
* Update sphinx requirement
+* Rename review.openstack.org to review.opendev.org
* Stop leaving temp files after unit test runs
+* Use cliff formattable columns in network commands
* Blacklist Bandit 1.6.0 due to directory exclusion bug
* Fix bug in endpoint group deletion
* document the --timing option
+* Change default security group protocol to 'any'
* Fix link to new opendev repo
* Document that server dump create requires 2.17
* Dropping the py35 testing
diff --git a/PKG-INFO b/PKG-INFO
index 4b18353..74dc726 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: python-openstackclient
-Version: 3.19.0
+Version: 4.0.0
Summary: OpenStack Command-line Client
Home-page: https://docs.openstack.org/python-openstackclient/latest/
Author: OpenStack
@@ -24,8 +24,8 @@ Description: ========================
:alt: Latest Version
OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
- the command set for Compute, Identity, Image, Object Store and Block Storage
- APIs together in a single shell with a uniform command structure.
+ the command set for Compute, Identity, Image, Network, Object Store and Block
+ Storage APIs together in a single shell with a uniform command structure.
The primary goal is to provide a unified shell command structure and a common
language to describe operations in OpenStack.
@@ -109,18 +109,6 @@ Description: ========================
If a password is not provided above (in plaintext), you will be interactively
prompted to provide one securely.
- Authentication may also be performed using an already-acquired token
- and a URL pointing directly to the service API that presumably was acquired
- from the Service Catalog::
-
- export OS_TOKEN=<token>
- export OS_URL=<url-to-openstack-service>
-
- The corresponding command-line options look very similar::
-
- --os-token <token>
- --os-url <url-to-openstack-service>
-
Platform: UNKNOWN
Classifier: Environment :: OpenStack
@@ -133,3 +121,4 @@ Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
diff --git a/README.rst b/README.rst
index e1824ae..cd80079 100644
--- a/README.rst
+++ b/README.rst
@@ -16,8 +16,8 @@ OpenStackClient
:alt: Latest Version
OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
-the command set for Compute, Identity, Image, Object Store and Block Storage
-APIs together in a single shell with a uniform command structure.
+the command set for Compute, Identity, Image, Network, Object Store and Block
+Storage APIs together in a single shell with a uniform command structure.
The primary goal is to provide a unified shell command structure and a common
language to describe operations in OpenStack.
@@ -100,15 +100,3 @@ The corresponding command-line options look very similar::
If a password is not provided above (in plaintext), you will be interactively
prompted to provide one securely.
-
-Authentication may also be performed using an already-acquired token
-and a URL pointing directly to the service API that presumably was acquired
-from the Service Catalog::
-
- export OS_TOKEN=<token>
- export OS_URL=<url-to-openstack-service>
-
-The corresponding command-line options look very similar::
-
- --os-token <token>
- --os-url <url-to-openstack-service>
diff --git a/debian/changelog b/debian/changelog
index c049180..eaba0c4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+python-openstackclient (4.0.0-0ubuntu1) eoan; urgency=medium
+
+ * New upstream release for OpenStack Train.
+ * d/control: Align (Build-)Depends with upstream.
+
+ -- Corey Bryant <corey.bryant@canonical.com> Wed, 25 Sep 2019 14:23:09 -0400
+
python-openstackclient (3.19.0-0ubuntu2) eoan; urgency=medium
* d/tests/control: Drop Python 2 autopkgtest now that we no longer
diff --git a/debian/control b/debian/control
index cc6b411..08b68eb 100644
--- a/debian/control
+++ b/debian/control
@@ -30,7 +30,7 @@ Build-Depends-Indep:
python3-fixtures (>= 3.0.0),
python3-glanceclient (>= 1:2.8.0),
python3-gnocchiclient (>= 3.3.1) <!nodoc>,
- python3-hacking (>= 0.12.0),
+ python3-hacking (>= 1.1.0),
python3-heatclient (>= 1.10.0) <!nodoc>,
python3-ironic-inspector-client (>= 1.5.0) <!nodoc>,
python3-ironicclient (>= 2.3.0) <!nodoc>,
@@ -42,12 +42,12 @@ Build-Depends-Indep:
python3-mock (>= 2.0.0),
python3-muranoclient (>= 0.8.2) <!nodoc>,
python3-neutronclient (>= 1:6.7.0) <!nodoc>,
- python3-novaclient (>= 2:10.0.0),
+ python3-novaclient (>= 2:15.0.0),
python3-octaviaclient (>= 1.3.0) <!nodoc>,
python3-openstackdocstheme (>= 1.23.2),
python3-openstacksdk (>= 0.17.0),
python3-os-client-config (>= 1.28.0),
- python3-osc-lib (>= 1.11.0),
+ python3-osc-lib (>= 1.14.0),
python3-oslo.i18n (>= 3.15.3),
python3-oslo.serialization (>= 1.2.0),
python3-oslo.utils (>= 3.33.0),
@@ -69,7 +69,7 @@ Build-Depends-Indep:
python3-troveclient (>= 1:2.2.0) <!nodoc>,
python3-wrapt (>= 1.7.0),
python3-zaqarclient (>= 1.0.0) <!nodoc>,
- python3-zunclient (>= 1.3.0) <!nodoc>,
+ python3-zunclient (>= 3.4.0) <!nodoc>,
subunit,
Standards-Version: 4.1.3
Vcs-Browser: https://git.launchpad.net/~ubuntu-server-dev/ubuntu/+source/python-openstackclient
@@ -108,9 +108,9 @@ Depends:
python3-keystoneauth1 (>= 3.6.2),
python3-keystoneclient (>= 1:3.17.0),
python3-neutronclient (>= 1:2.6.0),
- python3-novaclient (>= 2:10.0.0),
+ python3-novaclient (>= 2:15.0.0),
python3-openstacksdk (>= 0.17.0),
- python3-osc-lib (>= 1.11.0),
+ python3-osc-lib (>= 1.14.0),
python3-oslo.i18n (>= 3.15.3),
python3-oslo.serialization (>= 1.2.0),
python3-oslo.utils (>= 3.33.0),
diff --git a/doc/source/cli/authentication.rst b/doc/source/cli/authentication.rst
index b153f54..3b404bc 100644
--- a/doc/source/cli/authentication.rst
+++ b/doc/source/cli/authentication.rst
@@ -39,15 +39,6 @@ There are at least three authentication types that are always available:
(described below as token/endpoint) in that a token and an authentication
URL are supplied and the plugin retrieves a new token.
[Required: ``--os-auth-url``, ``--os-token``]
-* **Token/Endpoint**: This is the original token authentication (known as 'token
- flow' in the early CLI documentation in the OpenStack wiki). It requires
- a token and a direct endpoint that is used in the API call. The difference
- from the new Token type is this token is used as-is, no call is made
- to the Identity service from the client. This type is most often used to
- bootstrap a Keystone server where the token is the ``admin_token`` configured
- in ``keystone.conf``. It will also work with other services and a regular
- scoped token such as one obtained from a ``token issue`` command.
- [Required: ``--os-url``, ``--os-token``]
* **Others**: Other authentication plugins such as SAML, Kerberos, and OAuth1.0
are under development and also supported. To use them, they must be selected
by supplying the ``--os-auth-type`` option.
@@ -76,8 +67,6 @@ by the ``ClientManager`` object.
is selected based on the existing options. This is a short-circuit
evaluation, the first match wins.
- * If ``--os-url`` and ``--os-token`` are both present ``token_endpoint``
- is selected
* If ``--os-username`` is supplied ``password`` is selected
* If ``--os-token`` is supplied ``token`` is selected
* If no selection has been made by now exit with error
diff --git a/doc/source/cli/backwards-incompatible.rst b/doc/source/cli/backwards-incompatible.rst
index fcb6868..9d43754 100644
--- a/doc/source/cli/backwards-incompatible.rst
+++ b/doc/source/cli/backwards-incompatible.rst
@@ -16,16 +16,93 @@ from this backwards incompatible change handling.
Backwards Incompatible Changes
==============================
-.. Carry this section as comments until 4.0 release
-.. Release 4.0
-.. -----------
+Release 4.0
+-----------
+
+1. Remove ``ip fixed add|remove`` commands.
+ Use ``server add|remove fixed ip`` commands instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612781
+
+2. Remove ``ip floating add|remove`` commands.
+ Use ``server add|remove floating ip`` commands instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612781
+
+3. Remove ``service create`` option ``--type``. Service type is
+ a positional argument.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612798
+
+4. Remove ``role list`` options ``--project`` and ``--user``.
+ Use ``role assignment list`` options ``--project`` and ``--user`` instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612798
+
+5. Remove ``user role list`` command.
+ Use ``role assignment list`` options ``--project`` and ``--user`` instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612798
+
+6. Remove ``image create|set`` option ``--owner``.
+ Use ``--project`` option instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/659431
+
+7. Remove ``port create|set`` options ``--device-id`` and ``--host-id``.
+ Use ``--device`` and ``--host`` instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/613644
+
+8. Remove ``router set`` option ``--clear-routes``.
+ Use ``no-route`` option instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/613644
+
+9. Remove ``security group rule create`` options ``--src-ip`` and ``--src-group``.
+ Use ``--remote-ip`` and ``--remote-group`` options instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/613644
+
+10. Remove ``backup`` commands.
+ Use ``volume backup`` commands instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612751
+
+11. Remove ``snapshot`` commands.
+ Use ``volume snapshot`` commands instead.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612751
+
+12. Remove ``volume create`` options ``--project``, ``--user``, ``--multi-attach``.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612751
+
+13. Change ``volume transfer request accept`` to use new option ``--auth-key``
+ rather than a second positional argument.
+
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/612751
-.. 1. Change ``volume transfer request accept`` to use new option ``--auth-key``
-.. rather than a second positional argument.
+14. Remove 'Token/Endpoint' auth plugin support (type ``token_endpoint``).
+ This remained as a compatibility for the ``admin_token`` auth type to
+ support the ``--url`` global option. That option is also now removed,
+ use ``--endpoint`` instead.
-.. * As of: 4.0
-.. * Remove in: <5.0>
-.. * Commit: <tbd>
+ * Removed in: 4.0
+ * Commit: https://review.opendev.org/<tbd>
Release 3.12
------------
@@ -46,7 +123,7 @@ Release 3.12
* As of: 3.12.0
* Removed in: n/a
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1657956
- * Commit: https://review.openstack.org/#/c/423081/
+ * Commit: https://review.opendev.org/#/c/423081/
Release 3.10
------------
@@ -55,7 +132,7 @@ Release 3.10
with Nova-network clouds.
* As of: 3.10
- * Commit: https://review.openstack.org/460679
+ * Commit: https://review.opendev.org/460679
2. The positional argument ``<snapshot-name>`` of the ``volume snapshot create``
command is no longer optional.
@@ -67,7 +144,7 @@ Release 3.10
* As of: 3.10
* Bug: 1659894
- * Commit: https://review.openstack.org/440497
+ * Commit: https://review.opendev.org/440497
Release 3.0
-----------
@@ -81,7 +158,7 @@ Release 3.0
* As of: 3.0
* Removed in: n/a
* Bug: n/a
- * Commit: https://review.openstack.org/332938
+ * Commit: https://review.opendev.org/332938
Releases Before 3.0
@@ -95,7 +172,7 @@ Releases Before 3.0
* As of: 1.0.2
* Removed in: TBD
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1406654
- * Commit: https://review.openstack.org/#/c/147379/
+ * Commit: https://review.opendev.org/#/c/147379/
2. <type> should not be optional for command `openstack service create`
@@ -107,7 +184,7 @@ Releases Before 3.0
* As of: 1.0.2
* Removed in: TBD
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1404073
- * Commit: https://review.openstack.org/#/c/143242/
+ * Commit: https://review.opendev.org/#/c/143242/
3. Command `openstack security group rule delete` now requires rule id
@@ -119,7 +196,7 @@ Releases Before 3.0
* As of: 1.2.1
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1450872
- * Commit: https://review.openstack.org/#/c/179446/
+ * Commit: https://review.opendev.org/#/c/179446/
4. Command `openstack image create` does not update already existing image
@@ -133,7 +210,7 @@ Releases Before 3.0
* As of: 1.5.0
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1461817
- * Commit: https://review.openstack.org/#/c/194654/
+ * Commit: https://review.opendev.org/#/c/194654/
5. Command `openstack network list --dhcp` has been removed
@@ -147,7 +224,7 @@ Releases Before 3.0
* As of: 1.6.0
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/472613
- * Commit: https://review.openstack.org/#/c/194654/
+ * Commit: https://review.opendev.org/#/c/194654/
6. Plugin interface change for default API versions
@@ -162,7 +239,7 @@ Releases Before 3.0
* As of: 1.2.1
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1453229
- * Commit: https://review.openstack.org/#/c/181514/
+ * Commit: https://review.opendev.org/#/c/181514/
7. `image set` commands will no longer return the modified resource
@@ -186,7 +263,7 @@ Releases Before 3.0
* As of 1.9.0
* Removed in: NA
* Bug: https://launchpad.net/bugs/1506841
- * Commit: https://review.openstack.org/#/c/236736/
+ * Commit: https://review.opendev.org/#/c/236736/
9. `flavor set/unset` commands will no longer return the modified resource
@@ -198,7 +275,7 @@ Releases Before 3.0
* As of: NA
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065
- * Commit: https://review.openstack.org/#/c/280663/
+ * Commit: https://review.opendev.org/#/c/280663/
10. `security group set` commands will no longer return the modified resource
@@ -210,7 +287,7 @@ Releases Before 3.0
* As of: NA
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065
- * Commit: https://review.openstack.org/#/c/281087/
+ * Commit: https://review.opendev.org/#/c/281087/
11. `compute agent set` commands will no longer return the modified resource
@@ -222,7 +299,7 @@ Releases Before 3.0
* As of: NA
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065
- * Commit: https://review.openstack.org/#/c/281088/
+ * Commit: https://review.opendev.org/#/c/281088/
12. `<version> <url> <md5hash>` should be optional for command `openstack
compute agent set`
@@ -235,7 +312,7 @@ Releases Before 3.0
* As of: NA
* Removed in: NA
* Bug: NA
- * Commit: https://review.openstack.org/#/c/328819/
+ * Commit: https://review.opendev.org/#/c/328819/
13. `aggregate set` commands will no longer return the modified resource
@@ -247,7 +324,7 @@ Releases Before 3.0
* As of: NA
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065
- * Commit: https://review.openstack.org/#/c/281089/
+ * Commit: https://review.opendev.org/#/c/281089/
14. Output of `ip floating list` command has changed.
@@ -291,7 +368,7 @@ Releases Before 3.0
* As of: NA
* Removed in: NA
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1519502
- * Commit: https://review.openstack.org/#/c/277720/
+ * Commit: https://review.opendev.org/#/c/277720/
For Developers
==============
@@ -302,4 +379,4 @@ update this file.
To review all changes that are affected, use the following query:
-https://review.openstack.org/#/q/project:openstack/python-openstackclient+AND+message:BackwardsIncompatibleImpact,n,z
+https://review.opendev.org/#/q/project:openstack/python-openstackclient+AND+message:BackwardsIncompatibleImpact,n,z
diff --git a/doc/source/cli/command-objects/backup.rst b/doc/source/cli/command-objects/backup.rst
deleted file mode 100644
index f892327..0000000
--- a/doc/source/cli/command-objects/backup.rst
+++ /dev/null
@@ -1,137 +0,0 @@
-======
-backup
-======
-
-Block Storage v1, v2
-
-backup create
--------------
-
-Create new backup
-(Deprecated, please use ``volume backup create`` instead)
-
-.. program:: backup create
-.. code:: bash
-
- openstack backup create
- [--container <container>]
- [--name <name>]
- [--description <description>]
- [--snapshot <snapshot>]
- [--force]
- [--incremental]
- <volume>
-
-.. option:: --container <container>
-
- Optional backup container name
-
-.. option:: --name <name>
-
- Name of the backup
-
-.. option:: --description <description>
-
- Description of the backup
-
-.. option:: --snapshot <snapshot>
-
- Snapshot to backup (name or ID)
-
- *Volume version 2 only*
-
-.. option:: --force
-
- Allow to back up an in-use volume
-
- *Volume version 2 only*
-
-.. option:: --incremental
-
- Perform an incremental backup
-
- *Volume version 2 only*
-
-.. _backup_create-backup:
-.. describe:: <volume>
-
- Volume to backup (name or ID)
-
-backup delete
--------------
-
-Delete backup(s)
-(Deprecated, please use ``volume backup delete`` instead)
-
-.. program:: backup delete
-.. code:: bash
-
- openstack backup delete
- [--force]
- <backup> [<backup> ...]
-
-.. option:: --force
-
- Allow delete in state other than error or available
-
- *Volume version 2 only*
-
-.. _backup_delete-backup:
-.. describe:: <backup>
-
- Backup(s) to delete (name or ID)
-
-backup list
------------
-
-List backups
-(Deprecated, please use ``volume backup list`` instead)
-
-.. program:: backup list
-.. code:: bash
-
- openstack backup list
-
-.. _backup_list-backup:
-.. option:: --long
-
- List additional fields in output
-
-backup restore
---------------
-
-Restore backup
-(Deprecated, please use ``volume backup restore`` instead)
-
-.. program:: backup restore
-.. code:: bash
-
- openstack backup restore
- <backup>
- <volume>
-
-.. _backup_restore-backup:
-.. describe:: <backup>
-
- Backup to restore (name or ID)
-
-.. describe:: <volume>
-
- Volume to restore to (name or ID)
-
-backup show
------------
-
-Display backup details
-(Deprecated, please use ``volume backup show`` instead)
-
-.. program:: backup show
-.. code:: bash
-
- openstack backup show
- <backup>
-
-.. _backup_show-backup:
-.. describe:: <backup>
-
- Backup to display (name or ID)
diff --git a/doc/source/cli/command-objects/compute-service.rst b/doc/source/cli/command-objects/compute-service.rst
index ba624ea..a76a9c2 100644
--- a/doc/source/cli/command-objects/compute-service.rst
+++ b/doc/source/cli/command-objects/compute-service.rst
@@ -18,13 +18,19 @@ Delete compute service(s)
.. _compute_service_delete-service:
.. describe:: <service>
- Compute service(s) to delete (ID only)
+ Compute service(s) to delete (ID only). If using
+ ``--os-compute-api-version`` 2.53 or greater, the ID is a UUID which can
+ be retrieved by listing compute services using the same 2.53+ microversion.
compute service list
--------------------
List compute services
+Using ``--os-compute-api-version`` 2.53 or greater will return the ID as a
+UUID value which can be used to uniquely identify the service in a multi-cell
+deployment.
+
.. program:: compute service list
.. code:: bash
@@ -39,7 +45,8 @@ List compute services
.. option:: --service <service>
- List only specified service (name only)
+ List only specified service binaries (name only). For example,
+ ``nova-compute``, ``nova-conductor``, etc.
.. option:: --long
@@ -73,11 +80,12 @@ Set compute service properties
.. option:: --up
- Force up service
+ Force up service. Requires ``--os-compute-api-version`` 2.11 or greater.
.. option:: --down
- Force down service
+ Force down service. . Requires ``--os-compute-api-version`` 2.11 or
+ greater.
.. _compute_service_set-host:
.. describe:: <host>
@@ -86,4 +94,4 @@ Set compute service properties
.. describe:: <service>
- Name of service (Binary name)
+ Name of service (Binary name), for example ``nova-compute``
diff --git a/doc/source/cli/command-objects/endpoint.rst b/doc/source/cli/command-objects/endpoint.rst
index 030947c..6d02532 100644
--- a/doc/source/cli/command-objects/endpoint.rst
+++ b/doc/source/cli/command-objects/endpoint.rst
@@ -7,7 +7,7 @@ Identity v2, v3
endpoint add project
--------------------
-Associate a project to and endpoint for endpoint filtering
+Associate a project to an endpoint for endpoint filtering
.. program:: endpoint add project
.. code:: bash
diff --git a/doc/source/cli/command-objects/floating-ip-pool.rst b/doc/source/cli/command-objects/floating-ip-pool.rst
index 9213b86..5c8f3aa 100644
--- a/doc/source/cli/command-objects/floating-ip-pool.rst
+++ b/doc/source/cli/command-objects/floating-ip-pool.rst
@@ -2,7 +2,7 @@
floating ip pool
================
-Compute v2, Network v2
+Network v2
floating ip pool list
---------------------
diff --git a/doc/source/cli/command-objects/floating-ip-port-forwarding.rst b/doc/source/cli/command-objects/floating-ip-port-forwarding.rst
new file mode 100644
index 0000000..5012787
--- /dev/null
+++ b/doc/source/cli/command-objects/floating-ip-port-forwarding.rst
@@ -0,0 +1,173 @@
+===========================
+floating ip port forwarding
+===========================
+
+Network v2
+
+floating ip port forwarding create
+----------------------------------
+
+Create floating IP Port Forwarding
+
+.. program:: floating ip port forwarding create
+.. code:: bash
+
+ openstack floating ip port forwarding create
+ --internal-ip-address <internal-ip-address>
+ --port <port>
+ --internal-protocol-port <port-number>
+ --external-protocol-port <port-number>
+ --protocol <protocol>
+ <floating-ip>
+
+
+.. describe:: --internal-ip-address <internal-ip-address>
+
+ The fixed IPv4 address of the network port associated
+ to the floating IP port forwarding
+
+.. describe:: --port <port>
+
+ The name or ID of the network port associated to the
+ floating IP port forwarding
+
+.. describe:: --internal-protocol-port <port-number>
+
+ The protocol port number of the network port fixed
+ IPv4 address associated to the floating IP port
+ forwarding
+
+.. describe:: --external-protocol-port <port-number>
+
+ The protocol port number of the port forwarding's
+ floating IP address
+
+.. describe:: --protocol <protocol>
+
+ The protocol used in the floating IP port forwarding,
+ for instance: TCP, UDP
+
+.. describe:: <floating-ip>
+
+ Floating IP that the port forwarding belongs to (IP
+ address or ID)
+
+floating ip port forwarding delete
+----------------------------------
+
+Delete floating IP Port Forwarding(s)
+
+.. program:: floating ip port forwarding delete
+.. code:: bash
+
+ openstack floating ip port forwarding delete <floating-ip>
+ <port-forwarding-id> [<port-forwarding-id> ...]
+
+.. describe:: <floating-ip>
+
+ Floating IP that the port forwarding belongs to (IP
+ address or ID)
+
+.. describe:: <port-forwarding-id>
+
+ The ID of the floating IP port forwarding
+
+floating ip port forwarding list
+--------------------------------
+
+List floating IP Port Forwarding(s)
+
+.. program:: floating ip port forwarding list
+.. code:: bash
+
+ openstack floating ip port forwarding list
+ [--port <port>]
+ [--external-protocol-port <port-number>]
+ [--protocol protocol]
+ <floating-ip>
+
+.. option:: --port <port>
+
+ The ID of the network port associated to the floating
+ IP port forwarding
+
+.. option:: --protocol <protocol>
+
+ The IP protocol used in the floating IP port
+ forwarding
+
+.. describe:: <floating-ip>
+
+ Floating IP that the port forwarding belongs to (IP
+ address or ID)
+
+floating ip port forwarding set
+-------------------------------
+
+Set floating IP Port Forwarding properties
+
+.. program:: floating ip port forwarding set
+.. code:: bash
+
+ openstack floating ip port forwarding set
+ [--port <port>]
+ [--internal-ip-address <internal-ip-address>]
+ [--internal-protocol-port <port-number>]
+ [--external-protocol-port <port-number>]
+ [--protocol <protocol>]
+ <floating-ip>
+ <port-forwarding-id>
+
+.. option:: --port <port>
+
+ The ID of the network port associated to the floating
+ IP port forwarding
+
+.. option:: --internal-ip-address <internal-ip-address>
+
+ The fixed IPv4 address of the network port associated
+ to the floating IP port forwarding
+
+.. option:: --internal-protocol-port <port-number>
+
+ The TCP/UDP/other protocol port number of the network
+ port fixed IPv4 address associated to the floating IP
+ port forwarding
+
+.. option:: --external-protocol-port <port-number>
+
+ The TCP/UDP/other protocol port number of the port
+ forwarding's floating IP address
+
+.. option:: --protocol <protocol>
+
+ The IP protocol used in the floating IP port
+ forwarding
+
+.. describe:: <floating-ip>
+
+ Floating IP that the port forwarding belongs to (IP
+ address or ID)
+
+.. describe:: <port-forwarding-id>
+
+ The ID of the floating IP port forwarding
+
+floating ip port forwarding show
+--------------------------------
+
+Display floating IP Port Forwarding details
+
+.. program:: floating ip port forwarding show
+.. code:: bash
+
+ openstack floating ip show <floating-ip> <port-forwarding-id>
+
+.. describe:: <floating-ip>
+
+ Floating IP that the port forwarding belongs to (IP
+ address or ID)
+
+.. describe:: <port-forwarding-id>
+
+ The ID of the floating IP port forwarding
diff --git a/doc/source/cli/command-objects/floating-ip.rst b/doc/source/cli/command-objects/floating-ip.rst
index 749c32d..d122ccb 100644
--- a/doc/source/cli/command-objects/floating-ip.rst
+++ b/doc/source/cli/command-objects/floating-ip.rst
@@ -2,7 +2,7 @@
floating ip
===========
-Compute v2, Network v2
+Network v2
floating ip create
------------------
diff --git a/doc/source/cli/command-objects/ip-fixed.rst b/doc/source/cli/command-objects/ip-fixed.rst
deleted file mode 100644
index f5b11dc..0000000
--- a/doc/source/cli/command-objects/ip-fixed.rst
+++ /dev/null
@@ -1,47 +0,0 @@
-========
-ip fixed
-========
-
-Compute v2
-
-ip fixed add
-------------
-
-Add fixed IP address to server
-(Deprecated, please use ``server add fixed ip`` instead)
-
-.. program:: ip fixed add
-.. code:: bash
-
- openstack ip fixed add
- <network>
- <server>
-
-.. describe:: <network>
-
- Network to fetch an IP address from (name or ID)
-
-.. describe:: <server>
-
- Server to receive the IP address (name or ID)
-
-ip fixed remove
----------------
-
-Remove fixed IP address from server
-(Deprecated, please use ``server remove fixed ip`` instead)
-
-.. program:: ip fixed remove
-.. code:: bash
-
- openstack ip fixed remove
- <ip-address>
- <server>
-
-.. describe:: <ip-address>
-
- IP address to remove from server (name only)
-
-.. describe:: <server>
-
- Server to remove the IP address from (name or ID)
diff --git a/doc/source/cli/command-objects/role.rst b/doc/source/cli/command-objects/role.rst
index 9819fd1..e2a2b45 100644
--- a/doc/source/cli/command-objects/role.rst
+++ b/doc/source/cli/command-objects/role.rst
@@ -146,68 +146,12 @@ List roles
.. code:: bash
openstack role list
- --domain <domain> | --project <project> [--project-domain <project-domain>]
- --user <user> [--user-domain <user-domain>] | --group <group> [--group-domain <group-domain>]
- --inherited
+ [--domain <domain>]
.. option:: --domain <domain>
Filter roles by <domain> (name or ID)
- (Deprecated if being used to list assignments in conjunction with the
- ``--user <user>``, option, please use ``role assignment list`` instead)
-
-.. option:: --project <project>
-
- Filter roles by <project> (name or ID)
-
- (Deprecated, please use ``role assignment list`` instead)
-
-.. option:: --user <user>
-
- Filter roles by <user> (name or ID)
-
- (Deprecated, please use ``role assignment list`` instead)
-
-.. option:: --group <group>
-
- Filter roles by <group> (name or ID)
-
- (Deprecated, please use ``role assignment list`` instead)
-
-.. option:: --user-domain <user-domain>
-
- Domain the user belongs to (name or ID).
- This can be used in case collisions between user names exist.
-
- (Deprecated, please use ``role assignment list`` instead)
-
- .. 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.
-
- (Deprecated, please use ``role assignment list`` instead)
-
- .. versionadded:: 3
-
-.. option:: --project-domain <project-domain>
-
- Domain the project belongs to (name or ID).
- This can be used in case collisions between project names exist.
-
- (Deprecated, please use ``role assignment list`` instead)
-
- .. versionadded:: 3
-
-.. option:: --inherited
-
- Specifies if the role grant is inheritable to the sub projects.
-
- (Deprecated, please use ``role assignment list`` instead)
-
.. versionadded:: 3
role remove
diff --git a/doc/source/cli/command-objects/security-group-rule.rst b/doc/source/cli/command-objects/security-group-rule.rst
index 1dbf16d..5a2d834 100644
--- a/doc/source/cli/command-objects/security-group-rule.rst
+++ b/doc/source/cli/command-objects/security-group-rule.rst
@@ -27,8 +27,9 @@ Create a new security group rule
.. option:: --remote-ip <ip-address>
- Remote IP address block
- (may use CIDR notation; default for IPv4 rule: 0.0.0.0/0)
+ Remote IP address block (may use CIDR notation;
+ default for IPv4 rule: 0.0.0.0/0,
+ default for IPv6 rule: ::/0)
.. option:: --remote-group <group>
@@ -61,8 +62,8 @@ Create a new security group rule
IP protocol (ah, dccp, egp, esp, gre, icmp, igmp,
ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt,
ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp,
- udp, udplite, vrrp and integer representations [0-255];
- default: tcp)
+ udp, udplite, vrrp and integer representations [0-255]
+ or any; default: any (all protocols))
*Network version 2*
@@ -134,6 +135,7 @@ List security group rules
openstack security group rule list
[--all-projects]
[--protocol <protocol>]
+ [--ethertype <ethertype>]
[--ingress | --egress]
[--long]
[<group>]
@@ -151,13 +153,18 @@ List security group rules
*Compute version 2 does not have additional fields to display.*
-
.. option:: --protocol
List rules by the IP protocol (ah, dhcp, egp, esp, gre, icmp, igmp,
ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt,ipv6-opts, ipv6-route,
ospf, pgm, rsvp, sctp, tcp, udp, udplite, vrrp and integer
- representations [0-255])
+ representations [0-255] or any; default: any (all protocols))
+
+ *Network version 2*
+
+.. option:: --ethertype
+
+ List rules by the Ethertype (IPv4 or IPv6)
*Network version 2*
diff --git a/doc/source/cli/command-objects/snapshot.rst b/doc/source/cli/command-objects/snapshot.rst
deleted file mode 100644
index fc51606..0000000
--- a/doc/source/cli/command-objects/snapshot.rst
+++ /dev/null
@@ -1,176 +0,0 @@
-========
-snapshot
-========
-
-Block Storage v1, v2
-
-snapshot create
----------------
-
-Create new snapshot
-(Deprecated, please use ``volume snapshot create`` instead)
-
-.. program:: snapshot create
-.. code:: bash
-
- openstack snapshot create
- [--name <name>]
- [--description <description>]
- [--force]
- [--property <key=value> [...] ]
- <volume>
-
-.. option:: --name <name>
-
- Name of the snapshot
-
-.. option:: --description <description>
-
- Description of the snapshot
-
-.. option:: --force
-
- Create a snapshot attached to an instance. Default is False
-
-.. option:: --property <key=value>
-
- Set a property to this snapshot (repeat option to set multiple properties)
-
- *Volume version 2 only*
-
-.. _snapshot_create-snapshot:
-.. describe:: <volume>
-
- Volume to snapshot (name or ID)
-
-snapshot delete
----------------
-
-Delete snapshot(s)
-(Deprecated, please use ``volume snapshot delete`` instead)
-
-.. program:: snapshot delete
-.. code:: bash
-
- openstack snapshot delete
- <snapshot> [<snapshot> ...]
-
-.. _snapshot_delete-snapshot:
-.. describe:: <snapshot>
-
- Snapshot(s) to delete (name or ID)
-
-snapshot list
--------------
-
-List snapshots
-(Deprecated, please use ``volume snapshot list`` instead)
-
-.. program:: snapshot list
-.. code:: bash
-
- openstack snapshot list
- [--all-projects]
- [--long]
- [--limit <num-snapshots>]
- [--marker <snapshot>]
-
-.. option:: --all-projects
-
- Include all projects (admin only)
-
-.. option:: --long
-
- List additional fields in output
-
-.. option:: --limit <num-snapshots>
-
- Maximum number of snapshots to display
-
- *Volume version 2 only*
-
-.. option:: --marker <snapshot>
-
- The last snapshot ID of the previous page
-
- *Volume version 2 only*
-
-snapshot set
-------------
-
-Set snapshot properties
-(Deprecated, please use ``volume snapshot set`` instead)
-
-.. program:: snapshot set
-.. code:: bash
-
- openstack snapshot set
- [--name <name>]
- [--description <description>]
- [--property <key=value> [...] ]
- [--state <state>]
- <snapshot>
-
-.. _snapshot_restore-snapshot:
-.. option:: --name <name>
-
- New snapshot name
-
-.. option:: --description <description>
-
- New snapshot description
-
-.. option:: --property <key=value>
-
- Property to add or modify for this snapshot (repeat option to set multiple properties)
-
-.. option:: --state <state>
-
- New snapshot state.
- ("available", "error", "creating", "deleting", or "error_deleting") (admin only)
- (This option simply changes the state of the snapshot in the database with
- no regard to actual status, exercise caution when using)
-
- *Volume version 2 only*
-
-.. describe:: <snapshot>
-
- Snapshot to modify (name or ID)
-
-snapshot show
--------------
-
-Display snapshot details
-(Deprecated, please use ``volume snapshot show`` instead)
-
-.. program:: snapshot show
-.. code:: bash
-
- openstack snapshot show
- <snapshot>
-
-.. _snapshot_show-snapshot:
-.. describe:: <snapshot>
-
- Snapshot to display (name or ID)
-
-snapshot unset
---------------
-
-Unset snapshot properties
-(Deprecated, please use ``volume snapshot unset`` instead)
-
-.. program:: snapshot unset
-.. code:: bash
-
- openstack snapshot unset
- [--property <key>]
- <snapshot>
-
-.. option:: --property <key>
-
- Property to remove from snapshot (repeat option to remove multiple properties)
-
-.. describe:: <snapshot>
-
- Snapshot to modify (name or ID)
diff --git a/doc/source/cli/command-objects/usage.rst b/doc/source/cli/command-objects/usage.rst
index 9cd0f70..bb13176 100644
--- a/doc/source/cli/command-objects/usage.rst
+++ b/doc/source/cli/command-objects/usage.rst
@@ -9,6 +9,9 @@ usage list
List resource usage per project
+Compute API v2.40+ returns all matching entities rather than being
+limited to the API server configured maximum (``CONF.api.max_limit``).
+
.. program:: usage list
.. code:: bash
diff --git a/doc/source/cli/command-objects/user-role.rst b/doc/source/cli/command-objects/user-role.rst
deleted file mode 100644
index 4f443f3..0000000
--- a/doc/source/cli/command-objects/user-role.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-=========
-user role
-=========
-
-Identity v2
-
-user role list
---------------
-
-List user-role assignments
-
-*Removed in version 3.*
-
-.. program:: user role list
-.. code:: bash
-
- openstack user role list
- [--project <project>]
- [<user>]
-
-.. option:: --project <project>
-
- Filter users by `<project>` (name or ID)
-
-.. describe:: <user>
-
- User to list (name or ID)
diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst
index e302fda..7c38aa5 100644
--- a/doc/source/cli/commands.rst
+++ b/doc/source/cli/commands.rst
@@ -95,9 +95,9 @@ referring to both Compute and Volume quotas.
* ``extension``: (**Compute**, **Identity**, **Network**, **Volume**) OpenStack server API extensions
* ``federation protocol``: (**Identity**) the underlying protocol used while federating identities
* ``flavor``: (**Compute**) predefined server configurations: ram, root disk and so on
-* ``fixed ip``: (**Compute**, **Network**) - an internal IP address assigned to a server
-* ``floating ip``: (**Compute**, **Network**) - a public IP address that can be mapped to a server
-* ``floating ip pool``: (**Compute**, **Network**) - a pool of public IP addresses
+* ``fixed ip``: (**Compute**) - an internal IP address assigned to a server
+* ``floating ip``: (**Network**) - a public IP address that can be mapped to a server
+* ``floating ip pool``: (**Network**) - a pool of public IP addresses
* ``group``: (**Identity**) a grouping of users
* ``host``: (**Compute**) - the physical computer running compute services
* ``hypervisor``: (**Compute**) the virtual machine manager
@@ -106,9 +106,6 @@ referring to both Compute and Volume quotas.
* ``image``: (**Image**) a disk image
* ``image member``: (**Image**) a project that is a member of an Image
* ``ip availability``: (**Network**) - details of IP usage of a network
-* ``ip fixed``: (**Compute**, **Network**) - an internal IP address assigned to a server
-* ``ip floating``: (**Compute**, **Network**) - a public IP address that can be mapped to a server
-* ``ip floating pool``: (**Compute**, **Network**) - a pool of public IP addresses
* ``keypair``: (**Compute**) an SSH public key
* ``limits``: (**Compute**, **Volume**) resource usage limits
* ``mapping``: (**Identity**) a definition to translate identity provider attributes to Identity concepts
diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv
index 2985e30..994a8fd 100644
--- a/doc/source/cli/data/glance.csv
+++ b/doc/source/cli/data/glance.csv
@@ -1,24 +1,24 @@
-explain,WONTFIX,Describe a specific model.
-image-create,image create,Create a new image.
-image-deactivate,image set --deactivate,Deactivate specified image.
-image-delete,image delete,Delete specified image.
-image-download,image save,Download a specific image.
-image-list,image list,List images you can access.
-image-reactivate,image set --activate,Reactivate specified image.
-image-show,image show,Describe a specific image.
-image-tag-delete,image set --tag <tag>,Delete the tag associated with the given image.
-image-tag-update,image unset --tag <tag>,Update an image with the given tag.
-image-update,image set,Update an existing image.
-image-upload,,Upload data for a specific image.
-location-add,,Add a location (and related metadata) to an image.
-location-delete,,Remove locations (and related metadata) from an image.
-location-update,,Update metadata of an image's location.
-member-create,image add project,Create member for a given image.
-member-delete,image remove project,Delete image member.
-member-list,,Describe sharing permissions by image.
-member-update,image set --accept --reject --status,Update the status of a member for a given image.
-task-create,,Create a new task.
-task-list,,List tasks you can access.
-task-show,,Describe a specific task.
-bash-completion,complete,Prints arguments for bash_completion.
-help,help,Display help about this program or one of its subcommands. \ No newline at end of file
+explain,WONTFIX,Describe a specific model.
+image-create,image create,Create a new image.
+image-deactivate,image set --deactivate,Deactivate specified image.
+image-delete,image delete,Delete specified image.
+image-download,image save,Download a specific image.
+image-list,image list,List images you can access.
+image-reactivate,image set --activate,Reactivate specified image.
+image-show,image show,Describe a specific image.
+image-tag-delete,image unset --tag <tag>,Delete the tag associated with the given image.
+image-tag-update,image set --tag <tag>,Update an image with the given tag.
+image-update,image set,Update an existing image.
+image-upload,,Upload data for a specific image.
+location-add,,Add a location (and related metadata) to an image.
+location-delete,,Remove locations (and related metadata) from an image.
+location-update,,Update metadata of an image's location.
+member-create,image add project,Create member for a given image.
+member-delete,image remove project,Delete image member.
+member-list,,Describe sharing permissions by image.
+member-update,image set --accept --reject --status,Update the status of a member for a given image.
+task-create,,Create a new task.
+task-list,,List tasks you can access.
+task-show,,Describe a specific task.
+bash-completion,complete,Prints arguments for bash_completion.
+help,help,Display help about this program or one of its subcommands.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index abe0803..ea4cd3e 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -55,7 +55,7 @@ Contributing
OpenStackClient utilizes all of the usual OpenStack processes and requirements for
contributions. The code is hosted `on OpenStack's Git server`_. `Bug reports`_
-and `blueprints`_ may be submitted to the :code:`python-openstackclient` project
+may be submitted to the :code:`python-openstackclient` Storyboard project
on `Launchpad`_. Code may be submitted to the
:code:`openstack/python-openstackclient` project using `Gerrit`_.
Developers may also be found in the `IRC channel`_ ``#openstack-sdks``.
@@ -64,7 +64,6 @@ Developers may also be found in the `IRC channel`_ ``#openstack-sdks``.
.. _Launchpad: https://launchpad.net/python-openstackclient
.. _Gerrit: http://docs.openstack.org/infra/manual/developers.html#development-workflow
.. _Bug reports: https://storyboard.openstack.org/#!/project/975
-.. _blueprints: https://blueprints.launchpad.net/python-openstackclient
.. _PyPi: https://pypi.org/project/python-openstackclient
.. _tarball: http://tarballs.openstack.org/python-openstackclient
.. _IRC channel: https://wiki.openstack.org/wiki/IRC
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 43d724a..a84643c 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -22,14 +22,14 @@ extras==1.0.0
fasteners==0.7.0
fixtures==3.0.0
flake8-import-order==0.13
-flake8==2.5.5
+flake8==2.6.2
future==0.16.0
futurist==1.2.0
gitdb==0.6.4
GitPython==1.0.1
gnocchiclient==3.3.1
greenlet==0.4.10
-hacking==0.12.0
+hacking==1.1.0
httplib2==0.9.1
idna==2.6
iso8601==0.1.11
@@ -54,7 +54,7 @@ openstacksdk==0.17.0
os-client-config==1.28.0
os-service-types==1.2.0
os-testr==1.0.0
-osc-lib==1.10.0
+osc-lib==1.14.0
oslo.concurrency==3.26.0
oslo.config==5.2.0
oslo.context==2.19.2
@@ -71,14 +71,13 @@ paramiko==2.0.0
Paste==2.0.2
PasteDeploy==1.5.0
pbr==2.0.0
-pep8==1.5.7
pika-pool==0.1.3
pika==0.10.0
ply==3.10
positional==1.2.1
prettytable==0.7.2
pyasn1==0.1.8
-pycodestyle==2.3.1
+pycodestyle==2.0.0
pycparser==2.18
pyflakes==0.8.1
pyinotify==0.9.6
@@ -100,7 +99,7 @@ python-mimeparse==1.6.0
python-mistralclient==3.1.0
python-muranoclient==0.8.2
python-neutronclient==6.7.0
-python-novaclient==10.0.0
+python-novaclient==15.0.0
python-octaviaclient==1.3.0
python-rsdclient==0.1.0
python-saharaclient==1.4.0
@@ -110,7 +109,7 @@ python-subunit==1.0.0
python-swiftclient==3.2.0
python-troveclient==2.2.0
python-zaqarclient==1.0.0
-python-zunclient==1.3.0
+python-zunclient==3.4.0
pytz==2013.6
PyYAML==3.12
repoze.lru==0.7
diff --git a/openstackclient/api/auth_plugin.py b/openstackclient/api/auth_plugin.py
deleted file mode 100644
index a106b1e..0000000
--- a/openstackclient/api/auth_plugin.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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.
-#
-
-"""Authentication Plugin Library"""
-
-from keystoneauth1 import loading
-from keystoneauth1 import token_endpoint
-
-from openstackclient.i18n import _
-
-
-class TokenEndpoint(loading.BaseLoader):
- """Auth plugin to handle traditional token/endpoint usage
-
- Keystoneauth contains a Token plugin class that now correctly
- handles the token/endpoint auth compatible with OSC. However,
- the AdminToken loader deprecates the 'url' argument, which breaks
- OSC compatibility, so make one that works.
- """
-
- @property
- def plugin_class(self):
- return token_endpoint.Token
-
- def load_from_options(self, url, token, **kwargs):
- """A plugin for static authentication with an existing token
-
- :param string url: Service endpoint
- :param string token: Existing token
- """
-
- return super(TokenEndpoint, self).load_from_options(
- endpoint=url,
- token=token,
- )
-
- def get_options(self):
- """Return the legacy options"""
-
- options = [
- loading.Opt(
- 'url',
- help=_('Specific service endpoint to use'),
- ),
- loading.Opt(
- 'token',
- secret=True,
- help=_('Authentication token to use'),
- ),
- ]
- return options
diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py
index 7dc4e44..e30177a 100644
--- a/openstackclient/api/compute_v2.py
+++ b/openstackclient/api/compute_v2.py
@@ -145,7 +145,7 @@ class APIv2(api.BaseAPI):
):
"""Create a new floating ip
- https://developer.openstack.org/api-ref/compute/#create-allocate-floating-ip-address
+ https://docs.openstack.org/api-ref/compute/#create-allocate-floating-ip-address
:param pool: Name of floating IP pool
"""
@@ -170,7 +170,7 @@ class APIv2(api.BaseAPI):
):
"""Delete a floating IP
- https://developer.openstack.org/api-ref/compute/#delete-deallocate-floating-ip-address
+ https://docs.openstack.org/api-ref/compute/#delete-deallocate-floating-ip-address
:param string floating_ip_id:
Floating IP ID
@@ -189,7 +189,7 @@ class APIv2(api.BaseAPI):
):
"""Return a security group given name or ID
- https://developer.openstack.org/api-ref/compute/#list-floating-ip-addresses
+ https://docs.openstack.org/api-ref/compute/#list-floating-ip-addresses
:param string floating_ip:
Floating IP address
@@ -209,7 +209,7 @@ class APIv2(api.BaseAPI):
):
"""Get floating IPs
- https://developer.openstack.org/api-ref/compute/#show-floating-ip-address-details
+ https://docs.openstack.org/api-ref/compute/#show-floating-ip-address-details
:returns:
list of floating IPs
@@ -258,7 +258,7 @@ class APIv2(api.BaseAPI):
):
"""Get floating IP pools
- https://developer.openstack.org/api-ref/compute/?expanded=#list-floating-ip-pools
+ https://docs.openstack.org/api-ref/compute/?expanded=#list-floating-ip-pools
:returns:
list of floating IP pools
@@ -276,7 +276,7 @@ class APIv2(api.BaseAPI):
):
"""Lists hypervisor Hosts
- https://developer.openstack.org/api-ref/compute/#list-hosts
+ https://docs.openstack.org/api-ref/compute/#list-hosts
Valid for Compute 2.0 - 2.42
:param string zone:
@@ -299,7 +299,7 @@ class APIv2(api.BaseAPI):
):
"""Modify host properties
- https://developer.openstack.org/api-ref/compute/#update-host-status
+ https://docs.openstack.org/api-ref/compute/#update-host-status
Valid for Compute 2.0 - 2.42
status
@@ -329,7 +329,7 @@ class APIv2(api.BaseAPI):
):
"""Show host
- https://developer.openstack.org/api-ref/compute/#show-host-details
+ https://docs.openstack.org/api-ref/compute/#show-host-details
Valid for Compute 2.0 - 2.42
"""
@@ -356,7 +356,7 @@ class APIv2(api.BaseAPI):
):
"""Create a new network
- https://developer.openstack.org/api-ref/compute/#create-network
+ https://docs.openstack.org/api-ref/compute/#create-network
:param string name:
Network label (required)
@@ -387,7 +387,7 @@ class APIv2(api.BaseAPI):
):
"""Delete a network
- https://developer.openstack.org/api-ref/compute/#delete-network
+ https://docs.openstack.org/api-ref/compute/#delete-network
:param string network:
Network name or ID
@@ -411,7 +411,7 @@ class APIv2(api.BaseAPI):
):
"""Return a network given name or ID
- https://developer.openstack.org/api-ref/compute/#show-network-details
+ https://docs.openstack.org/api-ref/compute/#show-network-details
:param string network:
Network name or ID
@@ -431,7 +431,7 @@ class APIv2(api.BaseAPI):
):
"""Get networks
- https://developer.openstack.org/api-ref/compute/#list-networks
+ https://docs.openstack.org/api-ref/compute/#list-networks
:returns:
list of networks
@@ -450,7 +450,7 @@ class APIv2(api.BaseAPI):
):
"""Create a new security group
- https://developer.openstack.org/api-ref/compute/#create-security-group
+ https://docs.openstack.org/api-ref/compute/#create-security-group
:param string name:
Security group name
@@ -476,7 +476,7 @@ class APIv2(api.BaseAPI):
):
"""Delete a security group
- https://developer.openstack.org/api-ref/compute/#delete-security-group
+ https://docs.openstack.org/api-ref/compute/#delete-security-group
:param string security_group:
Security group name or ID
@@ -500,7 +500,7 @@ class APIv2(api.BaseAPI):
):
"""Return a security group given name or ID
- https://developer.openstack.org/api-ref/compute/#show-security-group-details
+ https://docs.openstack.org/api-ref/compute/#show-security-group-details
:param string security_group:
Security group name or ID
@@ -523,7 +523,7 @@ class APIv2(api.BaseAPI):
):
"""Get security groups
- https://developer.openstack.org/api-ref/compute/#list-security-groups
+ https://docs.openstack.org/api-ref/compute/#list-security-groups
:param integer limit:
query return count limit
@@ -556,7 +556,7 @@ class APIv2(api.BaseAPI):
):
"""Update a security group
- https://developer.openstack.org/api-ref/compute/#update-security-group
+ https://docs.openstack.org/api-ref/compute/#update-security-group
:param string security_group:
Security group name or ID
@@ -600,7 +600,7 @@ class APIv2(api.BaseAPI):
):
"""Create a new security group rule
- https://developer.openstack.org/api-ref/compute/#create-security-group-rule
+ https://docs.openstack.org/api-ref/compute/#create-security-group-rule
:param string security_group_id:
Security group ID
@@ -643,7 +643,7 @@ class APIv2(api.BaseAPI):
):
"""Delete a security group rule
- https://developer.openstack.org/api-ref/compute/#delete-security-group-rule
+ https://docs.openstack.org/api-ref/compute/#delete-security-group-rule
:param string security_group_rule_id:
Security group rule ID
diff --git a/openstackclient/common/client_config.py b/openstackclient/common/client_config.py
deleted file mode 100644
index a22dd0c..0000000
--- a/openstackclient/common/client_config.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# 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.
-#
-
-"""OpenStackConfig subclass for argument compatibility"""
-
-from osc_lib.cli import client_config
-
-
-# Sublcass OpenStackConfig in order to munge config values
-# before auth plugins are loaded
-class OSC_Config(client_config.OSC_Config):
-
- # TODO(dtroyer): Remove _auth_default_domain when the v3otp fix is
- # backported to osc-lib, should be in release 1.3.0
- def _auth_default_domain(self, config):
- """Set a default domain from available arguments
-
- Migrated from clientmanager.setup_auth()
- """
-
- identity_version = config.get('identity_api_version', '')
- auth_type = config.get('auth_type', None)
-
- # TODO(mordred): This is a usability improvement that's broadly useful
- # We should port it back up into os-client-config.
- default_domain = config.get('default_domain', None)
- if (identity_version == '3' and
- not auth_type.startswith('v2') and
- default_domain):
-
- # NOTE(stevemar): If PROJECT_DOMAIN_ID or PROJECT_DOMAIN_NAME is
- # present, then do not change the behaviour. Otherwise, set the
- # PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
- if (
- auth_type in ("password", "v3password", "v3totp") and
- not config['auth'].get('project_domain_id') and
- not config['auth'].get('project_domain_name')
- ):
- config['auth']['project_domain_id'] = default_domain
-
- # NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present,
- # then do not change the behaviour. Otherwise, set the
- # USER_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
- # NOTE(aloga): this should only be set if there is a username.
- # TODO(dtroyer): Move this to os-client-config after the plugin has
- # been loaded so we can check directly if the options are accepted.
- if (
- auth_type in ("password", "v3password", "v3totp") and
- not config['auth'].get('user_domain_id') and
- not config['auth'].get('user_domain_name')
- ):
- config['auth']['user_domain_id'] = default_domain
- return config
-
- def load_auth_plugin(self, config):
- """Get auth plugin and validate args"""
-
- loader = self._get_auth_loader(config)
- config = self._validate_auth(config, loader)
- auth_plugin = loader.load_from_options(**config['auth'])
- return auth_plugin
diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py
index 57825bb..53b30d5 100644
--- a/openstackclient/common/configuration.py
+++ b/openstackclient/common/configuration.py
@@ -25,6 +25,8 @@ REDACTED = "<redacted>"
class ShowConfiguration(command.ShowOne):
_description = _("Display configuration details")
+ auth_required = False
+
def get_parser(self, prog_name):
parser = super(ShowConfiguration, self).get_parser(prog_name)
mask_group = parser.add_mutually_exclusive_group()
@@ -45,13 +47,21 @@ class ShowConfiguration(command.ShowOne):
def take_action(self, parsed_args):
- auth_plg_name = self.app.client_manager.auth_plugin_name
- secret_opts = [o.dest for o in base.get_plugin_options(auth_plg_name)
- if o.secret]
-
info = self.app.client_manager.get_configuration()
+
+ # Assume a default secret list in case we do not have an auth_plugin
+ secret_opts = ["password", "token"]
+
+ if getattr(self.app.client_manager, "auth_plugin_name", None):
+ auth_plg_name = self.app.client_manager.auth_plugin_name
+ secret_opts = [
+ o.dest for o in base.get_plugin_options(auth_plg_name)
+ if o.secret
+ ]
+
for key, value in six.iteritems(info.pop('auth', {})):
if parsed_args.mask and key.lower() in secret_opts:
- value = REDACTED
+ value = REDACTED
info['auth.' + key] = value
+
return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py
index fa64647..3834de1 100644
--- a/openstackclient/compute/v2/aggregate.py
+++ b/openstackclient/compute/v2/aggregate.py
@@ -18,6 +18,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -58,6 +59,15 @@ class AddAggregateHost(command.ShowOne):
info = {}
info.update(data._info)
+
+ # Special mapping for columns to make the output easier to read:
+ # 'metadata' --> 'properties'
+ info.update(
+ {
+ 'hosts': format_columns.ListColumn(info.pop('hosts')),
+ 'properties': format_columns.DictColumn(info.pop('metadata')),
+ },
+ )
return zip(*sorted(six.iteritems(info)))
@@ -101,8 +111,20 @@ class CreateAggregate(command.ShowOne):
parsed_args.property,
)._info)
- # TODO(dtroyer): re-format metadata field to properites as
- # in the set command
+ # Special mapping for columns to make the output easier to read:
+ # 'metadata' --> 'properties'
+ hosts = None
+ properties = None
+ if 'hosts' in info.keys():
+ hosts = format_columns.ListColumn(info.pop('hosts'))
+ if 'metadata' in info.keys():
+ properties = format_columns.DictColumn(info.pop('metadata'))
+ info.update(
+ {
+ 'hosts': hosts,
+ 'properties': properties,
+ },
+ )
return zip(*sorted(six.iteritems(info)))
@@ -186,6 +208,10 @@ class ListAggregate(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
+ formatters={
+ 'Hosts': format_columns.ListColumn,
+ 'Metadata': format_columns.DictColumn,
+ },
) for s in data))
@@ -220,6 +246,15 @@ class RemoveAggregateHost(command.ShowOne):
info = {}
info.update(data._info)
+
+ # Special mapping for columns to make the output easier to read:
+ # 'metadata' --> 'properties'
+ info.update(
+ {
+ 'hosts': format_columns.ListColumn(info.pop('hosts')),
+ 'properties': format_columns.DictColumn(info.pop('metadata')),
+ },
+ )
return zip(*sorted(six.iteritems(info)))
@@ -326,7 +361,12 @@ class ShowAggregate(command.ShowOne):
# 'metadata' --> 'properties'
data._info.update(
{
- 'properties': utils.format_dict(data._info.pop('metadata')),
+ 'hosts': format_columns.ListColumn(
+ data._info.pop('hosts')
+ ),
+ 'properties': format_columns.DictColumn(
+ data._info.pop('metadata')
+ ),
},
)
diff --git a/openstackclient/compute/v2/fixedip.py b/openstackclient/compute/v2/fixedip.py
deleted file mode 100644
index 0c0b619..0000000
--- a/openstackclient/compute/v2/fixedip.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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.
-#
-
-"""Fixed IP action implementations"""
-
-import logging
-
-from osc_lib.command import command
-from osc_lib import utils
-
-from openstackclient.i18n import _
-
-
-class AddFixedIP(command.Command):
- _description = _("Add fixed IP address to server")
-
- # TODO(tangchen): Remove this class and ``ip fixed add`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def get_parser(self, prog_name):
- parser = super(AddFixedIP, self).get_parser(prog_name)
- parser.add_argument(
- "network",
- metavar="<network>",
- help=_("Network to fetch an IP address from (name or ID)"),
- )
- parser.add_argument(
- "server",
- metavar="<server>",
- help=_("Server to receive the IP address (name or ID)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "server add fixed ip" instead.'))
-
- compute_client = self.app.client_manager.compute
-
- network = utils.find_resource(
- compute_client.networks, parsed_args.network)
-
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
-
- server.add_fixed_ip(network.id)
-
-
-class RemoveFixedIP(command.Command):
- _description = _("Remove fixed IP address from server")
-
- # TODO(tangchen): Remove this class and ``ip fixed remove`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def get_parser(self, prog_name):
- parser = super(RemoveFixedIP, self).get_parser(prog_name)
- parser.add_argument(
- "ip_address",
- metavar="<ip-address>",
- help=_("IP address to remove from server (name only)"),
- )
- parser.add_argument(
- "server",
- metavar="<server>",
- help=_("Server to remove the IP address from (name or ID)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "server remove fixed ip" instead.'))
-
- compute_client = self.app.client_manager.compute
-
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
-
- server.remove_fixed_ip(parsed_args.ip_address)
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 2cc5f1e..4f1e48a 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -432,7 +432,7 @@ class ShowFlavor(command.ShowOne):
projects = [utils.get_field(access, 'tenant_id')
for access in flavor_access]
# TODO(Huanxuan Ao): This format case can be removed after
- # patch https://review.openstack.org/#/c/330223/ merged.
+ # patch https://review.opendev.org/#/c/330223/ merged.
access_projects = utils.format_list(projects)
except Exception as e:
msg = _("Failed to get access projects list "
diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py
deleted file mode 100644
index 69595be..0000000
--- a/openstackclient/compute/v2/floatingip.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-#
-# 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.
-#
-
-"""Floating IP action implementations"""
-
-import logging
-
-from osc_lib.command import command
-from osc_lib import utils
-
-from openstackclient.i18n import _
-
-
-class AddFloatingIP(command.Command):
- _description = _("Add floating IP address to server")
-
- # TODO(tangchen): Remove this class and ``ip floating add`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def get_parser(self, prog_name):
- parser = super(AddFloatingIP, self).get_parser(prog_name)
- parser.add_argument(
- "ip_address",
- metavar="<ip-address>",
- help=_("IP address to add to server (name only)"),
- )
- parser.add_argument(
- "server",
- metavar="<server>",
- help=_("Server to receive the IP address (name or ID)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "server add floating ip" instead.'))
-
- compute_client = self.app.client_manager.compute
-
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
-
- server.add_floating_ip(parsed_args.ip_address)
-
-
-class RemoveFloatingIP(command.Command):
- _description = _("Remove floating IP address from server")
-
- # TODO(tangchen): Remove this class and ``ip floating remove`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def get_parser(self, prog_name):
- parser = super(RemoveFloatingIP, self).get_parser(prog_name)
- parser.add_argument(
- "ip_address",
- metavar="<ip-address>",
- help=_("IP address to remove from server (name only)"),
- )
- parser.add_argument(
- "server",
- metavar="<server>",
- help=_("Server to remove the IP address from (name or ID)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "server remove floating ip" instead.'))
-
- compute_client = self.app.client_manager.compute
-
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
-
- server.remove_floating_ip(parsed_args.ip_address)
diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py
index 406aa91..0d367fe 100644
--- a/openstackclient/compute/v2/hypervisor.py
+++ b/openstackclient/compute/v2/hypervisor.py
@@ -112,7 +112,7 @@ class ShowHypervisor(command.ShowOne):
# example: 17:37:14 up 2:33, 3 users,
# load average: 0.33, 0.36, 0.34
m = re.match(
- "\s*(.+)\sup\s+(.+),\s+(.+)\susers?,\s+load average:\s(.+)",
+ r"\s*(.+)\sup\s+(.+),\s+(.+)\susers?,\s+load average:\s(.+)",
uptime['uptime'])
if m:
hypervisor["host_time"] = m.group(1)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 3e1deed..538c9c4 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -79,37 +79,37 @@ def _format_servers_list_power_state(state):
def _get_ip_address(addresses, address_type, ip_address_family):
- # Old style addresses
- if address_type in addresses:
- for addy in addresses[address_type]:
+ # Old style addresses
+ if address_type in addresses:
+ for addy in addresses[address_type]:
+ if int(addy['version']) in ip_address_family:
+ return addy['addr']
+
+ # New style addresses
+ new_address_type = address_type
+ if address_type == 'public':
+ new_address_type = 'floating'
+ if address_type == 'private':
+ new_address_type = 'fixed'
+ for network in addresses:
+ for addy in addresses[network]:
+ # Case where it is list of strings
+ if isinstance(addy, six.string_types):
+ if new_address_type == 'fixed':
+ return addresses[network][0]
+ else:
+ return addresses[network][-1]
+ # Case where it is a dict
+ if 'OS-EXT-IPS:type' not in addy:
+ continue
+ if addy['OS-EXT-IPS:type'] == new_address_type:
if int(addy['version']) in ip_address_family:
return addy['addr']
-
- # New style addresses
- new_address_type = address_type
- if address_type == 'public':
- new_address_type = 'floating'
- if address_type == 'private':
- new_address_type = 'fixed'
- for network in addresses:
- for addy in addresses[network]:
- # Case where it is list of strings
- if isinstance(addy, six.string_types):
- if new_address_type == 'fixed':
- return addresses[network][0]
- else:
- return addresses[network][-1]
- # Case where it is a dict
- if 'OS-EXT-IPS:type' not in addy:
- continue
- if addy['OS-EXT-IPS:type'] == new_address_type:
- if int(addy['version']) in ip_address_family:
- return addy['addr']
- msg = _("ERROR: No %(type)s IP version %(family)s address found")
- raise exceptions.CommandError(
- msg % {"type": address_type,
- "family": ip_address_family}
- )
+ msg = _("ERROR: No %(type)s IP version %(family)s address found")
+ raise exceptions.CommandError(
+ msg % {"type": address_type,
+ "family": ip_address_family}
+ )
def _prefix_checked_value(prefix):
@@ -424,7 +424,10 @@ class AddServerSecurityGroup(command.Command):
class AddServerVolume(command.Command):
- _description = _("Add volume to server")
+ _description = _(
+ "Add volume to server. "
+ "Specify ``--os-compute-api-version 2.20`` or higher to add a volume "
+ "to a server with status ``SHELVED`` or ``SHELVED_OFFLOADED``.")
def get_parser(self, prog_name):
parser = super(AddServerVolume, self).get_parser(prog_name)
@@ -551,6 +554,33 @@ class CreateServer(command.ShowOne):
help=_('Select an availability zone for the server'),
)
parser.add_argument(
+ '--host',
+ metavar='<host>',
+ help=_('Requested host to create servers. Admin only '
+ 'by default. (supported by --os-compute-api-version 2.74 '
+ 'or above)'),
+ )
+ parser.add_argument(
+ '--hypervisor-hostname',
+ metavar='<hypervisor-hostname>',
+ help=_('Requested hypervisor hostname to create servers. Admin '
+ 'only by default. (supported by --os-compute-api-version '
+ '2.74 or above)'),
+ )
+ parser.add_argument(
+ '--boot-from-volume',
+ metavar='<volume-size>',
+ type=int,
+ help=_('When used in conjunction with the ``--image`` or '
+ '``--image-property`` option, this option automatically '
+ 'creates a block device mapping with a boot index of 0 '
+ 'and tells the compute service to create a volume of the '
+ 'given size (in GB) from the specified image and use it '
+ 'as the root disk of the server. The root volume will not '
+ 'be deleted when the server is deleted. This option is '
+ 'mutually exclusive with the ``--volume`` option.')
+ )
+ parser.add_argument(
'--block-device-mapping',
metavar='<dev-name=mapping>',
action=parseractions.KeyValueAction,
@@ -563,9 +593,11 @@ class CreateServer(command.ShowOne):
'<dev-name>=<id>:<type>:<size(GB)>:<delete-on-terminate>\n'
'<dev-name>: block device name, like: vdb, xvdc '
'(required)\n'
- '<id>: UUID of the volume or snapshot (required)\n'
- '<type>: volume or snapshot; default: volume (optional)\n'
- '<size(GB)>: volume size if create from snapshot '
+ '<id>: Name or ID of the volume, volume snapshot or image '
+ '(required)\n'
+ '<type>: volume, snapshot or image; default: volume '
+ '(optional)\n'
+ '<size(GB)>: volume size if create from image or snapshot '
'(optional)\n'
'<delete-on-terminate>: true or false; default: false '
'(optional)\n'
@@ -711,6 +743,10 @@ class CreateServer(command.ShowOne):
# Lookup parsed_args.volume
volume = None
if parsed_args.volume:
+ # --volume and --boot-from-volume are mutually exclusive.
+ if parsed_args.boot_from_volume:
+ raise exceptions.CommandError(
+ _('--volume is not allowed with --boot-from-volume'))
volume = utils.find_resource(
volume_client.volumes,
parsed_args.volume,
@@ -720,8 +756,6 @@ class CreateServer(command.ShowOne):
flavor = utils.find_resource(compute_client.flavors,
parsed_args.flavor)
- boot_args = [parsed_args.server_name, image, flavor]
-
files = {}
for f in parsed_args.file:
dst, src = f.split('=', 1)
@@ -768,6 +802,20 @@ class CreateServer(command.ShowOne):
'source_type': 'volume',
'destination_type': 'volume'
}]
+ elif parsed_args.boot_from_volume:
+ # Tell nova to create a root volume from the image provided.
+ block_device_mapping_v2 = [{
+ 'uuid': image.id,
+ 'boot_index': '0',
+ 'source_type': 'image',
+ 'destination_type': 'volume',
+ 'volume_size': parsed_args.boot_from_volume
+ }]
+ # If booting from volume we do not pass an image to compute.
+ image = None
+
+ boot_args = [parsed_args.server_name, image, flavor]
+
# Handle block device by device name order, like: vdb -> vdc -> vdd
for dev_name in sorted(six.iterkeys(parsed_args.block_device_mapping)):
dev_map = parsed_args.block_device_mapping[dev_name]
@@ -776,7 +824,7 @@ class CreateServer(command.ShowOne):
mapping = {'device_name': dev_name}
# 1. decide source and destination type
if (len(dev_map) > 1 and
- dev_map[1] in ('volume', 'snapshot')):
+ dev_map[1] in ('volume', 'snapshot', 'image')):
mapping['source_type'] = dev_map[1]
else:
mapping['source_type'] = 'volume'
@@ -791,14 +839,29 @@ class CreateServer(command.ShowOne):
snapshot_id = utils.find_resource(
volume_client.volume_snapshots, dev_map[0]).id
mapping['uuid'] = snapshot_id
+ elif mapping['source_type'] == 'image':
+ # NOTE(mriedem): In case --image is specified with the same
+ # image, that becomes the root disk for the server. If the
+ # block device is specified with a root device name, e.g.
+ # vda, then the compute API will likely fail complaining
+ # that there is a conflict. So if using the same image ID,
+ # which doesn't really make sense but it's allowed, the
+ # device name would need to be a non-root device, e.g. vdb.
+ # Otherwise if the block device image is different from the
+ # one specified by --image, then the compute service will
+ # create a volume from the image and attach it to the
+ # server as a non-root volume.
+ image_id = utils.find_resource(
+ image_client.images, dev_map[0]).id
+ mapping['uuid'] = image_id
# 3. append size and delete_on_termination if exist
if len(dev_map) > 2 and dev_map[2]:
mapping['volume_size'] = dev_map[2]
if len(dev_map) > 3 and dev_map[3]:
mapping['delete_on_termination'] = dev_map[3]
else:
- msg = _("Volume or snapshot (name or ID) must be specified if "
- "--block-device-mapping is specified")
+ msg = _("Volume, volume snapshot or image (name or ID) must "
+ "be specified if --block-device-mapping is specified")
raise exceptions.CommandError(msg)
block_device_mapping_v2.append(mapping)
@@ -924,6 +987,21 @@ class CreateServer(command.ShowOne):
if parsed_args.description:
boot_kwargs['description'] = parsed_args.description
+ if parsed_args.host:
+ if compute_client.api_version < api_versions.APIVersion("2.74"):
+ msg = _("Specifying --host is not supported for "
+ "--os-compute-api-version less than 2.74")
+ raise exceptions.CommandError(msg)
+ boot_kwargs['host'] = parsed_args.host
+
+ if parsed_args.hypervisor_hostname:
+ if compute_client.api_version < api_versions.APIVersion("2.74"):
+ msg = _("Specifying --hypervisor-hostname is not supported "
+ "for --os-compute-api-version less than 2.74")
+ raise exceptions.CommandError(msg)
+ boot_kwargs['hypervisor_hostname'] = (
+ parsed_args.hypervisor_hostname)
+
LOG.debug('boot_args: %s', boot_args)
LOG.debug('boot_kwargs: %s', boot_kwargs)
@@ -1160,6 +1238,21 @@ class ListServer(command.Lister):
" The provided time should be an ISO 8061 formatted time"
" (e.g., 2016-03-04T06:27:59Z).")
)
+ lock_group = parser.add_mutually_exclusive_group()
+ lock_group.add_argument(
+ '--locked',
+ action='store_true',
+ default=False,
+ help=_('Only display locked servers. '
+ 'Requires ``--os-compute-api-version`` 2.73 or greater.'),
+ )
+ lock_group.add_argument(
+ '--unlocked',
+ action='store_true',
+ default=False,
+ help=_('Only display unlocked servers. '
+ 'Requires ``--os-compute-api-version`` 2.73 or greater.'),
+ )
return parser
def take_action(self, parsed_args):
@@ -1215,6 +1308,18 @@ class ListServer(command.Lister):
'changes-before': parsed_args.changes_before,
'changes-since': parsed_args.changes_since,
}
+ support_locked = (compute_client.api_version >=
+ api_versions.APIVersion('2.73'))
+ if not support_locked and (parsed_args.locked or parsed_args.unlocked):
+ msg = _('--os-compute-api-version 2.73 or greater is required to '
+ 'use the (un)locked filter option.')
+ raise exceptions.CommandError(msg)
+ elif support_locked:
+ # Only from 2.73.
+ if parsed_args.locked:
+ search_opts['locked'] = True
+ if parsed_args.unlocked:
+ search_opts['locked'] = False
LOG.debug('search options: %s', search_opts)
if search_opts['changes-before']:
@@ -1320,9 +1425,12 @@ class ListServer(command.Lister):
# Create a dict that maps image_id to image object.
# Needed so that we can display the "Image Name" column.
# "Image Name" is not crucial, so we swallow any exceptions.
+ # The 'image' attribute can be an empty string if the server was
+ # booted from a volume.
if parsed_args.name_lookup_one_by_one or image_id:
for i_id in set(filter(lambda x: x is not None,
- (s.image.get('id') for s in data))):
+ (s.image.get('id') for s in data
+ if s.image))):
try:
images[i_id] = image_client.images.get(i_id)
except Exception:
@@ -1412,16 +1520,28 @@ class LockServer(command.Command):
nargs='+',
help=_('Server(s) to lock (name or ID)'),
)
+ parser.add_argument(
+ '--reason',
+ metavar='<reason>',
+ default=None,
+ help=_("Reason for locking the server(s). Requires "
+ "``--os-compute-api-version`` 2.73 or greater.")
+ )
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
+ support_reason = compute_client.api_version >= api_versions.APIVersion(
+ '2.73')
+ if not support_reason and parsed_args.reason:
+ msg = _('--os-compute-api-version 2.73 or greater is required to '
+ 'use the --reason option.')
+ raise exceptions.CommandError(msg)
for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
- server,
- ).lock()
+ serv = utils.find_resource(compute_client.servers, server)
+ (serv.lock(reason=parsed_args.reason) if support_reason
+ else serv.lock())
# FIXME(dtroyer): Here is what I want, how with argparse/cliff?
@@ -1470,13 +1590,13 @@ class MigrateServer(command.Command):
'and ``--live-migration`` are used, ``--live-migration`` '
'takes priority.'),
)
- # TODO(mriedem): Add support for --os-compute-api-version >= 2.56 where
- # you can cold migrate to a specified target host.
host_group.add_argument(
'--host',
metavar='<hostname>',
- help=_('Live migrate the server to the specified host. Requires '
- '``--os-compute-api-version`` 2.30 or greater.'),
+ help=_('Migrate the server to the specified host. Requires '
+ '``--os-compute-api-version`` 2.30 or greater when used '
+ 'with the ``--live-migration`` option, otherwise requires '
+ '``--os-compute-api-version`` 2.56 or greater.'),
)
migration_group = parser.add_mutually_exclusive_group()
migration_group.add_argument(
@@ -1566,16 +1686,22 @@ class MigrateServer(command.Command):
kwargs['disk_over_commit'] = parsed_args.disk_overcommit
server.live_migrate(**kwargs)
else:
- if (parsed_args.block_migration or parsed_args.disk_overcommit or
- parsed_args.host):
- # TODO(mriedem): Allow --host for cold migration if
- # --os-compute-api-version >= 2.56.
+ if parsed_args.block_migration or parsed_args.disk_overcommit:
raise exceptions.CommandError(
"--live-migration must be specified if "
- "--block-migration, --disk-overcommit or --host is "
+ "--block-migration or --disk-overcommit is "
"specified")
+ if parsed_args.host:
+ if (compute_client.api_version <
+ api_versions.APIVersion('2.56')):
+ msg = _(
+ '--os-compute-api-version 2.56 or greater is '
+ 'required to use --host without --live-migration.'
+ )
+ raise exceptions.CommandError(msg)
- server.migrate()
+ kwargs = {'host': parsed_args.host} if parsed_args.host else {}
+ server.migrate(**kwargs)
if parsed_args.wait:
if utils.wait_for_status(
@@ -1949,7 +2075,11 @@ class RemoveServerSecurityGroup(command.Command):
class RemoveServerVolume(command.Command):
- _description = _("Remove volume from server")
+ _description = _(
+ "Remove volume from server. "
+ "Specify ``--os-compute-api-version 2.20`` or higher to remove a "
+ "volume from a server with status ``SHELVED`` or "
+ "``SHELVED_OFFLOADED``.")
def get_parser(self, prog_name):
parser = super(RemoveServerVolume, self).get_parser(prog_name)
@@ -2096,11 +2226,66 @@ the new server and restart the old one.""")
self.app.stdout.write(_('Error resizing server\n'))
raise SystemExit
elif parsed_args.confirm:
+ self.log.warning(_(
+ "The --confirm option has been deprecated. Please use the "
+ "'openstack server resize confirm' command instead."))
compute_client.servers.confirm_resize(server)
elif parsed_args.revert:
+ self.log.warning(_(
+ "The --revert option has been deprecated. Please use the "
+ "'openstack server resize revert' command instead."))
compute_client.servers.revert_resize(server)
+class ResizeConfirm(command.Command):
+ _description = _("""Confirm server resize.
+
+Confirm (verify) success of resize operation and release the old server.""")
+
+ def get_parser(self, prog_name):
+ parser = super(ResizeConfirm, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help=_('Server (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+
+ compute_client = self.app.client_manager.compute
+ server = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ )
+ server.confirm_resize()
+
+
+class ResizeRevert(command.Command):
+ _description = _("""Revert server resize.
+
+Revert the resize operation. Release the new server and restart the old
+one.""")
+
+ def get_parser(self, prog_name):
+ parser = super(ResizeRevert, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help=_('Server (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+
+ compute_client = self.app.client_manager.compute
+ server = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ )
+ server.revert_resize()
+
+
class RestoreServer(command.Command):
_description = _("Restore server(s)")
@@ -2626,12 +2811,32 @@ class UnshelveServer(command.Command):
nargs='+',
help=_('Server(s) to unshelve (name or ID)'),
)
+ parser.add_argument(
+ '--availability-zone',
+ default=None,
+ help=_('Name of the availability zone in which to unshelve a '
+ 'SHELVED_OFFLOADED server (supported by '
+ '--os-compute-api-version 2.77 or above)'),
+ )
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
+ support_az = compute_client.api_version >= api_versions.APIVersion(
+ '2.77')
+ if not support_az and parsed_args.availability_zone:
+ msg = _("--os-compute-api-version 2.77 or greater is required "
+ "to support the '--availability-zone' option.")
+ raise exceptions.CommandError(msg)
+
for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
- server,
- ).unshelve()
+ if support_az:
+ utils.find_resource(
+ compute_client.servers,
+ server
+ ).unshelve(availability_zone=parsed_args.availability_zone)
+ else:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).unshelve()
diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py
index c7d2e2e..6d33d02 100644
--- a/openstackclient/compute/v2/server_event.py
+++ b/openstackclient/compute/v2/server_event.py
@@ -28,7 +28,10 @@ LOG = logging.getLogger(__name__)
class ListServerEvent(command.Lister):
- _description = _("List recent events of a server")
+ _description = _(
+ "List recent events of a server. "
+ "Specify ``--os-compute-api-version 2.21`` "
+ "or higher to show events for a deleted server.")
def get_parser(self, prog_name):
parser = super(ListServerEvent, self).get_parser(prog_name)
@@ -92,8 +95,11 @@ class ListServerEvent(command.Lister):
class ShowServerEvent(command.ShowOne):
_description = _(
- "Show server event details. Specify ``--os-compute-api-version 2.51`` "
- "or higher to show events for non-admin users.")
+ "Show server event details. "
+ "Specify ``--os-compute-api-version 2.21`` "
+ "or higher to show event details for a deleted server. "
+ "Specify ``--os-compute-api-version 2.51`` "
+ "or higher to show event details for non-admin users.")
def get_parser(self, prog_name):
parser = super(ShowServerEvent, self).get_parser(prog_name)
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index 18e6d9d..625c0fa 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -37,7 +37,10 @@ class DeleteService(command.Command):
"service",
metavar="<service>",
nargs='+',
- help=_("Compute service(s) to delete (ID only)")
+ help=_("Compute service(s) to delete (ID only). If using "
+ "``--os-compute-api-version`` 2.53 or greater, the ID is "
+ "a UUID which can be retrieved by listing compute services "
+ "using the same 2.53+ microversion.")
)
return parser
@@ -60,7 +63,11 @@ class DeleteService(command.Command):
class ListService(command.Lister):
- _description = _("List compute services")
+ _description = _("List compute services. Using "
+ "``--os-compute-api-version`` 2.53 or greater will "
+ "return the ID as a UUID value which can be used to "
+ "uniquely identify the service in a multi-cell "
+ "deployment.")
def get_parser(self, prog_name):
parser = super(ListService, self).get_parser(prog_name)
@@ -72,7 +79,8 @@ class ListService(command.Lister):
parser.add_argument(
"--service",
metavar="<service>",
- help=_("List only specified service (name only)")
+ help=_("List only specified service binaries (name only). For "
+ "example, ``nova-compute``, ``nova-conductor``, etc.")
)
parser.add_argument(
"--long",
@@ -126,7 +134,8 @@ class SetService(command.Command):
parser.add_argument(
"service",
metavar="<service>",
- help=_("Name of service (Binary name)")
+ help=_("Name of service (Binary name), for example "
+ "``nova-compute``")
)
enabled_group = parser.add_mutually_exclusive_group()
enabled_group.add_argument(
@@ -150,15 +159,44 @@ class SetService(command.Command):
up_down_group.add_argument(
'--up',
action='store_true',
- help=_('Force up service'),
+ help=_('Force up service. Requires ``--os-compute-api-version`` '
+ '2.11 or greater.'),
)
up_down_group.add_argument(
'--down',
action='store_true',
- help=_('Force down service'),
+ help=_('Force down service. Requires ``--os-compute-api-version`` '
+ '2.11 or greater.'),
)
return parser
+ @staticmethod
+ def _find_service_by_host_and_binary(cs, host, binary):
+ """Utility method to find a compute service by host and binary
+
+ :param host: the name of the compute service host
+ :param binary: the compute service binary, e.g. nova-compute
+ :returns: novaclient.v2.services.Service dict-like object
+ :raises: CommandError if no or multiple results were found
+ """
+ services = cs.list(host=host, binary=binary)
+ # Did we find anything?
+ if not len(services):
+ msg = _('Compute service for host "%(host)s" and binary '
+ '"%(binary)s" not found.') % {
+ 'host': host, 'binary': binary}
+ raise exceptions.CommandError(msg)
+ # Did we find more than one result? This should not happen but let's
+ # be safe.
+ if len(services) > 1:
+ # TODO(mriedem): If we have an --id option for 2.53+ then we can
+ # say to use that option to uniquely identify the service.
+ msg = _('Multiple compute services found for host "%(host)s" and '
+ 'binary "%(binary)s". Unable to proceed.') % {
+ 'host': host, 'binary': binary}
+ raise exceptions.CommandError(msg)
+ return services[0]
+
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
cs = compute_client.services
@@ -169,6 +207,20 @@ class SetService(command.Command):
"--disable specified.")
raise exceptions.CommandError(msg)
+ # Starting with microversion 2.53, there is a single
+ # PUT /os-services/{service_id} API for updating nova-compute
+ # services. If 2.53+ is used we need to find the nova-compute
+ # service using the --host and --service (binary) values.
+ requires_service_id = (
+ compute_client.api_version >= api_versions.APIVersion('2.53'))
+ service_id = None
+ if requires_service_id:
+ # TODO(mriedem): Add an --id option so users can pass the service
+ # id (as a uuid) directly rather than make us look it up using
+ # host/binary.
+ service_id = SetService._find_service_by_host_and_binary(
+ cs, parsed_args.host, parsed_args.service).id
+
result = 0
enabled = None
try:
@@ -179,14 +231,21 @@ class SetService(command.Command):
if enabled is not None:
if enabled:
- cs.enable(parsed_args.host, parsed_args.service)
+ args = (service_id,) if requires_service_id else (
+ parsed_args.host, parsed_args.service)
+ cs.enable(*args)
else:
if parsed_args.disable_reason:
- cs.disable_log_reason(parsed_args.host,
- parsed_args.service,
- parsed_args.disable_reason)
+ args = (service_id, parsed_args.disable_reason) if \
+ requires_service_id else (
+ parsed_args.host,
+ parsed_args.service,
+ parsed_args.disable_reason)
+ cs.disable_log_reason(*args)
else:
- cs.disable(parsed_args.host, parsed_args.service)
+ args = (service_id,) if requires_service_id else (
+ parsed_args.host, parsed_args.service)
+ cs.disable(*args)
except Exception:
status = "enabled" if enabled else "disabled"
LOG.error("Failed to set service status to %s", status)
@@ -204,8 +263,9 @@ class SetService(command.Command):
'required')
raise exceptions.CommandError(msg)
try:
- cs.force_down(parsed_args.host, parsed_args.service,
- force_down=force_down)
+ args = (service_id, force_down) if requires_service_id else (
+ parsed_args.host, parsed_args.service, force_down)
+ cs.force_down(*args)
except Exception:
state = "down" if force_down else "up"
LOG.error("Failed to set service state to %s", state)
diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py
index 993bdd5..5d1e306 100644
--- a/openstackclient/identity/v2_0/catalog.py
+++ b/openstackclient/identity/v2_0/catalog.py
@@ -15,6 +15,7 @@
import logging
+from cliff import columns as cliff_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,20 +27,21 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-def _format_endpoints(eps=None):
- if not eps:
- return ""
- ret = ''
- for index, ep in enumerate(eps):
- region = eps[index].get('region')
- if region is None:
- region = '<none>'
- ret += region + '\n'
- for endpoint_type in ['publicURL', 'internalURL', 'adminURL']:
- url = eps[index].get(endpoint_type)
- if url:
- ret += " %s: %s\n" % (endpoint_type, url)
- return ret
+class EndpointsColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ if not self._value:
+ return ""
+ ret = ''
+ for ep in self._value:
+ region = ep.get('region')
+ if region is None:
+ region = '<none>'
+ ret += region + '\n'
+ for endpoint_type in ['publicURL', 'internalURL', 'adminURL']:
+ url = ep.get(endpoint_type)
+ if url:
+ ret += " %s: %s\n" % (endpoint_type, url)
+ return ret
class ListCatalog(command.Lister):
@@ -60,7 +62,7 @@ class ListCatalog(command.Lister):
(utils.get_dict_properties(
s, columns,
formatters={
- 'Endpoints': _format_endpoints,
+ 'Endpoints': EndpointsColumn,
},
) for s in data))
@@ -91,7 +93,7 @@ class ShowCatalog(command.ShowOne):
if (service.get('name') == parsed_args.service or
service.get('type') == parsed_args.service):
data = service
- data['endpoints'] = _format_endpoints(data['endpoints'])
+ data['endpoints'] = EndpointsColumn(data['endpoints'])
if 'endpoints_links' in data:
data.pop('endpoints_links')
break
diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py
index 9bb5fc4..e001886 100644
--- a/openstackclient/identity/v2_0/project.py
+++ b/openstackclient/identity/v2_0/project.py
@@ -18,6 +18,7 @@
import logging
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -297,7 +298,7 @@ class ShowProject(command.ShowOne):
if v is not None:
properties[k] = v
- info['properties'] = utils.format_dict(properties)
+ info['properties'] = format_columns.DictColumn(properties)
return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py
index e254e05..e9fe50f 100644
--- a/openstackclient/identity/v2_0/role.py
+++ b/openstackclient/identity/v2_0/role.py
@@ -148,155 +148,11 @@ class DeleteRole(command.Command):
class ListRole(command.Lister):
_description = _("List roles")
- def get_parser(self, prog_name):
- parser = super(ListRole, self).get_parser(prog_name)
- parser.add_argument(
- '--project',
- metavar='<project>',
- help=_('Filter roles by <project> (name or ID)'),
- )
- parser.add_argument(
- '--user',
- metavar='<user>',
- help=_('Filter roles by <user> (name or ID)'),
- )
- return parser
-
def take_action(self, parsed_args):
-
- def _deprecated():
- # NOTE(henry-nash): Deprecated as of Newton, so we should remove
- # this in the 'P' release.
- self.log.warning(_('Listing assignments using role list is '
- 'deprecated as of the Newton release. Use role '
- 'assignment list --user <user-name> --project '
- '<project-name> --names instead.'))
-
identity_client = self.app.client_manager.identity
- auth_ref = self.app.client_manager.auth_ref
-
- # No user or project specified, list all roles in the system
- if not parsed_args.user and not parsed_args.project:
- columns = ('ID', 'Name')
- data = identity_client.roles.list()
- elif parsed_args.user and parsed_args.project:
- user = utils.find_resource(
- identity_client.users,
- parsed_args.user,
- )
- project = utils.find_resource(
- identity_client.projects,
- parsed_args.project,
- )
- _deprecated()
- data = identity_client.roles.roles_for_user(user.id, project.id)
-
- elif parsed_args.user:
- user = utils.find_resource(
- identity_client.users,
- parsed_args.user,
- )
- if self.app.client_manager.auth_ref:
- project = utils.find_resource(
- identity_client.projects,
- auth_ref.project_id
- )
- else:
- msg = _("Project must be specified")
- raise exceptions.CommandError(msg)
- _deprecated()
- data = identity_client.roles.roles_for_user(user.id, project.id)
- elif parsed_args.project:
- project = utils.find_resource(
- identity_client.projects,
- parsed_args.project,
- )
- if self.app.client_manager.auth_ref:
- user = utils.find_resource(
- identity_client.users,
- auth_ref.user_id
- )
- else:
- msg = _("User must be specified")
- raise exceptions.CommandError(msg)
- _deprecated()
- data = identity_client.roles.roles_for_user(user.id, project.id)
-
- if parsed_args.user or parsed_args.project:
- columns = ('ID', 'Name', 'Project', 'User')
- for user_role in data:
- user_role.user = user.name
- user_role.project = project.name
-
- return (columns,
- (utils.get_item_properties(
- s, columns,
- formatters={},
- ) for s in data))
-
-
-class ListUserRole(command.Lister):
- _description = _("List user-role assignments")
-
- def get_parser(self, prog_name):
- parser = super(ListUserRole, self).get_parser(prog_name)
- parser.add_argument(
- 'user',
- metavar='<user>',
- nargs='?',
- help=_('User to list (name or ID)'),
- )
- parser.add_argument(
- '--project',
- metavar='<project>',
- help=_('Filter users by <project> (name or ID)'),
- )
- return parser
-
- def take_action(self, parsed_args):
- identity_client = self.app.client_manager.identity
- auth_ref = self.app.client_manager.auth_ref
-
- # Project and user are required, if not included in command args
- # default to the values used for authentication. For token-flow
- # authentication they must be included on the command line.
- if (not parsed_args.project and
- self.app.client_manager.auth_ref.project_id):
- parsed_args.project = auth_ref.project_id
- if not parsed_args.project:
- msg = _("Project must be specified")
- raise exceptions.CommandError(msg)
-
- if (not parsed_args.user and
- self.app.client_manager.auth_ref.user_id):
- parsed_args.user = auth_ref.user_id
- if not parsed_args.user:
- msg = _("User must be specified")
- raise exceptions.CommandError(msg)
-
- self.log.warning(_('Listing assignments using user role list is '
- 'deprecated as of the Newton release. Use role '
- 'assignment list --user <user-name> --project '
- '<project-name> --names instead.'))
- project = utils.find_resource(
- identity_client.tenants,
- parsed_args.project,
- )
- user = utils.find_resource(identity_client.users, parsed_args.user)
-
- data = identity_client.roles.roles_for_user(user.id, project.id)
-
- columns = (
- 'ID',
- 'Name',
- 'Project',
- 'User',
- )
- # Add the names to the output even though they will be constant
- for role in data:
- role.user = user.name
- role.project = project.name
+ columns = ('ID', 'Name')
+ data = identity_client.roles.list()
return (columns,
(utils.get_item_properties(
diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py
index 80f2d72..653de8e 100644
--- a/openstackclient/identity/v2_0/service.py
+++ b/openstackclient/identity/v2_0/service.py
@@ -15,7 +15,6 @@
"""Service action implementations"""
-import argparse
import logging
from osc_lib.command import command
@@ -36,17 +35,11 @@ class CreateService(command.ShowOne):
def get_parser(self, prog_name):
parser = super(CreateService, self).get_parser(prog_name)
parser.add_argument(
- 'type_or_name',
+ 'type',
metavar='<type>',
help=_('New service type (compute, image, identity, volume, etc)'),
)
- type_or_name_group = parser.add_mutually_exclusive_group()
- type_or_name_group.add_argument(
- '--type',
- metavar='<type>',
- help=argparse.SUPPRESS,
- )
- type_or_name_group.add_argument(
+ parser.add_argument(
'--name',
metavar='<name>',
help=_('New service name'),
@@ -61,29 +54,14 @@ class CreateService(command.ShowOne):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- type_or_name = parsed_args.type_or_name
name = parsed_args.name
type = parsed_args.type
- # If only a single positional is present, it's a <type>.
- # This is not currently legal so it is considered a new case.
- if not type and not name:
- type = type_or_name
- # If --type option is present then positional is handled as <name>;
- # display deprecation message.
- elif type:
- name = type_or_name
- LOG.warning(_('The argument --type is deprecated, use service'
- ' create --name <service-name> type instead.'))
- # If --name option is present the positional is handled as <type>.
- # Making --type optional is new, but back-compatible
- elif name:
- type = type_or_name
-
service = identity_client.services.create(
name,
type,
- parsed_args.description)
+ parsed_args.description,
+ )
info = {}
info.update(service._info)
diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py
index 2a3dde6..0675877 100644
--- a/openstackclient/identity/v2_0/user.py
+++ b/openstackclient/identity/v2_0/user.py
@@ -15,8 +15,10 @@
"""Identity v2.0 User action implementations"""
+import functools
import logging
+from cliff import columns as cliff_columns
from keystoneauth1 import exceptions as ks_exc
from osc_lib.command import command
from osc_lib import exceptions
@@ -29,6 +31,31 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
+class ProjectColumn(cliff_columns.FormattableColumn):
+ """Formattable column for project column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes project_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(ProjectColumn, project_cache)``.
+ """
+
+ def __init__(self, value, project_cache=None):
+ super(ProjectColumn, self).__init__(value)
+ self.project_cache = project_cache or {}
+
+ def human_readable(self):
+ project = self._value
+ if not project:
+ return ""
+ if project in self.project_cache.keys():
+ return self.project_cache[project].name
+ else:
+ return project
+
+
class CreateUser(command.ShowOne):
_description = _("Create new user")
@@ -187,15 +214,7 @@ class ListUser(command.Lister):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
-
- def _format_project(project):
- if not project:
- return ""
- if project in project_cache.keys():
- return project_cache[project].name
- else:
- return project
-
+ formatters = {}
project = None
if parsed_args.project:
project = utils.find_resource(
@@ -227,6 +246,8 @@ class ListUser(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ formatters['tenantId'] = functools.partial(
+ ProjectColumn, project_cache=project_cache)
else:
columns = column_headers = ('ID', 'Name')
data = identity_client.users.list(tenant_id=project)
@@ -251,7 +272,7 @@ class ListUser(command.Lister):
(utils.get_item_properties(
s, columns,
mixed_case_fields=('tenantId',),
- formatters={'tenantId': _format_project},
+ formatters=formatters,
) for s in data))
diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py
index 28f4fad..59430c4 100644
--- a/openstackclient/identity/v3/catalog.py
+++ b/openstackclient/identity/v3/catalog.py
@@ -15,6 +15,7 @@
import logging
+from cliff import columns as cliff_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,15 +27,16 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-def _format_endpoints(eps=None):
- if not eps:
- return ""
- ret = ''
- for ep in eps:
- region = ep.get('region_id') or ep.get('region') or '<none>'
- ret += region + '\n'
- ret += " %s: %s\n" % (ep['interface'], ep['url'])
- return ret
+class EndpointsColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ if not self._value:
+ return ""
+ ret = ''
+ for ep in self._value:
+ region = ep.get('region_id') or ep.get('region') or '<none>'
+ ret += region + '\n'
+ ret += " %s: %s\n" % (ep['interface'], ep['url'])
+ return ret
class ListCatalog(command.Lister):
@@ -55,7 +57,7 @@ class ListCatalog(command.Lister):
(utils.get_dict_properties(
s, columns,
formatters={
- 'Endpoints': _format_endpoints,
+ 'Endpoints': EndpointsColumn,
},
) for s in data))
@@ -86,7 +88,7 @@ class ShowCatalog(command.ShowOne):
if (service.get('name') == parsed_args.service or
service.get('type') == parsed_args.service):
data = dict(service)
- data['endpoints'] = _format_endpoints(data['endpoints'])
+ data['endpoints'] = EndpointsColumn(data['endpoints'])
if 'links' in data:
data.pop('links')
break
diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py
index d8951d3..b331518 100644
--- a/openstackclient/identity/v3/identity_provider.py
+++ b/openstackclient/identity/v3/identity_provider.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -103,7 +104,7 @@ class CreateIdentityProvider(command.ShowOne):
enabled=parsed_args.enabled)
idp._info.pop('links', None)
- remote_ids = utils.format_list(idp._info.pop('remote_ids', []))
+ remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', []))
idp._info['remote_ids'] = remote_ids
return zip(*sorted(six.iteritems(idp._info)))
@@ -245,6 +246,6 @@ class ShowIdentityProvider(command.ShowOne):
id=parsed_args.identity_provider)
idp._info.pop('links', None)
- remote_ids = utils.format_list(idp._info.pop('remote_ids', []))
+ remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', []))
idp._info['remote_ids'] = remote_ids
return zip(*sorted(six.iteritems(idp._info)))
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 58a76f8..0eeddd3 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -266,131 +266,28 @@ class ListRole(command.Lister):
def get_parser(self, prog_name):
parser = super(ListRole, self).get_parser(prog_name)
-
- # TODO(henry-nash): The use of the List Role command to list
- # assignments (as well as roles) has been deprecated. In order
- # to support domain specific roles, we are overriding the domain
- # option to allow specification of the domain for the role. This does
- # not conflict with any existing commands, since for the deprecated
- # assignments listing you were never allowed to only specify a domain
- # (you also needed to specify a user).
- #
- # Once we have removed the deprecated options entirely, we must
- # replace the call to _add_identity_and_resource_options_to_parser()
- # below with just adding the domain option into the parser.
- _add_identity_and_resource_options_to_parser(parser)
+ parser.add_argument(
+ '--domain',
+ metavar='<domain>',
+ help=_('Include <domain> (name or ID)'),
+ )
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- if parsed_args.user:
- user = common.find_user(
- identity_client,
- parsed_args.user,
- parsed_args.user_domain,
- )
- elif parsed_args.group:
- group = common.find_group(
- identity_client,
- parsed_args.group,
- parsed_args.group_domain,
- )
-
if parsed_args.domain:
domain = common.find_domain(
identity_client,
parsed_args.domain,
)
- elif parsed_args.project:
- project = common.find_project(
- identity_client,
- parsed_args.project,
- parsed_args.project_domain,
- )
-
- # no user or group specified, list all roles in the system
- if not parsed_args.user and not parsed_args.group:
- if not parsed_args.domain:
- columns = ('ID', 'Name')
- data = identity_client.roles.list()
- else:
- columns = ('ID', 'Name', 'Domain')
- data = identity_client.roles.list(domain_id=domain.id)
- for role in data:
- role.domain = domain.name
- elif parsed_args.user and parsed_args.domain:
- columns = ('ID', 'Name', 'Domain', 'User')
- data = identity_client.roles.list(
- user=user,
- domain=domain,
- os_inherit_extension_inherited=parsed_args.inherited
- )
- for user_role in data:
- user_role.user = user.name
- user_role.domain = domain.name
- self.log.warning(_('Listing assignments using role list is '
- 'deprecated. Use role assignment list --user '
- '<user-name> --domain <domain-name> --names '
- 'instead.'))
- elif parsed_args.user and parsed_args.project:
- columns = ('ID', 'Name', 'Project', 'User')
- data = identity_client.roles.list(
- user=user,
- project=project,
- os_inherit_extension_inherited=parsed_args.inherited
- )
- for user_role in data:
- user_role.user = user.name
- user_role.project = project.name
- self.log.warning(_('Listing assignments using role list is '
- 'deprecated. Use role assignment list --user '
- '<user-name> --project <project-name> --names '
- 'instead.'))
- elif parsed_args.user:
- columns = ('ID', 'Name')
- data = identity_client.roles.list(
- user=user,
- domain='default',
- os_inherit_extension_inherited=parsed_args.inherited
- )
- self.log.warning(_('Listing assignments using role list is '
- 'deprecated. Use role assignment list --user '
- '<user-name> --domain default --names '
- 'instead.'))
- elif parsed_args.group and parsed_args.domain:
- columns = ('ID', 'Name', 'Domain', 'Group')
- data = identity_client.roles.list(
- group=group,
- domain=domain,
- os_inherit_extension_inherited=parsed_args.inherited
- )
- for group_role in data:
- group_role.group = group.name
- group_role.domain = domain.name
- self.log.warning(_('Listing assignments using role list is '
- 'deprecated. Use role assignment list --group '
- '<group-name> --domain <domain-name> --names '
- 'instead.'))
- elif parsed_args.group and parsed_args.project:
- columns = ('ID', 'Name', 'Project', 'Group')
- data = identity_client.roles.list(
- group=group,
- project=project,
- os_inherit_extension_inherited=parsed_args.inherited
- )
- for group_role in data:
- group_role.group = group.name
- group_role.project = project.name
- self.log.warning(_('Listing assignments using role list is '
- 'deprecated. Use role assignment list --group '
- '<group-name> --project <project-name> --names '
- 'instead.'))
+ columns = ('ID', 'Name', 'Domain')
+ data = identity_client.roles.list(domain_id=domain.id)
+ for role in data:
+ role.domain = domain.name
else:
- msg = _("Error: If a user or group is specified, "
- "either --domain or --project must also be "
- "specified to list role grants.")
- raise exceptions.CommandError(msg)
+ columns = ('ID', 'Name')
+ data = identity_client.roles.list()
return (columns,
(utils.get_item_properties(
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index 64c4049..c2dab3e 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -21,8 +21,10 @@ import logging
import os
import sys
+from cliff import columns as cliff_columns
from glanceclient.common import utils as gc_utils
from osc_lib.api import utils as api_utils
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import utils
@@ -46,19 +48,18 @@ DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx",
LOG = logging.getLogger(__name__)
-def _format_visibility(data):
- """Return a formatted visibility string
+class VisibilityColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ """Return a formatted visibility string
- :param data:
- The server's visibility (is_public) status value: True, False
- :rtype:
- A string formatted to public/private
- """
+ :rtype:
+ A string formatted to public/private
+ """
- if data:
- return 'public'
- else:
- return 'private'
+ if self._value:
+ return 'public'
+ else:
+ return 'private'
class CreateImage(command.ShowOne):
@@ -182,29 +183,16 @@ class CreateImage(command.ShowOne):
help=_("Set a property on this image "
"(repeat option to set multiple properties)"),
)
- # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early
- # 2.x release. Do not remove before Jan 2017
- # and a 3.x release.
- project_group = parser.add_mutually_exclusive_group()
- project_group.add_argument(
+ parser.add_argument(
"--project",
metavar="<project>",
help=_("Set an alternate project on this image (name or ID)"),
)
- project_group.add_argument(
- "--owner",
- metavar="<project>",
- help=argparse.SUPPRESS,
- )
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- if getattr(parsed_args, 'owner', None) is not None:
- LOG.warning(_('The --owner option is deprecated, '
- 'please use --project instead.'))
-
# Build an attribute dict from the parsed args, only include
# attributes that were actually set on the command line
kwargs = {}
@@ -281,7 +269,8 @@ class CreateImage(command.ShowOne):
kwargs['data'].close()
info.update(image._info)
- info['properties'] = utils.format_dict(info.get('properties', {}))
+ info['properties'] = format_columns.DictColumn(
+ info.get('properties', {}))
return zip(*sorted(six.iteritems(info)))
@@ -442,8 +431,8 @@ class ListImage(command.Lister):
s,
columns,
formatters={
- 'is_public': _format_visibility,
- 'properties': utils.format_dict,
+ 'is_public': VisibilityColumn,
+ 'properties': format_columns.DictColumn,
},
) for s in data)
)
@@ -599,29 +588,16 @@ class SetImage(command.Command):
metavar="<checksum>",
help=_("Image hash used for verification"),
)
- # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early
- # 2.x release. Do not remove before Jan 2017
- # and a 3.x release.
- project_group = parser.add_mutually_exclusive_group()
- project_group.add_argument(
+ parser.add_argument(
"--project",
metavar="<project>",
help=_("Set an alternate project on this image (name or ID)"),
)
- project_group.add_argument(
- "--owner",
- metavar="<project>",
- help=argparse.SUPPRESS,
- )
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- if getattr(parsed_args, 'owner', None) is not None:
- LOG.warning(_('The --owner option is deprecated, '
- 'please use --project instead.'))
-
kwargs = {}
copy_attrs = ('name', 'owner', 'min_disk', 'min_ram', 'properties',
'container_format', 'disk_format', 'size', 'store',
@@ -740,5 +716,6 @@ class ShowImage(command.ShowOne):
if parsed_args.human_readable:
if 'size' in info:
info['size'] = utils.format_size(info['size'])
- info['properties'] = utils.format_dict(info.get('properties', {}))
+ info['properties'] = format_columns.DictColumn(
+ info.get('properties', {}))
return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index bdec99d..2b10c3a 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -22,6 +22,7 @@ import logging
from glanceclient.common import utils as gc_utils
from openstack.image import image_signer
from osc_lib.api import utils as api_utils
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -65,11 +66,11 @@ def _format_image(image):
properties[key] = image.get(key)
# format the tags if they are there
- info['tags'] = utils.format_list(image.get('tags'))
+ info['tags'] = format_columns.ListColumn(image.get('tags'))
# add properties back into the dictionary as a top-level key
if properties:
- info['properties'] = utils.format_dict(properties)
+ info['properties'] = format_columns.DictColumn(properties)
return info
@@ -250,20 +251,11 @@ class CreateImage(command.ShowOne):
help=_("Set a tag on this image "
"(repeat option to set multiple tags)"),
)
- # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early
- # 2.x release. Do not remove before Jan 2017
- # and a 3.x release.
- project_group = parser.add_mutually_exclusive_group()
- project_group.add_argument(
+ parser.add_argument(
"--project",
metavar="<project>",
help=_("Set an alternate project on this image (name or ID)"),
)
- project_group.add_argument(
- "--owner",
- metavar="<project>",
- help=argparse.SUPPRESS,
- )
common.add_project_domain_option_to_parser(parser)
for deadopt in self.deadopts:
parser.add_argument(
@@ -321,16 +313,10 @@ class CreateImage(command.ShowOne):
kwargs['visibility'] = 'community'
if parsed_args.shared:
kwargs['visibility'] = 'shared'
- # Handle deprecated --owner option
- project_arg = parsed_args.project
- if parsed_args.owner:
- project_arg = parsed_args.owner
- LOG.warning(_('The --owner option is deprecated, '
- 'please use --project instead.'))
- if project_arg:
+ if parsed_args.project:
kwargs['owner'] = common.find_project(
identity_client,
- project_arg,
+ parsed_args.project,
parsed_args.project_domain,
).id
@@ -347,13 +333,6 @@ class CreateImage(command.ShowOne):
LOG.warning(_("Failed to get an image file."))
return {}, {}
- if parsed_args.owner:
- kwargs['owner'] = common.find_project(
- identity_client,
- parsed_args.owner,
- parsed_args.project_domain,
- ).id
-
# sign an image using a given local private key file
if parsed_args.sign_key_path or parsed_args.sign_cert_id:
if not parsed_args.file:
@@ -678,7 +657,7 @@ class ListImage(command.Lister):
s,
columns,
formatters={
- 'tags': utils.format_list,
+ 'tags': format_columns.ListColumn,
},
) for s in data)
)
@@ -933,20 +912,11 @@ class SetImage(command.Command):
action="store_true",
help=_("Activate the image"),
)
- # NOTE(dtroyer): --owner is deprecated in Jan 2016 in an early
- # 2.x release. Do not remove before Jan 2017
- # and a 3.x release.
- project_group = parser.add_mutually_exclusive_group()
- project_group.add_argument(
+ parser.add_argument(
"--project",
metavar="<project>",
help=_("Set an alternate project on this image (name or ID)"),
)
- project_group.add_argument(
- "--owner",
- metavar="<project>",
- help=argparse.SUPPRESS,
- )
common.add_project_domain_option_to_parser(parser)
for deadopt in self.deadopts:
parser.add_argument(
@@ -1020,17 +990,11 @@ class SetImage(command.Command):
kwargs['visibility'] = 'community'
if parsed_args.shared:
kwargs['visibility'] = 'shared'
- # Handle deprecated --owner option
- project_arg = parsed_args.project
- if parsed_args.owner:
- project_arg = parsed_args.owner
- LOG.warning(_('The --owner option is deprecated, '
- 'please use --project instead.'))
project_id = None
- if project_arg:
+ if parsed_args.project:
project_id = common.find_project(
identity_client,
- project_arg,
+ parsed_args.project,
parsed_args.project_domain,
).id
kwargs['owner'] = project_id
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 71c1a9a..7efbb63 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,6 +28,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -100,7 +106,7 @@ class CreateAddressScope(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_address_scope(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -289,6 +295,6 @@ class ShowAddressScope(command.ShowOne):
parsed_args.address_scope,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 8ac8e10..e0aa043 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -13,6 +13,7 @@
"""IP Floating action implementations"""
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -24,6 +25,7 @@ from openstackclient.network.v2 import _tag
_formatters = {
+ 'location': format_columns.DictColumn,
'port_details': utils.format_dict,
}
diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py
new file mode 100644
index 0000000..f94bcc0
--- /dev/null
+++ b/openstackclient/network/v2/floating_ip_port_forwarding.py
@@ -0,0 +1,371 @@
+# 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.
+#
+
+"""Floating IP Port Forwarding action implementations"""
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+from openstackclient.network import sdk_utils
+
+
+LOG = logging.getLogger(__name__)
+
+
+def _get_columns(item):
+ column_map = {
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+
+
+class CreateFloatingIPPortForwarding(command.ShowOne):
+ _description = _("Create floating IP port forwarding")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateFloatingIPPortForwarding,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ '--internal-ip-address',
+ required=True,
+ metavar='<internal-ip-address>',
+ help=_("The fixed IPv4 address of the network "
+ "port associated to the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--port',
+ metavar='<port>',
+ required=True,
+ help=_("The name or ID of the network port associated "
+ "to the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--internal-protocol-port',
+ type=int,
+ metavar='<port-number>',
+ required=True,
+ help=_("The protocol port number "
+ "of the network port fixed IPv4 address "
+ "associated to the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--external-protocol-port',
+ type=int,
+ metavar='<port-number>',
+ required=True,
+ help=_("The protocol port number of "
+ "the port forwarding's floating IP address")
+ )
+ parser.add_argument(
+ '--protocol',
+ metavar='<protocol>',
+ required=True,
+ help=_("The protocol used in the floating IP "
+ "port forwarding, for instance: TCP, UDP")
+ )
+ parser.add_argument(
+ 'floating_ip',
+ metavar='<floating-ip>',
+ help=_("Floating IP that the port forwarding belongs to "
+ "(IP address or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ attrs = {}
+ client = self.app.client_manager.network
+ floating_ip = client.find_ip(
+ parsed_args.floating_ip,
+ ignore_missing=False,
+ )
+
+ if parsed_args.internal_protocol_port is not None:
+ if (parsed_args.internal_protocol_port <= 0 or
+ parsed_args.internal_protocol_port > 65535):
+ msg = _("The port number range is <1-65535>")
+ raise exceptions.CommandError(msg)
+ attrs['internal_port'] = parsed_args.internal_protocol_port
+
+ if parsed_args.external_protocol_port is not None:
+ if (parsed_args.external_protocol_port <= 0 or
+ parsed_args.external_protocol_port > 65535):
+ msg = _("The port number range is <1-65535>")
+ raise exceptions.CommandError(msg)
+ attrs['external_port'] = parsed_args.external_protocol_port
+
+ if parsed_args.port:
+ port = client.find_port(parsed_args.port,
+ ignore_missing=False)
+ attrs['internal_port_id'] = port.id
+ attrs['internal_ip_address'] = parsed_args.internal_ip_address
+ attrs['protocol'] = parsed_args.protocol
+
+ obj = client.create_floating_ip_port_forwarding(
+ floating_ip.id,
+ **attrs
+ )
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return (display_columns, data)
+
+
+class DeleteFloatingIPPortForwarding(command.Command):
+ _description = _("Delete floating IP port forwarding")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteFloatingIPPortForwarding,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ 'floating_ip',
+ metavar='<floating-ip>',
+ help=_("Floating IP that the port forwarding belongs to "
+ "(IP address or ID)")
+ )
+ parser.add_argument(
+ 'port_forwarding_id',
+ nargs="+",
+ metavar="<port-forwarding-id>",
+ help=_("The ID of the floating IP port forwarding(s) to delete")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ floating_ip = client.find_ip(
+ parsed_args.floating_ip,
+ ignore_missing=False,
+ )
+ result = 0
+
+ for port_forwarding_id in parsed_args.port_forwarding_id:
+ try:
+ client.delete_floating_ip_port_forwarding(
+ floating_ip.id,
+ port_forwarding_id,
+ ignore_missing=False,
+ )
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete floating IP port forwarding "
+ "'%(port_forwarding_id)s': %(e)s"),
+ {'port_forwarding_id': port_forwarding_id, 'e': e})
+ if result > 0:
+ total = len(parsed_args.port_forwarding_id)
+ msg = (_("%(result)s of %(total)s Port forwarding failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListFloatingIPPortForwarding(command.Lister):
+ _description = _("List floating IP port forwarding")
+
+ def get_parser(self, prog_name):
+ parser = super(ListFloatingIPPortForwarding,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ 'floating_ip',
+ metavar='<floating-ip>',
+ help=_("Floating IP that the port forwarding belongs to "
+ "(IP address or ID)")
+ )
+ parser.add_argument(
+ '--port',
+ metavar='<port>',
+ help=_("Filter the list result by the ID or name of "
+ "the internal network port")
+ )
+ parser.add_argument(
+ '--external-protocol-port',
+ metavar='<port-number>',
+ dest='external_protocol_port',
+ help=_("Filter the list result by the "
+ "protocol port number of the floating IP")
+ )
+ parser.add_argument(
+ '--protocol',
+ metavar='protocol',
+ help=_("Filter the list result by the port protocol")
+ )
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+
+ columns = (
+ 'id',
+ 'internal_port_id',
+ 'internal_ip_address',
+ 'internal_port',
+ 'external_port',
+ 'protocol',
+ )
+ headers = (
+ 'ID',
+ 'Internal Port ID',
+ 'Internal IP Address',
+ 'Internal Port',
+ 'External Port',
+ 'Protocol',
+ )
+
+ query = {}
+
+ if parsed_args.port:
+ port = client.find_port(parsed_args.port,
+ ignore_missing=False)
+ query['internal_port_id'] = port.id
+ if parsed_args.external_protocol_port is not None:
+ query['external_port'] = parsed_args.external_protocol_port
+ if parsed_args.protocol is not None:
+ query['protocol'] = parsed_args.protocol
+
+ obj = client.find_ip(
+ parsed_args.floating_ip,
+ ignore_missing=False,
+ )
+
+ data = client.floating_ip_port_forwardings(obj, **query)
+
+ return (headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={},
+ ) for s in data))
+
+
+class SetFloatingIPPortForwarding(command.Command):
+ _description = _("Set floating IP Port Forwarding Properties")
+
+ def get_parser(self, prog_name):
+ parser = super(SetFloatingIPPortForwarding,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ 'floating_ip',
+ metavar='<floating-ip>',
+ help=_("Floating IP that the port forwarding belongs to "
+ "(IP address or ID)")
+ )
+ parser.add_argument(
+ 'port_forwarding_id',
+ metavar='<port-forwarding-id>',
+ help=_("The ID of the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--port',
+ metavar='<port>',
+ help=_("The ID of the network port associated to "
+ "the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--internal-ip-address',
+ metavar='<internal-ip-address>',
+ help=_("The fixed IPv4 address of the network port "
+ "associated to the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--internal-protocol-port',
+ metavar='<port-number>',
+ type=int,
+ help=_("The TCP/UDP/other protocol port number of the "
+ "network port fixed IPv4 address associated to "
+ "the floating IP port forwarding")
+ )
+ parser.add_argument(
+ '--external-protocol-port',
+ type=int,
+ metavar='<port-number>',
+ help=_("The TCP/UDP/other protocol port number of the "
+ "port forwarding's floating IP address")
+ )
+ parser.add_argument(
+ '--protocol',
+ metavar='<protocol>',
+ choices=['tcp', 'udp'],
+ help=_("The IP protocol used in the floating IP port forwarding")
+ )
+
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ floating_ip = client.find_ip(
+ parsed_args.floating_ip,
+ ignore_missing=False,
+ )
+
+ attrs = {}
+ if parsed_args.port:
+ port = client.find_port(parsed_args.port,
+ ignore_missing=False)
+ attrs['internal_port_id'] = port.id
+
+ if parsed_args.internal_ip_address:
+ attrs['internal_ip_address'] = parsed_args.internal_ip_address
+ if parsed_args.internal_protocol_port is not None:
+ if (parsed_args.internal_protocol_port <= 0 or
+ parsed_args.internal_protocol_port > 65535):
+ msg = _("The port number range is <1-65535>")
+ raise exceptions.CommandError(msg)
+ attrs['internal_port'] = parsed_args.internal_protocol_port
+
+ if parsed_args.external_protocol_port is not None:
+ if (parsed_args.external_protocol_port <= 0 or
+ parsed_args.external_protocol_port > 65535):
+ msg = _("The port number range is <1-65535>")
+ raise exceptions.CommandError(msg)
+ attrs['external_port'] = parsed_args.external_protocol_port
+
+ if parsed_args.protocol:
+ attrs['protocol'] = parsed_args.protocol
+
+ client.update_floating_ip_port_forwarding(
+ floating_ip.id, parsed_args.port_forwarding_id, **attrs)
+
+
+class ShowFloatingIPPortForwarding(command.ShowOne):
+ _description = _("Display floating IP Port Forwarding details")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowFloatingIPPortForwarding,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ 'floating_ip',
+ metavar='<floating-ip>',
+ help=_("Floating IP that the port forwarding belongs to "
+ "(IP address or ID)")
+ )
+ parser.add_argument(
+ 'port_forwarding_id',
+ metavar="<port-forwarding-id>",
+ help=_("The ID of the floating IP port forwarding")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ floating_ip = client.find_ip(
+ parsed_args.floating_ip,
+ ignore_missing=False,
+ )
+ obj = client.find_floating_ip_port_forwarding(
+ floating_ip,
+ parsed_args.port_forwarding_id,
+ ignore_missing=False,
+ )
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ return (display_columns, data)
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index 1d96358..c026baa 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -13,6 +13,7 @@
"""IP Availability Info implementations"""
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -20,8 +21,10 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils
+
_formatters = {
- 'subnet_ip_availability': utils.format_list_of_dicts,
+ 'location': format_columns.DictColumn,
+ 'subnet_ip_availability': format_columns.ListDictColumn,
}
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index f512393..09f3556 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -13,6 +13,8 @@
"""Network action implementations"""
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -23,24 +25,27 @@ from openstackclient.network import sdk_utils
from openstackclient.network.v2 import _tag
-def _format_admin_state(item):
- return 'UP' if item else 'DOWN'
+class AdminStateColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return 'UP' if self._value else 'DOWN'
-def _format_router_external(item):
- return 'External' if item else 'Internal'
+class RouterExternalColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return 'External' if self._value else 'Internal'
_formatters = {
- 'subnets': utils.format_list,
- 'subnet_ids': utils.format_list,
- 'admin_state_up': _format_admin_state,
- 'is_admin_state_up': _format_admin_state,
- 'router:external': _format_router_external,
- 'is_router_external': _format_router_external,
- 'availability_zones': utils.format_list,
- 'availability_zone_hints': utils.format_list,
- 'tags': utils.format_list,
+ 'subnets': format_columns.ListColumn,
+ 'subnet_ids': format_columns.ListColumn,
+ 'admin_state_up': AdminStateColumn,
+ 'is_admin_state_up': AdminStateColumn,
+ 'location': format_columns.DictColumn,
+ 'router:external': RouterExternalColumn,
+ 'is_router_external': RouterExternalColumn,
+ 'availability_zones': format_columns.ListColumn,
+ 'availability_zone_hints': format_columns.ListColumn,
+ 'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index 46e8d4b..a6ed362 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -15,6 +15,8 @@
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,19 +28,23 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-def _format_alive(alive):
- return ":-)" if alive else "XXX"
+class AliveColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return ":-)" if self._value else "XXX"
-def _format_admin_state(state):
- return 'UP' if state else 'DOWN'
+class AdminStateColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return 'UP' if self._value else 'DOWN'
+
_formatters = {
- 'is_alive': _format_alive,
- 'alive': _format_alive,
- 'admin_state_up': _format_admin_state,
- 'is_admin_state_up': _format_admin_state,
- 'configurations': utils.format_dict,
+ 'is_alive': AliveColumn,
+ 'alive': AliveColumn,
+ 'admin_state_up': AdminStateColumn,
+ 'is_admin_state_up': AdminStateColumn,
+ 'location': format_columns.DictColumn,
+ 'configurations': format_columns.DictColumn,
}
diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py
index 36f3920..f6070a0 100644
--- a/openstackclient/network/v2/network_auto_allocated_topology.py
+++ b/openstackclient/network/v2/network_auto_allocated_topology.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -25,6 +26,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -93,16 +99,17 @@ class CreateAutoAllocatedTopology(command.ShowOne):
obj = client.validate_auto_allocated_topology(parsed_args.project)
columns = _format_check_resource_columns()
- data = utils.get_item_properties(_format_check_resource(obj),
- columns,
- formatters={})
-
+ data = utils.get_item_properties(
+ _format_check_resource(obj),
+ columns,
+ formatters=_formatters,
+ )
return (columns, data)
def get_topology(self, client, parsed_args):
obj = client.get_auto_allocated_topology(parsed_args.project)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
def take_action(self, parsed_args):
diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py
index c9d368b..355d04c 100644
--- a/openstackclient/network/v2/network_flavor.py
+++ b/openstackclient/network/v2/network_flavor.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,6 +28,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
@@ -136,7 +142,7 @@ class CreateNetworkFlavor(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_flavor(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -300,5 +306,5 @@ class ShowNetworkFlavor(command.ShowOne):
client = self.app.client_manager.network
obj = client.find_flavor(parsed_args.flavor, ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py
index 6cf0c41..492fd43 100644
--- a/openstackclient/network/v2/network_flavor_profile.py
+++ b/openstackclient/network/v2/network_flavor_profile.py
@@ -13,6 +13,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -25,6 +26,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
@@ -110,7 +116,7 @@ class CreateNetworkFlavorProfile(command.ShowOne):
obj = client.create_service_profile(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -246,5 +252,5 @@ class ShowNetworkFlavorProfile(command.ShowOne):
obj = client.find_service_profile(parsed_args.flavor_profile,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py
index df0e1da..cde7a30 100644
--- a/openstackclient/network/v2/network_meter.py
+++ b/openstackclient/network/v2/network_meter.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,6 +27,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -102,7 +108,7 @@ class CreateMeter(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_metering_label(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -186,5 +192,5 @@ class ShowMeter(command.ShowOne):
obj = client.find_metering_label(parsed_args.meter,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py
index 49ff9e1..5f31255 100644
--- a/openstackclient/network/v2/network_meter_rule.py
+++ b/openstackclient/network/v2/network_meter_rule.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,6 +27,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -116,7 +122,7 @@ class CreateMeterRule(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_metering_label_rule(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -199,5 +205,5 @@ class ShowMeterRule(command.ShowOne):
obj = client.find_metering_label_rule(parsed_args.meter_rule_id,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py
index fd5ff93..1622de4 100644
--- a/openstackclient/network/v2/network_qos_policy.py
+++ b/openstackclient/network/v2/network_qos_policy.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,6 +28,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -119,7 +125,7 @@ class CreateNetworkQosPolicy(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_qos_policy(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters={})
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -279,5 +285,5 @@ class ShowNetworkQosPolicy(command.ShowOne):
obj = client.find_qos_policy(parsed_args.policy,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
index 28c5600..d74beda 100644
--- a/openstackclient/network/v2/network_qos_rule.py
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -15,6 +15,7 @@
import itertools
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -46,6 +47,11 @@ ACTION_SET = 'update'
ACTION_SHOW = 'get'
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -208,7 +214,7 @@ class CreateNetworkQosRule(command.ShowOne):
msg = (_('Failed to create Network QoS rule: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
@@ -358,5 +364,5 @@ class ShowNetworkQosRule(command.ShowOne):
{'rule': rule_id, 'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py
index 7b92c8a..e842944 100644
--- a/openstackclient/network/v2/network_qos_rule_type.py
+++ b/openstackclient/network/v2/network_qos_rule_type.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -20,6 +21,11 @@ from openstackclient.i18n import _
from openstackclient.network import sdk_utils
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
"type": "rule_type_name",
@@ -65,5 +71,5 @@ class ShowNetworkQosRuleType(command.ShowOne):
client = self.app.client_manager.network
obj = client.get_qos_rule_type(parsed_args.rule_type)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index 140c837..1781193 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -27,6 +28,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
column_map = {
'target_tenant': 'target_project_id',
@@ -136,7 +142,7 @@ class CreateNetworkRBAC(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_rbac_policy(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
@@ -293,5 +299,5 @@ class ShowNetworkRBAC(command.ShowOne):
obj = client.find_rbac_policy(parsed_args.rbac_policy,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return display_columns, data
diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py
index c1a672e..5899dc6 100644
--- a/openstackclient/network/v2/network_segment.py
+++ b/openstackclient/network/v2/network_segment.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,6 +27,11 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
@@ -90,7 +96,7 @@ class CreateNetworkSegment(command.ShowOne):
attrs['segmentation_id'] = parsed_args.segment
obj = client.create_segment(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
@@ -242,5 +248,5 @@ class ShowNetworkSegment(command.ShowOne):
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py
index 567b5b6..f03bcc1 100644
--- a/openstackclient/network/v2/network_segment_range.py
+++ b/openstackclient/network/v2/network_segment_range.py
@@ -19,6 +19,7 @@
import itertools
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -31,6 +32,10 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
@@ -212,7 +217,7 @@ class CreateNetworkSegmentRange(command.ShowOne):
attrs['physical_network'] = parsed_args.physical_network
obj = network_client.create_network_segment_range(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
@@ -358,7 +363,7 @@ class ListNetworkSegmentRange(command.Lister):
parsed_args.used and _is_prop_empty(columns, props, 'used') or \
parsed_args.unused and \
not _is_prop_empty(columns, props, 'used'):
- continue
+ continue
if parsed_args.long:
props = _update_additional_fields_from_props(columns, props)
display_props += (props,)
@@ -451,6 +456,6 @@ class ShowNetworkSegmentRange(command.ShowOne):
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index f6d6fc7..4d7e518 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -18,6 +18,8 @@ import copy
import json
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -33,23 +35,25 @@ from openstackclient.network.v2 import _tag
LOG = logging.getLogger(__name__)
-def _format_admin_state(state):
- return 'UP' if state else 'DOWN'
+class AdminStateColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return 'UP' if self._value else 'DOWN'
_formatters = {
- 'admin_state_up': _format_admin_state,
- 'is_admin_state_up': _format_admin_state,
- 'allowed_address_pairs': utils.format_list_of_dicts,
- 'binding_profile': utils.format_dict,
- 'binding_vif_details': utils.format_dict,
- 'binding:profile': utils.format_dict,
- 'binding:vif_details': utils.format_dict,
- 'dns_assignment': utils.format_list_of_dicts,
- 'extra_dhcp_opts': utils.format_list_of_dicts,
- 'fixed_ips': utils.format_list_of_dicts,
- 'security_group_ids': utils.format_list,
- 'tags': utils.format_list,
+ 'admin_state_up': AdminStateColumn,
+ 'is_admin_state_up': AdminStateColumn,
+ 'allowed_address_pairs': format_columns.ListDictColumn,
+ 'binding_profile': format_columns.DictColumn,
+ 'binding_vif_details': format_columns.DictColumn,
+ 'binding:profile': format_columns.DictColumn,
+ 'binding:vif_details': format_columns.DictColumn,
+ 'dns_assignment': format_columns.ListDictColumn,
+ 'extra_dhcp_opts': format_columns.ListDictColumn,
+ 'fixed_ips': format_columns.ListDictColumn,
+ 'location': format_columns.DictColumn,
+ 'security_group_ids': format_columns.ListColumn,
+ 'tags': format_columns.ListColumn,
}
@@ -96,21 +100,6 @@ class JSONKeyValueAction(argparse.Action):
def _get_attrs(client_manager, parsed_args):
attrs = {}
- # Handle deprecated options
- # NOTE(dtroyer): --device-id and --host-id were deprecated in Mar 2016.
- # Do not remove before 3.x release or Mar 2017.
- if parsed_args.device_id:
- attrs['device_id'] = parsed_args.device_id
- LOG.warning(_(
- 'The --device-id option is deprecated, '
- 'please use --device instead.'
- ))
- if parsed_args.host_id:
- attrs['binding:host_id'] = parsed_args.host_id
- LOG.warning(_(
- 'The --host-id option is deprecated, '
- 'please use --host instead.'
- ))
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
if parsed_args.device:
@@ -235,19 +224,11 @@ def _add_updatable_args(parser):
metavar='<description>',
help=_("Description of this port")
)
- # NOTE(dtroyer): --device-id is deprecated in Mar 2016. Do not
- # remove before 3.x release or Mar 2017.
- device_group = parser.add_mutually_exclusive_group()
- device_group.add_argument(
+ parser.add_argument(
'--device',
metavar='<device-id>',
help=_("Port device ID")
)
- device_group.add_argument(
- '--device-id',
- metavar='<device-id>',
- help=argparse.SUPPRESS,
- )
parser.add_argument(
'--mac-address',
metavar='<mac-address>',
@@ -268,19 +249,11 @@ def _add_updatable_args(parser):
"macvtap | normal | baremetal | virtio-forwarder, "
"default: normal)")
)
- # NOTE(dtroyer): --host-id is deprecated in Mar 2016. Do not
- # remove before 3.x release or Mar 2017.
- host_group = parser.add_mutually_exclusive_group()
- host_group.add_argument(
+ parser.add_argument(
'--host',
metavar='<host-id>',
help=_("Allocate port on host <host-id> (ID only)")
)
- host_group.add_argument(
- '--host-id',
- metavar='<host-id>',
- help=argparse.SUPPRESS,
- )
parser.add_argument(
'--dns-domain',
metavar='dns-domain',
@@ -308,6 +281,19 @@ def _convert_address_pairs(parsed_args):
return ops
+def _convert_extra_dhcp_options(parsed_args):
+ dhcp_options = []
+ for opt in parsed_args.extra_dhcp_options:
+ option = {}
+ option['opt_name'] = opt['name']
+ if 'value' in opt:
+ option['opt_value'] = opt['value']
+ if 'ip-version' in opt:
+ option['ip_version'] = opt['ip-version']
+ dhcp_options.append(option)
+ return dhcp_options
+
+
class CreatePort(command.ShowOne):
_description = _("Create a new port")
@@ -378,8 +364,18 @@ class CreatePort(command.ShowOne):
metavar='<name>',
help=_("Name of this port")
)
- # TODO(singhj): Add support for extended options:
- # dhcp
+ parser.add_argument(
+ '--extra-dhcp-option',
+ metavar='name=<name>[,value=<value>,ip-version={4,6}]',
+ default=[],
+ action=parseractions.MultiKeyValueCommaAction,
+ dest='extra_dhcp_options',
+ required_keys=['name'],
+ optional_keys=['value', "ip-version"],
+ help=_('Extra DHCP options to be assigned to this port: '
+ 'name=<name>[,value=<value>,ip-version={4,6}] '
+ '(repeat option to set multiple extra DHCP options)'))
+
secgroups = parser.add_mutually_exclusive_group()
secgroups.add_argument(
'--security-group',
@@ -453,6 +449,9 @@ class CreatePort(command.ShowOne):
attrs['allowed_address_pairs'] = (
_convert_address_pairs(parsed_args))
+ if parsed_args.extra_dhcp_options:
+ attrs["extra_dhcp_opts"] = _convert_extra_dhcp_options(parsed_args)
+
if parsed_args.qos_policy:
attrs['qos_policy_id'] = client.find_qos_policy(
parsed_args.qos_policy, ignore_missing=False).id
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 2ec3e2f..02da50c 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -13,11 +13,12 @@
"""Router action implementations"""
-import argparse
import copy
import json
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -32,33 +33,37 @@ from openstackclient.network.v2 import _tag
LOG = logging.getLogger(__name__)
-def _format_admin_state(state):
- return 'UP' if state else 'DOWN'
+class AdminStateColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return 'UP' if self._value else 'DOWN'
-def _format_router_info(info):
- try:
- return json.dumps(info)
- except (TypeError, KeyError):
- return ''
+class RouterInfoColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ try:
+ return json.dumps(self._value)
+ except (TypeError, KeyError):
+ return ''
-def _format_routes(routes):
- # Map the route keys to match --route option.
- for route in routes:
- if 'nexthop' in route:
- route['gateway'] = route.pop('nexthop')
- return utils.format_list_of_dicts(routes)
+class RoutesColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ # Map the route keys to match --route option.
+ for route in self._value:
+ if 'nexthop' in route:
+ route['gateway'] = route.pop('nexthop')
+ return utils.format_list_of_dicts(self._value)
_formatters = {
- 'admin_state_up': _format_admin_state,
- 'is_admin_state_up': _format_admin_state,
- 'external_gateway_info': _format_router_info,
- 'availability_zones': utils.format_list,
- 'availability_zone_hints': utils.format_list,
- 'routes': _format_routes,
- 'tags': utils.format_list,
+ 'admin_state_up': AdminStateColumn,
+ 'is_admin_state_up': AdminStateColumn,
+ 'external_gateway_info': RouterInfoColumn,
+ 'availability_zones': format_columns.ListColumn,
+ 'availability_zone_hints': format_columns.ListColumn,
+ 'location': format_columns.DictColumn,
+ 'routes': RoutesColumn,
+ 'tags': format_columns.ListColumn,
}
@@ -528,8 +533,6 @@ class SetRouter(command.Command):
action='store_true',
help=_("Set router to centralized mode (disabled router only)")
)
- routes_group = parser.add_mutually_exclusive_group()
- # ToDo(Reedip):Remove mutual exclusiveness once clear-routes is removed
parser.add_argument(
'--route',
metavar='destination=<subnet>,gateway=<ip-address>',
@@ -542,18 +545,13 @@ class SetRouter(command.Command):
"gateway: nexthop IP address "
"(repeat option to set multiple routes)")
)
- routes_group.add_argument(
+ parser.add_argument(
'--no-route',
action='store_true',
help=_("Clear routes associated with the router. "
"Specify both --route and --no-route to overwrite "
"current value of route.")
)
- routes_group.add_argument(
- '--clear-routes',
- action='store_true',
- help=argparse.SUPPRESS,
- )
routes_ha = parser.add_mutually_exclusive_group()
routes_ha.add_argument(
'--ha',
@@ -619,21 +617,16 @@ class SetRouter(command.Command):
attrs['ha'] = True
elif parsed_args.no_ha:
attrs['ha'] = False
- if parsed_args.clear_routes:
- LOG.warning(_(
- 'The --clear-routes option is deprecated, '
- 'please use --no-route instead.'
- ))
if parsed_args.routes is not None:
for route in parsed_args.routes:
route['nexthop'] = route.pop('gateway')
attrs['routes'] = parsed_args.routes
- if not (parsed_args.no_route or parsed_args.clear_routes):
+ if not parsed_args.no_route:
# Map the route keys and append to the current routes.
# The REST API will handle route validation and duplicates.
attrs['routes'] += obj.routes
- elif parsed_args.no_route or parsed_args.clear_routes:
+ elif parsed_args.no_route:
attrs['routes'] = []
if (parsed_args.disable_snat or parsed_args.enable_snat or
parsed_args.fixed_ip) and not parsed_args.external_gateway:
@@ -720,7 +713,7 @@ class ShowRouter(command.ShowOne):
setattr(obj, 'interfaces_info', interfaces_info)
display_columns, columns = _get_columns(obj)
- _formatters['interfaces_info'] = _format_router_info
+ _formatters['interfaces_info'] = RouterInfoColumn
data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index ed6c8d7..38b4e97 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -15,6 +15,8 @@
import argparse
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
import six
@@ -65,13 +67,25 @@ def _format_compute_security_group_rules(sg_rules):
return utils.format_list(rules, separator='\n')
+class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return _format_network_security_group_rules(self._value)
+
+
+class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ return _format_compute_security_group_rules(self._value)
+
+
_formatters_network = {
- 'security_group_rules': _format_network_security_group_rules,
+ 'location': format_columns.DictColumn,
+ 'security_group_rules': NetworkSecurityGroupRulesColumn,
}
_formatters_compute = {
- 'rules': _format_compute_security_group_rules,
+ 'location': format_columns.DictColumn,
+ 'rules': ComputeSecurityGroupRulesColumn,
}
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index 961125a..15f099b 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -16,6 +16,7 @@
import argparse
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib import exceptions
from osc_lib import utils
@@ -31,6 +32,11 @@ from openstackclient.network import utils as network_utils
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'location': format_columns.DictColumn,
+}
+
+
def _format_security_group_rule_show(obj):
data = network_utils.transform_compute_security_group_rule(obj)
return zip(*sorted(six.iteritems(data)))
@@ -62,6 +68,17 @@ def _format_network_port_range(rule):
return port_range
+def _format_remote_ip_prefix(rule):
+ remote_ip_prefix = rule['remote_ip_prefix']
+ if remote_ip_prefix is None:
+ ethertype = rule['ether_type']
+ if ethertype == 'IPv4':
+ remote_ip_prefix = '0.0.0.0/0'
+ elif ethertype == 'IPv6':
+ remote_ip_prefix = '::/0'
+ return remote_ip_prefix
+
+
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -108,26 +125,14 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
"--remote-ip",
metavar="<ip-address>",
help=_("Remote IP address block (may use CIDR notation; "
- "default for IPv4 rule: 0.0.0.0/0)"),
+ "default for IPv4 rule: 0.0.0.0/0, "
+ "default for IPv6 rule: ::/0)"),
)
remote_group.add_argument(
"--remote-group",
metavar="<group>",
help=_("Remote security group (name or ID)"),
)
- # Handle deprecated options
- # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016.
- # Do not remove before 4.x release or Nov 2017.
- remote_group.add_argument(
- "--src-ip",
- metavar="<ip-address>",
- help=argparse.SUPPRESS,
- )
- remote_group.add_argument(
- "--src-group",
- metavar="<group>",
- help=argparse.SUPPRESS,
- )
return parser
def update_parser_network(self, parser):
@@ -168,7 +173,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
"ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, "
"ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, "
"udp, udplite, vrrp and integer representations [0-255] "
- "or any; default: tcp)")
+ "or any; default: any (all protocols))")
)
protocol_group.add_argument(
'--proto',
@@ -233,8 +238,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
)
return parser
- def _get_protocol(self, parsed_args):
- protocol = 'tcp'
+ def _get_protocol(self, parsed_args, default_protocol='any'):
+ protocol = default_protocol
if parsed_args.protocol is not None:
protocol = parsed_args.protocol
if parsed_args.proto is not None:
@@ -243,6 +248,14 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
protocol = None
return protocol
+ def _get_ethertype(self, parsed_args, protocol):
+ ethertype = 'IPv4'
+ if parsed_args.ethertype is not None:
+ ethertype = parsed_args.ethertype
+ elif self._is_ipv6_protocol(protocol):
+ ethertype = 'IPv6'
+ return ethertype
+
def _is_ipv6_protocol(self, protocol):
# NOTE(rtheis): Neutron has deprecated protocol icmpv6.
# However, while the OSC CLI doesn't document the protocol,
@@ -277,12 +290,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
# NOTE(rtheis): Use ethertype specified else default based
# on IP protocol.
- if parsed_args.ethertype:
- attrs['ethertype'] = parsed_args.ethertype
- elif self._is_ipv6_protocol(attrs['protocol']):
- attrs['ethertype'] = 'IPv6'
- else:
- attrs['ethertype'] = 'IPv4'
+ attrs['ethertype'] = self._get_ethertype(parsed_args,
+ attrs['protocol'])
# NOTE(rtheis): Validate the port range and ICMP type and code.
# It would be ideal if argparse could do this.
@@ -310,33 +319,17 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
if parsed_args.icmp_code is not None and parsed_args.icmp_code >= 0:
attrs['port_range_max'] = parsed_args.icmp_code
- # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016.
- # Do not remove before 4.x release or Nov 2017.
- if not (parsed_args.remote_group is None and
- parsed_args.src_group is None):
+ if parsed_args.remote_group is not None:
attrs['remote_group_id'] = client.find_security_group(
- parsed_args.remote_group or parsed_args.src_group,
+ parsed_args.remote_group,
ignore_missing=False
).id
- if parsed_args.src_group:
- LOG.warning(
- _("The %(old)s option is deprecated, "
- "please use %(new)s instead."),
- {'old': '--src-group', 'new': '--remote-group'},
- )
- elif not (parsed_args.remote_ip is None and
- parsed_args.src_ip is None):
- attrs['remote_ip_prefix'] = (
- parsed_args.remote_ip or parsed_args.src_ip
- )
- if parsed_args.src_ip:
- LOG.warning(
- _("The %(old)s option is deprecated, "
- "please use %(new)s instead."),
- {'old': '--src-ip', 'new': '--remote-ip'},
- )
+ elif parsed_args.remote_ip is not None:
+ attrs['remote_ip_prefix'] = parsed_args.remote_ip
elif attrs['ethertype'] == 'IPv4':
attrs['remote_ip_prefix'] = '0.0.0.0/0'
+ elif attrs['ethertype'] == 'IPv6':
+ attrs['remote_ip_prefix'] = '::/0'
attrs['security_group_id'] = security_group_id
if parsed_args.project is not None:
identity_client = self.app.client_manager.identity
@@ -350,40 +343,24 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
# Create and show the security group rule.
obj = client.create_security_group_rule(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
def take_action_compute(self, client, parsed_args):
group = client.api.security_group_find(parsed_args.group)
- protocol = self._get_protocol(parsed_args)
+ protocol = self._get_protocol(parsed_args, default_protocol='tcp')
if protocol == 'icmp':
from_port, to_port = -1, -1
else:
from_port, to_port = parsed_args.dst_port
- # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016.
- # Do not remove before 4.x release or Nov 2017.
remote_ip = None
- if not (parsed_args.remote_group is None and
- parsed_args.src_group is None):
+ if parsed_args.remote_group is not None:
parsed_args.remote_group = client.api.security_group_find(
- parsed_args.remote_group or parsed_args.src_group,
+ parsed_args.remote_group,
)['id']
- if parsed_args.src_group:
- LOG.warning(
- _("The %(old)s option is deprecated, "
- "please use %(new)s instead."),
- {'old': '--src-group', 'new': '--remote-group'},
- )
- if not (parsed_args.remote_ip is None and
- parsed_args.src_ip is None):
- remote_ip = parsed_args.remote_ip or parsed_args.src_ip
- if parsed_args.src_ip:
- LOG.warning(
- _("The %(old)s option is deprecated, "
- "please use %(new)s instead."),
- {'old': '--src-ip', 'new': '--remote-ip'},
- )
+ if parsed_args.remote_ip is not None:
+ remote_ip = parsed_args.remote_ip
else:
remote_ip = '0.0.0.0/0'
@@ -434,6 +411,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
"""
rule = rule.to_dict()
rule['port_range'] = _format_network_port_range(rule)
+ rule['remote_ip_prefix'] = _format_remote_ip_prefix(rule)
return rule
def update_parser_common(self, parser):
@@ -462,8 +440,14 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
"ah, dhcp, egp, esp, gre, icmp, igmp, "
"ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, "
"ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, "
- "udp, udplite, vrrp and integer representations [0-255])."
- )
+ "udp, udplite, vrrp and integer representations [0-255] "
+ "or any; default: any (all protocols))")
+ )
+ parser.add_argument(
+ '--ethertype',
+ metavar='<ethertype>',
+ type=_convert_to_lowercase,
+ help=_("List rules by the Ethertype (IPv4 or IPv6)")
)
direction_group = parser.add_mutually_exclusive_group()
direction_group.add_argument(
@@ -505,11 +489,12 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
column_headers = (
'ID',
'IP Protocol',
+ 'Ethertype',
'IP Range',
'Port Range',
)
if parsed_args.long:
- column_headers = column_headers + ('Direction', 'Ethertype',)
+ column_headers = column_headers + ('Direction',)
column_headers = column_headers + ('Remote Security Group',)
if parsed_args.group is None:
column_headers = column_headers + ('Security Group',)
@@ -520,11 +505,12 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
columns = (
'id',
'protocol',
+ 'ether_type',
'remote_ip_prefix',
'port_range',
)
if parsed_args.long:
- columns = columns + ('direction', 'ether_type',)
+ columns = columns + ('direction',)
columns = columns + ('remote_group_id',)
# Get the security group rules using the requested query.
@@ -563,6 +549,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
columns = (
"ID",
"IP Protocol",
+ "Ethertype",
"IP Range",
"Port Range",
"Remote Security Group",
@@ -611,8 +598,11 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
def take_action_network(self, client, parsed_args):
obj = client.find_security_group_rule(parsed_args.rule,
ignore_missing=False)
+ # necessary for old rules that have None in this field
+ if not obj['remote_ip_prefix']:
+ obj['remote_ip_prefix'] = _format_remote_ip_prefix(obj)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns)
+ data = utils.get_item_properties(obj, columns, formatters=_formatters)
return (display_columns, data)
def take_action_compute(self, client, parsed_args):
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index 0733f37..c536886 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -16,6 +16,8 @@
import copy
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -40,23 +42,28 @@ def _update_arguments(obj_list, parsed_args_list, option):
raise exceptions.CommandError(msg)
-def _format_allocation_pools(data):
- pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', ''))
- for pool in data]
- return ','.join(pool_formatted)
+class AllocationPoolsColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ pool_formatted = ['%s-%s' % (pool.get('start', ''),
+ pool.get('end', ''))
+ for pool in self._value]
+ return ','.join(pool_formatted)
-def _format_host_routes(data):
- # Map the host route keys to match --host-route option.
- return utils.format_list_of_dicts(convert_entries_to_gateway(data))
+class HostRoutesColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ # Map the host route keys to match --host-route option.
+ return utils.format_list_of_dicts(
+ convert_entries_to_gateway(self._value))
_formatters = {
- 'allocation_pools': _format_allocation_pools,
- 'dns_nameservers': utils.format_list,
- 'host_routes': _format_host_routes,
- 'service_types': utils.format_list,
- 'tags': utils.format_list,
+ 'allocation_pools': AllocationPoolsColumn,
+ 'dns_nameservers': format_columns.ListColumn,
+ 'host_routes': HostRoutesColumn,
+ 'location': format_columns.DictColumn,
+ 'service_types': format_columns.ListColumn,
+ 'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index ba0b6c4..d5a1547 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -15,6 +15,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -41,8 +42,9 @@ def _get_columns(item):
_formatters = {
- 'prefixes': utils.format_list,
- 'tags': utils.format_list,
+ 'location': format_columns.DictColumn,
+ 'prefixes': format_columns.ListColumn,
+ 'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py
index 4847f8b..95be813 100644
--- a/openstackclient/object/v1/account.py
+++ b/openstackclient/object/v1/account.py
@@ -13,9 +13,9 @@
"""Account v1 action implementations"""
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
-from osc_lib import utils
import six
from openstackclient.i18n import _
@@ -48,7 +48,8 @@ class ShowAccount(command.ShowOne):
def take_action(self, parsed_args):
data = self.app.client_manager.object_store.account_show()
if 'properties' in data:
- data['properties'] = utils.format_dict(data.pop('properties'))
+ data['properties'] = format_columns.DictColumn(
+ data.pop('properties'))
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py
index 9f689ab..02e8d27 100644
--- a/openstackclient/object/v1/container.py
+++ b/openstackclient/object/v1/container.py
@@ -17,6 +17,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import utils
@@ -230,7 +231,7 @@ class ShowContainer(command.ShowOne):
container=parsed_args.container,
)
if 'properties' in data:
- data['properties'] = utils.format_dict(data.pop('properties'))
+ data['properties'] = format_columns.DictColumn(data['properties'])
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py
index aeb0253..3747e19 100644
--- a/openstackclient/object/v1/object.py
+++ b/openstackclient/object/v1/object.py
@@ -17,6 +17,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -284,7 +285,7 @@ class ShowObject(command.ShowOne):
object=parsed_args.object,
)
if 'properties' in data:
- data['properties'] = utils.format_dict(data.pop('properties'))
+ data['properties'] = format_columns.DictColumn(data['properties'])
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 22d8412..7fbda1a 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -25,7 +25,6 @@ from osc_lib import shell
import six
import openstackclient
-from openstackclient.common import client_config as cloud_config
from openstackclient.common import clientmanager
@@ -58,10 +57,10 @@ class OpenStackShell(shell.OpenStackShell):
def _final_defaults(self):
super(OpenStackShell, self)._final_defaults()
- # Set the default plugin to token_endpoint if url and token are given
- if (self.options.url and self.options.token):
- # Use service token authentication
- self._auth_type = 'token_endpoint'
+ # Set the default plugin to admin_token if endpoint and token are given
+ if (self.options.endpoint and self.options.token):
+ # Use token authentication
+ self._auth_type = 'admin_token'
else:
self._auth_type = 'password'
@@ -133,37 +132,7 @@ class OpenStackShell(shell.OpenStackShell):
def initialize_app(self, argv):
super(OpenStackShell, self).initialize_app(argv)
- # Argument precedence is really broken in multiple places
- # so we're just going to fix it here until o-c-c and osc-lib
- # get sorted out.
- # TODO(dtroyer): remove when os-client-config and osc-lib are fixed
-
- # First, throw away what has already been done with o-c-c and
- # use our own.
- try:
- self.cloud_config = cloud_config.OSC_Config(
- override_defaults={
- 'interface': None,
- 'auth_type': self._auth_type,
- },
- )
- except (IOError, OSError):
- self.log.critical("Could not read clouds.yaml configuration file")
- self.print_help_if_requested()
- raise
-
- if not self.options.debug:
- self.options.debug = None
-
- # NOTE(dtroyer): Need to do this with validate=False to defer the
- # auth plugin handling to ClientManager.setup_auth()
- self.cloud = self.cloud_config.get_one_cloud(
- cloud=self.options.cloud,
- argparse=self.options,
- validate=False,
- )
-
- # Then, re-create the client_manager with the correct arguments
+ # Re-create the client_manager with our subclass
self.client_manager = clientmanager.ClientManager(
cli_options=self.cloud,
api_version=self.api_version,
@@ -209,5 +178,6 @@ def main(argv=None):
return OpenStackShell().run(argv)
+
if __name__ == "__main__":
sys.exit(main())
diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py
index 1414e6b..08e9390 100644
--- a/openstackclient/tests/functional/base.py
+++ b/openstackclient/tests/functional/base.py
@@ -44,10 +44,32 @@ class TestCase(testtools.TestCase):
@classmethod
def openstack(cls, cmd, cloud=ADMIN_CLOUD, fail_ok=False):
- """Executes openstackclient command for the given action."""
- return execute(
- 'openstack --os-cloud={cloud} '.format(cloud=cloud) +
- cmd, fail_ok=fail_ok)
+ """Executes openstackclient command for the given action
+
+ NOTE(dtroyer): There is a subtle distinction between pasing
+ cloud=None and cloud='': for compatibility reasons passing
+ cloud=None continues to include the option '--os-auth-type none'
+ in the command while passing cloud='' omits the '--os-auth-type'
+ option completely to let the default handlers be invoked.
+ """
+ if cloud is None:
+ # Execute command with no auth
+ return execute(
+ 'openstack --os-auth-type none ' + cmd,
+ fail_ok=fail_ok
+ )
+ elif cloud == '':
+ # Execute command with no auth options at all
+ return execute(
+ 'openstack ' + cmd,
+ fail_ok=fail_ok
+ )
+ else:
+ # Execure command with an explicit cloud specified
+ return execute(
+ 'openstack --os-cloud=' + cloud + ' ' + cmd,
+ fail_ok=fail_ok
+ )
@classmethod
def is_service_enabled(cls, service):
diff --git a/openstackclient/tests/functional/common/test_args.py b/openstackclient/tests/functional/common/test_args.py
new file mode 100644
index 0000000..02cad6c
--- /dev/null
+++ b/openstackclient/tests/functional/common/test_args.py
@@ -0,0 +1,79 @@
+# 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 json
+
+from tempest.lib import exceptions as tempest_exc
+
+from openstackclient.tests.functional import base
+
+
+class ArgumentTests(base.TestCase):
+ """Functional tests for command line arguments"""
+
+ def test_default_auth_type(self):
+ cmd_output = json.loads(self.openstack(
+ 'configuration show -f json',
+ cloud='',
+ ))
+ self.assertIsNotNone(cmd_output)
+ self.assertIn(
+ 'auth_type',
+ cmd_output.keys(),
+ )
+ self.assertEqual(
+ 'password',
+ cmd_output['auth_type'],
+ )
+
+ def test_auth_type_none(self):
+ cmd_output = json.loads(self.openstack(
+ 'configuration show -f json',
+ cloud=None,
+ ))
+ self.assertIsNotNone(cmd_output)
+ self.assertIn(
+ 'auth_type',
+ cmd_output.keys(),
+ )
+ self.assertEqual(
+ 'none',
+ cmd_output['auth_type'],
+ )
+
+ def test_auth_type_token_endpoint_opt(self):
+ # Make sure token_endpoint is really gone
+ try:
+ self.openstack(
+ 'configuration show -f json --os-auth-type token_endpoint',
+ cloud=None,
+ )
+ except tempest_exc.CommandFailed as e:
+ self.assertIn('--os-auth-type: invalid choice:', str(e))
+ self.assertIn('token_endpoint', str(e))
+ else:
+ self.fail('CommandFailed should be raised')
+
+ def test_auth_type_password_opt(self):
+ cmd_output = json.loads(self.openstack(
+ 'configuration show -f json --os-auth-type password',
+ cloud=None,
+ ))
+ self.assertIsNotNone(cmd_output)
+ self.assertIn(
+ 'auth_type',
+ cmd_output.keys(),
+ )
+ self.assertEqual(
+ 'password',
+ cmd_output['auth_type'],
+ )
diff --git a/openstackclient/tests/functional/common/test_configuration.py b/openstackclient/tests/functional/common/test_configuration.py
index 63a17d0..17e0f45 100644
--- a/openstackclient/tests/functional/common/test_configuration.py
+++ b/openstackclient/tests/functional/common/test_configuration.py
@@ -37,6 +37,10 @@ class ConfigurationTests(base.TestCase):
configuration.REDACTED,
cmd_output['auth.password']
)
+ self.assertIn(
+ 'auth.password',
+ cmd_output.keys(),
+ )
# Test show --mask
cmd_output = json.loads(self.openstack(
@@ -65,3 +69,26 @@ class ConfigurationTests(base.TestCase):
configuration.REDACTED,
cmd_output['auth.password']
)
+
+
+class ConfigurationTestsNoAuth(base.TestCase):
+ """Functional test for configuration with no auth"""
+
+ def test_configuration_show(self):
+
+ # Test show without option
+ raw_output = self.openstack(
+ 'configuration show',
+ cloud=None,
+ )
+ items = self.parse_listing(raw_output)
+ self.assert_table_structure(items, BASIC_CONFIG_HEADERS)
+
+ cmd_output = json.loads(self.openstack(
+ 'configuration show -f json',
+ cloud=None,
+ ))
+ self.assertNotIn(
+ 'auth.password',
+ cmd_output,
+ )
diff --git a/openstackclient/tests/functional/compute/v2/test_aggregate.py b/openstackclient/tests/functional/compute/v2/test_aggregate.py
index c591dd6..be318ae 100644
--- a/openstackclient/tests/functional/compute/v2/test_aggregate.py
+++ b/openstackclient/tests/functional/compute/v2/test_aggregate.py
@@ -11,6 +11,7 @@
# under the License.
import json
+import time
import uuid
from openstackclient.tests.functional import base
@@ -19,6 +20,31 @@ from openstackclient.tests.functional import base
class AggregateTests(base.TestCase):
"""Functional tests for aggregate"""
+ def wait_for_status(self, check_type, check_name, desired_status,
+ wait=120, interval=5, failures=None):
+ current_status = "notset"
+ if failures is None:
+ failures = ['error']
+ total_sleep = 0
+ while total_sleep < wait:
+ output = json.loads(self.openstack(
+ check_type + ' show -f json ' + check_name))
+ current_status = output['name']
+ if (current_status == desired_status):
+ print('{} {} now has status {}'
+ .format(check_type, check_name, current_status))
+ return
+ print('Checking {} {} Waiting for {} current status: {}'
+ .format(check_type, check_name,
+ desired_status, current_status))
+ if current_status in failures:
+ raise Exception(
+ 'Current status {} of {} {} is one of failures {}'
+ .format(current_status, check_type, check_name, failures))
+ time.sleep(interval)
+ total_sleep += interval
+ self.assertOutput(desired_status, current_status)
+
def test_aggregate_crud(self):
"""Test create, delete multiple"""
name1 = uuid.uuid4().hex
@@ -36,12 +62,16 @@ class AggregateTests(base.TestCase):
'nova',
cmd_output['availability_zone']
)
- # TODO(dtroyer): enable the following once the properties field
- # is correctly formatted in the create output
- # self.assertIn(
- # "a='b'",
- # cmd_output['properties']
- # )
+ self.assertIn(
+ 'a',
+ cmd_output['properties']
+ )
+ self.wait_for_status('aggregate', name1, name1)
+ self.addCleanup(
+ self.openstack,
+ 'aggregate delete ' + name1,
+ fail_ok=True,
+ )
name2 = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
@@ -57,6 +87,12 @@ class AggregateTests(base.TestCase):
'external',
cmd_output['availability_zone']
)
+ self.wait_for_status('aggregate', name2, name2)
+ self.addCleanup(
+ self.openstack,
+ 'aggregate delete ' + name2,
+ fail_ok=True,
+ )
# Test aggregate set
name3 = uuid.uuid4().hex
@@ -69,6 +105,11 @@ class AggregateTests(base.TestCase):
name1
)
self.assertOutput('', raw_output)
+ self.addCleanup(
+ self.openstack,
+ 'aggregate delete ' + name3,
+ fail_ok=True,
+ )
cmd_output = json.loads(self.openstack(
'aggregate show -f json ' +
@@ -83,11 +124,11 @@ class AggregateTests(base.TestCase):
cmd_output['availability_zone']
)
self.assertIn(
- "c='d'",
+ 'c',
cmd_output['properties']
)
self.assertNotIn(
- "a='b'",
+ 'a',
cmd_output['properties']
)
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index 6330ac9..2bca70d 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -485,7 +485,7 @@ class ServerTests(common.ComputeTestCase):
cmd_output['status'],
)
- # NOTE(dtroyer): Prior to https://review.openstack.org/#/c/407111
+ # NOTE(dtroyer): Prior to https://review.opendev.org/#/c/407111
# --block-device-mapping was ignored if --volume
# present on the command line. Now we should see the
# attachment.
@@ -614,6 +614,169 @@ class ServerTests(common.ComputeTestCase):
# the attached volume had been deleted
pass
+ def test_server_boot_with_bdm_image(self):
+ # Tests creating a server where the root disk is backed by the given
+ # --image but a --block-device-mapping with type=image is provided so
+ # that the compute service creates a volume from that image and
+ # attaches it as a non-root volume on the server. The block device is
+ # marked as delete_on_termination=True so it will be automatically
+ # deleted when the server is deleted.
+
+ # create server with bdm type=image
+ # NOTE(mriedem): This test is a bit unrealistic in that specifying the
+ # same image in the block device as the --image option does not really
+ # make sense, but we just want to make sure everything is processed
+ # as expected where nova creates a volume from the image and attaches
+ # that volume to the server.
+ server_name = uuid.uuid4().hex
+ server = json.loads(self.openstack(
+ 'server create -f json ' +
+ '--flavor ' + self.flavor_name + ' ' +
+ '--image ' + self.image_name + ' ' +
+ '--block-device-mapping '
+ # This means create a 1GB volume from the specified image, attach
+ # it to the server at /dev/vdb and delete the volume when the
+ # server is deleted.
+ 'vdb=' + self.image_name + ':image:1:true ' +
+ self.network_arg + ' ' +
+ '--wait ' +
+ server_name
+ ))
+ self.assertIsNotNone(server["id"])
+ self.assertEqual(
+ server_name,
+ server['name'],
+ )
+ self.wait_for_status(server_name, 'ACTIVE')
+
+ # check server volumes_attached, format is
+ # {"volumes_attached": "id='2518bc76-bf0b-476e-ad6b-571973745bb5'",}
+ cmd_output = json.loads(self.openstack(
+ 'server show -f json ' +
+ server_name
+ ))
+ volumes_attached = cmd_output['volumes_attached']
+ self.assertTrue(volumes_attached.startswith('id='))
+ attached_volume_id = volumes_attached.replace('id=', '')
+
+ # check the volume that attached on server
+ cmd_output = json.loads(self.openstack(
+ 'volume show -f json ' +
+ attached_volume_id
+ ))
+ attachments = cmd_output['attachments']
+ self.assertEqual(
+ 1,
+ len(attachments),
+ )
+ self.assertEqual(
+ server['id'],
+ attachments[0]['server_id'],
+ )
+ self.assertEqual(
+ "in-use",
+ cmd_output['status'],
+ )
+ # TODO(mriedem): If we can parse the volume_image_metadata field from
+ # the volume show output we could assert the image_name is what we
+ # specified. volume_image_metadata is something like this:
+ # {u'container_format': u'bare', u'min_ram': u'0',
+ # u'disk_format': u'qcow2', u'image_name': u'cirros-0.4.0-x86_64-disk',
+ # u'image_id': u'05496c83-e2df-4c2f-9e48-453b6e49160d',
+ # u'checksum': u'443b7623e27ecf03dc9e01ee93f67afe', u'min_disk': u'0',
+ # u'size': u'12716032'}
+
+ # delete server, then check the attached volume has been deleted
+ self.openstack('server delete --wait ' + server_name)
+ cmd_output = json.loads(self.openstack(
+ 'volume list -f json'
+ ))
+ target_volume = [each_volume
+ for each_volume in cmd_output
+ if each_volume['ID'] == attached_volume_id]
+ if target_volume:
+ # check the attached volume is 'deleting' status
+ self.assertEqual('deleting', target_volume[0]['Status'])
+ else:
+ # the attached volume had been deleted
+ pass
+
+ def test_boot_from_volume(self):
+ # Tests creating a server using --image and --boot-from-volume where
+ # the compute service will create a root volume of the specified size
+ # using the provided image, attach it as the root disk for the server
+ # and not delete the volume when the server is deleted.
+ server_name = uuid.uuid4().hex
+ server = json.loads(self.openstack(
+ 'server create -f json ' +
+ '--flavor ' + self.flavor_name + ' ' +
+ '--image ' + self.image_name + ' ' +
+ '--boot-from-volume 1 ' + # create a 1GB volume from the image
+ self.network_arg + ' ' +
+ '--wait ' +
+ server_name
+ ))
+ self.assertIsNotNone(server["id"])
+ self.assertEqual(
+ server_name,
+ server['name'],
+ )
+ self.wait_for_status(server_name, 'ACTIVE')
+
+ # check server volumes_attached, format is
+ # {"volumes_attached": "id='2518bc76-bf0b-476e-ad6b-571973745bb5'",}
+ cmd_output = json.loads(self.openstack(
+ 'server show -f json ' +
+ server_name
+ ))
+ volumes_attached = cmd_output['volumes_attached']
+ self.assertTrue(volumes_attached.startswith('id='))
+ attached_volume_id = volumes_attached.replace('id=', '')
+ # Don't leak the volume when the test exits.
+ self.addCleanup(self.openstack, 'volume delete ' + attached_volume_id)
+
+ # Since the server is volume-backed the GET /servers/{server_id}
+ # response will have image=''.
+ self.assertEqual('', cmd_output['image'])
+
+ # check the volume that attached on server
+ cmd_output = json.loads(self.openstack(
+ 'volume show -f json ' +
+ attached_volume_id
+ ))
+ # The volume size should be what we specified on the command line.
+ self.assertEqual(1, int(cmd_output['size']))
+ attachments = cmd_output['attachments']
+ self.assertEqual(
+ 1,
+ len(attachments),
+ )
+ self.assertEqual(
+ server['id'],
+ attachments[0]['server_id'],
+ )
+ self.assertEqual(
+ "in-use",
+ cmd_output['status'],
+ )
+ # TODO(mriedem): If we can parse the volume_image_metadata field from
+ # the volume show output we could assert the image_name is what we
+ # specified. volume_image_metadata is something like this:
+ # {u'container_format': u'bare', u'min_ram': u'0',
+ # u'disk_format': u'qcow2', u'image_name': u'cirros-0.4.0-x86_64-disk',
+ # u'image_id': u'05496c83-e2df-4c2f-9e48-453b6e49160d',
+ # u'checksum': u'443b7623e27ecf03dc9e01ee93f67afe', u'min_disk': u'0',
+ # u'size': u'12716032'}
+
+ # delete server, then check the attached volume was not deleted
+ self.openstack('server delete --wait ' + server_name)
+ cmd_output = json.loads(self.openstack(
+ 'volume show -f json ' +
+ attached_volume_id
+ ))
+ # check the volume is in 'available' status
+ self.assertEqual('available', cmd_output['status'])
+
def test_server_create_with_none_network(self):
"""Test server create with none network option."""
server_name = uuid.uuid4().hex
diff --git a/openstackclient/tests/functional/identity/v2/test_role.py b/openstackclient/tests/functional/identity/v2/test_role.py
index 82e19aa..124603d 100644
--- a/openstackclient/tests/functional/identity/v2/test_role.py
+++ b/openstackclient/tests/functional/identity/v2/test_role.py
@@ -29,38 +29,6 @@ class RoleTests(common.IdentityTests):
items = self.parse_listing(raw_output)
self.assert_table_structure(items, common.BASIC_LIST_HEADERS)
- def test_role_list_with_user_project(self):
- project_name = self._create_dummy_project()
- role_name = self._create_dummy_role()
- username = self._create_dummy_user()
- raw_output = self.openstack(
- 'role add '
- '--project %(project)s '
- '--user %(user)s '
- '%(role)s' % {'project': project_name,
- 'user': username,
- 'role': role_name})
- self.addCleanup(
- self.openstack,
- 'role remove '
- '--project %(project)s '
- '--user %(user)s '
- '%(role)s' % {'project': project_name,
- 'user': username,
- 'role': role_name})
- items = self.parse_show(raw_output)
- self.assert_show_fields(items, self.ROLE_FIELDS)
-
- raw_output = self.openstack(
- 'role list '
- '--project %(project)s '
- '--user %(user)s '
- '' % {'project': project_name,
- 'user': username})
- items = self.parse_listing(raw_output)
- self.assert_table_structure(items, common.BASIC_LIST_HEADERS)
- self.assertEqual(1, len(items))
-
def test_role_show(self):
role_name = self._create_dummy_role()
raw_output = self.openstack('role show %s' % role_name)
diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py
index fb9e061..38bfff7 100644
--- a/openstackclient/tests/functional/identity/v3/test_role.py
+++ b/openstackclient/tests/functional/identity/v3/test_role.py
@@ -31,47 +31,6 @@ class RoleTests(common.IdentityTests):
items = self.parse_listing(raw_output)
self.assert_table_structure(items, common.BASIC_LIST_HEADERS)
- def test_role_list_with_user_project(self):
- role_name = self._create_dummy_role()
- username = self._create_dummy_user()
- raw_output = self.openstack(
- 'role add '
- '--project %(project)s '
- '--project-domain %(project_domain)s '
- '--user %(user)s '
- '--user-domain %(user_domain)s '
- '%(role)s' % {'project': self.project_name,
- 'project_domain': self.domain_name,
- 'user': username,
- 'user_domain': self.domain_name,
- 'role': role_name})
- self.addCleanup(
- self.openstack,
- 'role remove '
- '--project %(project)s '
- '--project-domain %(project_domain)s '
- '--user %(user)s '
- '--user-domain %(user_domain)s '
- '%(role)s' % {'project': self.project_name,
- 'project_domain': self.domain_name,
- 'user': username,
- 'user_domain': self.domain_name,
- 'role': role_name})
- self.assertEqual(0, len(raw_output))
- raw_output = self.openstack(
- 'role list '
- '--project %(project)s '
- '--project-domain %(project_domain)s '
- '--user %(user)s '
- '--user-domain %(user_domain)s '
- '' % {'project': self.project_name,
- 'project_domain': self.domain_name,
- 'user': username,
- 'user_domain': self.domain_name})
- items = self.parse_listing(raw_output)
- self.assert_table_structure(items, common.BASIC_LIST_HEADERS)
- self.assertEqual(1, len(items))
-
def test_role_show(self):
role_name = self._create_dummy_role()
raw_output = self.openstack('role show %s' % role_name)
diff --git a/openstackclient/tests/functional/image/v1/test_image.py b/openstackclient/tests/functional/image/v1/test_image.py
index 30490bf..b9774ab 100644
--- a/openstackclient/tests/functional/image/v1/test_image.py
+++ b/openstackclient/tests/functional/image/v1/test_image.py
@@ -104,6 +104,6 @@ class ImageTests(base.BaseImageTests):
self.name
))
self.assertEqual(
- "a='b', c='d'",
+ {'a': 'b', 'c': 'd'},
json_output["properties"],
)
diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py
index 3185c3b..264ba51 100644
--- a/openstackclient/tests/functional/image/v2/test_image.py
+++ b/openstackclient/tests/functional/image/v2/test_image.py
@@ -14,7 +14,6 @@ import json
import uuid
import fixtures
-# from glanceclient import exc as image_exceptions
from openstackclient.tests.functional.image import base
@@ -81,7 +80,7 @@ class ImageTests(base.BaseImageTests):
json_output = json.loads(self.openstack(
'image list --tag ' + self.image_tag + ' --long -f json'
))
- for taglist in [img['Tags'].split(', ') for img in json_output]:
+ for taglist in [img['Tags'] for img in json_output]:
self.assertIn(
self.image_tag,
taglist
@@ -127,10 +126,8 @@ class ImageTests(base.BaseImageTests):
'image show -f json ' +
self.name
))
- # NOTE(dtroyer): Don't do a full-string compare so we are tolerant of
- # new artributes in the returned data
- self.assertIn("a='b'", json_output["properties"])
- self.assertIn("c='d'", json_output["properties"])
+ self.assertIn("a", json_output["properties"])
+ self.assertIn("c", json_output["properties"])
self.openstack(
'image unset ' +
@@ -142,15 +139,13 @@ class ImageTests(base.BaseImageTests):
'image show -f json ' +
self.name
))
- # NOTE(dtroyer): Don't do a full-string compare so we are tolerant of
- # new artributes in the returned data
- self.assertNotIn("a='b'", json_output["properties"])
- self.assertNotIn("c='d'", json_output["properties"])
+ self.assertNotIn("a", json_output["properties"])
+ self.assertNotIn("c", json_output["properties"])
# Test tags
self.assertNotIn(
'01',
- json_output["tags"].split(', ')
+ json_output["tags"]
)
self.openstack(
'image set ' +
@@ -163,7 +158,7 @@ class ImageTests(base.BaseImageTests):
))
self.assertIn(
'01',
- json_output["tags"].split(', ')
+ json_output["tags"]
)
self.openstack(
@@ -177,7 +172,7 @@ class ImageTests(base.BaseImageTests):
))
self.assertNotIn(
'01',
- json_output["tags"].split(', ')
+ json_output["tags"]
)
def test_image_set_rename(self):
diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py
index 5243ecd..2287f32 100644
--- a/openstackclient/tests/functional/network/v2/common.py
+++ b/openstackclient/tests/functional/network/v2/common.py
@@ -62,21 +62,13 @@ class NetworkTagTests(NetworkTests):
self._set_resource_and_tag_check('unset', name1, '--all-tag', [])
self._set_resource_and_tag_check('set', name2, '--no-tag', [])
- def _assertTagsEqual(self, expected, actual):
- # TODO(amotoki): Should migrate to cliff format columns.
- # At now, unit test assert method needs to be replaced
- # to handle format columns, so format_list() is used.
- # NOTE: The order of tag is undeterminestic.
- actual_tags = filter(bool, actual.split(', '))
- self.assertEqual(set(expected), set(actual_tags))
-
def _list_tag_check(self, project_id, expected):
cmd_output = json.loads(self.openstack(
'{} list --long --project {} -f json'.format(self.base_command,
project_id)))
for name, tags in expected:
net = [n for n in cmd_output if n['Name'] == name][0]
- self._assertTagsEqual(tags, net['Tags'])
+ self.assertEqual(set(tags), set(net['Tags']))
def _create_resource_for_tag_test(self, name, args):
return json.loads(self.openstack(
@@ -89,7 +81,7 @@ class NetworkTagTests(NetworkTests):
self.addCleanup(
self.openstack, '{} delete {}'.format(self.base_command, name))
self.assertIsNotNone(cmd_output["id"])
- self._assertTagsEqual(expected, cmd_output['tags'])
+ self.assertEqual(set(expected), set(cmd_output['tags']))
return name
def _set_resource_and_tag_check(self, command, name, args, expected):
@@ -100,4 +92,4 @@ class NetworkTagTests(NetworkTests):
cmd_output = json.loads(self.openstack(
'{} show -f json {}'.format(self.base_command, name)
))
- self._assertTagsEqual(expected, cmd_output['tags'])
+ self.assertEqual(set(expected), set(cmd_output['tags']))
diff --git a/openstackclient/tests/functional/network/v2/test_floating_ip.py b/openstackclient/tests/functional/network/v2/test_floating_ip.py
index f189c2d..9d109f8 100644
--- a/openstackclient/tests/functional/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/functional/network/v2/test_floating_ip.py
@@ -24,12 +24,9 @@ class FloatingIpTests(common.NetworkTests):
def setUpClass(cls):
common.NetworkTests.setUpClass()
if cls.haz_network:
+ # Create common networks that all tests share
cls.EXTERNAL_NETWORK_NAME = uuid.uuid4().hex
- cls.EXTERNAL_SUBNET_NAME = uuid.uuid4().hex
cls.PRIVATE_NETWORK_NAME = uuid.uuid4().hex
- cls.PRIVATE_SUBNET_NAME = uuid.uuid4().hex
- cls.ROUTER = uuid.uuid4().hex
- cls.PORT_NAME = uuid.uuid4().hex
# Create a network for the floating ip
json_output = json.loads(cls.openstack(
@@ -46,57 +43,11 @@ class FloatingIpTests(common.NetworkTests):
))
cls.private_network_id = json_output["id"]
- # Try random subnet range for subnet creating
- # Because we can not determine ahead of time what subnets are
- # already in use, possibly by another test running in parallel,
- # try 4 times
- for i in range(4):
- # Make a random subnet
- cls.external_subnet = ".".join(map(
- str,
- (random.randint(0, 223) for _ in range(3))
- )) + ".0/26"
- cls.private_subnet = ".".join(map(
- str,
- (random.randint(0, 223) for _ in range(3))
- )) + ".0/26"
- try:
- # Create a subnet for the network
- json_output = json.loads(cls.openstack(
- 'subnet create -f json ' +
- '--network ' + cls.EXTERNAL_NETWORK_NAME + ' ' +
- '--subnet-range ' + cls.external_subnet + ' ' +
- cls.EXTERNAL_SUBNET_NAME
- ))
- cls.external_subnet_id = json_output["id"]
- # Create a subnet for the private network
- json_output = json.loads(cls.openstack(
- 'subnet create -f json ' +
- '--network ' + cls.PRIVATE_NETWORK_NAME + ' ' +
- '--subnet-range ' + cls.private_subnet + ' ' +
- cls.PRIVATE_SUBNET_NAME
- ))
- cls.private_subnet_id = json_output["id"]
- except Exception:
- if (i == 3):
- # raise the exception at the last time
- raise
- pass
- else:
- # break and no longer retry if create successfully
- break
-
@classmethod
def tearDownClass(cls):
try:
if cls.haz_network:
del_output = cls.openstack(
- 'subnet delete ' +
- cls.EXTERNAL_SUBNET_NAME + ' ' +
- cls.PRIVATE_SUBNET_NAME
- )
- cls.assertOutput('', del_output)
- del_output = cls.openstack(
'network delete ' +
cls.EXTERNAL_NETWORK_NAME + ' ' +
cls.PRIVATE_NETWORK_NAME
@@ -114,11 +65,50 @@ class FloatingIpTests(common.NetworkTests):
# Verify setup
self.assertIsNotNone(self.external_network_id)
self.assertIsNotNone(self.private_network_id)
- self.assertIsNotNone(self.external_subnet_id)
- self.assertIsNotNone(self.private_subnet_id)
+
+ def _create_subnet(self, network_name, subnet_name):
+ subnet_id = None
+
+ # Try random subnet range for subnet creating
+ # Because we can not determine ahead of time what subnets are
+ # already in use, possibly by another test running in parallel,
+ # try 4 times
+ for i in range(4):
+ # Make a random subnet
+ subnet = ".".join(map(
+ str,
+ (random.randint(0, 223) for _ in range(3))
+ )) + ".0/26"
+ try:
+ # Create a subnet for the network
+ json_output = json.loads(self.openstack(
+ 'subnet create -f json ' +
+ '--network ' + network_name + ' ' +
+ '--subnet-range ' + subnet + ' ' +
+ subnet_name
+ ))
+ self.assertIsNotNone(json_output["id"])
+ subnet_id = json_output["id"]
+ except Exception:
+ if (i == 3):
+ # raise the exception at the last time
+ raise
+ pass
+ else:
+ # break and no longer retry if create successfully
+ break
+ return subnet_id
def test_floating_ip_delete(self):
"""Test create, delete multiple"""
+
+ # Subnets must exist even if not directly referenced here
+ ext_subnet_id = self._create_subnet(
+ self.EXTERNAL_NETWORK_NAME,
+ "ext-test-delete"
+ )
+ self.addCleanup(self.openstack, 'subnet delete ' + ext_subnet_id)
+
json_output = json.loads(self.openstack(
'floating ip create -f json ' +
'--description aaaa ' +
@@ -151,6 +141,14 @@ class FloatingIpTests(common.NetworkTests):
def test_floating_ip_list(self):
"""Test create defaults, list filters, delete"""
+
+ # Subnets must exist even if not directly referenced here
+ ext_subnet_id = self._create_subnet(
+ self.EXTERNAL_NETWORK_NAME,
+ "ext-test-delete"
+ )
+ self.addCleanup(self.openstack, 'subnet delete ' + ext_subnet_id)
+
json_output = json.loads(self.openstack(
'floating ip create -f json ' +
'--description aaaa ' +
@@ -237,6 +235,22 @@ class FloatingIpTests(common.NetworkTests):
def test_floating_ip_set_and_unset_port(self):
"""Test Floating IP Set and Unset port"""
+
+ # Subnets must exist even if not directly referenced here
+ ext_subnet_id = self._create_subnet(
+ self.EXTERNAL_NETWORK_NAME,
+ "ext-test-delete"
+ )
+ self.addCleanup(self.openstack, 'subnet delete ' + ext_subnet_id)
+ priv_subnet_id = self._create_subnet(
+ self.PRIVATE_NETWORK_NAME,
+ "priv-test-delete"
+ )
+ self.addCleanup(self.openstack, 'subnet delete ' + priv_subnet_id)
+
+ self.ROUTER = uuid.uuid4().hex
+ self.PORT_NAME = uuid.uuid4().hex
+
json_output = json.loads(self.openstack(
'floating ip create -f json ' +
'--description aaaa ' +
@@ -253,7 +267,7 @@ class FloatingIpTests(common.NetworkTests):
json_output = json.loads(self.openstack(
'port create -f json ' +
'--network ' + self.PRIVATE_NETWORK_NAME + ' ' +
- '--fixed-ip subnet=' + self.PRIVATE_SUBNET_NAME + ' ' +
+ '--fixed-ip subnet=' + priv_subnet_id + ' ' +
self.PORT_NAME
))
self.assertIsNotNone(json_output["id"])
diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py
index 1a74496..71b533e 100644
--- a/openstackclient/tests/functional/network/v2/test_network.py
+++ b/openstackclient/tests/functional/network/v2/test_network.py
@@ -120,14 +120,14 @@ class NetworkTests(common.NetworkTagTests):
cmd_output["description"],
)
self.assertEqual(
- 'UP',
+ True,
cmd_output["admin_state_up"],
)
self.assertFalse(
cmd_output["shared"],
)
self.assertEqual(
- 'Internal',
+ False,
cmd_output["router:external"],
)
@@ -231,12 +231,12 @@ class NetworkTests(common.NetworkTagTests):
)
# Check the default values
self.assertEqual(
- 'UP',
+ True,
cmd_output["admin_state_up"],
)
self.assertFalse(cmd_output["shared"])
self.assertEqual(
- 'Internal',
+ False,
cmd_output["router:external"],
)
self.assertFalse(cmd_output["is_default"])
@@ -266,7 +266,7 @@ class NetworkTests(common.NetworkTagTests):
cmd_output["description"],
)
self.assertEqual(
- 'DOWN',
+ False,
cmd_output["admin_state_up"],
)
self.assertTrue(cmd_output["shared"])
@@ -398,12 +398,12 @@ class NetworkTests(common.NetworkTagTests):
cmd_output["description"],
)
self.assertEqual(
- 'UP',
+ True,
cmd_output["admin_state_up"],
)
self.assertFalse(cmd_output["shared"])
self.assertEqual(
- 'Internal',
+ False,
cmd_output["router:external"],
)
@@ -432,12 +432,12 @@ class NetworkTests(common.NetworkTagTests):
cmd_output["description"],
)
self.assertEqual(
- 'DOWN',
+ False,
cmd_output["admin_state_up"],
)
self.assertTrue(cmd_output["shared"])
self.assertEqual(
- 'External',
+ True,
cmd_output["router:external"],
)
self.assertFalse(cmd_output["is_default"])
diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py
index 86769e0..963227d 100644
--- a/openstackclient/tests/functional/network/v2/test_network_agent.py
+++ b/openstackclient/tests/functional/network/v2/test_network_agent.py
@@ -59,7 +59,7 @@ class NetworkAgentTests(common.NetworkTests):
'network agent show -f json %s' % agent_ids[0]
))
self.assertEqual(
- "DOWN",
+ False,
cmd_output['admin_state_up'],
)
@@ -72,7 +72,7 @@ class NetworkAgentTests(common.NetworkTests):
'network agent show -f json %s' % agent_ids[0]
))
self.assertEqual(
- "UP",
+ True,
cmd_output['admin_state_up'],
)
diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py
index e3067d9..a20d204 100644
--- a/openstackclient/tests/functional/network/v2/test_port.py
+++ b/openstackclient/tests/functional/network/v2/test_port.py
@@ -155,7 +155,7 @@ class PortTests(common.NetworkTagTests):
self.addCleanup(self.openstack, 'port delete %s' % id1)
self.assertEqual(name, json_output.get('name'))
self.assertEqual('xyzpdq', json_output.get('description'))
- self.assertEqual('DOWN', json_output.get('admin_state_up'))
+ self.assertEqual(False, json_output.get('admin_state_up'))
raw_output = self.openstack(
'port set --enable %s' %
@@ -166,11 +166,11 @@ class PortTests(common.NetworkTagTests):
json_output = json.loads(self.openstack(
'port show -f json %s' % name
))
- sg_id = json_output.get('security_group_ids')
+ sg_id = json_output.get('security_group_ids')[0]
self.assertEqual(name, json_output.get('name'))
self.assertEqual('xyzpdq', json_output.get('description'))
- self.assertEqual('UP', json_output.get('admin_state_up'))
+ self.assertEqual(True, json_output.get('admin_state_up'))
self.assertIsNotNone(json_output.get('mac_address'))
raw_output = self.openstack(
@@ -180,7 +180,7 @@ class PortTests(common.NetworkTagTests):
json_output = json.loads(self.openstack(
'port show -f json %s' % name
))
- self.assertEqual('', json_output.get('security_group_ids'))
+ self.assertEqual([], json_output.get('security_group_ids'))
def test_port_admin_set(self):
"""Test create, set (as admin), show, delete"""
@@ -229,7 +229,7 @@ class PortTests(common.NetworkTagTests):
id1 = json_output.get('id')
self.addCleanup(self.openstack, 'port delete %s' % id1)
self.assertEqual(name, json_output.get('name'))
- self.assertEqual(sg_id1, json_output.get('security_group_ids'))
+ self.assertEqual([sg_id1], json_output.get('security_group_ids'))
raw_output = self.openstack(
'port set '
@@ -242,16 +242,10 @@ class PortTests(common.NetworkTagTests):
'port show -f json %s' % name
))
self.assertEqual(name, json_output.get('name'))
- self.assertIn(
- # TODO(dtroyer): output formatters should not mess with JSON!
- sg_id1,
- json_output.get('security_group_ids'),
- )
- self.assertIn(
- # TODO(dtroyer): output formatters should not mess with JSON!
- sg_id2,
- json_output.get('security_group_ids'),
- )
+ # NOTE(amotoki): The order of the field is not predictable,
+ self.assertIsInstance(json_output.get('security_group_ids'), list)
+ self.assertEqual(sorted([sg_id1, sg_id2]),
+ sorted(json_output.get('security_group_ids')))
raw_output = self.openstack(
'port unset --security-group %s %s' % (sg_id1, id1))
@@ -261,9 +255,8 @@ class PortTests(common.NetworkTagTests):
'port show -f json %s' % name
))
self.assertEqual(
- # TODO(dtroyer): output formatters should do this on JSON!
- sg_id2,
- json_output.get('security_group_ids'),
+ [sg_id2],
+ json_output.get('security_group_ids')
)
def _create_resource_for_tag_test(self, name, args):
diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py
index 9d5beff..05aad7a 100644
--- a/openstackclient/tests/functional/network/v2/test_router.py
+++ b/openstackclient/tests/functional/network/v2/test_router.py
@@ -90,7 +90,7 @@ class RouterTests(common.NetworkTagTests):
cmd_output["name"],
)
self.assertEqual(
- "DOWN",
+ False,
cmd_output["admin_state_up"],
)
self.assertEqual(
@@ -109,7 +109,7 @@ class RouterTests(common.NetworkTagTests):
cmd_output["name"],
)
self.assertEqual(
- "UP",
+ True,
cmd_output["admin_state_up"],
)
self.assertEqual(
@@ -230,7 +230,7 @@ class RouterTests(common.NetworkTagTests):
cmd_output["description"],
)
self.assertEqual(
- 'DOWN',
+ False,
cmd_output["admin_state_up"],
)
diff --git a/openstackclient/tests/functional/network/v2/test_subnet.py b/openstackclient/tests/functional/network/v2/test_subnet.py
index d5309ee..38030e0 100644
--- a/openstackclient/tests/functional/network/v2/test_subnet.py
+++ b/openstackclient/tests/functional/network/v2/test_subnet.py
@@ -236,7 +236,7 @@ class SubnetTests(common.NetworkTagTests):
cmd_output["gateway_ip"],
)
self.assertEqual(
- 'network:floatingip_agent_gateway',
+ ['network:floatingip_agent_gateway'],
cmd_output["service_types"],
)
@@ -253,7 +253,7 @@ class SubnetTests(common.NetworkTagTests):
new_name
))
self.assertEqual(
- '',
+ [],
cmd_output["service_types"],
)
diff --git a/openstackclient/tests/functional/network/v2/test_subnet_pool.py b/openstackclient/tests/functional/network/v2/test_subnet_pool.py
index 6be5052..dbcf01e 100644
--- a/openstackclient/tests/functional/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/functional/network/v2/test_subnet_pool.py
@@ -38,7 +38,7 @@ class SubnetPoolTests(common.NetworkTagTests):
cmd_output["name"]
)
self.assertEqual(
- pool_prefix,
+ [pool_prefix],
cmd_output["prefixes"]
)
@@ -50,7 +50,7 @@ class SubnetPoolTests(common.NetworkTagTests):
cmd_output["name"]
)
self.assertEqual(
- pool_prefix,
+ [pool_prefix],
cmd_output["prefixes"]
)
@@ -104,7 +104,7 @@ class SubnetPoolTests(common.NetworkTagTests):
cmd_output["project_id"],
)
self.assertEqual(
- pool_prefix,
+ [pool_prefix],
cmd_output["prefixes"],
)
@@ -126,7 +126,7 @@ class SubnetPoolTests(common.NetworkTagTests):
cmd_output["project_id"],
)
self.assertEqual(
- pool_prefix,
+ [pool_prefix],
cmd_output["prefixes"],
)
@@ -193,7 +193,7 @@ class SubnetPoolTests(common.NetworkTagTests):
cmd_output["description"],
)
self.assertEqual(
- pool_prefix,
+ [pool_prefix],
cmd_output["prefixes"],
)
self.assertEqual(
@@ -239,9 +239,9 @@ class SubnetPoolTests(common.NetworkTagTests):
'bbbb',
cmd_output["description"],
)
- self.assertInOutput(
- "10.110.0.0/16",
- cmd_output["prefixes"],
+ self.assertEqual(
+ sorted(["10.110.0.0/16", pool_prefix]),
+ sorted(cmd_output["prefixes"]),
)
self.assertEqual(
8,
diff --git a/openstackclient/tests/functional/volume/v1/test_qos.py b/openstackclient/tests/functional/volume/v1/test_qos.py
index 434840f..d8277df 100644
--- a/openstackclient/tests/functional/volume/v1/test_qos.py
+++ b/openstackclient/tests/functional/volume/v1/test_qos.py
@@ -93,7 +93,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['name']
)
self.assertEqual(
- "Alpha='c', Beta='b'",
+ {'Alpha': 'c', 'Beta': 'b'},
cmd_output['properties']
)
@@ -114,7 +114,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['name']
)
self.assertEqual(
- "Beta='b'",
+ {'Beta': 'b'},
cmd_output['properties']
)
diff --git a/openstackclient/tests/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py
index 083cd1b..5a76a2e 100644
--- a/openstackclient/tests/functional/volume/v1/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v1/test_snapshot.py
@@ -204,7 +204,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
cmd_output["display_description"],
)
self.assertEqual(
- "Alpha='a', Beta='b'",
+ {'Alpha': 'a', 'Beta': 'b'},
cmd_output["properties"],
)
@@ -221,7 +221,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
new_name
))
self.assertEqual(
- "Beta='b'",
+ {'Beta': 'b'},
cmd_output["properties"],
)
@@ -236,7 +236,4 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
'volume snapshot show -f json ' +
new_name
))
- self.assertNotIn(
- "Beta='b'",
- cmd_output["properties"],
- )
+ self.assertEqual({}, cmd_output["properties"])
diff --git a/openstackclient/tests/functional/volume/v1/test_volume.py b/openstackclient/tests/functional/volume/v1/test_volume.py
index 001bbe6..013bc6a 100644
--- a/openstackclient/tests/functional/volume/v1/test_volume.py
+++ b/openstackclient/tests/functional/volume/v1/test_volume.py
@@ -123,7 +123,7 @@ class VolumeTests(common.BaseVolumeTests):
cmd_output["display_description"],
)
self.assertEqual(
- "Alpha='a'",
+ {'Alpha': 'a'},
cmd_output["properties"],
)
self.assertEqual(
@@ -165,7 +165,7 @@ class VolumeTests(common.BaseVolumeTests):
cmd_output["display_description"],
)
self.assertEqual(
- "Beta='b', Gamma='c'",
+ {'Beta': 'b', 'Gamma': 'c'},
cmd_output["properties"],
)
self.assertEqual(
@@ -186,7 +186,7 @@ class VolumeTests(common.BaseVolumeTests):
new_name
))
self.assertEqual(
- "Gamma='c'",
+ {'Gamma': 'c'},
cmd_output["properties"],
)
diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py
index eb9d7f6..fb8dabd 100644
--- a/openstackclient/tests/functional/volume/v1/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v1/test_volume_type.py
@@ -66,8 +66,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- # TODO(amotoki): properties output should be machine-readable
- self.assertEqual("a='b', c='d'", cmd_output['properties'])
+ self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
raw_output = self.openstack(
'volume type unset --property a %s' % name
@@ -76,7 +75,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- self.assertEqual("c='d'", cmd_output['properties'])
+ self.assertEqual({'c': 'd'}, cmd_output['properties'])
def test_volume_type_set_unset_multiple_properties(self):
name = uuid.uuid4().hex
@@ -97,7 +96,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- self.assertEqual("a='b', c='d'", cmd_output['properties'])
+ self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
raw_output = self.openstack(
'volume type unset --property a --property c %s' % name
@@ -106,7 +105,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- self.assertEqual("", cmd_output['properties'])
+ self.assertEqual({}, cmd_output['properties'])
def test_multi_delete(self):
vol_type1 = uuid.uuid4().hex
@@ -133,34 +132,32 @@ class VolumeTypeTests(common.BaseVolumeTests):
'--encryption-key-size 128 '
'--encryption-control-location front-end ' +
encryption_type))
- # TODO(amotoki): encryption output should be machine-readable
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test show encryption type
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + encryption_type))
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test list encryption type
cmd_output = json.loads(self.openstack(
'volume type list -f json --encryption-type'))
encryption_output = [t['Encryption'] for t in cmd_output
if t['Name'] == encryption_type][0]
- # TODO(amotoki): encryption output should be machine-readable
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, encryption_output)
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, encryption_output[attr])
# test set new encryption type
raw_output = self.openstack(
'volume type set '
@@ -185,12 +182,12 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + name
))
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test unset encryption type
raw_output = self.openstack(
'volume type unset --encryption-type ' + name
@@ -199,7 +196,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + name
))
- self.assertEqual('', cmd_output['encryption'])
+ self.assertEqual({}, cmd_output['encryption'])
# test delete encryption type
raw_output = self.openstack('volume type delete ' + encryption_type)
self.assertEqual('', raw_output)
diff --git a/openstackclient/tests/functional/volume/v2/test_qos.py b/openstackclient/tests/functional/volume/v2/test_qos.py
index 646becc..f9f6e09 100644
--- a/openstackclient/tests/functional/volume/v2/test_qos.py
+++ b/openstackclient/tests/functional/volume/v2/test_qos.py
@@ -74,7 +74,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['consumer']
)
self.assertEqual(
- "Alpha='a'",
+ {'Alpha': 'a'},
cmd_output['properties']
)
@@ -97,7 +97,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['name']
)
self.assertEqual(
- "Alpha='c', Beta='b'",
+ {'Alpha': 'c', 'Beta': 'b'},
cmd_output['properties']
)
@@ -118,7 +118,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['name']
)
self.assertEqual(
- "Beta='b'",
+ {'Beta': 'b'},
cmd_output['properties']
)
diff --git a/openstackclient/tests/functional/volume/v2/test_volume.py b/openstackclient/tests/functional/volume/v2/test_volume.py
index 2930d48..19fd589 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume.py
@@ -128,7 +128,7 @@ class VolumeTests(common.BaseVolumeTests):
cmd_output["description"],
)
self.assertEqual(
- "Alpha='a'",
+ {'Alpha': 'a'},
cmd_output["properties"],
)
self.assertEqual(
@@ -170,7 +170,7 @@ class VolumeTests(common.BaseVolumeTests):
cmd_output["description"],
)
self.assertEqual(
- "Beta='b', Gamma='c'",
+ {'Beta': 'b', 'Gamma': 'c'},
cmd_output["properties"],
)
self.assertEqual(
@@ -196,7 +196,7 @@ class VolumeTests(common.BaseVolumeTests):
new_name
))
self.assertEqual(
- "Gamma='c'",
+ {'Gamma': 'c'},
cmd_output["properties"],
)
self.assertEqual(
diff --git a/openstackclient/tests/functional/volume/v2/test_backup.py b/openstackclient/tests/functional/volume/v2/test_volume_backup.py
index 8f5c032..6868bd4 100644
--- a/openstackclient/tests/functional/volume/v2/test_backup.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_backup.py
@@ -46,14 +46,14 @@ class VolumeBackupTests(common.BaseVolumeTests):
'volume backup create -f json ' +
vol_id
))
- self.wait_for_status("backup", backup['id'], "available")
+ self.wait_for_status("volume backup", backup['id'], "available")
# restore the backup
backup_restored = json.loads(self.openstack(
'volume backup restore -f json %s %s'
% (backup['id'], vol_id)))
self.assertEqual(backup_restored['backup_id'], backup['id'])
- self.wait_for_status("backup", backup['id'], "available")
+ self.wait_for_status("volume backup", backup['id'], "available")
self.wait_for_status("volume", backup_restored['volume_id'],
"available")
self.addCleanup(self.openstack, 'volume delete %s' % vol_id)
diff --git a/openstackclient/tests/functional/volume/v2/test_snapshot.py b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py
index 264f4ad..8d32d99 100644
--- a/openstackclient/tests/functional/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py
@@ -182,7 +182,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
cmd_output["description"],
)
self.assertEqual(
- "Alpha='a'",
+ {'Alpha': 'a'},
cmd_output["properties"],
)
self.wait_for_status('volume snapshot', name, 'available')
@@ -216,7 +216,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
cmd_output["description"],
)
self.assertEqual(
- "Alpha='c', Beta='b'",
+ {'Alpha': 'c', 'Beta': 'b'},
cmd_output["properties"],
)
@@ -233,7 +233,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
new_name
))
self.assertEqual(
- "Beta='b'",
+ {'Beta': 'b'},
cmd_output["properties"],
)
@@ -249,6 +249,6 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
new_name
))
self.assertNotIn(
- "Beta='b'",
+ {'Beta': 'b'},
cmd_output["properties"],
)
diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py
index d8dd5bd..3f1a6ea 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py
@@ -65,8 +65,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- # TODO(amotoki): properties output should be machine-readable
- self.assertEqual("a='b', c='d'", cmd_output['properties'])
+ self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
raw_output = self.openstack(
'volume type unset --property a %s' % name
@@ -75,7 +74,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- self.assertEqual("c='d'", cmd_output['properties'])
+ self.assertEqual({'c': 'd'}, cmd_output['properties'])
def test_volume_type_set_unset_multiple_properties(self):
name = uuid.uuid4().hex
@@ -96,7 +95,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- self.assertEqual("a='b', c='d'", cmd_output['properties'])
+ self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
raw_output = self.openstack(
'volume type unset --property a --property c %s' % name
@@ -105,7 +104,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
- self.assertEqual("", cmd_output['properties'])
+ self.assertEqual({}, cmd_output['properties'])
def test_volume_type_set_unset_project(self):
name = uuid.uuid4().hex
@@ -155,35 +154,32 @@ class VolumeTypeTests(common.BaseVolumeTests):
'--encryption-key-size 128 '
'--encryption-control-location front-end ' +
encryption_type))
- # TODO(amotoki): encryption output should be machine-readable
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test show encryption type
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + encryption_type))
- # TODO(amotoki): encryption output should be machine-readable
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test list encryption type
cmd_output = json.loads(self.openstack(
'volume type list -f json --encryption-type'))
encryption_output = [t['Encryption'] for t in cmd_output
if t['Name'] == encryption_type][0]
- # TODO(amotoki): encryption output should be machine-readable
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, encryption_output)
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, encryption_output[attr])
# test set existing encryption type
raw_output = self.openstack(
'volume type set '
@@ -193,12 +189,12 @@ class VolumeTypeTests(common.BaseVolumeTests):
self.assertEqual('', raw_output)
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + encryption_type))
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='256'",
- "control_location='back-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 256,
+ 'control_location': 'back-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test set new encryption type
cmd_output = json.loads(self.openstack(
'volume type create -f json --private ' +
@@ -222,12 +218,12 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + name
))
- expected = ["provider='LuksEncryptor'",
- "cipher='aes-xts-plain64'",
- "key_size='128'",
- "control_location='front-end'"]
- for attr in expected:
- self.assertIn(attr, cmd_output['encryption'])
+ expected = {'provider': 'LuksEncryptor',
+ 'cipher': 'aes-xts-plain64',
+ 'key_size': 128,
+ 'control_location': 'front-end'}
+ for attr, value in expected.items():
+ self.assertEqual(value, cmd_output['encryption'][attr])
# test unset encryption type
raw_output = self.openstack(
'volume type unset --encryption-type ' + name
@@ -236,7 +232,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + name
))
- self.assertEqual('', cmd_output['encryption'])
+ self.assertEqual({}, cmd_output['encryption'])
# test delete encryption type
raw_output = self.openstack('volume type delete ' + encryption_type)
self.assertEqual('', raw_output)
diff --git a/openstackclient/tests/functional/volume/v3/test_snapshot.py b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py
index 38e0563..28eee6d 100644
--- a/openstackclient/tests/functional/volume/v3/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py
@@ -10,7 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from openstackclient.tests.functional.volume.v2 import test_snapshot as v2
+from openstackclient.tests.functional.volume.v2 import test_volume_snapshot as v2 # noqa
from openstackclient.tests.functional.volume.v3 import common
diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py
index acf9555..74b6249 100644
--- a/openstackclient/tests/unit/api/test_object_store_v1.py
+++ b/openstackclient/tests/unit/api/test_object_store_v1.py
@@ -184,7 +184,7 @@ class TestObject(TestObjectAPIv1):
}
# TODO(dtroyer): When requests_mock gains the ability to
# match against request.body add this check
- # https://review.openstack.org/127316
+ # https://review.opendev.org/127316
self.requests_mock.register_uri(
'PUT',
FAKE_URL + '/qaz/counter.txt',
diff --git a/openstackclient/tests/unit/common/test_clientmanager.py b/openstackclient/tests/unit/common/test_clientmanager.py
index f15f9af..a83b131 100644
--- a/openstackclient/tests/unit/common/test_clientmanager.py
+++ b/openstackclient/tests/unit/common/test_clientmanager.py
@@ -28,19 +28,19 @@ class TestClientManager(osc_lib_test_utils.TestClientManager):
"""Allow subclasses to override the ClientManager class"""
return clientmanager.ClientManager
- def test_client_manager_token_endpoint(self):
+ def test_client_manager_admin_token(self):
token_auth = {
- 'url': fakes.AUTH_URL,
+ 'endpoint': fakes.AUTH_URL,
'token': fakes.AUTH_TOKEN,
}
client_manager = self._make_clientmanager(
auth_args=token_auth,
- auth_plugin_name='token_endpoint',
+ auth_plugin_name='admin_token',
)
self.assertEqual(
fakes.AUTH_URL,
- client_manager._cli_options.config['auth']['url'],
+ client_manager._cli_options.config['auth']['endpoint'],
)
self.assertEqual(
fakes.AUTH_TOKEN,
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index ee7d498..7357e14 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -505,7 +505,7 @@ class FakeSecurityGroup(object):
@staticmethod
def get_security_groups(security_groups=None, count=2):
- """Get an iterable MagicMock object with a list of faked security groups.
+ """Get an iterable MagicMock with a list of faked security groups.
If security groups list is provided, then initialize the Mock object
with the list. Otherwise create one.
diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py
index 3efe0db..0937047 100644
--- a/openstackclient/tests/unit/compute/v2/test_aggregate.py
+++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py
@@ -16,6 +16,7 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -31,16 +32,16 @@ class TestAggregate(compute_fakes.TestComputev2):
'availability_zone',
'hosts',
'id',
- 'metadata',
'name',
+ 'properties',
)
data = (
fake_ag.availability_zone,
- fake_ag.hosts,
+ format_columns.ListColumn(fake_ag.hosts),
fake_ag.id,
- fake_ag.metadata,
fake_ag.name,
+ format_columns.DictColumn(fake_ag.metadata),
)
def setUp(self):
@@ -75,7 +76,7 @@ class TestAggregateAddHost(TestAggregate):
self.aggregate_mock.add_host.assert_called_once_with(self.fake_ag,
parsed_args.host)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestAggregateCreate(TestAggregate):
@@ -99,7 +100,7 @@ class TestAggregateCreate(TestAggregate):
self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
None)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_aggregate_create_with_zone(self):
arglist = [
@@ -116,7 +117,7 @@ class TestAggregateCreate(TestAggregate):
self.aggregate_mock.create.assert_called_once_with(parsed_args.name,
parsed_args.zone)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_aggregate_create_with_property(self):
arglist = [
@@ -135,7 +136,7 @@ class TestAggregateCreate(TestAggregate):
self.aggregate_mock.set_metadata.assert_called_once_with(
self.fake_ag, parsed_args.property)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestAggregateDelete(TestAggregate):
@@ -234,8 +235,11 @@ class TestAggregateList(TestAggregate):
TestAggregate.fake_ag.id,
TestAggregate.fake_ag.name,
TestAggregate.fake_ag.availability_zone,
- {key: value for key, value in TestAggregate.fake_ag.metadata.items()
- if key != 'availability_zone'},
+ format_columns.DictColumn({
+ key: value
+ for key, value in TestAggregate.fake_ag.metadata.items()
+ if key != 'availability_zone'
+ }),
), )
def setUp(self):
@@ -250,7 +254,7 @@ class TestAggregateList(TestAggregate):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.list_columns, columns)
- self.assertEqual(self.list_data, tuple(data))
+ self.assertItemEqual(self.list_data, tuple(data))
def test_aggregate_list_with_long(self):
arglist = [
@@ -263,7 +267,7 @@ class TestAggregateList(TestAggregate):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.list_columns_long, columns)
- self.assertEqual(self.list_data_long, tuple(data))
+ self.assertListItemEqual(self.list_data_long, tuple(data))
class TestAggregateRemoveHost(TestAggregate):
@@ -290,7 +294,7 @@ class TestAggregateRemoveHost(TestAggregate):
self.aggregate_mock.remove_host.assert_called_once_with(
self.fake_ag, parsed_args.host)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestAggregateSet(TestAggregate):
@@ -440,13 +444,14 @@ class TestAggregateShow(TestAggregate):
data = (
TestAggregate.fake_ag.availability_zone,
- TestAggregate.fake_ag.hosts,
+ format_columns.ListColumn(TestAggregate.fake_ag.hosts),
TestAggregate.fake_ag.id,
TestAggregate.fake_ag.name,
- utils.format_dict(
- {key: value
- for key, value in TestAggregate.fake_ag.metadata.items()
- if key != 'availability_zone'}),
+ format_columns.DictColumn({
+ key: value
+ for key, value in TestAggregate.fake_ag.metadata.items()
+ if key != 'availability_zone'
+ }),
)
def setUp(self):
@@ -467,7 +472,7 @@ class TestAggregateShow(TestAggregate):
self.aggregate_mock.get.assert_called_once_with(parsed_args.aggregate)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, tuple(data))
class TestAggregateUnset(TestAggregate):
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 4a37a81..5c98188 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -14,6 +14,7 @@
#
import argparse
import collections
+import copy
import getpass
import mock
@@ -65,9 +66,22 @@ class TestServer(compute_fakes.TestComputev2):
self.methods = {}
def setup_servers_mock(self, count):
- servers = compute_fakes.FakeServer.create_servers(attrs=self.attrs,
- methods=self.methods,
- count=count)
+ # If we are creating more than one server, make one of them
+ # boot-from-volume
+ include_bfv = count > 1
+ servers = compute_fakes.FakeServer.create_servers(
+ attrs=self.attrs,
+ methods=self.methods,
+ count=count - 1 if include_bfv else count
+ )
+ if include_bfv:
+ attrs = copy.deepcopy(self.attrs)
+ attrs['image'] = ''
+ bfv_server = compute_fakes.FakeServer.create_one_server(
+ attrs=attrs,
+ methods=self.methods
+ )
+ servers.append(bfv_server)
# This is the return value for utils.find_resource()
self.servers_mock.get = compute_fakes.FakeServer.get_servers(servers,
@@ -91,7 +105,18 @@ class TestServer(compute_fakes.TestComputev2):
for s in servers:
method = getattr(s, method_name)
- method.assert_called_with()
+ if method_name == 'lock':
+ version = self.app.client_manager.compute.api_version
+ if version >= api_versions.APIVersion('2.73'):
+ method.assert_called_with(reason=None)
+ elif method_name == 'unshelve':
+ version = self.app.client_manager.compute.api_version
+ if version >= api_versions.APIVersion('2.77'):
+ method.assert_called_with(availability_zone=None)
+ else:
+ method.assert_called_with()
+ else:
+ method.assert_called_with()
self.assertIsNone(result)
@@ -1677,6 +1702,33 @@ class TestServerCreate(TestServer):
self.cmd.take_action,
parsed_args)
+ def test_server_create_volume_boot_from_volume_conflict(self):
+ # Tests that specifying --volume and --boot-from-volume results in
+ # an error. Since --boot-from-volume requires --image or
+ # --image-property but those are in a mutex group with --volume, we
+ # only specify --volume and --boot-from-volume for this test since
+ # the validation is not handled with argparse.
+ arglist = [
+ '--flavor', self.flavor.id,
+ '--volume', 'volume1',
+ '--boot-from-volume', '1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('flavor', self.flavor.id),
+ ('volume', 'volume1'),
+ ('boot_from_volume', 1),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action, parsed_args)
+ # Assert it is the error we expect.
+ self.assertIn('--volume is not allowed with --boot-from-volume',
+ six.text_type(ex))
+
def test_server_create_image_property(self):
arglist = [
'--image-property', 'hypervisor_type=qemu',
@@ -1919,6 +1971,237 @@ class TestServerCreate(TestServer):
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
+ def test_server_create_with_host_v274(self):
+
+ # Explicit host is supported for nova api version 2.74 or above
+ self.app.client_manager.compute.api_version = 2.74
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--host', 'host1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('host', 'host1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.74):
+ # In base command class ShowOne in cliff, abstract method
+ # take_action() returns a two-part tuple with a tuple of
+ # column names and a tuple of data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ meta=None,
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='auto',
+ scheduler_hints={},
+ config_drive=None,
+ host='host1',
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ self.image,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+ self.assertFalse(self.images_mock.called)
+ self.assertFalse(self.flavors_mock.called)
+
+ def test_server_create_with_host_pre_v274(self):
+
+ # Host is not supported for nova api version below 2.74
+ self.app.client_manager.compute.api_version = 2.73
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--host', 'host1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('host', 'host1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.74):
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_server_create_with_hypervisor_hostname_v274(self):
+
+ # Explicit hypervisor_hostname is supported for nova api version
+ # 2.74 or above
+ self.app.client_manager.compute.api_version = 2.74
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--hypervisor-hostname', 'node1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('hypervisor_hostname', 'node1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.74):
+ # In base command class ShowOne in cliff, abstract method
+ # take_action() returns a two-part tuple with a tuple of
+ # column names and a tuple of data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ meta=None,
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='auto',
+ scheduler_hints={},
+ config_drive=None,
+ hypervisor_hostname='node1',
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ self.image,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+ self.assertFalse(self.images_mock.called)
+ self.assertFalse(self.flavors_mock.called)
+
+ def test_server_create_with_hypervisor_hostname_pre_v274(self):
+
+ # Hypervisor_hostname is not supported for nova api version below 2.74
+ self.app.client_manager.compute.api_version = 2.73
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--hypervisor-hostname', 'node1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('hypervisor_hostname', 'node1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.74):
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_server_create_with_host_and_hypervisor_hostname_v274(self):
+
+ # Explicit host and hypervisor_hostname is supported for nova api
+ # version 2.74 or above
+ self.app.client_manager.compute.api_version = 2.74
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--host', 'host1',
+ '--hypervisor-hostname', 'node1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('host', 'host1'),
+ ('hypervisor_hostname', 'node1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.74):
+ # In base command class ShowOne in cliff, abstract method
+ # take_action() returns a two-part tuple with a tuple of
+ # column names and a tuple of data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ meta=None,
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='auto',
+ scheduler_hints={},
+ config_drive=None,
+ host='host1',
+ hypervisor_hostname='node1',
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ self.image,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+ self.assertFalse(self.images_mock.called)
+ self.assertFalse(self.flavors_mock.called)
+
class TestServerDelete(TestServer):
@@ -2122,7 +2405,8 @@ class TestServerList(TestServer):
Image = collections.namedtuple('Image', 'id name')
self.images_mock.list.return_value = [
Image(id=s.image['id'], name=self.image.name)
- for s in self.servers
+ # Image will be an empty string if boot-from-volume
+ for s in self.servers if s.image
]
Flavor = collections.namedtuple('Flavor', 'id name')
@@ -2137,7 +2421,8 @@ class TestServerList(TestServer):
s.name,
s.status,
server._format_servers_list_networks(s.networks),
- self.image.name,
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else s.image,
self.flavor.name,
))
self.data_long.append((
@@ -2149,8 +2434,9 @@ class TestServerList(TestServer):
getattr(s, 'OS-EXT-STS:power_state')
),
server._format_servers_list_networks(s.networks),
- self.image.name,
- s.image['id'],
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else s.image,
+ s.image['id'] if s.image else s.image,
self.flavor.name,
s.flavor['id'],
getattr(s, 'OS-EXT-AZ:availability_zone'),
@@ -2162,7 +2448,8 @@ class TestServerList(TestServer):
s.name,
s.status,
server._format_servers_list_networks(s.networks),
- s.image['id'],
+ # Image will be an empty string if boot-from-volume
+ s.image['id'] if s.image else s.image,
s.flavor['id']
))
@@ -2296,6 +2583,80 @@ class TestServerList(TestServer):
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
+ def test_server_list_with_locked_pre_v273(self):
+
+ arglist = [
+ '--locked'
+ ]
+ verifylist = [
+ ('locked', True)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.73 or greater is required', str(ex))
+
+ def test_server_list_with_locked_v273(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+ arglist = [
+ '--locked'
+ ]
+ verifylist = [
+ ('locked', True)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['locked'] = True
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_list_with_unlocked_v273(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+ arglist = [
+ '--unlocked'
+ ]
+ verifylist = [
+ ('unlocked', True)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['locked'] = False
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_list_with_locked_and_unlocked_v273(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+ arglist = [
+ '--locked',
+ '--unlocked'
+ ]
+ verifylist = [
+ ('locked', True),
+ ('unlocked', True)
+ ]
+
+ ex = self.assertRaises(
+ utils.ParserException,
+ self.check_parser, self.cmd, arglist, verifylist)
+ self.assertIn('Argument parse failed', str(ex))
+
def test_server_list_with_flavor(self):
arglist = [
@@ -2487,6 +2848,72 @@ class TestServerLock(TestServer):
def test_server_lock_multi_servers(self):
self.run_method_with_servers('lock', 3)
+ def test_server_lock_with_reason(self):
+ server = compute_fakes.FakeServer.create_one_server()
+ arglist = [
+ server.id,
+ '--reason', "blah",
+ ]
+ verifylist = [
+ ('reason', "blah"),
+ ('server', [server.id])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.73 or greater is required', str(ex))
+
+
+class TestServerLockV273(TestServerLock):
+
+ def setUp(self):
+ super(TestServerLockV273, self).setUp()
+
+ self.server = compute_fakes.FakeServer.create_one_server(
+ methods=self.methods)
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get.return_value = self.server
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
+
+ # Get the command object to test
+ self.cmd = server.LockServer(self.app, None)
+
+ def test_server_lock_with_reason(self):
+ arglist = [
+ self.server.id,
+ '--reason', "blah",
+ ]
+ verifylist = [
+ ('reason', "blah"),
+ ('server', [self.server.id])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.lock.assert_called_with(reason="blah")
+
+ def test_server_lock_multi_servers_with_reason(self):
+ server2 = compute_fakes.FakeServer.create_one_server(
+ methods=self.methods)
+ arglist = [
+ self.server.id, server2.id,
+ '--reason', "choo..choo",
+ ]
+ verifylist = [
+ ('reason', "choo..choo"),
+ ('server', [self.server.id, server2.id])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+ self.assertEqual(2, self.servers_mock.get.call_count)
+ self.server.lock.assert_called_with(reason="choo..choo")
+ self.assertEqual(2, self.server.lock.call_count)
+
class TestServerMigrate(TestServer):
@@ -2528,6 +2955,32 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
self.assertIsNone(result)
+ def test_server_migrate_with_host_2_56(self):
+ # Tests that --host is allowed for a cold migration
+ # for microversion 2.56 and greater.
+ arglist = [
+ '--host', 'fakehost', self.server.id,
+ ]
+ verifylist = [
+ ('live', None),
+ ('live_migration', False),
+ ('host', 'fakehost'),
+ ('block_migration', False),
+ ('disk_overcommit', False),
+ ('wait', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.56')
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.migrate.assert_called_with(host='fakehost')
+ self.assertNotCalled(self.servers_mock.live_migrate)
+ self.assertIsNone(result)
+
def test_server_migrate_with_block_migration(self):
arglist = [
'--block-migration', self.server.id,
@@ -2566,8 +3019,9 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
self.assertNotCalled(self.servers_mock.migrate)
- def test_server_migrate_with_host(self):
- # Tests that --host is not allowed for a cold migration.
+ def test_server_migrate_with_host_pre_2_56(self):
+ # Tests that --host is not allowed for a cold migration
+ # before microversion 2.56 (the test defaults to 2.1).
arglist = [
'--host', 'fakehost', self.server.id,
]
@@ -2585,9 +3039,10 @@ class TestServerMigrate(TestServer):
parsed_args)
# Make sure it's the error we expect.
- self.assertIn("--live-migration must be specified if "
- "--block-migration, --disk-overcommit or --host is "
- "specified", six.text_type(ex))
+ self.assertIn('--os-compute-api-version 2.56 or greater is required '
+ 'to use --host without --live-migration.',
+ six.text_type(ex))
+
self.servers_mock.get.assert_called_with(self.server.id)
self.assertNotCalled(self.servers_mock.live_migrate)
self.assertNotCalled(self.servers_mock.migrate)
@@ -3603,13 +4058,18 @@ class TestServerResize(TestServer):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id)
self.assertNotCalled(self.servers_mock.resize)
self.servers_mock.confirm_resize.assert_called_with(self.server)
self.assertNotCalled(self.servers_mock.revert_resize)
self.assertIsNone(result)
+ # A warning should have been logged for using --confirm.
+ mock_warning.assert_called_once()
+ self.assertIn('The --confirm option has been deprecated.',
+ six.text_type(mock_warning.call_args[0][0]))
def test_server_resize_revert(self):
arglist = [
@@ -3623,13 +4083,18 @@ class TestServerResize(TestServer):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id)
self.assertNotCalled(self.servers_mock.resize)
self.assertNotCalled(self.servers_mock.confirm_resize)
self.servers_mock.revert_resize.assert_called_with(self.server)
self.assertIsNone(result)
+ # A warning should have been logged for using --revert.
+ mock_warning.assert_called_once()
+ self.assertIn('The --revert option has been deprecated.',
+ six.text_type(mock_warning.call_args[0][0]))
@mock.patch.object(common_utils, 'wait_for_status', return_value=True)
def test_server_resize_with_wait_ok(self, mock_wait_for_status):
@@ -3710,6 +4175,74 @@ class TestServerResize(TestServer):
)
+class TestServerResizeConfirm(TestServer):
+
+ def setUp(self):
+ super(TestServerResizeConfirm, self).setUp()
+
+ methods = {
+ 'confirm_resize': None,
+ }
+ self.server = compute_fakes.FakeServer.create_one_server(
+ methods=methods)
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get.return_value = self.server
+
+ self.servers_mock.confirm_resize.return_value = None
+
+ # Get the command object to test
+ self.cmd = server.ResizeConfirm(self.app, None)
+
+ def test_resize_confirm(self):
+ arglist = [
+ self.server.id,
+ ]
+ verifylist = [
+ ('server', self.server.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.confirm_resize.assert_called_with()
+
+
+class TestServerResizeRevert(TestServer):
+
+ def setUp(self):
+ super(TestServerResizeRevert, self).setUp()
+
+ methods = {
+ 'revert_resize': None,
+ }
+ self.server = compute_fakes.FakeServer.create_one_server(
+ methods=methods)
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get.return_value = self.server
+
+ self.servers_mock.revert_resize.return_value = None
+
+ # Get the command object to test
+ self.cmd = server.ResizeRevert(self.app, None)
+
+ def test_resize_revert(self):
+ arglist = [
+ self.server.id,
+ ]
+ verifylist = [
+ ('server', self.server.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.revert_resize.assert_called_with()
+
+
class TestServerRestore(TestServer):
def setUp(self):
@@ -4248,6 +4781,56 @@ class TestServerUnshelve(TestServer):
def test_unshelve_multi_servers(self):
self.run_method_with_servers('unshelve', 3)
+ def test_unshelve_server_with_specified_az(self):
+ server = compute_fakes.FakeServer.create_one_server()
+ arglist = [
+ server.id,
+ '--availability-zone', "foo-az",
+ ]
+ verifylist = [
+ ('availability_zone', "foo-az"),
+ ('server', [server.id])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.77 or greater is required', str(ex))
+
+
+class TestServerUnshelveV277(TestServerUnshelve):
+
+ def setUp(self):
+ super(TestServerUnshelveV277, self).setUp()
+
+ self.server = compute_fakes.FakeServer.create_one_server(
+ methods=self.methods)
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get.return_value = self.server
+
+ # Get the command object to test
+ self.cmd = server.UnshelveServer(self.app, None)
+
+ def test_specified_az_to_unshelve_with_v277(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.77')
+
+ arglist = [
+ '--availability-zone', "foo-az",
+ self.server.id,
+ ]
+ verifylist = [
+ ('availability_zone', "foo-az"),
+ ('server', [self.server.id])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.unshelve.assert_called_with(availability_zone="foo-az")
+
class TestServerGeneral(TestServer):
OLD = {
diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py
index 9a481e0..24a9453 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_backup.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py
@@ -13,6 +13,7 @@
import mock
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils as common_utils
@@ -69,7 +70,7 @@ class TestServerBackupCreate(TestServerBackup):
image['owner'],
image['protected'],
'active',
- common_utils.format_list(image.get('tags')),
+ format_columns.ListColumn(image.get('tags')),
image['visibility'],
)
return datalist
@@ -134,7 +135,7 @@ class TestServerBackupCreate(TestServerBackup):
)
self.assertEqual(self.image_columns(images[0]), columns)
- self.assertEqual(self.image_data(images[0]), data)
+ self.assertItemEqual(self.image_data(images[0]), data)
def test_server_backup_create_options(self):
servers = self.setup_servers_mock(count=1)
@@ -168,7 +169,7 @@ class TestServerBackupCreate(TestServerBackup):
)
self.assertEqual(self.image_columns(images[0]), columns)
- self.assertEqual(self.image_data(images[0]), data)
+ self.assertItemEqual(self.image_data(images[0]), data)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_backup_wait_fail(self, mock_wait_for_status):
@@ -268,4 +269,4 @@ class TestServerBackupCreate(TestServerBackup):
)
self.assertEqual(self.image_columns(images[0]), columns)
- self.assertEqual(self.image_data(images[0]), data)
+ self.assertItemEqual(self.image_data(images[0]), data)
diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py
index 47bd682..02e4312 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_image.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_image.py
@@ -12,6 +12,7 @@
#
import mock
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils as common_utils
@@ -67,7 +68,7 @@ class TestServerImageCreate(TestServerImage):
image['owner'],
image['protected'],
'active',
- common_utils.format_list(image.get('tags')),
+ format_columns.ListColumn(image.get('tags')),
image['visibility'],
)
return datalist
@@ -129,7 +130,7 @@ class TestServerImageCreate(TestServerImage):
)
self.assertEqual(self.image_columns(images[0]), columns)
- self.assertEqual(self.image_data(images[0]), data)
+ self.assertItemEqual(self.image_data(images[0]), data)
def test_server_image_create_options(self):
servers = self.setup_servers_mock(count=1)
@@ -157,7 +158,7 @@ class TestServerImageCreate(TestServerImage):
)
self.assertEqual(self.image_columns(images[0]), columns)
- self.assertEqual(self.image_data(images[0]), data)
+ self.assertItemEqual(self.image_data(images[0]), data)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_create_image_wait_fail(self, mock_wait_for_status):
@@ -225,4 +226,4 @@ class TestServerImageCreate(TestServerImage):
)
self.assertEqual(self.image_columns(images[0]), columns)
- self.assertEqual(self.image_data(images[0]), data)
+ self.assertItemEqual(self.image_data(images[0]), data)
diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py
index bd29912..0d663b2 100644
--- a/openstackclient/tests/unit/compute/v2/test_service.py
+++ b/openstackclient/tests/unit/compute/v2/test_service.py
@@ -17,6 +17,7 @@ import mock
from mock import call
from novaclient import api_versions
from osc_lib import exceptions
+import six
from openstackclient.compute.v2 import service
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -344,7 +345,7 @@ class TestServiceSet(TestService):
'2.11')
result = self.cmd.take_action(parsed_args)
self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, force_down=False)
+ self.service.host, self.service.binary, False)
self.assertNotCalled(self.service_mock.enable)
self.assertNotCalled(self.service_mock.disable)
self.assertIsNone(result)
@@ -365,7 +366,7 @@ class TestServiceSet(TestService):
'2.11')
result = self.cmd.take_action(parsed_args)
self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, force_down=True)
+ self.service.host, self.service.binary, True)
self.assertNotCalled(self.service_mock.enable)
self.assertNotCalled(self.service_mock.disable)
self.assertIsNone(result)
@@ -390,7 +391,7 @@ class TestServiceSet(TestService):
self.service_mock.enable.assert_called_once_with(
self.service.host, self.service.binary)
self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, force_down=True)
+ self.service.host, self.service.binary, True)
self.assertIsNone(result)
def test_service_set_enable_and_state_down_with_exception(self):
@@ -415,4 +416,99 @@ class TestServiceSet(TestService):
self.assertRaises(exceptions.CommandError,
self.cmd.take_action, parsed_args)
self.service_mock.force_down.assert_called_once_with(
- self.service.host, self.service.binary, force_down=True)
+ self.service.host, self.service.binary, True)
+
+ def test_service_set_2_53_disable_down(self):
+ # Tests disabling and forcing down a compute service with microversion
+ # 2.53 which requires looking up the service by host and binary.
+ arglist = [
+ '--disable',
+ '--down',
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('disable', True),
+ ('down', True),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.53')
+ service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c'
+ self.service_mock.list.return_value = [mock.Mock(id=service_id)]
+ result = self.cmd.take_action(parsed_args)
+ self.service_mock.disable.assert_called_once_with(service_id)
+ self.service_mock.force_down.assert_called_once_with(service_id, True)
+ self.assertIsNone(result)
+
+ def test_service_set_2_53_disable_reason(self):
+ # Tests disabling with reason a compute service with microversion
+ # 2.53 which requires looking up the service by host and binary.
+ reason = 'earthquake'
+ arglist = [
+ '--disable',
+ '--disable-reason', reason,
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('disable', True),
+ ('disable_reason', reason),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.53')
+ service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c'
+ self.service_mock.list.return_value = [mock.Mock(id=service_id)]
+ result = self.cmd.take_action(parsed_args)
+ self.service_mock.disable_log_reason.assert_called_once_with(
+ service_id, reason)
+ self.assertIsNone(result)
+
+ def test_service_set_2_53_enable_up(self):
+ # Tests enabling and bringing up a compute service with microversion
+ # 2.53 which requires looking up the service by host and binary.
+ arglist = [
+ '--enable',
+ '--up',
+ self.service.host,
+ self.service.binary,
+ ]
+ verifylist = [
+ ('enable', True),
+ ('up', True),
+ ('host', self.service.host),
+ ('service', self.service.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.53')
+ service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c'
+ self.service_mock.list.return_value = [mock.Mock(id=service_id)]
+ result = self.cmd.take_action(parsed_args)
+ self.service_mock.enable.assert_called_once_with(service_id)
+ self.service_mock.force_down.assert_called_once_with(service_id, False)
+ self.assertIsNone(result)
+
+ def test_service_set_find_service_by_host_and_binary_no_results(self):
+ # Tests that no compute services are found by host and binary.
+ self.service_mock.list.return_value = []
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd._find_service_by_host_and_binary,
+ self.service_mock, 'fake-host', 'nova-compute')
+ self.assertIn('Compute service for host "fake-host" and binary '
+ '"nova-compute" not found.', six.text_type(ex))
+
+ def test_service_set_find_service_by_host_and_binary_many_results(self):
+ # Tests that more than one compute service is found by host and binary.
+ self.service_mock.list.return_value = [mock.Mock(), mock.Mock()]
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd._find_service_by_host_and_binary,
+ self.service_mock, 'fake-host', 'nova-compute')
+ self.assertIn('Multiple compute services found for host "fake-host" '
+ 'and binary "nova-compute". Unable to proceed.',
+ six.text_type(ex))
diff --git a/openstackclient/tests/unit/identity/v2_0/test_catalog.py b/openstackclient/tests/unit/identity/v2_0/test_catalog.py
index 65af58a..362dec0 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_catalog.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_catalog.py
@@ -71,17 +71,9 @@ class TestCatalogList(TestCatalog):
datalist = ((
'supernova',
'compute',
- 'one\n publicURL: https://public.one.example.com\n '
- 'internalURL: https://internal.one.example.com\n '
- 'adminURL: https://admin.one.example.com\n'
- 'two\n publicURL: https://public.two.example.com\n '
- 'internalURL: https://internal.two.example.com\n '
- 'adminURL: https://admin.two.example.com\n'
- '<none>\n publicURL: https://public.none.example.com\n '
- 'internalURL: https://internal.none.example.com\n '
- 'adminURL: https://admin.none.example.com\n',
+ catalog.EndpointsColumn(self.service_catalog['endpoints']),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_catalog_list_with_endpoint_url(self):
attr = {
@@ -121,11 +113,9 @@ class TestCatalogList(TestCatalog):
datalist = ((
'supernova',
'compute',
- 'one\n publicURL: https://public.one.example.com\n'
- 'two\n publicURL: https://public.two.example.com\n '
- 'internalURL: https://internal.two.example.com\n'
+ catalog.EndpointsColumn(service_catalog['endpoints']),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
class TestCatalogShow(TestCatalog):
@@ -160,6 +150,18 @@ class TestCatalogShow(TestCatalog):
collist = ('endpoints', 'id', 'name', 'type')
self.assertEqual(collist, columns)
datalist = (
+ catalog.EndpointsColumn(self.service_catalog['endpoints']),
+ self.service_catalog.id,
+ 'supernova',
+ 'compute',
+ )
+ self.assertItemEqual(datalist, data)
+
+
+class TestFormatColumns(TestCatalog):
+ def test_endpoints_column_human_readabale(self):
+ col = catalog.EndpointsColumn(self.service_catalog['endpoints'])
+ self.assertEqual(
'one\n publicURL: https://public.one.example.com\n '
'internalURL: https://internal.one.example.com\n '
'adminURL: https://admin.one.example.com\n'
@@ -169,8 +171,23 @@ class TestCatalogShow(TestCatalog):
'<none>\n publicURL: https://public.none.example.com\n '
'internalURL: https://internal.none.example.com\n '
'adminURL: https://admin.none.example.com\n',
- self.service_catalog.id,
- 'supernova',
- 'compute',
- )
- self.assertEqual(datalist, data)
+ col.human_readable())
+
+ def test_endpoints_column_human_readable_with_partial_endpoint_urls(self):
+ endpoints = [
+ {
+ 'region': 'one',
+ 'publicURL': 'https://public.one.example.com',
+ },
+ {
+ 'region': 'two',
+ 'publicURL': 'https://public.two.example.com',
+ 'internalURL': 'https://internal.two.example.com',
+ },
+ ]
+ col = catalog.EndpointsColumn(endpoints)
+ self.assertEqual(
+ 'one\n publicURL: https://public.one.example.com\n'
+ 'two\n publicURL: https://public.two.example.com\n '
+ 'internalURL: https://internal.two.example.com\n',
+ col.human_readable())
diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py
index c726f2a..7af7b39 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_project.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_project.py
@@ -16,6 +16,7 @@
import mock
from keystoneauth1 import exceptions as ks_exc
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -640,9 +641,9 @@ class TestProjectShow(TestProject):
True,
self.fake_proj_show.id,
self.fake_proj_show.name,
- '',
+ format_columns.DictColumn({}),
)
- self.assertEqual(datalist, data)
+ self.assertItemEqual(datalist, data)
class TestProjectUnset(TestProject):
diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py
index 684ce80..643d77f 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_role.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_role.py
@@ -278,7 +278,7 @@ class TestRoleList(TestRole):
# Get the command object to test
self.cmd = role.ListRole(self.app, None)
- def test_role_list_no_options(self):
+ def test_role_list(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -299,137 +299,6 @@ class TestRoleList(TestRole):
self.assertEqual(datalist, tuple(data))
-class TestUserRoleList(TestRole):
-
- columns = (
- 'ID',
- 'Name',
- 'Project',
- 'User'
- )
-
- def setUp(self):
- super(TestUserRoleList, self).setUp()
-
- self.projects_mock.get.return_value = self.fake_project
-
- self.users_mock.get.return_value = self.fake_user
-
- self.roles_mock.roles_for_user.return_value = [self.fake_role]
-
- # Get the command object to test
- self.cmd = role.ListUserRole(self.app, None)
-
- def test_user_role_list_no_options_unscoped_token(self):
- auth_ref = identity_fakes.fake_auth_ref(
- identity_fakes.UNSCOPED_TOKEN,
- fake_service=self.fake_service,
- )
- self.ar_mock = mock.PropertyMock(return_value=auth_ref)
- type(self.app.client_manager).auth_ref = self.ar_mock
-
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # This argument combination should raise a CommandError
- self.assertRaises(
- exceptions.CommandError,
- self.cmd.take_action,
- parsed_args,
- )
-
- def test_user_role_list_no_options_scoped_token(self):
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- self.roles_mock.roles_for_user.assert_called_with(
- self.fake_user.id,
- self.fake_project.id,
- )
-
- collist = ('ID', 'Name', 'Project', 'User')
- self.assertEqual(collist, columns)
- datalist = ((
- self.fake_role.id,
- self.fake_role.name,
- self.fake_project.name,
- self.fake_user.name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
- def test_user_role_list_project_unscoped_token(self):
- auth_ref = identity_fakes.fake_auth_ref(
- identity_fakes.UNSCOPED_TOKEN,
- fake_service=self.fake_service,
- )
- self.ar_mock = mock.PropertyMock(return_value=auth_ref)
- type(self.app.client_manager).auth_ref = self.ar_mock
-
- self.projects_mock.get.return_value = self.fake_project
- arglist = [
- '--project', self.fake_project.name,
- ]
- verifylist = [
- ('project', self.fake_project.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- self.roles_mock.roles_for_user.assert_called_with(
- self.fake_user.id,
- self.fake_project.id,
- )
-
- self.assertEqual(columns, columns)
- datalist = ((
- self.fake_role.id,
- self.fake_role.name,
- self.fake_project.name,
- self.fake_user.name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
- def test_user_role_list_project_scoped_token(self):
- self.projects_mock.get.return_value = self.fake_project
- arglist = [
- '--project', self.fake_project.name,
- ]
- verifylist = [
- ('project', self.fake_project.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- self.roles_mock.roles_for_user.assert_called_with(
- self.fake_user.id,
- self.fake_project.id,
- )
-
- self.assertEqual(columns, columns)
- datalist = ((
- self.fake_role.id,
- self.fake_role.name,
- self.fake_project.name,
- self.fake_user.name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
-
class TestRoleRemove(TestRole):
def setUp(self):
diff --git a/openstackclient/tests/unit/identity/v2_0/test_service.py b/openstackclient/tests/unit/identity/v2_0/test_service.py
index 1948bf4..6c4374e 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_service.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_service.py
@@ -55,43 +55,14 @@ class TestServiceCreate(TestService):
# Get the command object to test
self.cmd = service.CreateService(self.app, None)
- def test_service_create_with_type_positional(self):
+ def test_service_create(self):
arglist = [
self.fake_service_c.type,
]
verifylist = [
- ('type_or_name', self.fake_service_c.type),
- ('type', None),
- ('description', None),
- ('name', None),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class ShowOne in cliff, abstract method take_action()
- # returns a two-part tuple with a tuple of column names and a tuple of
- # data to be shown.
- columns, data = self.cmd.take_action(parsed_args)
-
- # ServiceManager.create(name, service_type, description)
- self.services_mock.create.assert_called_with(
- None,
- self.fake_service_c.type,
- None,
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
-
- def test_service_create_with_type_option(self):
- arglist = [
- '--type', self.fake_service_c.type,
- self.fake_service_c.name,
- ]
- verifylist = [
- ('type_or_name', self.fake_service_c.name),
('type', self.fake_service_c.type),
- ('description', None),
('name', None),
+ ('description', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -102,7 +73,7 @@ class TestServiceCreate(TestService):
# ServiceManager.create(name, service_type, description)
self.services_mock.create.assert_called_with(
- self.fake_service_c.name,
+ None,
self.fake_service_c.type,
None,
)
@@ -116,10 +87,9 @@ class TestServiceCreate(TestService):
self.fake_service_c.type,
]
verifylist = [
- ('type_or_name', self.fake_service_c.type),
- ('type', None),
- ('description', None),
+ ('type', self.fake_service_c.type),
('name', self.fake_service_c.name),
+ ('description', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -145,10 +115,9 @@ class TestServiceCreate(TestService):
self.fake_service_c.type,
]
verifylist = [
- ('type_or_name', self.fake_service_c.type),
- ('type', None),
- ('description', self.fake_service_c.description),
+ ('type', self.fake_service_c.type),
('name', self.fake_service_c.name),
+ ('description', self.fake_service_c.description),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
diff --git a/openstackclient/tests/unit/identity/v2_0/test_user.py b/openstackclient/tests/unit/identity/v2_0/test_user.py
index a8b9497..0a0d4b3 100644
--- a/openstackclient/tests/unit/identity/v2_0/test_user.py
+++ b/openstackclient/tests/unit/identity/v2_0/test_user.py
@@ -482,7 +482,7 @@ class TestUserList(TestUser):
self.users_mock.list.assert_called_with(tenant_id=None)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_user_list_project(self):
arglist = [
@@ -502,7 +502,7 @@ class TestUserList(TestUser):
self.users_mock.list.assert_called_with(tenant_id=project_id)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_user_list_long(self):
arglist = [
@@ -525,11 +525,13 @@ class TestUserList(TestUser):
datalist = ((
self.fake_user_l.id,
self.fake_user_l.name,
- self.fake_project_l.name,
+ user.ProjectColumn(
+ self.fake_project_l.id,
+ {self.fake_project_l.id: self.fake_project_l}),
self.fake_user_l.email,
True,
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
class TestUserSet(TestUser):
@@ -817,4 +819,4 @@ class TestUserShow(TestUser):
self.fake_user.name,
self.fake_project.id,
)
- self.assertEqual(datalist, data)
+ self.assertItemEqual(datalist, data)
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index e5727a6..e9ff068 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -20,6 +20,7 @@ import uuid
from keystoneauth1 import access
from keystoneauth1 import fixture
import mock
+from osc_lib.cli import format_columns
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit import utils
@@ -304,7 +305,7 @@ TOKEN_WITH_DOMAIN_ID = {
idp_id = 'test_idp'
idp_description = 'super exciting IdP description'
idp_remote_ids = ['entity1', 'entity2']
-formatted_idp_remote_ids = 'entity1, entity2'
+formatted_idp_remote_ids = format_columns.ListColumn(idp_remote_ids)
IDENTITY_PROVIDER = {
'id': idp_id,
diff --git a/openstackclient/tests/unit/identity/v3/test_catalog.py b/openstackclient/tests/unit/identity/v3/test_catalog.py
index 53008e8..ba076db 100644
--- a/openstackclient/tests/unit/identity/v3/test_catalog.py
+++ b/openstackclient/tests/unit/identity/v3/test_catalog.py
@@ -91,12 +91,9 @@ class TestCatalogList(TestCatalog):
datalist = ((
'supernova',
'compute',
- 'onlyone\n public: https://public.example.com\n'
- 'onlyone\n admin: https://admin.example.com\n'
- '<none>\n internal: https://internal.example.com\n'
- '<none>\n none: https://none.example.com\n',
+ catalog.EndpointsColumn(self.fake_service['endpoints']),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
class TestCatalogShow(TestCatalog):
@@ -131,12 +128,20 @@ class TestCatalogShow(TestCatalog):
collist = ('endpoints', 'id', 'name', 'type')
self.assertEqual(collist, columns)
datalist = (
- 'onlyone\n public: https://public.example.com\nonlyone\n'
- ' admin: https://admin.example.com\n'
- '<none>\n internal: https://internal.example.com\n'
- '<none>\n none: https://none.example.com\n',
+ catalog.EndpointsColumn(self.fake_service['endpoints']),
'qwertyuiop',
'supernova',
'compute',
)
- self.assertEqual(datalist, data)
+ self.assertItemEqual(datalist, data)
+
+
+class TestFormatColumns(TestCatalog):
+ def test_endpoints_column_human_readabale(self):
+ col = catalog.EndpointsColumn(self.fake_service['endpoints'])
+ self.assertEqual(
+ 'onlyone\n public: https://public.example.com\n'
+ 'onlyone\n admin: https://admin.example.com\n'
+ '<none>\n internal: https://internal.example.com\n'
+ '<none>\n none: https://none.example.com\n',
+ col.human_readable())
diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py
index dc82ab7..0163c6c 100644
--- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py
+++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py
@@ -90,7 +90,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_create_identity_provider_description(self):
arglist = [
@@ -118,7 +118,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_create_identity_provider_remote_id(self):
arglist = [
@@ -146,7 +146,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_create_identity_provider_remote_ids_multiple(self):
arglist = [
@@ -175,7 +175,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_create_identity_provider_remote_ids_file(self):
arglist = [
@@ -208,7 +208,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_create_identity_provider_disabled(self):
@@ -251,7 +251,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
identity_fakes.idp_id,
identity_fakes.formatted_idp_remote_ids
)
- self.assertEqual(datalist, data)
+ self.assertItemEqual(datalist, data)
def test_create_identity_provider_domain_name(self):
arglist = [
@@ -279,7 +279,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_create_identity_provider_domain_id(self):
arglist = [
@@ -307,7 +307,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
class TestIdentityProviderDelete(TestIdentityProvider):
@@ -383,7 +383,7 @@ class TestIdentityProviderList(TestIdentityProvider):
identity_fakes.domain_id,
identity_fakes.idp_description,
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
class TestIdentityProviderSet(TestIdentityProvider):
@@ -668,4 +668,4 @@ class TestIdentityProviderShow(TestIdentityProvider):
identity_fakes.idp_id,
identity_fakes.formatted_idp_remote_ids
)
- self.assertEqual(datalist, data)
+ self.assertItemEqual(datalist, data)
diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py
index 281d530..99f3a2d 100644
--- a/openstackclient/tests/unit/identity/v3/test_role.py
+++ b/openstackclient/tests/unit/identity/v3/test_role.py
@@ -508,21 +508,6 @@ class TestRoleList(TestRole):
copy.deepcopy(identity_fakes.DOMAIN),
loaded=True,
)
- self.projects_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.PROJECT),
- loaded=True,
- )
- self.users_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.USER),
- loaded=True,
- )
- self.groups_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(identity_fakes.GROUP),
- loaded=True,
- )
# Get the command object to test
self.cmd = role.ListRole(self.app, None)
@@ -542,212 +527,6 @@ class TestRoleList(TestRole):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
- def test_user_list_inherited(self):
- arglist = [
- '--user', identity_fakes.user_id,
- '--inherited',
- ]
- verifylist = [
- ('user', identity_fakes.user_id),
- ('inherited', True),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'domain': 'default',
- 'user': self.users_mock.get(),
- 'os_inherit_extension_inherited': True,
- }
- # RoleManager.list(user=, group=, domain=, project=, **kwargs)
- self.roles_mock.list.assert_called_with(
- **kwargs
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
-
- def test_user_list_user(self):
- arglist = [
- '--user', identity_fakes.user_id,
- ]
- verifylist = [
- ('user', identity_fakes.user_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'domain': 'default',
- 'user': self.users_mock.get(),
- 'os_inherit_extension_inherited': False
- }
- # RoleManager.list(user=, group=, domain=, project=, **kwargs)
- self.roles_mock.list.assert_called_with(
- **kwargs
- )
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
-
- def test_role_list_domain_user(self):
- arglist = [
- '--domain', identity_fakes.domain_name,
- '--user', identity_fakes.user_id,
- ]
- verifylist = [
- ('domain', identity_fakes.domain_name),
- ('user', identity_fakes.user_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'domain': self.domains_mock.get(),
- 'user': self.users_mock.get(),
- 'os_inherit_extension_inherited': False
- }
- # RoleManager.list(user=, group=, domain=, project=, **kwargs)
- self.roles_mock.list.assert_called_with(
- **kwargs
- )
-
- collist = ('ID', 'Name', 'Domain', 'User')
- self.assertEqual(collist, columns)
- datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
- identity_fakes.domain_name,
- identity_fakes.user_name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
- def test_role_list_domain_group(self):
- arglist = [
- '--domain', identity_fakes.domain_name,
- '--group', identity_fakes.group_id,
- ]
- verifylist = [
- ('domain', identity_fakes.domain_name),
- ('group', identity_fakes.group_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'domain': self.domains_mock.get(),
- 'group': self.groups_mock.get(),
- 'os_inherit_extension_inherited': False
- }
- # RoleManager.list(user=, group=, domain=, project=, **kwargs)
- self.roles_mock.list.assert_called_with(
- **kwargs
- )
-
- collist = ('ID', 'Name', 'Domain', 'Group')
- self.assertEqual(collist, columns)
- datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
- identity_fakes.domain_name,
- identity_fakes.group_name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
- def test_role_list_project_user(self):
- arglist = [
- '--project', identity_fakes.project_name,
- '--user', identity_fakes.user_id,
- ]
- verifylist = [
- ('project', identity_fakes.project_name),
- ('user', identity_fakes.user_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'project': self.projects_mock.get(),
- 'user': self.users_mock.get(),
- 'os_inherit_extension_inherited': False
- }
- # RoleManager.list(user=, group=, domain=, project=, **kwargs)
- self.roles_mock.list.assert_called_with(
- **kwargs
- )
-
- collist = ('ID', 'Name', 'Project', 'User')
- self.assertEqual(collist, columns)
- datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
- identity_fakes.project_name,
- identity_fakes.user_name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
- def test_role_list_project_group(self):
- arglist = [
- '--project', identity_fakes.project_name,
- '--group', identity_fakes.group_id,
- ]
- verifylist = [
- ('project', identity_fakes.project_name),
- ('group', identity_fakes.group_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # In base command class Lister in cliff, abstract method take_action()
- # returns a tuple containing the column names and an iterable
- # containing the data to be listed.
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'project': self.projects_mock.get(),
- 'group': self.groups_mock.get(),
- 'os_inherit_extension_inherited': False
- }
- # RoleManager.list(user=, group=, domain=, project=, **kwargs)
- self.roles_mock.list.assert_called_with(
- **kwargs
- )
-
- collist = ('ID', 'Name', 'Project', 'Group')
- self.assertEqual(collist, columns)
- datalist = ((
- identity_fakes.role_id,
- identity_fakes.role_name,
- identity_fakes.project_name,
- identity_fakes.group_name,
- ), )
- self.assertEqual(datalist, tuple(data))
-
def test_role_list_domain_role(self):
self.roles_mock.list.return_value = [
fakes.FakeResource(
@@ -787,17 +566,6 @@ class TestRoleList(TestRole):
), )
self.assertEqual(datalist, tuple(data))
- def test_role_list_group_with_error(self):
- arglist = [
- '--group', identity_fakes.group_id,
- ]
- verifylist = [
- ('group', identity_fakes.group_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action, parsed_args)
-
class TestRoleRemove(TestRole):
diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py
index 6babd4f..0997d76 100644
--- a/openstackclient/tests/unit/image/v1/test_image.py
+++ b/openstackclient/tests/unit/image/v1/test_image.py
@@ -16,8 +16,9 @@
import copy
import mock
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.image.v1 import image
from openstackclient.tests.unit import fakes
@@ -58,7 +59,7 @@ class TestImageCreate(TestImage):
new_image.min_ram,
new_image.name,
new_image.owner,
- utils.format_dict(new_image.properties),
+ format_columns.DictColumn(new_image.properties),
new_image.protected,
)
@@ -106,7 +107,7 @@ class TestImageCreate(TestImage):
self.assertEqual(self.images_mock.update.call_args_list, [])
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_image_reserve_options(self):
mock_exception = {
@@ -160,7 +161,7 @@ class TestImageCreate(TestImage):
self.assertEqual(self.images_mock.update.call_args_list, [])
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
@mock.patch('openstackclient.image.v1.image.io.open', name='Open')
def test_image_create_file(self, mock_open):
@@ -224,7 +225,7 @@ class TestImageCreate(TestImage):
self.assertEqual(self.images_mock.update.call_args_list, [])
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestImageDelete(TestImage):
@@ -410,12 +411,13 @@ class TestImageList(TestImage):
'',
'',
'',
- 'public',
+ image.VisibilityColumn(True),
False,
self._image.owner,
- "Alpha='a', Beta='b', Gamma='g'",
+ format_columns.DictColumn(
+ {'Alpha': 'a', 'Beta': 'b', 'Gamma': 'g'}),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
@mock.patch('osc_lib.api.utils.simple_filter')
def test_image_list_property_option(self, sf_mock):
@@ -742,7 +744,7 @@ class TestImageShow(TestImage):
_image.min_ram,
_image.name,
_image.owner,
- utils.format_dict(_image.properties),
+ format_columns.DictColumn(_image.properties),
_image.protected,
_image.size,
)
@@ -773,7 +775,7 @@ class TestImageShow(TestImage):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_image_show_human_readable(self):
arglist = [
diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py
index d82d811..f69a2bc 100644
--- a/openstackclient/tests/unit/image/v2/fakes.py
+++ b/openstackclient/tests/unit/image/v2/fakes.py
@@ -19,7 +19,7 @@ import uuid
from glanceclient.v2 import schemas
import mock
-from osc_lib import utils as common_utils
+from osc_lib.cli import format_columns
import warlock
from openstackclient.tests.unit import fakes
@@ -48,7 +48,7 @@ IMAGE_columns = tuple(sorted(IMAGE))
IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE)))
IMAGE_SHOW = copy.copy(IMAGE)
-IMAGE_SHOW['tags'] = ''
+IMAGE_SHOW['tags'] = format_columns.ListColumn(IMAGE_SHOW['tags'])
IMAGE_SHOW_data = tuple((IMAGE_SHOW[x] for x in sorted(IMAGE_SHOW)))
# Just enough v2 schema to do some testing
@@ -281,7 +281,7 @@ class FakeImage(object):
if x == 'tags':
# The 'tags' should be format_list
data_list.append(
- common_utils.format_list(getattr(image, x)))
+ format_columns.ListColumn(getattr(image, x)))
else:
data_list.append(getattr(image, x))
return tuple(data_list)
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index 4cc8f44..748a61a 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -18,8 +18,8 @@ import copy
from glanceclient.common import utils as glanceclient_utils
from glanceclient.v2 import schemas
import mock
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils as common_utils
import warlock
from openstackclient.image.v2 import image
@@ -116,7 +116,7 @@ class TestImageCreate(TestImage):
self.assertEqual(
image_fakes.FakeImage.get_image_columns(self.new_image),
columns)
- self.assertEqual(
+ self.assertItemEqual(
image_fakes.FakeImage.get_image_data(self.new_image),
data)
@@ -184,44 +184,10 @@ class TestImageCreate(TestImage):
self.assertEqual(
image_fakes.FakeImage.get_image_columns(self.new_image),
columns)
- self.assertEqual(
+ self.assertItemEqual(
image_fakes.FakeImage.get_image_data(self.new_image),
data)
- def test_image_create_with_unexist_owner(self):
- self.project_mock.get.side_effect = exceptions.NotFound(None)
- self.project_mock.find.side_effect = exceptions.NotFound(None)
-
- arglist = [
- '--container-format', 'ovf',
- '--disk-format', 'ami',
- '--min-disk', '10',
- '--min-ram', '4',
- '--owner', 'unexist_owner',
- '--protected',
- '--private',
- image_fakes.image_name,
- ]
- verifylist = [
- ('container_format', 'ovf'),
- ('disk_format', 'ami'),
- ('min_disk', 10),
- ('min_ram', 4),
- ('owner', 'unexist_owner'),
- ('protected', True),
- ('unprotected', False),
- ('public', False),
- ('private', True),
- ('name', image_fakes.image_name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(
- exceptions.CommandError,
- self.cmd.take_action,
- parsed_args,
- )
-
def test_image_create_with_unexist_project(self):
self.project_mock.get.side_effect = exceptions.NotFound(None)
self.project_mock.find.side_effect = exceptions.NotFound(None)
@@ -318,7 +284,7 @@ class TestImageCreate(TestImage):
self.assertEqual(
image_fakes.FakeImage.get_image_columns(self.new_image),
columns)
- self.assertEqual(
+ self.assertItemEqual(
image_fakes.FakeImage.get_image_data(self.new_image),
data)
@@ -542,7 +508,7 @@ class TestImageList(TestImage):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_image_list_public_option(self):
arglist = [
@@ -567,7 +533,7 @@ class TestImageList(TestImage):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_image_list_private_option(self):
arglist = [
@@ -592,7 +558,7 @@ class TestImageList(TestImage):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_image_list_community_option(self):
arglist = [
@@ -642,7 +608,7 @@ class TestImageList(TestImage):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_image_list_shared_member_status_option(self):
arglist = [
@@ -730,9 +696,9 @@ class TestImageList(TestImage):
self._image.visibility,
self._image.protected,
self._image.owner,
- common_utils.format_list(self._image.tags),
+ format_columns.ListColumn(self._image.tags),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
@mock.patch('osc_lib.api.utils.simple_filter')
def test_image_list_property_option(self, sf_mock):
@@ -761,7 +727,7 @@ class TestImageList(TestImage):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
@mock.patch('osc_lib.utils.sort_items')
def test_image_list_sort_option(self, si_mock):
@@ -784,7 +750,7 @@ class TestImageList(TestImage):
str,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_image_list_limit_option(self):
ret_limit = 1
@@ -1146,24 +1112,6 @@ class TestImageSet(TestImage):
image_fakes.image_id, **kwargs)
self.assertIsNone(result)
- def test_image_set_with_unexist_owner(self):
- self.project_mock.get.side_effect = exceptions.NotFound(None)
- self.project_mock.find.side_effect = exceptions.NotFound(None)
-
- arglist = [
- '--owner', 'unexist_owner',
- image_fakes.image_id,
- ]
- verifylist = [
- ('owner', 'unexist_owner'),
- ('image', image_fakes.image_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(
- exceptions.CommandError,
- self.cmd.take_action, parsed_args)
-
def test_image_set_with_unexist_project(self):
self.project_mock.get.side_effect = exceptions.NotFound(None)
self.project_mock.find.side_effect = exceptions.NotFound(None)
@@ -1512,7 +1460,7 @@ class TestImageShow(TestImage):
)
self.assertEqual(image_fakes.IMAGE_columns, columns)
- self.assertEqual(image_fakes.IMAGE_SHOW_data, data)
+ self.assertItemEqual(image_fakes.IMAGE_SHOW_data, data)
def test_image_show_human_readable(self):
self.images_mock.get.return_value = self.new_image
diff --git a/openstackclient/tests/unit/integ/cli/test_project.py b/openstackclient/tests/unit/integ/cli/test_project.py
index 6a7c6d1..4e707a3 100644
--- a/openstackclient/tests/unit/integ/cli/test_project.py
+++ b/openstackclient/tests/unit/integ/cli/test_project.py
@@ -36,10 +36,10 @@ class TestIntegV2ProjectID(test_base.TestInteg):
def test_project_id_env(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -57,10 +57,10 @@ class TestIntegV2ProjectID(test_base.TestInteg):
def test_project_id_arg(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-project-id wsx configuration show".split())
+ _shell.run("--os-project-id wsx extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -94,10 +94,10 @@ class TestIntegV2ProjectName(test_base.TestInteg):
def test_project_name_env(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -115,10 +115,10 @@ class TestIntegV2ProjectName(test_base.TestInteg):
def test_project_name_arg(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-project-name qaz configuration show".split())
+ _shell.run("--os-project-name qaz extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -154,10 +154,10 @@ class TestIntegV3ProjectID(test_base.TestInteg):
def test_project_id_env(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -173,10 +173,10 @@ class TestIntegV3ProjectID(test_base.TestInteg):
def test_project_id_arg(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-project-id wsx configuration show".split())
+ _shell.run("--os-project-id wsx extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -210,10 +210,10 @@ class TestIntegV3ProjectName(test_base.TestInteg):
def test_project_name_env(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -234,10 +234,10 @@ class TestIntegV3ProjectName(test_base.TestInteg):
def test_project_name_arg(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-project-name wsx configuration show".split())
+ _shell.run("--os-project-name wsx extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py
index 200f9b1..2598517 100644
--- a/openstackclient/tests/unit/integ/cli/test_shell.py
+++ b/openstackclient/tests/unit/integ/cli/test_shell.py
@@ -31,6 +31,51 @@ except ImportError:
CONFIG_MOCK_BASE = "os_client_config.config"
+class TestIntegShellCliNoAuth(test_base.TestInteg):
+
+ def setUp(self):
+ super(TestIntegShellCliNoAuth, self).setUp()
+ env = {}
+ self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env)))
+
+ # self.token = test_base.make_v2_token(self.requests_mock)
+
+ def test_shell_args_no_options(self):
+ _shell = shell.OpenStackShell()
+ _shell.run("configuration show".split())
+
+ # Check general calls
+ self.assertEqual(len(self.requests_mock.request_history), 0)
+
+ def test_shell_args_verify(self):
+ _shell = shell.OpenStackShell()
+ _shell.run("--verify configuration show".split())
+
+ # Check general calls
+ self.assertEqual(len(self.requests_mock.request_history), 0)
+
+ def test_shell_args_insecure(self):
+ _shell = shell.OpenStackShell()
+ _shell.run("--insecure configuration show".split())
+
+ # Check general calls
+ self.assertEqual(len(self.requests_mock.request_history), 0)
+
+ def test_shell_args_cacert(self):
+ _shell = shell.OpenStackShell()
+ _shell.run("--os-cacert xyzpdq configuration show".split())
+
+ # Check general calls
+ self.assertEqual(len(self.requests_mock.request_history), 0)
+
+ def test_shell_args_cacert_insecure(self):
+ _shell = shell.OpenStackShell()
+ _shell.run("--os-cacert xyzpdq --insecure configuration show".split())
+
+ # Check general calls
+ self.assertEqual(len(self.requests_mock.request_history), 0)
+
+
class TestIntegShellCliV2(test_base.TestInteg):
def setUp(self):
@@ -48,10 +93,10 @@ class TestIntegShellCliV2(test_base.TestInteg):
def test_shell_args_no_options(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -77,30 +122,30 @@ class TestIntegShellCliV2(test_base.TestInteg):
def test_shell_args_verify(self):
_shell = shell.OpenStackShell()
- _shell.run("--verify configuration show".split())
+ _shell.run("--verify extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertTrue(self.requests_mock.request_history[0].verify)
def test_shell_args_insecure(self):
_shell = shell.OpenStackShell()
- _shell.run("--insecure configuration show".split())
+ _shell.run("--insecure extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertFalse(self.requests_mock.request_history[0].verify)
def test_shell_args_cacert(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-cacert xyzpdq configuration show".split())
+ _shell.run("--os-cacert xyzpdq extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertEqual(
@@ -110,10 +155,10 @@ class TestIntegShellCliV2(test_base.TestInteg):
def test_shell_args_cacert_insecure(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-cacert xyzpdq --insecure configuration show".split())
+ _shell.run("--os-cacert xyzpdq --insecure extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertFalse(self.requests_mock.request_history[0].verify)
@@ -138,10 +183,10 @@ class TestIntegShellCliV2Ignore(test_base.TestInteg):
def test_shell_args_ignore_v3(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -184,10 +229,10 @@ class TestIntegShellCliV3(test_base.TestInteg):
def test_shell_args_no_options(self):
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -213,30 +258,30 @@ class TestIntegShellCliV3(test_base.TestInteg):
def test_shell_args_verify(self):
_shell = shell.OpenStackShell()
- _shell.run("--verify configuration show".split())
+ _shell.run("--verify extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertTrue(self.requests_mock.request_history[0].verify)
def test_shell_args_insecure(self):
_shell = shell.OpenStackShell()
- _shell.run("--insecure configuration show".split())
+ _shell.run("--insecure extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertFalse(self.requests_mock.request_history[0].verify)
def test_shell_args_cacert(self):
_shell = shell.OpenStackShell()
- _shell.run("--os-cacert xyzpdq configuration show".split())
+ _shell.run("--os-cacert xyzpdq extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertEqual(
@@ -248,10 +293,10 @@ class TestIntegShellCliV3(test_base.TestInteg):
# This test verifies the outcome of bug 1447784
# https://bugs.launchpad.net/python-openstackclient/+bug/1447784
_shell = shell.OpenStackShell()
- _shell.run("--os-cacert xyzpdq --insecure configuration show".split())
+ _shell.run("--os-cacert xyzpdq --insecure extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check verify
self.assertFalse(self.requests_mock.request_history[0].verify)
@@ -276,10 +321,10 @@ class TestIntegShellCliV3Prompt(test_base.TestInteg):
def test_shell_callback(self, mock_prompt):
mock_prompt.return_value = "qaz"
_shell = shell.OpenStackShell()
- _shell.run("configuration show".split())
+ _shell.run("extension list".split())
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check password callback set correctly
self.assertEqual(
@@ -332,11 +377,11 @@ class TestIntegShellCliPrecedence(test_base.TestInteg):
_shell = shell.OpenStackShell()
_shell.run(
"--os-username zarquon --os-password qaz "
- "configuration show".split(),
+ "extension list".split(),
)
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -431,11 +476,11 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE)
_shell = shell.OpenStackShell()
_shell.run(
- "--os-password qaz configuration show".split(),
+ "--os-password qaz extension list".split(),
)
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
@@ -504,11 +549,11 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
_shell = shell.OpenStackShell()
_shell.run(
"--os-username zarquon --os-password qaz "
- "--os-project-domain-id 5678 configuration show".split(),
+ "--os-project-domain-id 5678 extension list".split(),
)
# Check general calls
- self.assertEqual(len(self.requests_mock.request_history), 2)
+ self.assertNotEqual(len(self.requests_mock.request_history), 0)
# Check discovery request
self.assertEqual(
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index 100ea2b..35f0b1a 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -1305,7 +1305,7 @@ class FakeSecurityGroupRule(object):
'id': 'security-group-rule-id-' + uuid.uuid4().hex,
'port_range_max': None,
'port_range_min': None,
- 'protocol': 'tcp',
+ 'protocol': None,
'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': 'security-group-id-' + uuid.uuid4().hex,
@@ -1344,7 +1344,7 @@ class FakeSecurityGroupRule(object):
@staticmethod
def get_security_group_rules(security_group_rules=None, count=2):
- """Get an iterable Mock object with a list of faked security group rules.
+ """Get an iterable Mock with a list of faked security group rules.
If security group rules list is provided, then initialize the Mock
object with the list. Otherwise create one.
@@ -1816,3 +1816,80 @@ class FakeQuota(object):
info=copy.deepcopy(quota_attrs),
loaded=True)
return quota
+
+
+class FakeFloatingIPPortForwarding(object):
+ """"Fake one or more Port forwarding"""
+
+ @staticmethod
+ def create_one_port_forwarding(attrs=None):
+ """Create a fake Port Forwarding.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, id, etc.
+ """
+ attrs = attrs or {}
+ floatingip_id = (
+ attrs.get('floatingip_id') or'floating-ip-id-' + uuid.uuid4().hex
+ )
+ # Set default attributes.
+ port_forwarding_attrs = {
+ 'id': uuid.uuid4().hex,
+ 'floatingip_id': floatingip_id,
+ 'internal_port_id': 'internal-port-id-' + uuid.uuid4().hex,
+ 'internal_ip_address': '192.168.1.2',
+ 'internal_port': randint(1, 65535),
+ 'external_port': randint(1, 65535),
+ 'protocol': 'tcp',
+ }
+
+ # Overwrite default attributes.
+ port_forwarding_attrs.update(attrs)
+
+ port_forwarding = fakes.FakeResource(
+ info=copy.deepcopy(port_forwarding_attrs),
+ loaded=True
+ )
+ return port_forwarding
+
+ @staticmethod
+ def create_port_forwardings(attrs=None, count=2):
+ """Create multiple fake Port Forwarding.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of Port Forwarding rule to fake
+ :return:
+ A list of FakeResource objects faking the Port Forwardings
+ """
+ port_forwardings = []
+ for i in range(0, count):
+ port_forwardings.append(
+ FakeFloatingIPPortForwarding.create_one_port_forwarding(attrs)
+ )
+ return port_forwardings
+
+ @staticmethod
+ def get_port_forwardings(port_forwardings=None, count=2):
+ """Get a list of faked Port Forwardings.
+
+ If port forwardings list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List port forwardings:
+ A list of FakeResource objects faking port forwardings
+ :param int count:
+ The number of Port Forwardings to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ Port Forwardings
+ """
+ if port_forwardings is None:
+ port_forwardings = (
+ FakeFloatingIPPortForwarding.create_port_forwardings(count)
+ )
+
+ return mock.Mock(side_effect=port_forwardings)
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
new file mode 100644
index 0000000..b51158b
--- /dev/null
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
@@ -0,0 +1,502 @@
+# Copyright (c) 2018 China Telecom Corporation
+# All Rights Reserved.
+#
+# 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 mock
+from mock import call
+
+from osc_lib import exceptions
+
+from openstackclient.network.v2 import floating_ip_port_forwarding
+from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes_v2
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
+from openstackclient.tests.unit import utils as tests_utils
+
+
+class TestFloatingIPPortForwarding(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super(TestFloatingIPPortForwarding, self).setUp()
+ self.network = self.app.client_manager.network
+ self.floating_ip = (network_fakes.FakeFloatingIP.
+ create_one_floating_ip())
+ self.port = network_fakes.FakePort.create_one_port()
+ self.project = identity_fakes_v2.FakeProject.create_one_project()
+ self.network.find_port = mock.Mock(return_value=self.port)
+
+
+class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
+
+ def setUp(self):
+ project_id = ''
+ super(TestCreateFloatingIPPortForwarding, self).setUp()
+ self.new_port_forwarding = (
+ network_fakes.FakeFloatingIPPortForwarding.
+ create_one_port_forwarding(
+ attrs={
+ 'internal_port_id': self.port.id,
+ 'floatingip_id': self.floating_ip.id,
+ }
+ )
+ )
+ self.network.create_floating_ip_port_forwarding = mock.Mock(
+ return_value=self.new_port_forwarding)
+
+ self.network.find_ip = mock.Mock(
+ return_value=self.floating_ip
+ )
+
+ # Get the command object to test
+ self.cmd = floating_ip_port_forwarding.CreateFloatingIPPortForwarding(
+ self.app, self.namespace)
+
+ self.columns = (
+ 'external_port',
+ 'floatingip_id',
+ 'id',
+ 'internal_ip_address',
+ 'internal_port',
+ 'internal_port_id',
+ 'project_id',
+ 'protocol'
+ )
+
+ self.data = (
+ self.new_port_forwarding.external_port,
+ self.new_port_forwarding.floatingip_id,
+ self.new_port_forwarding.id,
+ self.new_port_forwarding.internal_ip_address,
+ self.new_port_forwarding.internal_port,
+ self.new_port_forwarding.internal_port_id,
+ project_id,
+ self.new_port_forwarding.protocol,
+ )
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--port', self.new_port_forwarding.internal_port_id,
+ '--internal-protocol-port',
+ str(self.new_port_forwarding.internal_port),
+ '--external-protocol-port',
+ str(self.new_port_forwarding.external_port),
+ '--protocol', self.new_port_forwarding.protocol,
+ self.new_port_forwarding.floatingip_id,
+ '--internal-ip-address',
+ self.new_port_forwarding.internal_ip_address,
+ ]
+ verifylist = [
+ ('port', self.new_port_forwarding.internal_port_id),
+ ('internal_protocol_port', self.new_port_forwarding.internal_port),
+ ('external_protocol_port', self.new_port_forwarding.external_port),
+ ('protocol', self.new_port_forwarding.protocol),
+ ('floating_ip', self.new_port_forwarding.floatingip_id),
+ ('internal_ip_address', self.new_port_forwarding.
+ internal_ip_address),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_floating_ip_port_forwarding.\
+ assert_called_once_with(
+ self.new_port_forwarding.floatingip_id,
+ **{
+ 'external_port': self.new_port_forwarding.external_port,
+ 'internal_ip_address': self.new_port_forwarding.
+ internal_ip_address,
+ 'internal_port': self.new_port_forwarding.internal_port,
+ 'internal_port_id': self.new_port_forwarding.
+ internal_port_id,
+ 'protocol': self.new_port_forwarding.protocol,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestDeleteFloatingIPPortForwarding(TestFloatingIPPortForwarding):
+
+ def setUp(self):
+ super(TestDeleteFloatingIPPortForwarding, self).setUp()
+ self._port_forwarding = (
+ network_fakes.FakeFloatingIPPortForwarding.create_port_forwardings(
+ count=2, attrs={
+ 'floatingip_id': self.floating_ip.id,
+ }
+ )
+ )
+ self.network.delete_floating_ip_port_forwarding = mock.Mock(
+ return_value=None
+ )
+
+ self.network.find_ip = mock.Mock(
+ return_value=self.floating_ip
+ )
+ # Get the command object to test
+ self.cmd = floating_ip_port_forwarding.DeleteFloatingIPPortForwarding(
+ self.app, self.namespace)
+
+ def test_port_forwarding_delete(self):
+ arglist = [
+ self.floating_ip.id,
+ self._port_forwarding[0].id,
+ ]
+ verifylist = [
+ ('floating_ip', self.floating_ip.id),
+ ('port_forwarding_id', [self._port_forwarding[0].id]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.delete_floating_ip_port_forwarding.\
+ assert_called_once_with(
+ self.floating_ip.id,
+ self._port_forwarding[0].id,
+ ignore_missing=False
+ )
+
+ self.assertIsNone(result)
+
+ def test_multi_port_forwardings_delete(self):
+ arglist = []
+ pf_id = []
+
+ arglist.append(str(self.floating_ip))
+
+ for a in self._port_forwarding:
+ arglist.append(a.id)
+ pf_id.append(a.id)
+
+ verifylist = [
+ ('floating_ip', str(self.floating_ip)),
+ ('port_forwarding_id', pf_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for a in self._port_forwarding:
+ calls.append(call(a.floatingip_id, a.id, ignore_missing=False))
+
+ self.network.delete_floating_ip_port_forwarding.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_port_forwarding_delete_with_exception(self):
+ arglist = [
+ self.floating_ip.id,
+ self._port_forwarding[0].id,
+ 'unexist_port_forwarding_id',
+ ]
+ verifylist = [
+ ('floating_ip', self.floating_ip.id),
+ ('port_forwarding_id',
+ [self._port_forwarding[0].id, 'unexist_port_forwarding_id']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ delete_mock_result = [None, exceptions.CommandError]
+
+ self.network.delete_floating_ip_port_forwarding = (
+ mock.MagicMock(side_effect=delete_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual(
+ '1 of 2 Port forwarding failed to delete.',
+ str(e)
+ )
+
+ self.network.delete_floating_ip_port_forwarding.\
+ assert_any_call(
+ self.floating_ip.id,
+ 'unexist_port_forwarding_id',
+ ignore_missing=False
+ )
+ self.network.delete_floating_ip_port_forwarding.\
+ assert_any_call(
+ self.floating_ip.id,
+ self._port_forwarding[0].id,
+ ignore_missing=False
+ )
+
+
+class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
+
+ columns = (
+ 'ID',
+ 'Internal Port ID',
+ 'Internal IP Address',
+ 'Internal Port',
+ 'External Port',
+ 'Protocol'
+ )
+
+ def setUp(self):
+ super(TestListFloatingIPPortForwarding, self).setUp()
+ self.port_forwardings = (
+ network_fakes.FakeFloatingIPPortForwarding.create_port_forwardings(
+ count=3, attrs={
+ 'internal_port_id': self.port.id,
+ 'floatingip_id': self.floating_ip.id,
+ }
+ )
+ )
+ self.data = []
+ for port_forwarding in self.port_forwardings:
+ self.data.append((
+ port_forwarding.id,
+ port_forwarding.internal_port_id,
+ port_forwarding.internal_ip_address,
+ port_forwarding.internal_port,
+ port_forwarding.external_port,
+ port_forwarding.protocol,
+ ))
+ self.network.floating_ip_port_forwardings = mock.Mock(
+ return_value=self.port_forwardings
+ )
+ self.network.find_ip = mock.Mock(
+ return_value=self.floating_ip
+ )
+ # Get the command object to test
+ self.cmd = floating_ip_port_forwarding.ListFloatingIPPortForwarding(
+ self.app,
+ self.namespace
+ )
+
+ def test_port_forwarding_list(self):
+ arglist = [
+ self.floating_ip.id
+ ]
+ verifylist = [
+ ('floating_ip', self.floating_ip.id)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.floating_ip_port_forwardings.assert_called_once_with(
+ self.floating_ip,
+ **{}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_port_forwarding_list_all_options(self):
+ arglist = [
+ '--port', self.port_forwardings[0].internal_port_id,
+ '--external-protocol-port',
+ str(self.port_forwardings[0].external_port),
+ '--protocol', self.port_forwardings[0].protocol,
+ self.port_forwardings[0].floatingip_id,
+ ]
+
+ verifylist = [
+ ('port', self.port_forwardings[0].internal_port_id),
+ ('external_protocol_port',
+ str(self.port_forwardings[0].external_port)),
+ ('protocol', self.port_forwardings[0].protocol),
+ ('floating_ip', self.port_forwardings[0].floatingip_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ query = {
+ 'internal_port_id': self.port_forwardings[0].internal_port_id,
+ 'external_port': str(self.port_forwardings[0].external_port),
+ 'protocol': self.port_forwardings[0].protocol,
+ }
+
+ self.network.floating_ip_port_forwardings.assert_called_once_with(
+ self.floating_ip,
+ **query
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+
+class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
+
+ # The Port Forwarding to set.
+ def setUp(self):
+ super(TestSetFloatingIPPortForwarding, self).setUp()
+ self._port_forwarding = (
+ network_fakes.FakeFloatingIPPortForwarding.
+ create_one_port_forwarding(
+ attrs={
+ 'floatingip_id': self.floating_ip.id,
+ }
+ )
+ )
+ self.network.update_floating_ip_port_forwarding = mock.Mock(
+ return_value=None
+ )
+
+ self.network.find_floating_ip_port_forwarding = mock.Mock(
+ return_value=self._port_forwarding)
+ self.network.find_ip = mock.Mock(
+ return_value=self.floating_ip
+ )
+ # Get the command object to test
+ self.cmd = floating_ip_port_forwarding.SetFloatingIPPortForwarding(
+ self.app,
+ self.namespace
+ )
+
+ def test_set_nothing(self):
+ arglist = [
+ self._port_forwarding.floatingip_id,
+ self._port_forwarding.id,
+ ]
+ verifylist = [
+ ('floating_ip', self._port_forwarding.floatingip_id),
+ ('port_forwarding_id', self._port_forwarding.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_floating_ip_port_forwarding.assert_called_with(
+ self._port_forwarding.floatingip_id,
+ self._port_forwarding.id,
+ **attrs
+ )
+ self.assertIsNone(result)
+
+ def test_set_all_thing(self):
+ arglist = [
+ '--port', self.port.id,
+ '--internal-ip-address', 'new_internal_ip_address',
+ '--internal-protocol-port', '100',
+ '--external-protocol-port', '200',
+ '--protocol', 'tcp',
+ self._port_forwarding.floatingip_id,
+ self._port_forwarding.id,
+ ]
+ verifylist = [
+ ('port', self.port.id),
+ ('internal_ip_address', 'new_internal_ip_address'),
+ ('internal_protocol_port', 100),
+ ('external_protocol_port', 200),
+ ('protocol', 'tcp'),
+ ('floating_ip', self._port_forwarding.floatingip_id),
+ ('port_forwarding_id', self._port_forwarding.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'internal_port_id': self.port.id,
+ 'internal_ip_address': 'new_internal_ip_address',
+ 'internal_port': 100,
+ 'external_port': 200,
+ 'protocol': 'tcp',
+ }
+ self.network.update_floating_ip_port_forwarding.assert_called_with(
+ self._port_forwarding.floatingip_id,
+ self._port_forwarding.id,
+ **attrs
+ )
+ self.assertIsNone(result)
+
+
+class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
+
+ # The port forwarding to show.
+ columns = (
+ 'external_port',
+ 'floatingip_id',
+ 'id',
+ 'internal_ip_address',
+ 'internal_port',
+ 'internal_port_id',
+ 'project_id',
+ 'protocol',
+ )
+
+ def setUp(self):
+ project_id = ''
+ super(TestShowFloatingIPPortForwarding, self).setUp()
+ self._port_forwarding = (
+ network_fakes.FakeFloatingIPPortForwarding.
+ create_one_port_forwarding(
+ attrs={
+ 'floatingip_id': self.floating_ip.id,
+ }
+ )
+ )
+ self.data = (
+ self._port_forwarding.external_port,
+ self._port_forwarding.floatingip_id,
+ self._port_forwarding.id,
+ self._port_forwarding.internal_ip_address,
+ self._port_forwarding.internal_port,
+ self._port_forwarding.internal_port_id,
+ project_id,
+ self._port_forwarding.protocol,
+ )
+ self.network.find_floating_ip_port_forwarding = mock.Mock(
+ return_value=self._port_forwarding
+ )
+ self.network.find_ip = mock.Mock(
+ return_value=self.floating_ip
+ )
+ # Get the command object to test
+ self.cmd = floating_ip_port_forwarding.ShowFloatingIPPortForwarding(
+ self.app,
+ self.namespace
+ )
+
+ def test_show_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_show_default_options(self):
+ arglist = [
+ self._port_forwarding.floatingip_id,
+ self._port_forwarding.id,
+ ]
+ verifylist = [
+ ('floating_ip', self._port_forwarding.floatingip_id),
+ ('port_forwarding_id', self._port_forwarding.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.find_floating_ip_port_forwarding.assert_called_once_with(
+ self.floating_ip,
+ self._port_forwarding.id,
+ ignore_missing=False
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(list(self.data), list(data))
diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py
index c7c5a9b..21508a8 100644
--- a/openstackclient/tests/unit/network/v2/test_ip_availability.py
+++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py
@@ -13,7 +13,7 @@
import mock
-from osc_lib import utils as common_utils
+from osc_lib.cli import format_columns
from openstackclient.network.v2 import ip_availability
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -75,7 +75,7 @@ class TestListIPAvailability(TestIPAvailability):
self.network.network_ip_availabilities.assert_called_once_with(
**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_ip_version(self):
arglist = [
@@ -93,7 +93,7 @@ class TestListIPAvailability(TestIPAvailability):
self.network.network_ip_availabilities.assert_called_once_with(
**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_project(self):
arglist = [
@@ -113,7 +113,7 @@ class TestListIPAvailability(TestIPAvailability):
self.network.network_ip_availabilities.assert_called_once_with(
**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
class TestShowIPAvailability(TestIPAvailability):
@@ -135,7 +135,7 @@ class TestShowIPAvailability(TestIPAvailability):
_ip_availability.network_id,
_ip_availability.network_name,
_ip_availability.tenant_id,
- common_utils.format_list(
+ format_columns.ListDictColumn(
_ip_availability.subnet_ip_availability),
_ip_availability.total_ips,
_ip_availability.used_ips,
@@ -176,4 +176,4 @@ class TestShowIPAvailability(TestIPAvailability):
self._ip_availability.network_name,
ignore_missing=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 0f57f0e..5c97c36 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -15,8 +15,9 @@ import random
import mock
from mock import call
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.network.v2 import network
from openstackclient.tests.unit import fakes
@@ -81,9 +82,9 @@ class TestCreateNetworkIdentityV3(TestNetwork):
)
data = (
- network._format_admin_state(_network.admin_state_up),
- utils.format_list(_network.availability_zone_hints),
- utils.format_list(_network.availability_zones),
+ network.AdminStateColumn(_network.admin_state_up),
+ format_columns.ListColumn(_network.availability_zone_hints),
+ format_columns.ListColumn(_network.availability_zones),
_network.description,
_network.dns_domain,
_network.id,
@@ -98,11 +99,11 @@ class TestCreateNetworkIdentityV3(TestNetwork):
_network.provider_physical_network,
_network.provider_segmentation_id,
_network.qos_policy_id,
- network._format_router_external(_network.is_router_external),
+ network.RouterExternalColumn(_network.is_router_external),
_network.shared,
_network.status,
- utils.format_list(_network.subnets),
- utils.format_list(_network.tags),
+ format_columns.ListColumn(_network.subnets),
+ format_columns.ListColumn(_network.tags),
)
def setUp(self):
@@ -146,7 +147,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_all_options(self):
arglist = [
@@ -211,7 +212,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'dns_domain': 'example.org.',
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_other_options(self):
arglist = [
@@ -238,7 +239,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'port_security_enabled': False,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def _test_create_with_tag(self, add_tags=True):
arglist = [self._network.name]
@@ -270,7 +271,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
@@ -313,9 +314,9 @@ class TestCreateNetworkIdentityV2(TestNetwork):
)
data = (
- network._format_admin_state(_network.admin_state_up),
- utils.format_list(_network.availability_zone_hints),
- utils.format_list(_network.availability_zones),
+ network.AdminStateColumn(_network.admin_state_up),
+ format_columns.ListColumn(_network.availability_zone_hints),
+ format_columns.ListColumn(_network.availability_zones),
_network.description,
_network.dns_domain,
_network.id,
@@ -330,11 +331,11 @@ class TestCreateNetworkIdentityV2(TestNetwork):
_network.provider_physical_network,
_network.provider_segmentation_id,
_network.qos_policy_id,
- network._format_router_external(_network.is_router_external),
+ network.RouterExternalColumn(_network.is_router_external),
_network.shared,
_network.status,
- utils.format_list(_network.subnets),
- utils.format_list(_network.tags),
+ format_columns.ListColumn(_network.subnets),
+ format_columns.ListColumn(_network.tags),
)
def setUp(self):
@@ -385,7 +386,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_domain_identityv2(self):
arglist = [
@@ -525,7 +526,7 @@ class TestListNetwork(TestNetwork):
data.append((
net.id,
net.name,
- utils.format_list(net.subnets),
+ format_columns.ListColumn(net.subnets),
))
data_long = []
@@ -535,13 +536,13 @@ class TestListNetwork(TestNetwork):
net.name,
net.status,
net.project_id,
- network._format_admin_state(net.admin_state_up),
+ network.AdminStateColumn(net.admin_state_up),
net.shared,
- utils.format_list(net.subnets),
+ format_columns.ListColumn(net.subnets),
net.provider_network_type,
- network._format_router_external(net.is_router_external),
- utils.format_list(net.availability_zones),
- utils.format_list(net.tags),
+ network.RouterExternalColumn(net.is_router_external),
+ format_columns.ListColumn(net.availability_zones),
+ format_columns.ListColumn(net.tags),
))
def setUp(self):
@@ -577,7 +578,7 @@ class TestListNetwork(TestNetwork):
self.network.networks.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_external(self):
arglist = [
@@ -598,7 +599,7 @@ class TestListNetwork(TestNetwork):
**{'router:external': True, 'is_router_external': True}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_internal(self):
arglist = [
@@ -615,7 +616,7 @@ class TestListNetwork(TestNetwork):
**{'router:external': False, 'is_router_external': False}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_long(self):
arglist = [
@@ -634,7 +635,7 @@ class TestListNetwork(TestNetwork):
self.network.networks.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_list_name(self):
test_name = "fakename"
@@ -653,7 +654,7 @@ class TestListNetwork(TestNetwork):
**{'name': test_name}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_enable(self):
arglist = [
@@ -671,7 +672,7 @@ class TestListNetwork(TestNetwork):
**{'admin_state_up': True, 'is_admin_state_up': True}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_disable(self):
arglist = [
@@ -689,7 +690,7 @@ class TestListNetwork(TestNetwork):
**{'admin_state_up': False, 'is_admin_state_up': False}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_project(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -708,7 +709,7 @@ class TestListNetwork(TestNetwork):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_project_domain(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -727,7 +728,7 @@ class TestListNetwork(TestNetwork):
self.network.networks.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_share(self):
arglist = [
@@ -744,7 +745,7 @@ class TestListNetwork(TestNetwork):
**{'shared': True, 'is_shared': True}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_no_share(self):
arglist = [
@@ -761,7 +762,7 @@ class TestListNetwork(TestNetwork):
**{'shared': False, 'is_shared': False}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_status(self):
choices = ['ACTIVE', 'BUILD', 'DOWN', 'ERROR']
@@ -780,7 +781,7 @@ class TestListNetwork(TestNetwork):
**{'status': test_status}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_provider_network_type(self):
network_type = self._network[0].provider_network_type
@@ -798,7 +799,7 @@ class TestListNetwork(TestNetwork):
'provider_network_type': network_type}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_provider_physical_network(self):
physical_network = self._network[0].provider_physical_network
@@ -816,7 +817,7 @@ class TestListNetwork(TestNetwork):
'provider_physical_network': physical_network}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_provider_segment(self):
segmentation_id = self._network[0].provider_segmentation_id
@@ -834,7 +835,7 @@ class TestListNetwork(TestNetwork):
'provider_segmentation_id': segmentation_id}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_list_dhcp_agent(self):
arglist = [
@@ -853,7 +854,7 @@ class TestListNetwork(TestNetwork):
*attrs)
self.assertEqual(self.columns, columns)
- self.assertEqual(list(data), list(self.data))
+ self.assertListItemEqual(list(data), list(self.data))
def test_list_with_tag_options(self):
arglist = [
@@ -878,7 +879,7 @@ class TestListNetwork(TestNetwork):
'not_any_tags': 'black,white'}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
class TestSetNetwork(TestNetwork):
@@ -1057,9 +1058,9 @@ class TestShowNetwork(TestNetwork):
)
data = (
- network._format_admin_state(_network.admin_state_up),
- utils.format_list(_network.availability_zone_hints),
- utils.format_list(_network.availability_zones),
+ network.AdminStateColumn(_network.admin_state_up),
+ format_columns.ListColumn(_network.availability_zone_hints),
+ format_columns.ListColumn(_network.availability_zones),
_network.description,
_network.dns_domain,
_network.id,
@@ -1074,11 +1075,11 @@ class TestShowNetwork(TestNetwork):
_network.provider_physical_network,
_network.provider_segmentation_id,
_network.qos_policy_id,
- network._format_router_external(_network.is_router_external),
+ network.RouterExternalColumn(_network.is_router_external),
_network.shared,
_network.status,
- utils.format_list(_network.subnets),
- utils.format_list(_network.tags),
+ format_columns.ListColumn(_network.subnets),
+ format_columns.ListColumn(_network.tags),
)
def setUp(self):
@@ -1111,7 +1112,7 @@ class TestShowNetwork(TestNetwork):
self._network.name, ignore_missing=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestUnsetNetwork(TestNetwork):
diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py
index 709fb1c..8500d08 100644
--- a/openstackclient/tests/unit/network/v2/test_network_agent.py
+++ b/openstackclient/tests/unit/network/v2/test_network_agent.py
@@ -14,8 +14,8 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.network.v2 import network_agent
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
@@ -207,8 +207,8 @@ class TestListNetworkAgent(TestNetworkAgent):
agent.agent_type,
agent.host,
agent.availability_zone,
- network_agent._format_alive(agent.alive),
- network_agent._format_admin_state(agent.admin_state_up),
+ network_agent.AliveColumn(agent.alive),
+ network_agent.AdminStateColumn(agent.admin_state_up),
agent.binary,
))
@@ -246,7 +246,7 @@ class TestListNetworkAgent(TestNetworkAgent):
self.network.agents.assert_called_once_with(**{})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_agents_list_agent_type(self):
arglist = [
@@ -263,7 +263,7 @@ class TestListNetworkAgent(TestNetworkAgent):
'agent_type': 'DHCP agent',
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_agents_list_host(self):
arglist = [
@@ -280,7 +280,7 @@ class TestListNetworkAgent(TestNetworkAgent):
'host': self.network_agents[0].host,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_agents_list_networks(self):
arglist = [
@@ -298,7 +298,7 @@ class TestListNetworkAgent(TestNetworkAgent):
self.network.network_hosting_dhcp_agents.assert_called_once_with(
*attrs)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_agents_list_routers(self):
arglist = [
@@ -318,7 +318,7 @@ class TestListNetworkAgent(TestNetworkAgent):
*attrs)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_network_agents_list_routers_with_long_option(self):
arglist = [
@@ -343,7 +343,7 @@ class TestListNetworkAgent(TestNetworkAgent):
router_agent_data = [d + ('',) for d in self.data]
self.assertEqual(router_agent_columns, columns)
- self.assertEqual(router_agent_data, list(data))
+ self.assertListItemEqual(router_agent_data, list(data))
class TestRemoveNetworkFromAgent(TestNetworkAgent):
@@ -531,12 +531,12 @@ class TestShowNetworkAgent(TestNetworkAgent):
'id',
)
data = (
- network_agent._format_admin_state(_network_agent.is_admin_state_up),
+ network_agent.AdminStateColumn(_network_agent.admin_state_up),
_network_agent.agent_type,
- network_agent._format_alive(_network_agent.is_alive),
+ network_agent.AliveColumn(_network_agent.is_alive),
_network_agent.availability_zone,
_network_agent.binary,
- utils.format_dict(_network_agent.configurations),
+ format_columns.DictColumn(_network_agent.configurations),
_network_agent.host,
_network_agent.id,
)
@@ -571,4 +571,4 @@ class TestShowNetworkAgent(TestNetworkAgent):
self.network.get_agent.assert_called_once_with(
self._network_agent.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(list(self.data), list(data))
+ self.assertItemEqual(list(self.data), list(data))
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index 8ac3e54..c30d682 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -15,6 +15,7 @@ import argparse
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -68,22 +69,22 @@ class TestPort(network_fakes.TestNetworkV2):
)
data = (
- port._format_admin_state(fake_port.admin_state_up),
- utils.format_list_of_dicts(fake_port.allowed_address_pairs),
+ port.AdminStateColumn(fake_port.admin_state_up),
+ format_columns.ListDictColumn(fake_port.allowed_address_pairs),
fake_port.binding_host_id,
- utils.format_dict(fake_port.binding_profile),
- utils.format_dict(fake_port.binding_vif_details),
+ format_columns.DictColumn(fake_port.binding_profile),
+ format_columns.DictColumn(fake_port.binding_vif_details),
fake_port.binding_vif_type,
fake_port.binding_vnic_type,
fake_port.data_plane_status,
fake_port.description,
fake_port.device_id,
fake_port.device_owner,
- utils.format_list_of_dicts(fake_port.dns_assignment),
+ format_columns.ListDictColumn(fake_port.dns_assignment),
fake_port.dns_domain,
fake_port.dns_name,
- utils.format_list_of_dicts(fake_port.extra_dhcp_opts),
- utils.format_list_of_dicts(fake_port.fixed_ips),
+ format_columns.ListDictColumn(fake_port.extra_dhcp_opts),
+ format_columns.ListDictColumn(fake_port.fixed_ips),
fake_port.id,
fake_port.mac_address,
fake_port.name,
@@ -91,9 +92,9 @@ class TestPort(network_fakes.TestNetworkV2):
fake_port.port_security_enabled,
fake_port.project_id,
fake_port.qos_policy_id,
- utils.format_list(fake_port.security_group_ids),
+ format_columns.ListColumn(fake_port.security_group_ids),
fake_port.status,
- utils.format_list(fake_port.tags),
+ format_columns.ListColumn(fake_port.tags),
fake_port.uplink_status_propagation,
)
@@ -141,7 +142,7 @@ class TestCreatePort(TestPort):
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_full_options(self):
arglist = [
@@ -199,7 +200,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_invalid_json_binding_profile(self):
arglist = [
@@ -250,7 +251,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_security_group(self):
secgroup = network_fakes.FakeSecurityGroup.create_one_security_group()
@@ -279,7 +280,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_port_with_dns_name(self):
arglist = [
@@ -305,7 +306,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_security_groups(self):
sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group()
@@ -335,7 +336,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_no_security_groups(self):
arglist = [
@@ -361,7 +362,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_no_fixed_ips(self):
arglist = [
@@ -387,7 +388,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_port_with_allowed_address_pair_ipaddr(self):
pairs = [{'ip_address': '192.168.1.123'},
@@ -417,7 +418,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_port_with_allowed_address_pair(self):
pairs = [{'ip_address': '192.168.1.123',
@@ -453,7 +454,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_port_with_qos(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
@@ -481,7 +482,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_port_security_enabled(self):
arglist = [
@@ -565,7 +566,7 @@ class TestCreatePort(TestPort):
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
@@ -602,7 +603,7 @@ class TestCreatePort(TestPort):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_uplink_status_propagation_enabled(self):
self._test_create_with_uplink_status_propagation(enable=True)
@@ -610,6 +611,48 @@ class TestCreatePort(TestPort):
def test_create_with_uplink_status_propagation_disabled(self):
self._test_create_with_uplink_status_propagation(enable=False)
+ def test_create_port_with_extra_dhcp_option(self):
+ extra_dhcp_options = [{'opt_name': 'classless-static-route',
+ 'opt_value': '169.254.169.254/32,22.2.0.2,'
+ '0.0.0.0/0,22.2.0.1',
+ 'ip_version': '4'},
+ {'opt_name': 'dns-server',
+ 'opt_value': '240C::6666',
+ 'ip_version': '6'}]
+ arglist = [
+ '--network', self._port.network_id,
+ '--extra-dhcp-option', 'name=classless-static-route,'
+ 'value=169.254.169.254/32,22.2.0.2,'
+ '0.0.0.0/0,22.2.0.1,'
+ 'ip-version=4',
+ '--extra-dhcp-option', 'name=dns-server,value=240C::6666,'
+ 'ip-version=6',
+ 'test-port',
+ ]
+
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('extra_dhcp_options', [{'name': 'classless-static-route',
+ 'value': '169.254.169.254/32,22.2.0.2,'
+ '0.0.0.0/0,22.2.0.1',
+ 'ip-version': '4'},
+ {'name': 'dns-server',
+ 'value': '240C::6666',
+ 'ip-version': '6'}]),
+ ('name', 'test-port'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'extra_dhcp_opts': extra_dhcp_options,
+ 'name': 'test-port',
+ })
+
class TestDeletePort(TestPort):
@@ -719,7 +762,7 @@ class TestListPort(TestPort):
prt.id,
prt.name,
prt.mac_address,
- utils.format_list_of_dicts(prt.fixed_ips),
+ format_columns.ListDictColumn(prt.fixed_ips),
prt.status,
))
@@ -729,11 +772,11 @@ class TestListPort(TestPort):
prt.id,
prt.name,
prt.mac_address,
- utils.format_list_of_dicts(prt.fixed_ips),
+ format_columns.ListDictColumn(prt.fixed_ips),
prt.status,
- utils.format_list(prt.security_group_ids),
+ format_columns.ListColumn(prt.security_group_ids),
prt.device_owner,
- utils.format_list(prt.tags),
+ format_columns.ListColumn(prt.tags),
))
def setUp(self):
@@ -762,7 +805,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_router_opt(self):
arglist = [
@@ -781,7 +824,7 @@ class TestListPort(TestPort):
'device_id': 'fake-router-id'
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
@mock.patch.object(utils, 'find_resource')
def test_port_list_with_server_option(self, mock_find):
@@ -801,7 +844,7 @@ class TestListPort(TestPort):
device_id=fake_server.id)
mock_find.assert_called_once_with(mock.ANY, 'fake-server-name')
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_device_id_opt(self):
arglist = [
@@ -820,7 +863,7 @@ class TestListPort(TestPort):
'device_id': self._ports[0].device_id
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_device_owner_opt(self):
arglist = [
@@ -839,7 +882,7 @@ class TestListPort(TestPort):
'device_owner': self._ports[0].device_owner
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_all_opt(self):
arglist = [
@@ -867,7 +910,7 @@ class TestListPort(TestPort):
'mac_address': self._ports[0].mac_address
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_mac_address_opt(self):
arglist = [
@@ -886,7 +929,7 @@ class TestListPort(TestPort):
'mac_address': self._ports[0].mac_address
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_fixed_ip_opt_ip_address(self):
ip_address = self._ports[0].fixed_ips[0]['ip_address']
@@ -904,7 +947,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with(**{
'fixed_ips': ['ip_address=%s' % ip_address]})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_fixed_ip_opt_ip_address_substr(self):
ip_address_ss = self._ports[0].fixed_ips[0]['ip_address'][:-1]
@@ -922,7 +965,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with(**{
'fixed_ips': ['ip_address_substr=%s' % ip_address_ss]})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_fixed_ip_opt_subnet_id(self):
subnet_id = self._ports[0].fixed_ips[0]['subnet_id']
@@ -942,7 +985,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with(**{
'fixed_ips': ['subnet_id=%s' % subnet_id]})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_fixed_ip_opts(self):
subnet_id = self._ports[0].fixed_ips[0]['subnet_id']
@@ -966,7 +1009,7 @@ class TestListPort(TestPort):
'fixed_ips': ['subnet_id=%s' % subnet_id,
'ip_address=%s' % ip_address]})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_fixed_ips(self):
subnet_id = self._ports[0].fixed_ips[0]['subnet_id']
@@ -990,7 +1033,7 @@ class TestListPort(TestPort):
'fixed_ips': ['subnet_id=%s' % subnet_id,
'ip_address=%s' % ip_address]})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_port_with_long(self):
arglist = [
@@ -1007,7 +1050,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_port_list_project(self):
project = identity_fakes.FakeProject.create_one_project()
@@ -1025,7 +1068,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_port_list_project_domain(self):
project = identity_fakes.FakeProject.create_one_project()
@@ -1045,7 +1088,7 @@ class TestListPort(TestPort):
self.network.ports.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_with_tag_options(self):
arglist = [
@@ -1070,7 +1113,7 @@ class TestListPort(TestPort):
'not_any_tags': 'black,white'}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
class TestSetPort(TestPort):
@@ -1644,7 +1687,7 @@ class TestShowPort(TestPort):
self._port.name, ignore_missing=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestUnsetPort(TestPort):
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 618adf3..079b974 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -14,8 +14,8 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils as osc_utils
from openstackclient.network.v2 import router
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
@@ -132,19 +132,19 @@ class TestCreateRouter(TestRouter):
'tags',
)
data = (
- router._format_admin_state(new_router.admin_state_up),
- osc_utils.format_list(new_router.availability_zone_hints),
- osc_utils.format_list(new_router.availability_zones),
+ router.AdminStateColumn(new_router.admin_state_up),
+ format_columns.ListColumn(new_router.availability_zone_hints),
+ format_columns.ListColumn(new_router.availability_zones),
new_router.description,
new_router.distributed,
- router._format_router_info(new_router.external_gateway_info),
+ router.RouterInfoColumn(new_router.external_gateway_info),
new_router.ha,
new_router.id,
new_router.name,
new_router.tenant_id,
- router._format_routes(new_router.routes),
+ router.RoutesColumn(new_router.routes),
new_router.status,
- osc_utils.format_list(new_router.tags),
+ format_columns.ListColumn(new_router.tags),
)
def setUp(self):
@@ -184,7 +184,7 @@ class TestCreateRouter(TestRouter):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def _test_create_with_ha_options(self, option, ha):
arglist = [
@@ -208,7 +208,7 @@ class TestCreateRouter(TestRouter):
'ha': ha,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_ha_option(self):
self._test_create_with_ha_options('--ha', True)
@@ -237,7 +237,7 @@ class TestCreateRouter(TestRouter):
'distributed': distributed,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_distributed_option(self):
self._test_create_with_distributed_options('--distributed', True)
@@ -268,7 +268,7 @@ class TestCreateRouter(TestRouter):
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def _test_create_with_tag(self, add_tags=True):
arglist = [self.new_router.name]
@@ -301,7 +301,7 @@ class TestCreateRouter(TestRouter):
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
@@ -422,7 +422,7 @@ class TestListRouter(TestRouter):
r.id,
r.name,
r.status,
- router._format_admin_state(r.admin_state_up),
+ router.AdminStateColumn(r.admin_state_up),
r.tenant_id,
r.distributed,
r.ha,
@@ -447,10 +447,10 @@ class TestListRouter(TestRouter):
r = routers[i]
data_long.append(
data[i] + (
- router._format_routes(r.routes),
- router._format_router_info(r.external_gateway_info),
- osc_utils.format_list(r.availability_zones),
- osc_utils.format_list(r.tags),
+ router.RoutesColumn(r.routes),
+ router.RouterInfoColumn(r.external_gateway_info),
+ format_columns.ListColumn(r.availability_zones),
+ format_columns.ListColumn(r.tags),
)
)
data_long_no_az = []
@@ -458,9 +458,9 @@ class TestListRouter(TestRouter):
r = routers[i]
data_long_no_az.append(
data[i] + (
- router._format_routes(r.routes),
- router._format_router_info(r.external_gateway_info),
- osc_utils.format_list(r.tags),
+ router.RoutesColumn(r.routes),
+ router.RouterInfoColumn(r.external_gateway_info),
+ format_columns.ListColumn(r.tags),
)
)
@@ -494,7 +494,7 @@ class TestListRouter(TestRouter):
self.network.routers.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_router_list_no_ha_no_distributed(self):
_routers = network_fakes.FakeRouter.create_routers({
@@ -531,7 +531,7 @@ class TestListRouter(TestRouter):
self.network.routers.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_router_list_long_no_az(self):
arglist = [
@@ -552,7 +552,7 @@ class TestListRouter(TestRouter):
self.network.routers.assert_called_once_with()
self.assertEqual(self.columns_long_no_az, columns)
- self.assertEqual(self.data_long_no_az, list(data))
+ self.assertListItemEqual(self.data_long_no_az, list(data))
def test_list_name(self):
test_name = "fakename"
@@ -570,7 +570,7 @@ class TestListRouter(TestRouter):
**{'name': test_name}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_router_list_enable(self):
arglist = [
@@ -587,7 +587,7 @@ class TestListRouter(TestRouter):
**{'admin_state_up': True, 'is_admin_state_up': True}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_router_list_disable(self):
arglist = [
@@ -605,7 +605,7 @@ class TestListRouter(TestRouter):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_router_list_project(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -623,7 +623,7 @@ class TestListRouter(TestRouter):
self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_router_list_project_domain(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -643,7 +643,7 @@ class TestListRouter(TestRouter):
self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_router_list_agents_no_args(self):
arglist = [
@@ -671,7 +671,7 @@ class TestListRouter(TestRouter):
self.network.agent_hosted_routers(
*attrs)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_with_tag_options(self):
arglist = [
@@ -696,7 +696,7 @@ class TestListRouter(TestRouter):
'not_any_tags': 'black,white'}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
class TestRemovePortFromRouter(TestRouter):
@@ -939,52 +939,6 @@ class TestSetRouter(TestRouter):
_testrouter, **attrs)
self.assertIsNone(result)
- def test_set_clear_routes(self):
- arglist = [
- self._router.name,
- '--clear-routes',
- ]
- verifylist = [
- ('router', self._router.name),
- ('clear_routes', True),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- attrs = {
- 'routes': [],
- }
- self.network.update_router.assert_called_once_with(
- self._router, **attrs)
- self.assertIsNone(result)
-
- def test_overwrite_route_clear_routes(self):
- _testrouter = network_fakes.FakeRouter.create_one_router(
- {'routes': [{"destination": "10.0.0.2",
- "nexthop": "1.1.1.1"}]})
- self.network.find_router = mock.Mock(return_value=_testrouter)
- arglist = [
- _testrouter.name,
- '--route', 'destination=10.20.30.0/24,gateway=10.20.30.1',
- '--clear-routes',
- ]
- verifylist = [
- ('router', _testrouter.name),
- ('routes', [{'destination': '10.20.30.0/24',
- 'gateway': '10.20.30.1'}]),
- ('clear_routes', True),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
- attrs = {
- 'routes': [{'destination': '10.20.30.0/24',
- 'nexthop': '10.20.30.1'}]
- }
- self.network.update_router.assert_called_once_with(
- _testrouter, **attrs)
- self.assertIsNone(result)
-
def test_set_nothing(self):
arglist = [
self._router.name,
@@ -1260,20 +1214,20 @@ class TestShowRouter(TestRouter):
'tags',
)
data = (
- router._format_admin_state(_router.admin_state_up),
- osc_utils.format_list(_router.availability_zone_hints),
- osc_utils.format_list(_router.availability_zones),
+ router.AdminStateColumn(_router.admin_state_up),
+ format_columns.ListColumn(_router.availability_zone_hints),
+ format_columns.ListColumn(_router.availability_zones),
_router.description,
_router.distributed,
- router._format_router_info(_router.external_gateway_info),
+ router.RouterInfoColumn(_router.external_gateway_info),
_router.ha,
_router.id,
- router._format_router_info(_router.interfaces_info),
+ router.RouterInfoColumn(_router.interfaces_info),
_router.name,
_router.tenant_id,
- router._format_routes(_router.routes),
+ router.RoutesColumn(_router.routes),
_router.status,
- osc_utils.format_list(_router.tags),
+ format_columns.ListColumn(_router.tags),
)
def setUp(self):
@@ -1309,7 +1263,7 @@ class TestShowRouter(TestRouter):
'device_id': self._router.id
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_show_no_ha_no_distributed(self):
_router = network_fakes.FakeRouter.create_one_router({
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_compute.py
index c949e2c..df36006 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py
@@ -56,7 +56,7 @@ class TestCreateSecurityGroupCompute(TestSecurityGroupCompute):
_security_group['id'],
_security_group['name'],
_security_group['tenant_id'],
- '',
+ security_group.ComputeSecurityGroupRulesColumn([]),
)
def setUp(self):
@@ -88,7 +88,7 @@ class TestCreateSecurityGroupCompute(TestSecurityGroupCompute):
self._security_group['name'],
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_security_group_create_all_options(self, sg_mock):
sg_mock.return_value = self._security_group
@@ -109,7 +109,7 @@ class TestCreateSecurityGroupCompute(TestSecurityGroupCompute):
self._security_group['description'],
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
@mock.patch(
@@ -255,7 +255,7 @@ class TestListSecurityGroupCompute(TestSecurityGroupCompute):
kwargs = {'search_opts': {'all_tenants': False}}
sg_mock.assert_called_once_with(**kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_security_group_list_all_projects(self, sg_mock):
sg_mock.return_value = self._security_groups
@@ -272,7 +272,7 @@ class TestListSecurityGroupCompute(TestSecurityGroupCompute):
kwargs = {'search_opts': {'all_tenants': True}}
sg_mock.assert_called_once_with(**kwargs)
self.assertEqual(self.columns_all_projects, columns)
- self.assertEqual(self.data_all_projects, list(data))
+ self.assertListItemEqual(self.data_all_projects, list(data))
@mock.patch(
@@ -372,8 +372,7 @@ class TestShowSecurityGroupCompute(TestSecurityGroupCompute):
_security_group['id'],
_security_group['name'],
_security_group['tenant_id'],
- security_group._format_compute_security_group_rules(
- [_security_group_rule]),
+ security_group.ComputeSecurityGroupRulesColumn([_security_group_rule]),
)
def setUp(self):
@@ -402,4 +401,4 @@ class TestShowSecurityGroupCompute(TestSecurityGroupCompute):
sg_mock.assert_called_once_with(self._security_group['id'])
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py
index 8320828..57698ec 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py
@@ -57,7 +57,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.id,
_security_group.name,
_security_group.project_id,
- '',
+ security_group.NetworkSecurityGroupRulesColumn([]),
_security_group.tags,
)
@@ -94,7 +94,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'name': self._security_group.name,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_all_options(self):
arglist = [
@@ -119,7 +119,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'tenant_id': self.project.id,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def _test_create_with_tag(self, add_tags=True):
arglist = [self._security_group.name]
@@ -150,7 +150,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
@@ -287,7 +287,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.security_groups.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_security_group_list_all_projects(self):
arglist = [
@@ -302,7 +302,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.security_groups.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_security_group_list_project(self):
project = identity_fakes.FakeProject.create_one_project()
@@ -320,7 +320,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.security_groups.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_security_group_list_project_domain(self):
project = identity_fakes.FakeProject.create_one_project()
@@ -340,7 +340,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.security_groups.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_with_tag_options(self):
arglist = [
@@ -490,7 +490,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.id,
_security_group.name,
_security_group.project_id,
- security_group._format_network_security_group_rules(
+ security_group.NetworkSecurityGroupRulesColumn(
[_security_group_rule._info]),
_security_group.tags,
)
@@ -522,7 +522,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.find_security_group.assert_called_once_with(
self._security_group.id, ignore_missing=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork):
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py
index 5c1937e..cf5261b 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py
@@ -72,15 +72,6 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, [], [])
- def test_security_group_rule_create_all_source_options(self, sgr_mock):
- arglist = [
- '--src-ip', '10.10.0.0/24',
- '--src-group', self._security_group['id'],
- self._security_group['id'],
- ]
- self.assertRaises(tests_utils.ParserException,
- self.check_parser, self.cmd, arglist, [])
-
def test_security_group_rule_create_all_remote_options(self, sgr_mock):
arglist = [
'--remote-ip', '10.10.0.0/24',
@@ -151,41 +142,6 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
- def test_security_group_rule_create_source_group(self, sgr_mock):
- expected_columns, expected_data = self._setup_security_group_rule({
- 'from_port': 22,
- 'to_port': 22,
- 'group': {'name': self._security_group['name']},
- })
- sgr_mock.return_value = self._security_group_rule
- arglist = [
- '--dst-port', str(self._security_group_rule['from_port']),
- '--src-group', self._security_group['name'],
- self._security_group['id'],
- ]
- verifylist = [
- ('dst_port', (self._security_group_rule['from_port'],
- self._security_group_rule['to_port'])),
- ('src_group', self._security_group['name']),
- ('group', self._security_group['id']),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- # TODO(dtroyer): save this for the security group rule changes
- # self.compute.api.security_group_rule_create.assert_called_once_with(
- sgr_mock.assert_called_once_with(
- security_group_id=self._security_group['id'],
- ip_protocol=self._security_group_rule['ip_protocol'],
- from_port=self._security_group_rule['from_port'],
- to_port=self._security_group_rule['to_port'],
- remote_ip=self._security_group_rule['ip_range']['cidr'],
- remote_group=self._security_group['id'],
- )
- self.assertEqual(expected_columns, columns)
- self.assertEqual(expected_data, data)
-
def test_security_group_rule_create_remote_group(self, sgr_mock):
expected_columns, expected_data = self._setup_security_group_rule({
'from_port': 22,
@@ -221,41 +177,6 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
- def test_security_group_rule_create_source_ip(self, sgr_mock):
- expected_columns, expected_data = self._setup_security_group_rule({
- 'ip_protocol': 'icmp',
- 'from_port': -1,
- 'to_port': -1,
- 'ip_range': {'cidr': '10.0.2.0/24'},
- })
- sgr_mock.return_value = self._security_group_rule
- arglist = [
- '--protocol', self._security_group_rule['ip_protocol'],
- '--src-ip', self._security_group_rule['ip_range']['cidr'],
- self._security_group['id'],
- ]
- verifylist = [
- ('protocol', self._security_group_rule['ip_protocol']),
- ('src_ip', self._security_group_rule['ip_range']['cidr']),
- ('group', self._security_group['id']),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- # TODO(dtroyer): save this for the security group rule changes
- # self.compute.api.security_group_rule_create.assert_called_once_with(
- sgr_mock.assert_called_once_with(
- security_group_id=self._security_group['id'],
- ip_protocol=self._security_group_rule['ip_protocol'],
- from_port=self._security_group_rule['from_port'],
- to_port=self._security_group_rule['to_port'],
- remote_ip=self._security_group_rule['ip_range']['cidr'],
- remote_group=None,
- )
- self.assertEqual(expected_columns, columns)
- self.assertEqual(expected_data, data)
-
def test_security_group_rule_create_remote_ip(self, sgr_mock):
expected_columns, expected_data = self._setup_security_group_rule({
'ip_protocol': 'icmp',
@@ -301,13 +222,13 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
sgr_mock.return_value = self._security_group_rule
arglist = [
'--proto', self._security_group_rule['ip_protocol'],
- '--src-ip', self._security_group_rule['ip_range']['cidr'],
+ '--remote-ip', self._security_group_rule['ip_range']['cidr'],
self._security_group['id'],
]
verifylist = [
('proto', self._security_group_rule['ip_protocol']),
('protocol', None),
- ('src_ip', self._security_group_rule['ip_range']['cidr']),
+ ('remote_ip', self._security_group_rule['ip_range']['cidr']),
('group', self._security_group['id']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -416,6 +337,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
_security_group_rule_tcp = \
compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({
'ip_protocol': 'tcp',
+ 'ethertype': 'IPv4',
'from_port': 80,
'to_port': 80,
'group': {'name': _security_group['name']},
@@ -423,6 +345,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
_security_group_rule_icmp = \
compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({
'ip_protocol': 'icmp',
+ 'ethertype': 'IPv4',
'from_port': -1,
'to_port': -1,
'ip_range': {'cidr': '10.0.2.0/24'},
@@ -436,6 +359,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
expected_columns_with_group = (
'ID',
'IP Protocol',
+ 'Ethertype',
'IP Range',
'Port Range',
'Remote Security Group',
@@ -452,6 +376,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
expected_rule_with_group = (
rule['id'],
rule['ip_protocol'],
+ rule['ethertype'],
rule['ip_range'],
rule['port_range'],
rule['remote_security_group'],
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
index b070ab6..49c3d5d 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
@@ -99,15 +99,6 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, [], [])
- def test_create_all_source_options(self):
- arglist = [
- '--src-ip', '10.10.0.0/24',
- '--src-group', self._security_group.id,
- self._security_group.id,
- ]
- self.assertRaises(tests_utils.ParserException,
- self.check_parser, self.cmd, arglist, [])
-
def test_create_all_remote_options(self):
arglist = [
'--remote-ip', '10.10.0.0/24',
@@ -177,10 +168,12 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
def test_create_default_rule(self):
self._setup_security_group_rule({
+ 'protocol': 'tcp',
'port_range_max': 443,
'port_range_min': 443,
})
arglist = [
+ '--protocol', 'tcp',
'--dst-port', str(self._security_group_rule.port_range_min),
self._security_group.id,
]
@@ -212,13 +205,13 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
})
arglist = [
'--proto', self._security_group_rule.protocol,
- '--src-ip', self._security_group_rule.remote_ip_prefix,
+ '--remote-ip', self._security_group_rule.remote_ip_prefix,
self._security_group.id,
]
verifylist = [
('proto', self._security_group_rule.protocol),
('protocol', None),
- ('src_ip', self._security_group_rule.remote_ip_prefix),
+ ('remote_ip', self._security_group_rule.remote_ip_prefix),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -242,13 +235,13 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
})
arglist = [
'--proto', 'any',
- '--src-ip', self._security_group_rule.remote_ip_prefix,
+ '--remote-ip', self._security_group_rule.remote_ip_prefix,
self._security_group.id,
]
verifylist = [
('proto', 'any'),
('protocol', None),
- ('src_ip', self._security_group_rule.remote_ip_prefix),
+ ('remote_ip', self._security_group_rule.remote_ip_prefix),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -267,21 +260,22 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
def test_create_remote_group(self):
self._setup_security_group_rule({
+ 'protocol': 'tcp',
'port_range_max': 22,
'port_range_min': 22,
- 'remote_group_id': self._security_group.id,
})
arglist = [
+ '--protocol', 'tcp',
'--dst-port', str(self._security_group_rule.port_range_min),
'--ingress',
- '--src-group', self._security_group.name,
+ '--remote-group', self._security_group.name,
self._security_group.id,
]
verifylist = [
('dst_port', (self._security_group_rule.port_range_min,
self._security_group_rule.port_range_max)),
('ingress', True),
- ('src_group', self._security_group.name),
+ ('remote_group', self._security_group.name),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -294,7 +288,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_max': self._security_group_rule.port_range_max,
'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol,
- 'remote_group_id': self._security_group_rule.remote_group_id,
+ 'remote_group_id': self._security_group.id,
'security_group_id': self._security_group.id,
})
self.assertEqual(self.expected_columns, columns)
@@ -306,12 +300,12 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
})
arglist = [
'--ingress',
- '--src-group', self._security_group.name,
+ '--remote-group', self._security_group.name,
self._security_group.id,
]
verifylist = [
('ingress', True),
- ('src_group', self._security_group.name),
+ ('remote_group', self._security_group.name),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -335,12 +329,12 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
})
arglist = [
'--protocol', self._security_group_rule.protocol,
- '--src-ip', self._security_group_rule.remote_ip_prefix,
+ '--remote-ip', self._security_group_rule.remote_ip_prefix,
self._security_group.id,
]
verifylist = [
('protocol', self._security_group_rule.protocol),
- ('src_ip', self._security_group_rule.remote_ip_prefix),
+ ('remote_ip', self._security_group_rule.remote_ip_prefix),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -394,7 +388,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_min': 443,
'protocol': '6',
'remote_group_id': None,
- 'remote_ip_prefix': None,
+ 'remote_ip_prefix': '::/0',
})
arglist = [
'--dst-port', str(self._security_group_rule.port_range_min),
@@ -425,6 +419,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_max': self._security_group_rule.port_range_max,
'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol,
+ 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
'tenant_id': self.project.id,
})
@@ -670,6 +665,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_min': 139,
'port_range_max': 2,
'protocol': 'ipv6-icmp',
+ 'remote_ip_prefix': '::/0',
})
arglist = [
'--icmp-type', str(self._security_group_rule.port_range_min),
@@ -694,6 +690,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_min': self._security_group_rule.port_range_min,
'port_range_max': self._security_group_rule.port_range_max,
'protocol': self._security_group_rule.protocol,
+ 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
})
self.assertEqual(self.expected_columns, columns)
@@ -704,6 +701,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'ether_type': 'IPv6',
'port_range_min': 139,
'protocol': 'icmpv6',
+ 'remote_ip_prefix': '::/0',
})
arglist = [
'--icmp-type', str(self._security_group_rule.port_range_min),
@@ -726,6 +724,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'ethertype': self._security_group_rule.ether_type,
'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol,
+ 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
})
self.assertEqual(self.expected_columns, columns)
@@ -874,15 +873,16 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
expected_columns_with_group_and_long = (
'ID',
'IP Protocol',
+ 'Ethertype',
'IP Range',
'Port Range',
'Direction',
- 'Ethertype',
'Remote Security Group',
)
expected_columns_no_group = (
'ID',
'IP Protocol',
+ 'Ethertype',
'IP Range',
'Port Range',
'Remote Security Group',
@@ -895,16 +895,17 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
expected_data_with_group_and_long.append((
_security_group_rule.id,
_security_group_rule.protocol,
+ _security_group_rule.ether_type,
_security_group_rule.remote_ip_prefix,
security_group_rule._format_network_port_range(
_security_group_rule),
_security_group_rule.direction,
- _security_group_rule.ether_type,
_security_group_rule.remote_group_id,
))
expected_data_no_group.append((
_security_group_rule.id,
_security_group_rule.protocol,
+ _security_group_rule.ether_type,
_security_group_rule.remote_ip_prefix,
security_group_rule._format_network_port_range(
_security_group_rule),
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index 39cb4f5..9903b04 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -14,8 +14,8 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.network.v2 import subnet as subnet_v2
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
@@ -131,13 +131,13 @@ class TestCreateSubnet(TestSubnet):
)
self.data = (
- subnet_v2._format_allocation_pools(self._subnet.allocation_pools),
+ subnet_v2.AllocationPoolsColumn(self._subnet.allocation_pools),
self._subnet.cidr,
self._subnet.description,
- utils.format_list(self._subnet.dns_nameservers),
+ format_columns.ListColumn(self._subnet.dns_nameservers),
self._subnet.enable_dhcp,
self._subnet.gateway_ip,
- subnet_v2._format_host_routes(self._subnet.host_routes),
+ subnet_v2.HostRoutesColumn(self._subnet.host_routes),
self._subnet.id,
self._subnet.ip_version,
self._subnet.ipv6_address_mode,
@@ -146,20 +146,20 @@ class TestCreateSubnet(TestSubnet):
self._subnet.network_id,
self._subnet.project_id,
self._subnet.segment_id,
- utils.format_list(self._subnet.service_types),
+ format_columns.ListColumn(self._subnet.service_types),
self._subnet.subnetpool_id,
- utils.format_list(self._subnet.tags),
+ format_columns.ListColumn(self._subnet.tags),
)
self.data_subnet_pool = (
- subnet_v2._format_allocation_pools(
+ subnet_v2.AllocationPoolsColumn(
self._subnet_from_pool.allocation_pools),
self._subnet_from_pool.cidr,
self._subnet_from_pool.description,
- utils.format_list(self._subnet_from_pool.dns_nameservers),
+ format_columns.ListColumn(self._subnet_from_pool.dns_nameservers),
self._subnet_from_pool.enable_dhcp,
self._subnet_from_pool.gateway_ip,
- subnet_v2._format_host_routes(self._subnet_from_pool.host_routes),
+ subnet_v2.HostRoutesColumn(self._subnet_from_pool.host_routes),
self._subnet_from_pool.id,
self._subnet_from_pool.ip_version,
self._subnet_from_pool.ipv6_address_mode,
@@ -168,20 +168,20 @@ class TestCreateSubnet(TestSubnet):
self._subnet_from_pool.network_id,
self._subnet_from_pool.project_id,
self._subnet_from_pool.segment_id,
- utils.format_list(self._subnet_from_pool.service_types),
+ format_columns.ListColumn(self._subnet_from_pool.service_types),
self._subnet_from_pool.subnetpool_id,
- utils.format_list(self._subnet.tags),
+ format_columns.ListColumn(self._subnet_from_pool.tags),
)
self.data_ipv6 = (
- subnet_v2._format_allocation_pools(
+ subnet_v2.AllocationPoolsColumn(
self._subnet_ipv6.allocation_pools),
self._subnet_ipv6.cidr,
self._subnet_ipv6.description,
- utils.format_list(self._subnet_ipv6.dns_nameservers),
+ format_columns.ListColumn(self._subnet_ipv6.dns_nameservers),
self._subnet_ipv6.enable_dhcp,
self._subnet_ipv6.gateway_ip,
- subnet_v2._format_host_routes(self._subnet_ipv6.host_routes),
+ subnet_v2.HostRoutesColumn(self._subnet_ipv6.host_routes),
self._subnet_ipv6.id,
self._subnet_ipv6.ip_version,
self._subnet_ipv6.ipv6_address_mode,
@@ -190,9 +190,9 @@ class TestCreateSubnet(TestSubnet):
self._subnet_ipv6.network_id,
self._subnet_ipv6.project_id,
self._subnet_ipv6.segment_id,
- utils.format_list(self._subnet_ipv6.service_types),
+ format_columns.ListColumn(self._subnet_ipv6.service_types),
self._subnet_ipv6.subnetpool_id,
- utils.format_list(self._subnet.tags),
+ format_columns.ListColumn(self._subnet_ipv6.tags),
)
def setUp(self):
@@ -255,7 +255,7 @@ class TestCreateSubnet(TestSubnet):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_from_subnet_pool_options(self):
# Mock SDK calls for this test.
@@ -317,7 +317,7 @@ class TestCreateSubnet(TestSubnet):
'service_types': self._subnet_from_pool.service_types,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data_subnet_pool, data)
+ self.assertItemEqual(self.data_subnet_pool, data)
def test_create_options_subnet_range_ipv6(self):
# Mock SDK calls for this test.
@@ -390,7 +390,7 @@ class TestCreateSubnet(TestSubnet):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data_ipv6, data)
+ self.assertItemEqual(self.data_ipv6, data)
def test_create_with_network_segment(self):
# Mock SDK calls for this test.
@@ -424,7 +424,7 @@ class TestCreateSubnet(TestSubnet):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_description(self):
# Mock SDK calls for this test.
@@ -458,7 +458,7 @@ class TestCreateSubnet(TestSubnet):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def _test_create_with_tag(self, add_tags=True):
arglist = [
@@ -497,7 +497,7 @@ class TestCreateSubnet(TestSubnet):
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
@@ -625,13 +625,13 @@ class TestListSubnet(TestSubnet):
subnet.cidr,
subnet.tenant_id,
subnet.enable_dhcp,
- utils.format_list(subnet.dns_nameservers),
- subnet_v2._format_allocation_pools(subnet.allocation_pools),
- utils.format_list(subnet.host_routes),
+ format_columns.ListColumn(subnet.dns_nameservers),
+ subnet_v2.AllocationPoolsColumn(subnet.allocation_pools),
+ subnet_v2.HostRoutesColumn(subnet.host_routes),
subnet.ip_version,
subnet.gateway_ip,
- utils.format_list(subnet.service_types),
- utils.format_list(subnet.tags),
+ format_columns.ListColumn(subnet.service_types),
+ format_columns.ListColumn(subnet.tags),
))
def setUp(self):
@@ -653,7 +653,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_long(self):
arglist = [
@@ -668,7 +668,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_subnet_list_ip_version(self):
arglist = [
@@ -684,7 +684,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_dhcp(self):
arglist = [
@@ -700,7 +700,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_no_dhcp(self):
arglist = [
@@ -716,7 +716,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_service_type(self):
arglist = [
@@ -731,7 +731,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_project(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -749,7 +749,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_service_type_multiple(self):
arglist = [
@@ -767,7 +767,7 @@ class TestListSubnet(TestSubnet):
'network:floatingip_agent_gateway']}
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_project_domain(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -787,7 +787,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_network(self):
network = network_fakes.FakeNetwork.create_one_network()
@@ -805,7 +805,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_gateway(self):
subnet = network_fakes.FakeSubnet.create_one_subnet()
@@ -823,7 +823,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_name(self):
subnet = network_fakes.FakeSubnet.create_one_subnet()
@@ -841,7 +841,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_list_subnet_range(self):
subnet = network_fakes.FakeSubnet.create_one_subnet()
@@ -859,7 +859,7 @@ class TestListSubnet(TestSubnet):
self.network.subnets.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_with_tag_options(self):
arglist = [
@@ -1154,13 +1154,13 @@ class TestShowSubnet(TestSubnet):
)
data = (
- subnet_v2._format_allocation_pools(_subnet.allocation_pools),
+ subnet_v2.AllocationPoolsColumn(_subnet.allocation_pools),
_subnet.cidr,
_subnet.description,
- utils.format_list(_subnet.dns_nameservers),
+ format_columns.ListColumn(_subnet.dns_nameservers),
_subnet.enable_dhcp,
_subnet.gateway_ip,
- utils.format_list(_subnet.host_routes),
+ subnet_v2.HostRoutesColumn(_subnet.host_routes),
_subnet.id,
_subnet.ip_version,
_subnet.ipv6_address_mode,
@@ -1169,9 +1169,9 @@ class TestShowSubnet(TestSubnet):
_subnet.network_id,
_subnet.tenant_id,
_subnet.segment_id,
- utils.format_list(_subnet.service_types),
+ format_columns.ListColumn(_subnet.service_types),
_subnet.subnetpool_id,
- utils.format_list(_subnet.tags),
+ format_columns.ListColumn(_subnet.tags),
)
def setUp(self):
@@ -1206,7 +1206,7 @@ class TestShowSubnet(TestSubnet):
self._subnet.name, ignore_missing=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestUnsetSubnet(TestSubnet):
diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py
index 3d8f102..2271c08 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py
@@ -15,8 +15,9 @@ import argparse
import mock
from mock import call
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.network.v2 import subnet_pool
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
@@ -73,10 +74,10 @@ class TestCreateSubnetPool(TestSubnetPool):
_subnet_pool.max_prefixlen,
_subnet_pool.min_prefixlen,
_subnet_pool.name,
- utils.format_list(_subnet_pool.prefixes),
+ format_columns.ListColumn(_subnet_pool.prefixes),
_subnet_pool.project_id,
_subnet_pool.shared,
- utils.format_list(_subnet_pool.tags),
+ format_columns.ListColumn(_subnet_pool.tags),
)
def setUp(self):
@@ -133,7 +134,7 @@ class TestCreateSubnetPool(TestSubnetPool):
})
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_prefixlen_options(self):
arglist = [
@@ -163,7 +164,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'name': self._subnet_pool.name,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_len_negative(self):
arglist = [
@@ -201,7 +202,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'name': self._subnet_pool.name,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_address_scope_option(self):
arglist = [
@@ -224,7 +225,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'name': self._subnet_pool.name,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_default_and_shared_options(self):
arglist = [
@@ -250,7 +251,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'shared': True,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_description(self):
arglist = [
@@ -273,7 +274,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'description': self._subnet_pool.description,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_default_quota(self):
arglist = [
@@ -294,7 +295,7 @@ class TestCreateSubnetPool(TestSubnetPool):
'default_quota': 10,
})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def _test_create_with_tag(self, add_tags=True):
arglist = [
@@ -328,7 +329,7 @@ class TestCreateSubnetPool(TestSubnetPool):
else:
self.assertFalse(self.network.set_tags.called)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_create_with_tags(self):
self._test_create_with_tag(add_tags=True)
@@ -441,7 +442,7 @@ class TestListSubnetPool(TestSubnetPool):
data.append((
pool.id,
pool.name,
- utils.format_list(pool.prefixes),
+ format_columns.ListColumn(pool.prefixes),
))
data_long = []
@@ -449,12 +450,12 @@ class TestListSubnetPool(TestSubnetPool):
data_long.append((
pool.id,
pool.name,
- utils.format_list(pool.prefixes),
+ format_columns.ListColumn(pool.prefixes),
pool.default_prefixlen,
pool.address_scope_id,
pool.is_default,
pool.shared,
- utils.format_list(pool.tags),
+ format_columns.ListColumn(pool.tags),
))
def setUp(self):
@@ -476,7 +477,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_long(self):
arglist = [
@@ -491,7 +492,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_subnet_pool_list_no_share(self):
arglist = [
@@ -507,7 +508,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_share(self):
arglist = [
@@ -523,7 +524,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_no_default(self):
arglist = [
@@ -539,7 +540,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_default(self):
arglist = [
@@ -555,7 +556,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_project(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -573,7 +574,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_project_domain(self):
project = identity_fakes_v3.FakeProject.create_one_project()
@@ -593,7 +594,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_name(self):
subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
@@ -611,7 +612,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_subnet_pool_list_address_scope(self):
addr_scope = network_fakes.FakeAddressScope.create_one_address_scope()
@@ -629,7 +630,7 @@ class TestListSubnetPool(TestSubnetPool):
self.network.subnet_pools.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_list_with_tag_options(self):
arglist = [
@@ -654,7 +655,7 @@ class TestListSubnetPool(TestSubnetPool):
'not_any_tags': 'black,white'}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
class TestSetSubnetPool(TestSubnetPool):
@@ -969,10 +970,10 @@ class TestShowSubnetPool(TestSubnetPool):
_subnet_pool.max_prefixlen,
_subnet_pool.min_prefixlen,
_subnet_pool.name,
- utils.format_list(_subnet_pool.prefixes),
+ format_columns.ListColumn(_subnet_pool.prefixes),
_subnet_pool.tenant_id,
_subnet_pool.shared,
- utils.format_list(_subnet_pool.tags),
+ format_columns.ListColumn(_subnet_pool.tags),
)
def setUp(self):
@@ -1008,7 +1009,7 @@ class TestShowSubnetPool(TestSubnetPool):
ignore_missing=False
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestUnsetSubnetPool(TestSubnetPool):
diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py
index 5d413e7..31675c4 100644
--- a/openstackclient/tests/unit/test_shell.py
+++ b/openstackclient/tests/unit/test_shell.py
@@ -153,7 +153,7 @@ class TestShell(osc_lib_test_utils.TestShell):
# released in osc-lib
self.shell_class = importutils.import_class(self.shell_class_name)
- def _assert_token_endpoint_auth(self, cmd_options, default_args):
+ def _assert_admin_token_auth(self, cmd_options, default_args):
with mock.patch(
self.shell_class_name + ".initialize_app",
self.app,
@@ -172,9 +172,9 @@ class TestShell(osc_lib_test_utils.TestShell):
"token",
)
self.assertEqual(
- default_args.get("url", ''),
- _shell.options.url,
- "url",
+ default_args.get("endpoint", ''),
+ _shell.options.endpoint,
+ "endpoint",
)
def _assert_token_auth(self, cmd_options, default_args):
@@ -338,7 +338,7 @@ class TestShellTokenEndpointAuthEnv(TestShell):
super(TestShellTokenEndpointAuthEnv, self).setUp()
env = {
"OS_TOKEN": DEFAULT_TOKEN,
- "OS_URL": DEFAULT_SERVICE_URL,
+ "OS_ENDPOINT": DEFAULT_SERVICE_URL,
}
self.useFixture(osc_lib_test_utils.EnvFixture(env.copy()))
@@ -346,23 +346,23 @@ class TestShellTokenEndpointAuthEnv(TestShell):
flag = ""
kwargs = {
"token": DEFAULT_TOKEN,
- "url": DEFAULT_SERVICE_URL,
+ "endpoint": DEFAULT_SERVICE_URL,
}
- self._assert_token_endpoint_auth(flag, kwargs)
+ self._assert_admin_token_auth(flag, kwargs)
def test_only_token(self):
flag = "--os-token xyzpdq"
kwargs = {
"token": "xyzpdq",
- "url": DEFAULT_SERVICE_URL,
+ "endpoint": DEFAULT_SERVICE_URL,
}
self._assert_token_auth(flag, kwargs)
def test_only_url(self):
- flag = "--os-url http://cloud.local:555"
+ flag = "--os-endpoint http://cloud.local:555"
kwargs = {
"token": DEFAULT_TOKEN,
- "url": "http://cloud.local:555",
+ "endpoint": "http://cloud.local:555",
}
self._assert_token_auth(flag, kwargs)
@@ -371,7 +371,7 @@ class TestShellTokenEndpointAuthEnv(TestShell):
flag = ""
kwargs = {
"token": '',
- "url": '',
+ "endpoint": '',
}
self._assert_token_auth(flag, kwargs)
diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py
index 926dad8..c15d8bb 100644
--- a/openstackclient/tests/unit/utils.py
+++ b/openstackclient/tests/unit/utils.py
@@ -19,6 +19,8 @@ import os
import fixtures
import testtools
+from cliff import columns as cliff_columns
+
from openstackclient.tests.unit import fakes
@@ -80,3 +82,18 @@ class TestCommand(TestCase):
self.assertIn(attr, parsed_args)
self.assertEqual(value, getattr(parsed_args, attr))
return parsed_args
+
+ def assertListItemEqual(self, expected, actual):
+ self.assertEqual(len(expected), len(actual))
+ for item_expected, item_actual in zip(expected, actual):
+ self.assertItemEqual(item_expected, item_actual)
+
+ def assertItemEqual(self, expected, actual):
+ self.assertEqual(len(expected), len(actual))
+ for col_expected, col_actual in zip(expected, actual):
+ if isinstance(col_expected, cliff_columns.FormattableColumn):
+ self.assertIsInstance(col_actual, col_expected.__class__)
+ self.assertEqual(col_expected.human_readable(),
+ col_actual.human_readable())
+ else:
+ self.assertEqual(col_expected, col_actual)
diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py
index 442840f..11dc808 100644
--- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py
+++ b/openstackclient/tests/unit/volume/v1/test_qos_specs.py
@@ -17,6 +17,8 @@ import copy
import mock
from mock import call
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -85,7 +87,7 @@ class TestQosCreate(TestQos):
self.new_qos_spec.consumer,
self.new_qos_spec.id,
self.new_qos_spec.name,
- utils.format_dict(self.new_qos_spec.specs)
+ format_columns.DictColumn(self.new_qos_spec.specs)
)
self.qos_mock.create.return_value = self.new_qos_spec
# Get the command object to test
@@ -108,7 +110,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_qos_create_with_consumer(self):
arglist = [
@@ -128,7 +130,7 @@ class TestQosCreate(TestQos):
{'consumer': self.new_qos_spec.consumer}
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_qos_create_with_properties(self):
arglist = [
@@ -154,7 +156,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
class TestQosDelete(TestQos):
@@ -326,8 +328,8 @@ class TestQosList(TestQos):
q.id,
q.name,
q.consumer,
- qos_association.name,
- utils.format_dict(q.specs),
+ format_columns.ListColumn([qos_association.name]),
+ format_columns.DictColumn(q.specs),
))
def setUp(self):
@@ -349,7 +351,7 @@ class TestQosList(TestQos):
self.qos_mock.list.assert_called_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_qos_list_no_association(self):
self.qos_mock.reset_mock()
@@ -373,10 +375,10 @@ class TestQosList(TestQos):
self.qos_specs[1].id,
self.qos_specs[1].name,
self.qos_specs[1].consumer,
- None,
- utils.format_dict(self.qos_specs[1].specs),
+ format_columns.ListColumn(None),
+ format_columns.DictColumn(self.qos_specs[1].specs),
)
- self.assertEqual(ex_data, list(data))
+ self.assertListItemEqual(ex_data, list(data))
class TestQosSet(TestQos):
@@ -447,13 +449,13 @@ class TestQosShow(TestQos):
)
self.assertEqual(collist, columns)
datalist = (
- self.qos_association.name,
+ format_columns.ListColumn([self.qos_association.name]),
self.qos_spec.consumer,
self.qos_spec.id,
self.qos_spec.name,
- utils.format_dict(self.qos_spec.specs),
+ format_columns.DictColumn(self.qos_spec.specs),
)
- self.assertEqual(datalist, tuple(data))
+ self.assertItemEqual(datalist, tuple(data))
class TestQosUnset(TestQos):
diff --git a/openstackclient/tests/unit/volume/v1/test_snapshot.py b/openstackclient/tests/unit/volume/v1/test_snapshot.py
deleted file mode 100644
index 70b55ce..0000000
--- a/openstackclient/tests/unit/volume/v1/test_snapshot.py
+++ /dev/null
@@ -1,580 +0,0 @@
-#
-# 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 mock
-from mock import call
-
-from osc_lib import exceptions
-from osc_lib import utils
-
-from openstackclient.tests.unit import utils as tests_utils
-from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
-from openstackclient.volume.v1 import volume_snapshot
-
-
-class TestSnapshot(volume_fakes.TestVolumev1):
-
- def setUp(self):
- super(TestSnapshot, self).setUp()
-
- self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
- self.snapshots_mock.reset_mock()
- self.volumes_mock = self.app.client_manager.volume.volumes
- self.volumes_mock.reset_mock()
-
-
-class TestSnapshotCreate(TestSnapshot):
-
- columns = (
- 'created_at',
- 'display_description',
- 'display_name',
- 'id',
- 'properties',
- 'size',
- 'status',
- 'volume_id',
- )
-
- def setUp(self):
- super(TestSnapshotCreate, self).setUp()
-
- self.volume = volume_fakes.FakeVolume.create_one_volume()
- self.new_snapshot = volume_fakes.FakeSnapshot.create_one_snapshot(
- attrs={'volume_id': self.volume.id})
-
- self.data = (
- self.new_snapshot.created_at,
- self.new_snapshot.display_description,
- self.new_snapshot.display_name,
- self.new_snapshot.id,
- utils.format_dict(self.new_snapshot.metadata),
- self.new_snapshot.size,
- self.new_snapshot.status,
- self.new_snapshot.volume_id,
- )
-
- self.volumes_mock.get.return_value = self.volume
- self.snapshots_mock.create.return_value = self.new_snapshot
- # Get the command object to test
- self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
-
- def test_snapshot_create(self):
- arglist = [
- "--volume", self.new_snapshot.volume_id,
- "--description", self.new_snapshot.display_description,
- "--force",
- self.new_snapshot.display_name,
- ]
- verifylist = [
- ("volume", self.new_snapshot.volume_id),
- ("description", self.new_snapshot.display_description),
- ("force", True),
- ("snapshot_name", self.new_snapshot.display_name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.create.assert_called_with(
- self.new_snapshot.volume_id,
- True,
- self.new_snapshot.display_name,
- self.new_snapshot.display_description,
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
- def test_snapshot_create_without_name(self):
- arglist = [
- "--volume", self.new_snapshot.volume_id,
- ]
- verifylist = [
- ("volume", self.new_snapshot.volume_id),
- ]
- self.assertRaises(
- tests_utils.ParserException,
- self.check_parser,
- self.cmd,
- arglist,
- verifylist,
- )
-
- def test_snapshot_create_without_volume(self):
- arglist = [
- "--description", self.new_snapshot.display_description,
- "--force",
- self.new_snapshot.display_name
- ]
- verifylist = [
- ("description", self.new_snapshot.display_description),
- ("force", True),
- ("snapshot_name", self.new_snapshot.display_name)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.volumes_mock.get.assert_called_once_with(
- self.new_snapshot.display_name)
- self.snapshots_mock.create.assert_called_once_with(
- self.new_snapshot.volume_id,
- True,
- self.new_snapshot.display_name,
- self.new_snapshot.display_description,
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
-
-class TestSnapshotDelete(TestSnapshot):
-
- snapshots = volume_fakes.FakeSnapshot.create_snapshots(count=2)
-
- def setUp(self):
- super(TestSnapshotDelete, self).setUp()
-
- self.snapshots_mock.get = (
- volume_fakes.FakeSnapshot.get_snapshots(self.snapshots))
- self.snapshots_mock.delete.return_value = None
-
- # Get the command object to mock
- self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)
-
- def test_snapshot_delete(self):
- arglist = [
- self.snapshots[0].id
- ]
- verifylist = [
- ("snapshots", [self.snapshots[0].id])
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.delete.assert_called_with(
- self.snapshots[0].id)
- self.assertIsNone(result)
-
- def test_delete_multiple_snapshots(self):
- arglist = []
- for s in self.snapshots:
- arglist.append(s.id)
- verifylist = [
- ('snapshots', arglist),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
-
- calls = []
- for s in self.snapshots:
- calls.append(call(s.id))
- self.snapshots_mock.delete.assert_has_calls(calls)
- self.assertIsNone(result)
-
- def test_delete_multiple_snapshots_with_exception(self):
- arglist = [
- self.snapshots[0].id,
- 'unexist_snapshot',
- ]
- verifylist = [
- ('snapshots', arglist),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- find_mock_result = [self.snapshots[0], exceptions.CommandError]
- with mock.patch.object(utils, 'find_resource',
- side_effect=find_mock_result) as find_mock:
- try:
- self.cmd.take_action(parsed_args)
- self.fail('CommandError should be raised.')
- except exceptions.CommandError as e:
- self.assertEqual('1 of 2 snapshots failed to delete.',
- str(e))
-
- find_mock.assert_any_call(
- self.snapshots_mock, self.snapshots[0].id)
- find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot')
-
- self.assertEqual(2, find_mock.call_count)
- self.snapshots_mock.delete.assert_called_once_with(
- self.snapshots[0].id
- )
-
-
-class TestSnapshotList(TestSnapshot):
-
- volume = volume_fakes.FakeVolume.create_one_volume()
- snapshots = volume_fakes.FakeSnapshot.create_snapshots(
- attrs={'volume_id': volume.display_name}, count=3)
-
- columns = [
- "ID",
- "Name",
- "Description",
- "Status",
- "Size"
- ]
- columns_long = columns + [
- "Created At",
- "Volume",
- "Properties"
- ]
-
- data = []
- for s in snapshots:
- data.append((
- s.id,
- s.display_name,
- s.display_description,
- s.status,
- s.size,
- ))
- data_long = []
- for s in snapshots:
- data_long.append((
- s.id,
- s.display_name,
- s.display_description,
- s.status,
- s.size,
- s.created_at,
- s.volume_id,
- utils.format_dict(s.metadata),
- ))
-
- def setUp(self):
- super(TestSnapshotList, self).setUp()
-
- self.volumes_mock.list.return_value = [self.volume]
- self.volumes_mock.get.return_value = self.volume
- self.snapshots_mock.list.return_value = self.snapshots
- # Get the command to test
- self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
-
- def test_snapshot_list_without_options(self):
- arglist = []
- verifylist = [
- ('all_projects', False),
- ("long", False)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- search_opts={
- 'all_tenants': False,
- 'display_name': None,
- 'status': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_with_long(self):
- arglist = [
- "--long",
- ]
- verifylist = [
- ("long", True),
- ('all_projects', False),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- search_opts={
- 'all_tenants': False,
- 'display_name': None,
- 'status': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
-
- def test_snapshot_list_name_option(self):
- arglist = [
- '--name', self.snapshots[0].display_name,
- ]
- verifylist = [
- ('all_projects', False),
- ('long', False),
- ('name', self.snapshots[0].display_name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- search_opts={
- 'all_tenants': False,
- 'display_name': self.snapshots[0].display_name,
- 'status': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_status_option(self):
- arglist = [
- '--status', self.snapshots[0].status,
- ]
- verifylist = [
- ('all_projects', False),
- ('long', False),
- ('status', self.snapshots[0].status),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- search_opts={
- 'all_tenants': False,
- 'display_name': None,
- 'status': self.snapshots[0].status,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_volumeid_option(self):
- arglist = [
- '--volume', self.volume.id,
- ]
- verifylist = [
- ('all_projects', False),
- ('long', False),
- ('volume', self.volume.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- search_opts={
- 'all_tenants': False,
- 'display_name': None,
- 'status': None,
- 'volume_id': self.volume.id
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_all_projects(self):
- arglist = [
- '--all-projects',
- ]
- verifylist = [
- ('long', False),
- ('all_projects', True)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- search_opts={
- 'all_tenants': True,
- 'display_name': None,
- 'status': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
-
-class TestSnapshotSet(TestSnapshot):
-
- snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
-
- def setUp(self):
- super(TestSnapshotSet, self).setUp()
-
- self.snapshots_mock.get.return_value = self.snapshot
- self.snapshots_mock.set_metadata.return_value = None
- # Get the command object to mock
- self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
-
- def test_snapshot_set_all(self):
- arglist = [
- "--name", "new_snapshot",
- "--description", "new_description",
- "--property", "foo_1=foo_1",
- "--property", "foo_2=foo_2",
- "--no-property",
- self.snapshot.id,
- ]
- new_property = {"foo_1": "foo_1", "foo_2": "foo_2"}
- verifylist = [
- ("name", "new_snapshot"),
- ("description", "new_description"),
- ("property", new_property),
- ("no_property", True),
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- kwargs = {
- "display_name": "new_snapshot",
- "display_description": "new_description",
- }
- self.snapshot.update.assert_called_with(**kwargs)
- self.snapshots_mock.delete_metadata.assert_called_with(
- self.snapshot.id, ["foo"]
- )
- self.snapshots_mock.set_metadata.assert_called_with(
- self.snapshot.id, {"foo_2": "foo_2", "foo_1": "foo_1"}
- )
- self.assertIsNone(result)
-
- def test_snapshot_set_nothing(self):
- arglist = [
- self.snapshot.id,
- ]
- verifylist = [
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
- self.assertIsNone(result)
-
- def test_snapshot_set_fail(self):
- self.snapshots_mock.set_metadata.side_effect = (
- exceptions.CommandError())
- arglist = [
- "--name", "new_snapshot",
- "--description", "new_description",
- "--property", "x=y",
- "--property", "foo=foo",
- self.snapshot.id,
- ]
- new_property = {"x": "y", "foo": "foo"}
- verifylist = [
- ("name", "new_snapshot"),
- ("description", "new_description"),
- ("property", new_property),
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action, parsed_args)
-
-
-class TestSnapshotShow(TestSnapshot):
-
- columns = (
- 'created_at',
- 'display_description',
- 'display_name',
- 'id',
- 'properties',
- 'size',
- 'status',
- 'volume_id',
- )
-
- def setUp(self):
- super(TestSnapshotShow, self).setUp()
-
- self.snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
-
- self.data = (
- self.snapshot.created_at,
- self.snapshot.display_description,
- self.snapshot.display_name,
- self.snapshot.id,
- utils.format_dict(self.snapshot.metadata),
- self.snapshot.size,
- self.snapshot.status,
- self.snapshot.volume_id,
- )
-
- self.snapshots_mock.get.return_value = self.snapshot
- # Get the command object to test
- self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
-
- def test_snapshot_show(self):
- arglist = [
- self.snapshot.id
- ]
- verifylist = [
- ("snapshot", self.snapshot.id)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
- self.snapshots_mock.get.assert_called_with(self.snapshot.id)
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
-
-class TestSnapshotUnset(TestSnapshot):
-
- snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
-
- def setUp(self):
- super(TestSnapshotUnset, self).setUp()
-
- self.snapshots_mock.get.return_value = self.snapshot
- self.snapshots_mock.delete_metadata.return_value = None
- # Get the command object to mock
- self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
-
- def test_snapshot_unset(self):
- arglist = [
- "--property", "foo",
- self.snapshot.id,
- ]
- verifylist = [
- ("property", ["foo"]),
- ("snapshot", self.snapshot.id),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.delete_metadata.assert_called_with(
- self.snapshot.id, ["foo"]
- )
- self.assertIsNone(result)
-
- def test_snapshot_unset_nothing(self):
- arglist = [
- self.snapshot.id,
- ]
- verifylist = [
- ("snapshot", self.snapshot.id),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
- self.assertIsNone(result)
diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py
index 4c013dc..680561d 100644
--- a/openstackclient/tests/unit/volume/v1/test_transfer_request.py
+++ b/openstackclient/tests/unit/volume/v1/test_transfer_request.py
@@ -85,26 +85,6 @@ class TestTransferAccept(TestTransfer):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
- def test_transfer_accept_deprecated(self):
- arglist = [
- self.volume_transfer.id,
- 'key_value',
- ]
- verifylist = [
- ('transfer_request', self.volume_transfer.id),
- ('old_auth_key', 'key_value'),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.transfer_mock.accept.assert_called_once_with(
- self.volume_transfer.id,
- 'key_value',
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
def test_transfer_accept_no_option(self):
arglist = [
self.volume_transfer.id,
diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py
index dcdd3d5..beff833 100644
--- a/openstackclient/tests/unit/volume/v1/test_type.py
+++ b/openstackclient/tests/unit/volume/v1/test_type.py
@@ -15,6 +15,7 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -77,7 +78,7 @@ class TestTypeCreate(TestType):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_type_create_with_encryption(self):
encryption_info = {
@@ -102,7 +103,7 @@ class TestTypeCreate(TestType):
)
encryption_data = (
self.new_volume_type.description,
- utils.format_dict(encryption_info),
+ format_columns.DictColumn(encryption_info),
self.new_volume_type.id,
True,
self.new_volume_type.name,
@@ -138,7 +139,7 @@ class TestTypeCreate(TestType):
body,
)
self.assertEqual(encryption_columns, columns)
- self.assertEqual(encryption_data, data)
+ self.assertItemEqual(encryption_data, data)
class TestTypeDelete(TestType):
@@ -246,7 +247,7 @@ class TestTypeList(TestType):
t.id,
t.name,
t.is_public,
- utils.format_dict(t.extra_specs),
+ format_columns.DictColumn(t.extra_specs),
))
def setUp(self):
@@ -269,7 +270,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_type_list_with_options(self):
arglist = [
@@ -283,7 +284,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_type_list_with_encryption(self):
encryption_type = volume_fakes.FakeType.create_one_encryption_type(
@@ -302,13 +303,16 @@ class TestTypeList(TestType):
self.volume_types[0].id,
self.volume_types[0].name,
self.volume_types[0].is_public,
- utils.format_dict(encryption_info),
+ volume_type.EncryptionInfoColumn(
+ self.volume_types[0].id,
+ {self.volume_types[0].id: encryption_info}),
))
encryption_data.append((
self.volume_types[1].id,
self.volume_types[1].name,
self.volume_types[1].is_public,
- '-',
+ volume_type.EncryptionInfoColumn(
+ self.volume_types[1].id, {}),
))
self.encryption_types_mock.list.return_value = [encryption_type]
@@ -324,7 +328,7 @@ class TestTypeList(TestType):
self.encryption_types_mock.list.assert_called_once_with()
self.types_mock.list.assert_called_once_with()
self.assertEqual(encryption_columns, columns)
- self.assertEqual(encryption_data, list(data))
+ self.assertListItemEqual(encryption_data, list(data))
class TestTypeSet(TestType):
@@ -443,7 +447,7 @@ class TestTypeShow(TestType):
self.volume_type.id,
True,
self.volume_type.name,
- utils.format_dict(self.volume_type.extra_specs)
+ format_columns.DictColumn(self.volume_type.extra_specs)
)
self.types_mock.get.return_value = self.volume_type
@@ -465,7 +469,7 @@ class TestTypeShow(TestType):
self.types_mock.get.assert_called_with(self.volume_type.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_type_show_with_encryption(self):
encryption_type = volume_fakes.FakeType.create_one_encryption_type()
@@ -489,11 +493,11 @@ class TestTypeShow(TestType):
)
encryption_data = (
self.volume_type.description,
- utils.format_dict(encryption_info),
+ format_columns.DictColumn(encryption_info),
self.volume_type.id,
True,
self.volume_type.name,
- utils.format_dict(self.volume_type.extra_specs)
+ format_columns.DictColumn(self.volume_type.extra_specs)
)
arglist = [
'--encryption-type',
@@ -509,7 +513,7 @@ class TestTypeShow(TestType):
self.types_mock.get.assert_called_with(self.volume_type.id)
self.encryption_types_mock.get.assert_called_with(self.volume_type.id)
self.assertEqual(encryption_columns, columns)
- self.assertEqual(encryption_data, data)
+ self.assertItemEqual(encryption_data, data)
class TestTypeUnset(TestType):
@@ -587,3 +591,30 @@ class TestTypeUnset(TestType):
result = self.cmd.take_action(parsed_args)
self.encryption_types_mock.delete.assert_called_with(self.volume_type)
self.assertIsNone(result)
+
+
+class TestColumns(TestType):
+
+ def test_encryption_info_column_with_info(self):
+ fake_volume_type = volume_fakes.FakeType.create_one_type()
+ type_id = fake_volume_type.id
+
+ encryption_info = {
+ 'provider': 'LuksEncryptor',
+ 'cipher': None,
+ 'key_size': None,
+ 'control_location': 'front-end',
+ }
+ col = volume_type.EncryptionInfoColumn(type_id,
+ {type_id: encryption_info})
+ self.assertEqual(utils.format_dict(encryption_info),
+ col.human_readable())
+ self.assertEqual(encryption_info, col.machine_readable())
+
+ def test_encryption_info_column_without_info(self):
+ fake_volume_type = volume_fakes.FakeType.create_one_type()
+ type_id = fake_volume_type.id
+
+ col = volume_type.EncryptionInfoColumn(type_id, {})
+ self.assertEqual('-', col.human_readable())
+ self.assertIsNone(col.machine_readable())
diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py
index eee5acd..c415455 100644
--- a/openstackclient/tests/unit/volume/v1/test_volume.py
+++ b/openstackclient/tests/unit/volume/v1/test_volume.py
@@ -17,6 +17,8 @@ import argparse
import mock
from mock import call
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -88,7 +90,7 @@ class TestVolumeCreate(TestVolume):
self.new_volume.display_description,
self.new_volume.id,
self.new_volume.display_name,
- utils.format_dict(self.new_volume.metadata),
+ format_columns.DictColumn(self.new_volume.metadata),
self.new_volume.size,
self.new_volume.snapshot_id,
self.new_volume.status,
@@ -134,7 +136,7 @@ class TestVolumeCreate(TestVolume):
None,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_options(self):
arglist = [
@@ -178,7 +180,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_user_project_id(self):
# Return a project
@@ -225,7 +227,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_user_project_name(self):
# Return a project
@@ -272,7 +274,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_properties(self):
arglist = [
@@ -313,7 +315,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_image_id(self):
image = image_fakes.FakeImage.create_one_image()
@@ -356,7 +358,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_image_name(self):
image = image_fakes.FakeImage.create_one_image()
@@ -399,7 +401,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_with_source(self):
self.volumes_mock.get.return_value = self.new_volume
@@ -429,7 +431,7 @@ class TestVolumeCreate(TestVolume):
None,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_with_bootable_and_readonly(self):
arglist = [
@@ -467,7 +469,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, True)
self.volumes_mock.update_readonly_flag.assert_called_with(
@@ -509,7 +511,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, False)
self.volumes_mock.update_readonly_flag.assert_called_with(
@@ -561,7 +563,7 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(2, mock_error.call_count)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, True)
self.volumes_mock.update_readonly_flag.assert_called_with(
@@ -732,16 +734,13 @@ class TestVolumeList(TestVolume):
'Size',
'Attached to',
)
- server = _volume.attachments[0]['server_id']
- device = _volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = (
(
_volume.id,
_volume.display_name,
_volume.status,
_volume.size,
- msg,
+ volume.AttachmentsColumn(_volume.attachments),
),
)
@@ -767,7 +766,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_name(self):
arglist = [
@@ -784,7 +783,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, tuple(columns))
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_status(self):
arglist = [
@@ -801,7 +800,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, tuple(columns))
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_all_projects(self):
arglist = [
@@ -818,7 +817,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, tuple(columns))
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_long(self):
arglist = [
@@ -855,10 +854,10 @@ class TestVolumeList(TestVolume):
self._volume.size,
self._volume.volume_type,
self._volume.bootable,
- self.msg,
- utils.format_dict(self._volume.metadata),
+ volume.AttachmentsColumn(self._volume.attachments),
+ format_columns.DictColumn(self._volume.metadata),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_with_limit(self):
arglist = [
@@ -883,7 +882,7 @@ class TestVolumeList(TestVolume):
'all_tenants': False, }
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, tuple(data))
+ self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_negative_limit(self):
arglist = [
@@ -1251,7 +1250,7 @@ class TestVolumeShow(TestVolume):
self._volume.display_description,
self._volume.id,
self._volume.display_name,
- utils.format_dict(self._volume.metadata),
+ format_columns.DictColumn(self._volume.metadata),
self._volume.size,
self._volume.snapshot_id,
self._volume.status,
@@ -1274,7 +1273,7 @@ class TestVolumeShow(TestVolume):
self.volumes_mock.get.assert_called_with(self._volume.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_show_backward_compatibility(self):
arglist = [
@@ -1339,3 +1338,31 @@ class TestVolumeUnset(TestVolume):
self._volume.id, ['myprop']
)
self.assertIsNone(result)
+
+
+class TestColumns(TestVolume):
+
+ def test_attachments_column_without_server_cache(self):
+ _volume = volume_fakes.FakeVolume.create_one_volume()
+ server_id = _volume.attachments[0]['server_id']
+ device = _volume.attachments[0]['device']
+
+ col = volume.AttachmentsColumn(_volume.attachments, {})
+ self.assertEqual('Attached to %s on %s ' % (server_id, device),
+ col.human_readable())
+ self.assertEqual(_volume.attachments, col.machine_readable())
+
+ def test_attachments_column_with_server_cache(self):
+ _volume = volume_fakes.FakeVolume.create_one_volume()
+
+ server_id = _volume.attachments[0]['server_id']
+ device = _volume.attachments[0]['device']
+ fake_server = mock.Mock()
+ fake_server.name = 'fake-server-name'
+ server_cache = {server_id: fake_server}
+
+ col = volume.AttachmentsColumn(_volume.attachments, server_cache)
+ self.assertEqual(
+ 'Attached to %s on %s ' % ('fake-server-name', device),
+ col.human_readable())
+ self.assertEqual(_volume.attachments, col.machine_readable())
diff --git a/openstackclient/tests/unit/volume/v1/test_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py
index 1097d3f..20be6e4 100644
--- a/openstackclient/tests/unit/volume/v1/test_backup.py
+++ b/openstackclient/tests/unit/volume/v1/test_volume_backup.py
@@ -19,7 +19,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
-from openstackclient.volume.v1 import backup
+from openstackclient.volume.v1 import volume_backup
class TestBackup(volume_fakes.TestVolumev1):
@@ -74,7 +74,7 @@ class TestBackupCreate(TestBackup):
self.backups_mock.create.return_value = self.new_backup
# Get the command object to test
- self.cmd = backup.CreateVolumeBackup(self.app, None)
+ self.cmd = volume_backup.CreateVolumeBackup(self.app, None)
def test_backup_create(self):
arglist = [
@@ -100,7 +100,7 @@ class TestBackupCreate(TestBackup):
self.new_backup.description,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_backup_create_without_name(self):
arglist = [
@@ -124,7 +124,7 @@ class TestBackupCreate(TestBackup):
self.new_backup.description,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestBackupDelete(TestBackup):
@@ -139,7 +139,7 @@ class TestBackupDelete(TestBackup):
self.backups_mock.delete.return_value = None
# Get the command object to mock
- self.cmd = backup.DeleteVolumeBackup(self.app, None)
+ self.cmd = volume_backup.DeleteVolumeBackup(self.app, None)
def test_backup_delete(self):
arglist = [
@@ -240,7 +240,7 @@ class TestBackupList(TestBackup):
b.status,
b.size,
b.availability_zone,
- b.volume_id,
+ volume_backup.VolumeIdColumn(b.volume_id),
b.container,
))
@@ -251,7 +251,7 @@ class TestBackupList(TestBackup):
self.backups_mock.list.return_value = self.backups
self.volumes_mock.get.return_value = self.volume
# Get the command to test
- self.cmd = backup.ListVolumeBackup(self.app, None)
+ self.cmd = volume_backup.ListVolumeBackup(self.app, None)
def test_backup_list_without_options(self):
arglist = []
@@ -277,7 +277,7 @@ class TestBackupList(TestBackup):
search_opts=search_opts,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_backup_list_with_options(self):
arglist = [
@@ -309,7 +309,7 @@ class TestBackupList(TestBackup):
search_opts=search_opts,
)
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
class TestBackupRestore(TestBackup):
@@ -325,7 +325,7 @@ class TestBackupRestore(TestBackup):
self.volumes_mock.get.return_value = self.volume
self.restores_mock.restore.return_value = None
# Get the command object to mock
- self.cmd = backup.RestoreVolumeBackup(self.app, None)
+ self.cmd = volume_backup.RestoreVolumeBackup(self.app, None)
def test_backup_restore(self):
arglist = [
@@ -376,7 +376,7 @@ class TestBackupShow(TestBackup):
)
self.backups_mock.get.return_value = self.backup
# Get the command object to test
- self.cmd = backup.ShowVolumeBackup(self.app, None)
+ self.cmd = volume_backup.ShowVolumeBackup(self.app, None)
def test_backup_show(self):
arglist = [
@@ -391,4 +391,4 @@ class TestBackupShow(TestBackup):
self.backups_mock.get.assert_called_with(self.backup.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py
index c245cbf..5f976b0 100644
--- a/openstackclient/tests/unit/volume/v2/fakes.py
+++ b/openstackclient/tests/unit/volume/v2/fakes.py
@@ -17,7 +17,8 @@ import random
import uuid
import mock
-from osc_lib import utils as common_utils
+
+from osc_lib.cli import format_columns
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -468,7 +469,7 @@ class FakeVolume(object):
if x == 'tags':
# The 'tags' should be format_list
data_list.append(
- common_utils.format_list(volume.info.get(x)))
+ format_columns.ListColumn(volume.info.get(x)))
else:
data_list.append(volume.info.get(x))
return tuple(data_list)
diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py
index 6eeeae3..d238818 100644
--- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py
+++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py
@@ -15,6 +15,7 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -250,7 +251,7 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_consistency_group_create_from_source(self):
arglist = [
@@ -278,7 +279,7 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_consistency_group_create_from_snapshot(self):
arglist = [
@@ -306,7 +307,7 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestConsistencyGroupDelete(TestConsistencyGroup):
@@ -439,7 +440,7 @@ class TestConsistencyGroupList(TestConsistencyGroup):
c.availability_zone,
c.name,
c.description,
- utils.format_list(c.volume_types)
+ format_columns.ListColumn(c.volume_types)
))
def setUp(self):
@@ -462,7 +463,7 @@ class TestConsistencyGroupList(TestConsistencyGroup):
self.consistencygroups_mock.list.assert_called_once_with(
detailed=True, search_opts={'all_tenants': False})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_consistency_group_list_with_all_project(self):
arglist = [
@@ -479,7 +480,7 @@ class TestConsistencyGroupList(TestConsistencyGroup):
self.consistencygroups_mock.list.assert_called_once_with(
detailed=True, search_opts={'all_tenants': True})
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_consistency_group_list_with_long(self):
arglist = [
@@ -496,7 +497,7 @@ class TestConsistencyGroupList(TestConsistencyGroup):
self.consistencygroups_mock.list.assert_called_once_with(
detailed=True, search_opts={'all_tenants': False})
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
class TestConsistencyGroupRemoveVolume(TestConsistencyGroup):
@@ -704,4 +705,4 @@ class TestConsistencyGroupShow(TestConsistencyGroup):
self.consistencygroups_mock.get.assert_called_once_with(
self.consistency_group.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
diff --git a/openstackclient/tests/unit/volume/v2/test_qos_specs.py b/openstackclient/tests/unit/volume/v2/test_qos_specs.py
index 2b935e2..454747f 100644
--- a/openstackclient/tests/unit/volume/v2/test_qos_specs.py
+++ b/openstackclient/tests/unit/volume/v2/test_qos_specs.py
@@ -17,6 +17,8 @@ import copy
import mock
from mock import call
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -88,7 +90,7 @@ class TestQosCreate(TestQos):
self.new_qos_spec.consumer,
self.new_qos_spec.id,
self.new_qos_spec.name,
- utils.format_dict(self.new_qos_spec.specs)
+ format_columns.DictColumn(self.new_qos_spec.specs)
)
# Get the command object to test
@@ -111,7 +113,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_qos_create_with_consumer(self):
arglist = [
@@ -132,7 +134,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_qos_create_with_properties(self):
arglist = [
@@ -158,7 +160,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
class TestQosDelete(TestQos):
@@ -318,8 +320,8 @@ class TestQosList(TestQos):
q.id,
q.name,
q.consumer,
- qos_association.name,
- utils.format_dict(q.specs),
+ format_columns.ListColumn([qos_association.name]),
+ format_columns.DictColumn(q.specs),
))
def setUp(self):
@@ -341,7 +343,7 @@ class TestQosList(TestQos):
self.qos_mock.list.assert_called_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_qos_list_no_association(self):
self.qos_mock.reset_mock()
@@ -365,10 +367,10 @@ class TestQosList(TestQos):
self.qos_specs[1].id,
self.qos_specs[1].name,
self.qos_specs[1].consumer,
- None,
- utils.format_dict(self.qos_specs[1].specs),
+ format_columns.ListColumn(None),
+ format_columns.DictColumn(self.qos_specs[1].specs),
)
- self.assertEqual(ex_data, list(data))
+ self.assertListItemEqual(ex_data, list(data))
class TestQosSet(TestQos):
@@ -416,11 +418,11 @@ class TestQosShow(TestQos):
'properties'
)
data = (
- qos_association.name,
+ format_columns.ListColumn([qos_association.name]),
qos_spec.consumer,
qos_spec.id,
qos_spec.name,
- utils.format_dict(qos_spec.specs),
+ format_columns.DictColumn(qos_spec.specs),
)
def setUp(self):
@@ -448,7 +450,7 @@ class TestQosShow(TestQos):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, tuple(data))
+ self.assertItemEqual(self.data, tuple(data))
class TestQosUnset(TestQos):
diff --git a/openstackclient/tests/unit/volume/v2/test_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py
deleted file mode 100644
index e8f4ae5..0000000
--- a/openstackclient/tests/unit/volume/v2/test_snapshot.py
+++ /dev/null
@@ -1,741 +0,0 @@
-#
-# 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 argparse
-
-import mock
-from mock import call
-from osc_lib import exceptions
-from osc_lib import utils
-
-from openstackclient.tests.unit.identity.v3 import fakes as project_fakes
-from openstackclient.tests.unit import utils as tests_utils
-from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
-from openstackclient.volume.v2 import volume_snapshot
-
-
-class TestSnapshot(volume_fakes.TestVolume):
-
- def setUp(self):
- super(TestSnapshot, self).setUp()
-
- self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
- self.snapshots_mock.reset_mock()
- self.volumes_mock = self.app.client_manager.volume.volumes
- self.volumes_mock.reset_mock()
- self.project_mock = self.app.client_manager.identity.projects
- self.project_mock.reset_mock()
-
-
-class TestSnapshotCreate(TestSnapshot):
-
- columns = (
- 'created_at',
- 'description',
- 'id',
- 'name',
- 'properties',
- 'size',
- 'status',
- 'volume_id',
- )
-
- def setUp(self):
- super(TestSnapshotCreate, self).setUp()
-
- self.volume = volume_fakes.FakeVolume.create_one_volume()
- self.new_snapshot = volume_fakes.FakeSnapshot.create_one_snapshot(
- attrs={'volume_id': self.volume.id})
-
- self.data = (
- self.new_snapshot.created_at,
- self.new_snapshot.description,
- self.new_snapshot.id,
- self.new_snapshot.name,
- utils.format_dict(self.new_snapshot.metadata),
- self.new_snapshot.size,
- self.new_snapshot.status,
- self.new_snapshot.volume_id,
- )
-
- self.volumes_mock.get.return_value = self.volume
- self.snapshots_mock.create.return_value = self.new_snapshot
- self.snapshots_mock.manage.return_value = self.new_snapshot
- # Get the command object to test
- self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
-
- def test_snapshot_create(self):
- arglist = [
- "--volume", self.new_snapshot.volume_id,
- "--description", self.new_snapshot.description,
- "--force",
- '--property', 'Alpha=a',
- '--property', 'Beta=b',
- self.new_snapshot.name,
- ]
- verifylist = [
- ("volume", self.new_snapshot.volume_id),
- ("description", self.new_snapshot.description),
- ("force", True),
- ('property', {'Alpha': 'a', 'Beta': 'b'}),
- ("snapshot_name", self.new_snapshot.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.create.assert_called_with(
- self.new_snapshot.volume_id,
- force=True,
- name=self.new_snapshot.name,
- description=self.new_snapshot.description,
- metadata={'Alpha': 'a', 'Beta': 'b'},
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
- def test_snapshot_create_without_name(self):
- arglist = [
- "--volume", self.new_snapshot.volume_id,
- ]
- verifylist = [
- ("volume", self.new_snapshot.volume_id),
- ]
- self.assertRaises(
- tests_utils.ParserException,
- self.check_parser,
- self.cmd,
- arglist,
- verifylist,
- )
-
- def test_snapshot_create_without_volume(self):
- arglist = [
- "--description", self.new_snapshot.description,
- "--force",
- self.new_snapshot.name
- ]
- verifylist = [
- ("description", self.new_snapshot.description),
- ("force", True),
- ("snapshot_name", self.new_snapshot.name)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.volumes_mock.get.assert_called_once_with(
- self.new_snapshot.name)
- self.snapshots_mock.create.assert_called_once_with(
- self.new_snapshot.volume_id,
- force=True,
- name=self.new_snapshot.name,
- description=self.new_snapshot.description,
- metadata=None,
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
- def test_snapshot_create_with_remote_source(self):
- arglist = [
- '--remote-source', 'source-name=test_source_name',
- '--remote-source', 'source-id=test_source_id',
- '--volume', self.new_snapshot.volume_id,
- self.new_snapshot.name,
- ]
- ref_dict = {'source-name': 'test_source_name',
- 'source-id': 'test_source_id'}
- verifylist = [
- ('remote_source', ref_dict),
- ('volume', self.new_snapshot.volume_id),
- ("snapshot_name", self.new_snapshot.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.manage.assert_called_with(
- volume_id=self.new_snapshot.volume_id,
- ref=ref_dict,
- name=self.new_snapshot.name,
- description=None,
- metadata=None,
- )
- self.snapshots_mock.create.assert_not_called()
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
-
-class TestSnapshotDelete(TestSnapshot):
-
- snapshots = volume_fakes.FakeSnapshot.create_snapshots(count=2)
-
- def setUp(self):
- super(TestSnapshotDelete, self).setUp()
-
- self.snapshots_mock.get = (
- volume_fakes.FakeSnapshot.get_snapshots(self.snapshots))
- self.snapshots_mock.delete.return_value = None
-
- # Get the command object to mock
- self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)
-
- def test_snapshot_delete(self):
- arglist = [
- self.snapshots[0].id
- ]
- verifylist = [
- ("snapshots", [self.snapshots[0].id])
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.delete.assert_called_with(
- self.snapshots[0].id, False)
- self.assertIsNone(result)
-
- def test_snapshot_delete_with_force(self):
- arglist = [
- '--force',
- self.snapshots[0].id
- ]
- verifylist = [
- ('force', True),
- ("snapshots", [self.snapshots[0].id])
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.delete.assert_called_with(
- self.snapshots[0].id, True)
- self.assertIsNone(result)
-
- def test_delete_multiple_snapshots(self):
- arglist = []
- for s in self.snapshots:
- arglist.append(s.id)
- verifylist = [
- ('snapshots', arglist),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
-
- calls = []
- for s in self.snapshots:
- calls.append(call(s.id, False))
- self.snapshots_mock.delete.assert_has_calls(calls)
- self.assertIsNone(result)
-
- def test_delete_multiple_snapshots_with_exception(self):
- arglist = [
- self.snapshots[0].id,
- 'unexist_snapshot',
- ]
- verifylist = [
- ('snapshots', arglist),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- find_mock_result = [self.snapshots[0], exceptions.CommandError]
- with mock.patch.object(utils, 'find_resource',
- side_effect=find_mock_result) as find_mock:
- try:
- self.cmd.take_action(parsed_args)
- self.fail('CommandError should be raised.')
- except exceptions.CommandError as e:
- self.assertEqual('1 of 2 snapshots failed to delete.',
- str(e))
-
- find_mock.assert_any_call(
- self.snapshots_mock, self.snapshots[0].id)
- find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot')
-
- self.assertEqual(2, find_mock.call_count)
- self.snapshots_mock.delete.assert_called_once_with(
- self.snapshots[0].id, False
- )
-
-
-class TestSnapshotList(TestSnapshot):
-
- volume = volume_fakes.FakeVolume.create_one_volume()
- project = project_fakes.FakeProject.create_one_project()
- snapshots = volume_fakes.FakeSnapshot.create_snapshots(
- attrs={'volume_id': volume.name}, count=3)
-
- columns = [
- "ID",
- "Name",
- "Description",
- "Status",
- "Size"
- ]
- columns_long = columns + [
- "Created At",
- "Volume",
- "Properties"
- ]
-
- data = []
- for s in snapshots:
- data.append((
- s.id,
- s.name,
- s.description,
- s.status,
- s.size,
- ))
- data_long = []
- for s in snapshots:
- data_long.append((
- s.id,
- s.name,
- s.description,
- s.status,
- s.size,
- s.created_at,
- s.volume_id,
- utils.format_dict(s.metadata),
- ))
-
- def setUp(self):
- super(TestSnapshotList, self).setUp()
-
- self.volumes_mock.list.return_value = [self.volume]
- self.volumes_mock.get.return_value = self.volume
- self.project_mock.get.return_value = self.project
- self.snapshots_mock.list.return_value = self.snapshots
- # Get the command to test
- self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
-
- def test_snapshot_list_without_options(self):
- arglist = []
- verifylist = [
- ('all_projects', False),
- ('long', False)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None,
- search_opts={
- 'all_tenants': False,
- 'name': None,
- 'status': None,
- 'project_id': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_with_options(self):
- arglist = [
- "--long",
- "--limit", "2",
- "--project", self.project.id,
- "--marker", self.snapshots[0].id,
- ]
- verifylist = [
- ("long", True),
- ("limit", 2),
- ("project", self.project.id),
- ("marker", self.snapshots[0].id),
- ('all_projects', False),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- limit=2,
- marker=self.snapshots[0].id,
- search_opts={
- 'all_tenants': True,
- 'project_id': self.project.id,
- 'name': None,
- 'status': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
-
- def test_snapshot_list_all_projects(self):
- arglist = [
- '--all-projects',
- ]
- verifylist = [
- ('long', False),
- ('all_projects', True)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None,
- search_opts={
- 'all_tenants': True,
- 'name': None,
- 'status': None,
- 'project_id': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_name_option(self):
- arglist = [
- '--name', self.snapshots[0].name,
- ]
- verifylist = [
- ('all_projects', False),
- ('long', False),
- ('name', self.snapshots[0].name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None,
- search_opts={
- 'all_tenants': False,
- 'name': self.snapshots[0].name,
- 'status': None,
- 'project_id': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_status_option(self):
- arglist = [
- '--status', self.snapshots[0].status,
- ]
- verifylist = [
- ('all_projects', False),
- ('long', False),
- ('status', self.snapshots[0].status),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None,
- search_opts={
- 'all_tenants': False,
- 'name': None,
- 'status': self.snapshots[0].status,
- 'project_id': None,
- 'volume_id': None
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_volumeid_option(self):
- arglist = [
- '--volume', self.volume.id,
- ]
- verifylist = [
- ('all_projects', False),
- ('long', False),
- ('volume', self.volume.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None,
- search_opts={
- 'all_tenants': False,
- 'name': None,
- 'status': None,
- 'project_id': None,
- 'volume_id': self.volume.id
- }
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
-
- def test_snapshot_list_negative_limit(self):
- arglist = [
- "--limit", "-2",
- ]
- verifylist = [
- ("limit", -2),
- ]
- self.assertRaises(argparse.ArgumentTypeError, self.check_parser,
- self.cmd, arglist, verifylist)
-
-
-class TestSnapshotSet(TestSnapshot):
-
- snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
-
- def setUp(self):
- super(TestSnapshotSet, self).setUp()
-
- self.snapshots_mock.get.return_value = self.snapshot
- self.snapshots_mock.set_metadata.return_value = None
- self.snapshots_mock.update.return_value = None
- # Get the command object to mock
- self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
-
- def test_snapshot_set_no_option(self):
- arglist = [
- self.snapshot.id,
- ]
- verifylist = [
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
- self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot)
- self.assertNotCalled(self.snapshots_mock.reset_state)
- self.assertNotCalled(self.snapshots_mock.update)
- self.assertNotCalled(self.snapshots_mock.set_metadata)
- self.assertIsNone(result)
-
- def test_snapshot_set_name_and_property(self):
- arglist = [
- "--name", "new_snapshot",
- "--property", "x=y",
- "--property", "foo=foo",
- self.snapshot.id,
- ]
- new_property = {"x": "y", "foo": "foo"}
- verifylist = [
- ("name", "new_snapshot"),
- ("property", new_property),
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- kwargs = {
- "name": "new_snapshot",
- }
- self.snapshots_mock.update.assert_called_with(
- self.snapshot.id, **kwargs)
- self.snapshots_mock.set_metadata.assert_called_with(
- self.snapshot.id, new_property
- )
- self.assertIsNone(result)
-
- def test_snapshot_set_with_no_property(self):
- arglist = [
- "--no-property",
- self.snapshot.id,
- ]
- verifylist = [
- ("no_property", True),
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
- self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot)
- self.assertNotCalled(self.snapshots_mock.reset_state)
- self.assertNotCalled(self.snapshots_mock.update)
- self.assertNotCalled(self.snapshots_mock.set_metadata)
- self.snapshots_mock.delete_metadata.assert_called_with(
- self.snapshot.id, ["foo"]
- )
- self.assertIsNone(result)
-
- def test_snapshot_set_with_no_property_and_property(self):
- arglist = [
- "--no-property",
- "--property", "foo_1=bar_1",
- self.snapshot.id,
- ]
- verifylist = [
- ("no_property", True),
- ("property", {"foo_1": "bar_1"}),
- ("snapshot", self.snapshot.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
- self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot)
- self.assertNotCalled(self.snapshots_mock.reset_state)
- self.assertNotCalled(self.snapshots_mock.update)
- self.snapshots_mock.delete_metadata.assert_called_with(
- self.snapshot.id, ["foo"]
- )
- self.snapshots_mock.set_metadata.assert_called_once_with(
- self.snapshot.id, {"foo_1": "bar_1"})
- self.assertIsNone(result)
-
- def test_snapshot_set_state_to_error(self):
- arglist = [
- "--state", "error",
- self.snapshot.id
- ]
- verifylist = [
- ("state", "error"),
- ("snapshot", self.snapshot.id)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.reset_state.assert_called_with(
- self.snapshot.id, "error")
- self.assertIsNone(result)
-
- def test_volume_set_state_failed(self):
- self.snapshots_mock.reset_state.side_effect = exceptions.CommandError()
- arglist = [
- '--state', 'error',
- self.snapshot.id
- ]
- verifylist = [
- ('state', 'error'),
- ('snapshot', self.snapshot.id)
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- try:
- self.cmd.take_action(parsed_args)
- self.fail('CommandError should be raised.')
- except exceptions.CommandError as e:
- self.assertEqual('One or more of the set operations failed',
- str(e))
- self.snapshots_mock.reset_state.assert_called_once_with(
- self.snapshot.id, 'error')
-
- def test_volume_set_name_and_state_failed(self):
- self.snapshots_mock.reset_state.side_effect = exceptions.CommandError()
- arglist = [
- '--state', 'error',
- "--name", "new_snapshot",
- self.snapshot.id
- ]
- verifylist = [
- ('state', 'error'),
- ("name", "new_snapshot"),
- ('snapshot', self.snapshot.id)
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- try:
- self.cmd.take_action(parsed_args)
- self.fail('CommandError should be raised.')
- except exceptions.CommandError as e:
- self.assertEqual('One or more of the set operations failed',
- str(e))
- kwargs = {
- "name": "new_snapshot",
- }
- self.snapshots_mock.update.assert_called_once_with(
- self.snapshot.id, **kwargs)
- self.snapshots_mock.reset_state.assert_called_once_with(
- self.snapshot.id, 'error')
-
-
-class TestSnapshotShow(TestSnapshot):
-
- columns = (
- 'created_at',
- 'description',
- 'id',
- 'name',
- 'properties',
- 'size',
- 'status',
- 'volume_id',
- )
-
- def setUp(self):
- super(TestSnapshotShow, self).setUp()
-
- self.snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
-
- self.data = (
- self.snapshot.created_at,
- self.snapshot.description,
- self.snapshot.id,
- self.snapshot.name,
- utils.format_dict(self.snapshot.metadata),
- self.snapshot.size,
- self.snapshot.status,
- self.snapshot.volume_id,
- )
-
- self.snapshots_mock.get.return_value = self.snapshot
- # Get the command object to test
- self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
-
- def test_snapshot_show(self):
- arglist = [
- self.snapshot.id
- ]
- verifylist = [
- ("snapshot", self.snapshot.id)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
- self.snapshots_mock.get.assert_called_with(self.snapshot.id)
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
-
-class TestSnapshotUnset(TestSnapshot):
-
- snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
-
- def setUp(self):
- super(TestSnapshotUnset, self).setUp()
-
- self.snapshots_mock.get.return_value = self.snapshot
- self.snapshots_mock.delete_metadata.return_value = None
- # Get the command object to mock
- self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
-
- def test_snapshot_unset(self):
- arglist = [
- "--property", "foo",
- self.snapshot.id,
- ]
- verifylist = [
- ("property", ["foo"]),
- ("snapshot", self.snapshot.id),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.snapshots_mock.delete_metadata.assert_called_with(
- self.snapshot.id, ["foo"]
- )
- self.assertIsNone(result)
diff --git a/openstackclient/tests/unit/volume/v2/test_transfer_request.py b/openstackclient/tests/unit/volume/v2/test_transfer_request.py
index 37eed11..1ea6648 100644
--- a/openstackclient/tests/unit/volume/v2/test_transfer_request.py
+++ b/openstackclient/tests/unit/volume/v2/test_transfer_request.py
@@ -18,6 +18,7 @@ from mock import call
from osc_lib import exceptions
from osc_lib import utils
+from openstackclient.tests.unit import utils as test_utils
from openstackclient.tests.unit.volume.v2 import fakes as transfer_fakes
from openstackclient.volume.v2 import volume_transfer_request
@@ -85,26 +86,6 @@ class TestTransferAccept(TestTransfer):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
- def test_transfer_accept_deprecated(self):
- arglist = [
- self.volume_transfer.id,
- 'key_value',
- ]
- verifylist = [
- ('transfer_request', self.volume_transfer.id),
- ('old_auth_key', 'key_value'),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.transfer_mock.accept.assert_called_once_with(
- self.volume_transfer.id,
- 'key_value',
- )
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
def test_transfer_accept_no_option(self):
arglist = [
self.volume_transfer.id,
@@ -112,12 +93,13 @@ class TestTransferAccept(TestTransfer):
verifylist = [
('transfer_request', self.volume_transfer.id),
]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
- exceptions.CommandError,
- self.cmd.take_action,
- parsed_args,
+ test_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist,
)
diff --git a/openstackclient/tests/unit/volume/v2/test_type.py b/openstackclient/tests/unit/volume/v2/test_type.py
index 4023d55..1791592 100644
--- a/openstackclient/tests/unit/volume/v2/test_type.py
+++ b/openstackclient/tests/unit/volume/v2/test_type.py
@@ -15,6 +15,7 @@
import mock
from mock import call
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -92,7 +93,7 @@ class TestTypeCreate(TestType):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_type_create_private(self):
arglist = [
@@ -118,7 +119,7 @@ class TestTypeCreate(TestType):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_public_type_create_with_project(self):
arglist = [
@@ -158,7 +159,7 @@ class TestTypeCreate(TestType):
)
encryption_data = (
self.new_volume_type.description,
- utils.format_dict(encryption_info),
+ format_columns.DictColumn(encryption_info),
self.new_volume_type.id,
True,
self.new_volume_type.name,
@@ -195,7 +196,7 @@ class TestTypeCreate(TestType):
body,
)
self.assertEqual(encryption_columns, columns)
- self.assertEqual(encryption_data, data)
+ self.assertItemEqual(encryption_data, data)
class TestTypeDelete(TestType):
@@ -305,7 +306,7 @@ class TestTypeList(TestType):
t.name,
t.is_public,
t.description,
- utils.format_dict(t.extra_specs),
+ format_columns.DictColumn(t.extra_specs),
))
def setUp(self):
@@ -329,7 +330,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with(is_public=None)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_type_list_with_options(self):
arglist = [
@@ -347,7 +348,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with(is_public=True)
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
def test_type_list_with_private_option(self):
arglist = [
@@ -364,7 +365,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with(is_public=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_type_list_with_default_option(self):
arglist = [
@@ -382,7 +383,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.default.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data_with_default_type, list(data))
+ self.assertListItemEqual(self.data_with_default_type, list(data))
def test_type_list_with_encryption(self):
encryption_type = volume_fakes.FakeType.create_one_encryption_type(
@@ -401,13 +402,16 @@ class TestTypeList(TestType):
self.volume_types[0].id,
self.volume_types[0].name,
self.volume_types[0].is_public,
- utils.format_dict(encryption_info),
+ volume_type.EncryptionInfoColumn(
+ self.volume_types[0].id,
+ {self.volume_types[0].id: encryption_info}),
))
encryption_data.append((
self.volume_types[1].id,
self.volume_types[1].name,
self.volume_types[1].is_public,
- '-',
+ volume_type.EncryptionInfoColumn(
+ self.volume_types[1].id, {}),
))
self.encryption_types_mock.list.return_value = [encryption_type]
@@ -423,7 +427,7 @@ class TestTypeList(TestType):
self.encryption_types_mock.list.assert_called_once_with()
self.types_mock.list.assert_called_once_with(is_public=None)
self.assertEqual(encryption_columns, columns)
- self.assertEqual(encryption_data, list(data))
+ self.assertListItemEqual(encryption_data, list(data))
class TestTypeSet(TestType):
@@ -687,7 +691,7 @@ class TestTypeShow(TestType):
self.volume_type.id,
True,
self.volume_type.name,
- utils.format_dict(self.volume_type.extra_specs)
+ format_columns.DictColumn(self.volume_type.extra_specs)
)
self.types_mock.get.return_value = self.volume_type
@@ -709,7 +713,7 @@ class TestTypeShow(TestType):
self.types_mock.get.assert_called_with(self.volume_type.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_type_show_with_access(self):
arglist = [
@@ -735,14 +739,14 @@ class TestTypeShow(TestType):
self.assertEqual(self.columns, columns)
private_type_data = (
- utils.format_list([type_access_list.project_id]),
+ format_columns.ListColumn([type_access_list.project_id]),
private_type.description,
private_type.id,
private_type.is_public,
private_type.name,
- utils.format_dict(private_type.extra_specs)
+ format_columns.DictColumn(private_type.extra_specs)
)
- self.assertEqual(private_type_data, data)
+ self.assertItemEqual(private_type_data, data)
def test_type_show_with_list_access_exec(self):
arglist = [
@@ -772,9 +776,9 @@ class TestTypeShow(TestType):
private_type.id,
private_type.is_public,
private_type.name,
- utils.format_dict(private_type.extra_specs)
+ format_columns.DictColumn(private_type.extra_specs)
)
- self.assertEqual(private_type_data, data)
+ self.assertItemEqual(private_type_data, data)
def test_type_show_with_encryption(self):
encryption_type = volume_fakes.FakeType.create_one_encryption_type()
@@ -800,11 +804,11 @@ class TestTypeShow(TestType):
encryption_data = (
None,
self.volume_type.description,
- utils.format_dict(encryption_info),
+ format_columns.DictColumn(encryption_info),
self.volume_type.id,
True,
self.volume_type.name,
- utils.format_dict(self.volume_type.extra_specs)
+ format_columns.DictColumn(self.volume_type.extra_specs)
)
arglist = [
'--encryption-type',
@@ -820,7 +824,7 @@ class TestTypeShow(TestType):
self.types_mock.get.assert_called_with(self.volume_type.id)
self.encryption_types_mock.get.assert_called_with(self.volume_type.id)
self.assertEqual(encryption_columns, columns)
- self.assertEqual(encryption_data, data)
+ self.assertItemEqual(encryption_data, data)
class TestTypeUnset(TestType):
@@ -923,3 +927,30 @@ class TestTypeUnset(TestType):
result = self.cmd.take_action(parsed_args)
self.encryption_types_mock.delete.assert_called_with(self.volume_type)
self.assertIsNone(result)
+
+
+class TestColumns(TestType):
+
+ def test_encryption_info_column_with_info(self):
+ fake_volume_type = volume_fakes.FakeType.create_one_type()
+ type_id = fake_volume_type.id
+
+ encryption_info = {
+ 'provider': 'LuksEncryptor',
+ 'cipher': None,
+ 'key_size': None,
+ 'control_location': 'front-end',
+ }
+ col = volume_type.EncryptionInfoColumn(type_id,
+ {type_id: encryption_info})
+ self.assertEqual(utils.format_dict(encryption_info),
+ col.human_readable())
+ self.assertEqual(encryption_info, col.machine_readable())
+
+ def test_encryption_info_column_without_info(self):
+ fake_volume_type = volume_fakes.FakeType.create_one_type()
+ type_id = fake_volume_type.id
+
+ col = volume_type.EncryptionInfoColumn(type_id, {})
+ self.assertEqual('-', col.human_readable())
+ self.assertIsNone(col.machine_readable())
diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py
index dbe69ea..332b50a 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume.py
@@ -16,6 +16,8 @@ import argparse
import mock
from mock import call
+
+from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@@ -94,7 +96,7 @@ class TestVolumeCreate(TestVolume):
self.new_volume.description,
self.new_volume.id,
self.new_volume.name,
- utils.format_dict(self.new_volume.metadata),
+ format_columns.DictColumn(self.new_volume.metadata),
self.new_volume.size,
self.new_volume.snapshot_id,
self.new_volume.status,
@@ -135,7 +137,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_options(self):
consistency_group = (
@@ -181,41 +183,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
-
- def test_volume_create_user(self):
- arglist = [
- '--size', str(self.new_volume.size),
- '--user', self.user.id,
- self.new_volume.name,
- ]
- verifylist = [
- ('size', self.new_volume.size),
- ('user', self.user.id),
- ('name', self.new_volume.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
- self.volumes_mock.create.assert_not_called()
-
- def test_volume_create_project(self):
- arglist = [
- '--size', str(self.new_volume.size),
- '--project', self.project.id,
- self.new_volume.name,
- ]
- verifylist = [
- ('size', self.new_volume.size),
- ('project', self.project.id),
- ('name', self.new_volume.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
- self.volumes_mock.create.assert_not_called()
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_properties(self):
arglist = [
@@ -251,7 +219,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_image_id(self):
image = image_fakes.FakeImage.create_one_image()
@@ -289,7 +257,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_image_name(self):
image = image_fakes.FakeImage.create_one_image()
@@ -327,7 +295,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_with_snapshot(self):
snapshot = volume_fakes.FakeSnapshot.create_one_snapshot()
@@ -364,7 +332,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
def test_volume_create_with_bootable_and_readonly(self):
arglist = [
@@ -402,7 +370,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, True)
self.volumes_mock.update_readonly_flag.assert_called_with(
@@ -444,7 +412,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, False)
self.volumes_mock.update_readonly_flag.assert_called_with(
@@ -496,7 +464,7 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(2, mock_error.call_count)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.datalist, data)
+ self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, True)
self.volumes_mock.update_readonly_flag.assert_called_with(
@@ -706,17 +674,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_project(self):
arglist = [
@@ -749,17 +714,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_project_domain(self):
arglist = [
@@ -794,17 +756,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_user(self):
arglist = [
@@ -834,19 +793,16 @@ class TestVolumeList(TestVolume):
marker=None,
limit=None,
)
-
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
+
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_user_domain(self):
arglist = [
@@ -881,17 +837,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_name(self):
arglist = [
@@ -924,17 +877,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_status(self):
arglist = [
@@ -967,17 +917,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_all_projects(self):
arglist = [
@@ -1010,17 +957,14 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_long(self):
arglist = [
@@ -1064,9 +1008,6 @@ class TestVolumeList(TestVolume):
]
self.assertEqual(collist, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
@@ -1074,10 +1015,10 @@ class TestVolumeList(TestVolume):
self.mock_volume.size,
self.mock_volume.volume_type,
self.mock_volume.bootable,
- msg,
- utils.format_dict(self.mock_volume.metadata),
+ volume.AttachmentsColumn(self.mock_volume.attachments),
+ format_columns.DictColumn(self.mock_volume.metadata),
), )
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_with_marker_and_limit(self):
arglist = [
@@ -1098,15 +1039,12 @@ class TestVolumeList(TestVolume):
self.assertEqual(self.columns, columns)
- server = self.mock_volume.attachments[0]['server_id']
- device = self.mock_volume.attachments[0]['device']
- msg = 'Attached to %s on %s ' % (server, device)
datalist = ((
self.mock_volume.id,
self.mock_volume.name,
self.mock_volume.status,
self.mock_volume.size,
- msg,
+ volume.AttachmentsColumn(self.mock_volume.attachments),
), )
self.volumes_mock.list.assert_called_once_with(
@@ -1119,7 +1057,7 @@ class TestVolumeList(TestVolume):
'name': None,
'all_tenants': False, }
)
- self.assertEqual(datalist, tuple(data))
+ self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_negative_limit(self):
arglist = [
@@ -1513,7 +1451,7 @@ class TestVolumeShow(TestVolume):
volume_fakes.FakeVolume.get_volume_columns(self._volume),
columns)
- self.assertEqual(
+ self.assertItemEqual(
volume_fakes.FakeVolume.get_volume_data(self._volume),
data)
@@ -1596,3 +1534,31 @@ class TestVolumeUnset(TestVolume):
self.new_volume.id, parsed_args.image_property)
self.volumes_mock.delete_metadata.assert_called_with(
self.new_volume.id, parsed_args.property)
+
+
+class TestColumns(TestVolume):
+
+ def test_attachments_column_without_server_cache(self):
+ _volume = volume_fakes.FakeVolume.create_one_volume()
+ server_id = _volume.attachments[0]['server_id']
+ device = _volume.attachments[0]['device']
+
+ col = volume.AttachmentsColumn(_volume.attachments, {})
+ self.assertEqual('Attached to %s on %s ' % (server_id, device),
+ col.human_readable())
+ self.assertEqual(_volume.attachments, col.machine_readable())
+
+ def test_attachments_column_with_server_cache(self):
+ _volume = volume_fakes.FakeVolume.create_one_volume()
+
+ server_id = _volume.attachments[0]['server_id']
+ device = _volume.attachments[0]['device']
+ fake_server = mock.Mock()
+ fake_server.name = 'fake-server-name'
+ server_cache = {server_id: fake_server}
+
+ col = volume.AttachmentsColumn(_volume.attachments, server_cache)
+ self.assertEqual(
+ 'Attached to %s on %s ' % ('fake-server-name', device),
+ col.human_readable())
+ self.assertEqual(_volume.attachments, col.machine_readable())
diff --git a/openstackclient/tests/unit/volume/v2/test_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py
index 9a2ce71..30c7915 100644
--- a/openstackclient/tests/unit/volume/v2/test_backup.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py
@@ -19,7 +19,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
-from openstackclient.volume.v2 import backup
+from openstackclient.volume.v2 import volume_backup
class TestBackup(volume_fakes.TestVolume):
@@ -77,7 +77,7 @@ class TestBackupCreate(TestBackup):
self.backups_mock.create.return_value = self.new_backup
# Get the command object to test
- self.cmd = backup.CreateVolumeBackup(self.app, None)
+ self.cmd = volume_backup.CreateVolumeBackup(self.app, None)
def test_backup_create(self):
arglist = [
@@ -154,7 +154,7 @@ class TestBackupDelete(TestBackup):
self.backups_mock.delete.return_value = None
# Get the command object to mock
- self.cmd = backup.DeleteVolumeBackup(self.app, None)
+ self.cmd = volume_backup.DeleteVolumeBackup(self.app, None)
def test_backup_delete(self):
arglist = [
@@ -271,7 +271,7 @@ class TestBackupList(TestBackup):
b.status,
b.size,
b.availability_zone,
- b.volume_id,
+ volume_backup.VolumeIdColumn(b.volume_id),
b.container,
))
@@ -283,7 +283,7 @@ class TestBackupList(TestBackup):
self.volumes_mock.get.return_value = self.volume
self.backups_mock.get.return_value = self.backups[0]
# Get the command to test
- self.cmd = backup.ListVolumeBackup(self.app, None)
+ self.cmd = volume_backup.ListVolumeBackup(self.app, None)
def test_backup_list_without_options(self):
arglist = []
@@ -314,7 +314,7 @@ class TestBackupList(TestBackup):
limit=None,
)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ self.assertListItemEqual(self.data, list(data))
def test_backup_list_with_options(self):
arglist = [
@@ -353,7 +353,7 @@ class TestBackupList(TestBackup):
limit=3,
)
self.assertEqual(self.columns_long, columns)
- self.assertEqual(self.data_long, list(data))
+ self.assertListItemEqual(self.data_long, list(data))
class TestBackupRestore(TestBackup):
@@ -371,7 +371,7 @@ class TestBackupRestore(TestBackup):
volume_fakes.FakeVolume.create_one_volume(
{'id': self.volume['id']}))
# Get the command object to mock
- self.cmd = backup.RestoreVolumeBackup(self.app, None)
+ self.cmd = volume_backup.RestoreVolumeBackup(self.app, None)
def test_backup_restore(self):
arglist = [
@@ -400,7 +400,7 @@ class TestBackupSet(TestBackup):
self.backups_mock.get.return_value = self.backup
# Get the command object to test
- self.cmd = backup.SetVolumeBackup(self.app, None)
+ self.cmd = volume_backup.SetVolumeBackup(self.app, None)
def test_backup_set_name(self):
arglist = [
@@ -517,7 +517,7 @@ class TestBackupShow(TestBackup):
self.backups_mock.get.return_value = self.backup
# Get the command object to test
- self.cmd = backup.ShowVolumeBackup(self.app, None)
+ self.cmd = volume_backup.ShowVolumeBackup(self.app, None)
def test_backup_show(self):
arglist = [
diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py
index e0e670a..fdd1794 100644
--- a/openstackclient/volume/client.py
+++ b/openstackclient/volume/client.py
@@ -22,7 +22,7 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-DEFAULT_API_VERSION = '2'
+DEFAULT_API_VERSION = '3'
API_VERSION_OPTION = 'os_volume_api_version'
API_NAME = "volume"
API_VERSIONS = {
diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py
index 26c9182..0b6a7fa 100644
--- a/openstackclient/volume/v1/qos_specs.py
+++ b/openstackclient/volume/v1/qos_specs.py
@@ -17,6 +17,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -95,7 +96,8 @@ class CreateQos(command.ShowOne):
qos_spec = volume_client.qos_specs.create(parsed_args.name, specs)
qos_spec._info.update(
- {'properties': utils.format_dict(qos_spec._info.pop('specs'))}
+ {'properties':
+ format_columns.DictColumn(qos_spec._info.pop('specs'))}
)
return zip(*sorted(six.iteritems(qos_spec._info)))
@@ -208,8 +210,8 @@ class ListQos(command.Lister):
(utils.get_dict_properties(
s._info, columns,
formatters={
- 'Specs': utils.format_dict,
- 'Associations': utils.format_list
+ 'Specs': format_columns.DictColumn,
+ 'Associations': format_columns.ListColumn
},
) for s in qos_specs_list))
@@ -265,10 +267,11 @@ class ShowQos(command.ShowOne):
associations = [association.name
for association in qos_associations]
qos_spec._info.update({
- 'associations': utils.format_list(associations)
+ 'associations': format_columns.ListColumn(associations)
})
qos_spec._info.update(
- {'properties': utils.format_dict(qos_spec._info.pop('specs'))})
+ {'properties':
+ format_columns.DictColumn(qos_spec._info.pop('specs'))})
return zip(*sorted(six.iteritems(qos_spec._info)))
diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py
deleted file mode 100644
index e9e3894..0000000
--- a/openstackclient/volume/v1/snapshot.py
+++ /dev/null
@@ -1,318 +0,0 @@
-# Copyright 2012-2013 OpenStack Foundation
-#
-# 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.
-#
-
-# TODO(Huanxuan Ao): Remove this file and "snapshot create", "snapshot delete",
-# "snapshot set", "snapshot show" and "snapshot unset"
-# commands two cycles after Ocata.
-
-"""Volume v1 Snapshot action implementations"""
-
-import copy
-import logging
-
-from osc_lib.cli import parseractions
-from osc_lib.command import command
-from osc_lib import exceptions
-from osc_lib import utils
-import six
-
-from openstackclient.i18n import _
-
-
-deprecated = True
-LOG_DEP = logging.getLogger('deprecated')
-LOG = logging.getLogger(__name__)
-
-
-class CreateSnapshot(command.ShowOne):
- _description = _("Create new snapshot")
-
- def get_parser(self, prog_name):
- parser = super(CreateSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'volume',
- metavar='<volume>',
- help=_('Volume to snapshot (name or ID)'),
- )
- parser.add_argument(
- '--name',
- metavar='<name>',
- help=_('Name of the snapshot'),
- )
- parser.add_argument(
- '--description',
- metavar='<description>',
- help=_('Description of the snapshot'),
- )
- parser.add_argument(
- '--force',
- dest='force',
- action='store_true',
- default=False,
- help=_('Create a snapshot attached to an instance. '
- 'Default is False'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot create" instead.'))
- volume_client = self.app.client_manager.volume
- volume_id = utils.find_resource(volume_client.volumes,
- parsed_args.volume).id
- snapshot = volume_client.volume_snapshots.create(
- volume_id,
- parsed_args.force,
- parsed_args.name,
- parsed_args.description
- )
-
- snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
- )
-
- return zip(*sorted(six.iteritems(snapshot._info)))
-
-
-class DeleteSnapshot(command.Command):
- _description = _("Delete snapshot(s)")
-
- def get_parser(self, prog_name):
- parser = super(DeleteSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'snapshots',
- metavar='<snapshot>',
- nargs="+",
- help=_('Snapshot(s) to delete (name or ID)'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot delete" instead.'))
- volume_client = self.app.client_manager.volume
- result = 0
-
- for i in parsed_args.snapshots:
- try:
- snapshot_id = utils.find_resource(
- volume_client.volume_snapshots, i).id
- volume_client.volume_snapshots.delete(snapshot_id)
- except Exception as e:
- result += 1
- LOG.error(_("Failed to delete snapshot with "
- "name or ID '%(snapshot)s': %(e)s"),
- {'snapshot': i, 'e': e})
-
- if result > 0:
- total = len(parsed_args.snapshots)
- msg = (_("%(result)s of %(total)s snapshots failed "
- "to delete.") % {'result': result, 'total': total})
- raise exceptions.CommandError(msg)
-
-
-class ListSnapshot(command.Lister):
- _description = _("List snapshots")
-
- def get_parser(self, prog_name):
- parser = super(ListSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- '--all-projects',
- action='store_true',
- default=False,
- help=_('Include all projects (admin only)'),
- )
- parser.add_argument(
- '--long',
- action='store_true',
- default=False,
- help=_('List additional fields in output'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot list" instead.'))
-
- def _format_volume_id(volume_id):
- """Return a volume name if available
-
- :param volume_id: a volume ID
- :rtype: either the volume ID or name
- """
-
- volume = volume_id
- if volume_id in volume_cache.keys():
- volume = volume_cache[volume_id].display_name
- return volume
-
- if parsed_args.long:
- columns = ['ID', 'Display Name', 'Display Description', 'Status',
- 'Size', 'Created At', 'Volume ID', 'Metadata']
- column_headers = copy.deepcopy(columns)
- column_headers[6] = 'Volume'
- column_headers[7] = 'Properties'
- else:
- columns = ['ID', 'Display Name', 'Display Description', 'Status',
- 'Size']
- column_headers = copy.deepcopy(columns)
-
- # Always update Name and Description
- column_headers[1] = 'Name'
- column_headers[2] = 'Description'
-
- # Cache the volume list
- volume_cache = {}
- try:
- for s in self.app.client_manager.volume.volumes.list():
- volume_cache[s.id] = s
- except Exception:
- # Just forget it if there's any trouble
- pass
-
- search_opts = {
- 'all_tenants': parsed_args.all_projects,
- }
-
- data = self.app.client_manager.volume.volume_snapshots.list(
- search_opts=search_opts)
- return (column_headers,
- (utils.get_item_properties(
- s, columns,
- formatters={'Metadata': utils.format_dict,
- 'Volume ID': _format_volume_id},
- ) for s in data))
-
-
-class SetSnapshot(command.Command):
- _description = _("Set snapshot properties")
-
- def get_parser(self, prog_name):
- parser = super(SetSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'snapshot',
- metavar='<snapshot>',
- help=_('Snapshot to modify (name or ID)')
- )
- parser.add_argument(
- '--name',
- metavar='<name>',
- help=_('New snapshot name')
- )
- parser.add_argument(
- '--description',
- metavar='<description>',
- help=_('New snapshot description')
- )
- parser.add_argument(
- '--property',
- metavar='<key=value>',
- action=parseractions.KeyValueAction,
- help=_('Property to add/change for this snapshot '
- '(repeat option to set multiple properties)'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot set" instead.'))
- volume_client = self.app.client_manager.volume
- snapshot = utils.find_resource(volume_client.volume_snapshots,
- parsed_args.snapshot)
-
- result = 0
- if parsed_args.property:
- try:
- volume_client.volume_snapshots.set_metadata(
- snapshot.id, parsed_args.property)
- except Exception as e:
- LOG.error(_("Failed to set snapshot property: %s"), e)
- result += 1
-
- kwargs = {}
- if parsed_args.name:
- kwargs['display_name'] = parsed_args.name
- if parsed_args.description:
- kwargs['display_description'] = parsed_args.description
- if kwargs:
- try:
- snapshot.update(**kwargs)
- except Exception as e:
- LOG.error(_("Failed to update snapshot display name "
- "or display description: %s"), e)
- result += 1
-
- if result > 0:
- raise exceptions.CommandError(_("One or more of the "
- "set operations failed"))
-
-
-class ShowSnapshot(command.ShowOne):
- _description = _("Display snapshot details")
-
- def get_parser(self, prog_name):
- parser = super(ShowSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'snapshot',
- metavar='<snapshot>',
- help=_('Snapshot to display (name or ID)')
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot show" instead.'))
- volume_client = self.app.client_manager.volume
- snapshot = utils.find_resource(volume_client.volume_snapshots,
- parsed_args.snapshot)
-
- snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
- )
-
- return zip(*sorted(six.iteritems(snapshot._info)))
-
-
-class UnsetSnapshot(command.Command):
- _description = _("Unset snapshot properties")
-
- def get_parser(self, prog_name):
- parser = super(UnsetSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'snapshot',
- metavar='<snapshot>',
- help=_('Snapshot to modify (name or ID)'),
- )
- parser.add_argument(
- '--property',
- metavar='<key>',
- action='append',
- help=_('Property to remove from snapshot '
- '(repeat option to remove multiple properties)'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot unset" instead.'))
- volume_client = self.app.client_manager.volume
- snapshot = utils.find_resource(
- volume_client.volume_snapshots, parsed_args.snapshot)
-
- if parsed_args.property:
- volume_client.volume_snapshots.delete_metadata(
- snapshot.id,
- parsed_args.property,
- )
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index b29429e..36c7ef8 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -16,8 +16,11 @@
"""Volume v1 Volume action implementations"""
import argparse
+import functools
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -30,6 +33,37 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
+class AttachmentsColumn(cliff_columns.FormattableColumn):
+ """Formattable column for attachments column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes server_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(AttachmentsColumn, server_cache)``.
+ """
+
+ def __init__(self, value, server_cache=None):
+ super(AttachmentsColumn, self).__init__(value)
+ self._server_cache = server_cache or {}
+
+ def human_readable(self):
+ """Return a formatted string of a volume's attached instances
+
+ :rtype: a string of formatted instances
+ """
+
+ msg = ''
+ for attachment in self._value:
+ server = attachment['server_id']
+ if server in self._server_cache.keys():
+ server = self._server_cache[server].name
+ device = attachment['device']
+ msg += 'Attached to %s on %s ' % (server, device)
+ return msg
+
+
def _check_size_arg(args):
"""Check whether --size option is required or not.
@@ -207,7 +241,8 @@ class CreateVolume(command.ShowOne):
# Map 'metadata' column to 'properties'
volume._info.update(
{
- 'properties': utils.format_dict(volume._info.pop('metadata')),
+ 'properties':
+ format_columns.DictColumn(volume._info.pop('metadata')),
'type': volume._info.pop('volume_type'),
},
)
@@ -307,22 +342,6 @@ class ListVolume(command.Lister):
volume_client = self.app.client_manager.volume
compute_client = self.app.client_manager.compute
- def _format_attach(attachments):
- """Return a formatted string of a volume's attached instances
-
- :param attachments: a volume.attachments field
- :rtype: a string of formatted instances
- """
-
- msg = ''
- for attachment in attachments:
- server = attachment['server_id']
- if server in server_cache.keys():
- server = server_cache[server].name
- device = attachment['device']
- msg += 'Attached to %s on %s ' % (server, device)
- return msg
-
if parsed_args.long:
columns = (
'ID',
@@ -368,6 +387,8 @@ class ListVolume(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ AttachmentsColumnWithCache = functools.partial(
+ AttachmentsColumn, server_cache=server_cache)
search_opts = {
'all_tenants': parsed_args.all_projects,
@@ -385,8 +406,8 @@ class ListVolume(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Metadata': utils.format_dict,
- 'Attachments': _format_attach},
+ formatters={'Metadata': format_columns.DictColumn,
+ 'Attachments': AttachmentsColumnWithCache},
) for s in data))
@@ -575,7 +596,8 @@ class ShowVolume(command.ShowOne):
# Map 'metadata' column to 'properties'
volume._info.update(
{
- 'properties': utils.format_dict(volume._info.pop('metadata')),
+ 'properties':
+ format_columns.DictColumn(volume._info.pop('metadata')),
'type': volume._info.pop('volume_type'),
},
)
diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/volume_backup.py
index 9ac1302..2daa23c 100644
--- a/openstackclient/volume/v1/backup.py
+++ b/openstackclient/volume/v1/volume_backup.py
@@ -16,8 +16,10 @@
"""Volume v1 Backup action implementations"""
import copy
+import functools
import logging
+from cliff import columns as cliff_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -29,6 +31,33 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
+class VolumeIdColumn(cliff_columns.FormattableColumn):
+ """Formattable column for volume ID column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes volume_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(VolumeIdColumn, volume_cache)``.
+ """
+
+ def __init__(self, value, volume_cache=None):
+ super(VolumeIdColumn, self).__init__(value)
+ self._volume_cache = volume_cache or {}
+
+ def human_readable(self):
+ """Return a volume name if available
+
+ :rtype: either the volume ID or name
+ """
+ volume_id = self._value
+ volume = volume_id
+ if volume_id in self._volume_cache.keys():
+ volume = self._volume_cache[volume_id].display_name
+ return volume
+
+
class CreateVolumeBackup(command.ShowOne):
_description = _("Create new volume backup")
@@ -72,23 +101,6 @@ class CreateVolumeBackup(command.ShowOne):
return zip(*sorted(six.iteritems(backup._info)))
-class CreateBackup(CreateVolumeBackup):
- _description = _("Create new backup")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup create`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup create" instead.'))
- return super(CreateBackup, self).take_action(parsed_args)
-
-
class DeleteVolumeBackup(command.Command):
_description = _("Delete volume backup(s)")
@@ -124,23 +136,6 @@ class DeleteVolumeBackup(command.Command):
raise exceptions.CommandError(msg)
-class DeleteBackup(DeleteVolumeBackup):
- _description = _("Delete backup(s)")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup delete`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup delete" instead.'))
- return super(DeleteBackup, self).take_action(parsed_args)
-
-
class ListVolumeBackup(command.Lister):
_description = _("List volume backups")
@@ -183,18 +178,6 @@ class ListVolumeBackup(command.Lister):
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- def _format_volume_id(volume_id):
- """Return a volume name if available
-
- :param volume_id: a volume ID
- :rtype: either the volume ID or name
- """
-
- volume = volume_id
- if volume_id in volume_cache.keys():
- volume = volume_cache[volume_id].display_name
- return volume
-
if parsed_args.long:
columns = ['ID', 'Name', 'Description', 'Status', 'Size',
'Availability Zone', 'Volume ID', 'Container']
@@ -212,6 +195,8 @@ class ListVolumeBackup(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ VolumeIdColumnWithCache = functools.partial(VolumeIdColumn,
+ volume_cache=volume_cache)
filter_volume_id = None
if parsed_args.volume:
@@ -230,27 +215,10 @@ class ListVolumeBackup(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Volume ID': _format_volume_id},
+ formatters={'Volume ID': VolumeIdColumnWithCache},
) for s in data))
-class ListBackup(ListVolumeBackup):
- _description = _("List backups")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup list`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup list" instead.'))
- return super(ListBackup, self).take_action(parsed_args)
-
-
class RestoreVolumeBackup(command.Command):
_description = _("Restore volume backup")
@@ -278,23 +246,6 @@ class RestoreVolumeBackup(command.Command):
destination_volume.id)
-class RestoreBackup(RestoreVolumeBackup):
- _description = _("Restore backup")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup restore`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup restore" instead.'))
- return super(RestoreBackup, self).take_action(parsed_args)
-
-
class ShowVolumeBackup(command.ShowOne):
_description = _("Display volume backup details")
@@ -313,20 +264,3 @@ class ShowVolumeBackup(command.ShowOne):
parsed_args.backup)
backup._info.pop('links')
return zip(*sorted(six.iteritems(backup._info)))
-
-
-class ShowBackup(ShowVolumeBackup):
- _description = _("Display backup details")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup show`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup show" instead.'))
- return super(ShowBackup, self).take_action(parsed_args)
diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py
index 3e83da5..50f8177 100644
--- a/openstackclient/volume/v1/volume_snapshot.py
+++ b/openstackclient/volume/v1/volume_snapshot.py
@@ -16,8 +16,11 @@
"""Volume v1 Snapshot action implementations"""
import copy
+import functools
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -30,6 +33,33 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
+class VolumeIdColumn(cliff_columns.FormattableColumn):
+ """Formattable column for volume ID column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes volume_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(VolumeIdColumn, volume_cache)``.
+ """
+
+ def __init__(self, value, volume_cache=None):
+ super(VolumeIdColumn, self).__init__(value)
+ self._volume_cache = volume_cache or {}
+
+ def human_readable(self):
+ """Return a volume name if available
+
+ :rtype: either the volume ID or name
+ """
+ volume_id = self._value
+ volume = volume_id
+ if volume_id in self._volume_cache.keys():
+ volume = self._volume_cache[volume_id].display_name
+ return volume
+
+
class CreateVolumeSnapshot(command.ShowOne):
_description = _("Create new volume snapshot")
@@ -76,7 +106,8 @@ class CreateVolumeSnapshot(command.ShowOne):
)
snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ {'properties':
+ format_columns.DictColumn(snapshot._info.pop('metadata'))}
)
return zip(*sorted(six.iteritems(snapshot._info)))
@@ -160,18 +191,6 @@ class ListVolumeSnapshot(command.Lister):
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- def _format_volume_id(volume_id):
- """Return a volume name if available
-
- :param volume_id: a volume ID
- :rtype: either the volume ID or name
- """
-
- volume = volume_id
- if volume_id in volume_cache.keys():
- volume = volume_cache[volume_id].display_name
- return volume
-
if parsed_args.long:
columns = ['ID', 'Display Name', 'Display Description', 'Status',
'Size', 'Created At', 'Volume ID', 'Metadata']
@@ -195,6 +214,8 @@ class ListVolumeSnapshot(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ VolumeIdColumnWithCache = functools.partial(VolumeIdColumn,
+ volume_cache=volume_cache)
volume_id = None
if parsed_args.volume:
@@ -213,8 +234,8 @@ class ListVolumeSnapshot(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Metadata': utils.format_dict,
- 'Volume ID': _format_volume_id},
+ formatters={'Metadata': format_columns.DictColumn,
+ 'Volume ID': VolumeIdColumnWithCache},
) for s in data))
@@ -317,7 +338,8 @@ class ShowVolumeSnapshot(command.ShowOne):
parsed_args.snapshot)
snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ {'properties':
+ format_columns.DictColumn(snapshot._info.pop('metadata'))}
)
return zip(*sorted(six.iteritems(snapshot._info)))
diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py
index f5b567b..6f79658 100644
--- a/openstackclient/volume/v1/volume_transfer_request.py
+++ b/openstackclient/volume/v1/volume_transfer_request.py
@@ -14,7 +14,6 @@
"""Volume v1 transfer action implementations"""
-import argparse
import logging
from osc_lib.command import command
@@ -39,12 +38,6 @@ class AcceptTransferRequest(command.ShowOne):
help=_('Volume transfer request to accept (ID only)'),
)
parser.add_argument(
- 'old_auth_key',
- metavar="<key>",
- nargs="?",
- help=argparse.SUPPRESS,
- )
- parser.add_argument(
'--auth-key',
metavar="<key>",
help=_('Volume transfer request authentication key'),
@@ -64,20 +57,9 @@ class AcceptTransferRequest(command.ShowOne):
# move on and attempt with the user-supplied information
transfer_request_id = parsed_args.transfer_request
- # Remain backward-compatible for the previous command layout
- # TODO(dtroyer): Remove this back-compat in 4.0 or Oct 2017
if not parsed_args.auth_key:
- if parsed_args.old_auth_key:
- # Move the old one into the correct place
- parsed_args.auth_key = parsed_args.old_auth_key
- self.log.warning(_(
- 'Specifying the auth-key as a positional argument '
- 'has been deprecated. Please use the --auth-key '
- 'option in the future.'
- ))
- else:
- msg = _("argument --auth-key is required")
- raise exceptions.CommandError(msg)
+ msg = _("argument --auth-key is required")
+ raise exceptions.CommandError(msg)
transfer_accept = volume_client.transfers.accept(
transfer_request_id,
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index b4d8eac..e744e92 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -15,8 +15,11 @@
"""Volume v1 Type action implementations"""
+import functools
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -29,6 +32,36 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
+class EncryptionInfoColumn(cliff_columns.FormattableColumn):
+ """Formattable column for encryption info column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes encryption_data as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(EncryptionInfoColumn encryption_data)``.
+ """
+
+ def __init__(self, value, encryption_data=None):
+ super(EncryptionInfoColumn, self).__init__(value)
+ self._encryption_data = encryption_data or {}
+
+ def _get_encryption_info(self):
+ type_id = self._value
+ return self._encryption_data.get(type_id)
+
+ def human_readable(self):
+ encryption_info = self._get_encryption_info()
+ if encryption_info:
+ return utils.format_dict(encryption_info)
+ else:
+ return '-'
+
+ def machine_readable(self):
+ return self._get_encryption_info()
+
+
def _create_encryption_type(volume_client, volume_type, parsed_args):
if not parsed_args.encryption_provider:
msg = _("'--encryption-provider' should be specified while "
@@ -110,7 +143,8 @@ class CreateVolumeType(command.ShowOne):
volume_type._info.pop('extra_specs')
if parsed_args.property:
result = volume_type.set_keys(parsed_args.property)
- volume_type._info.update({'properties': utils.format_dict(result)})
+ volume_type._info.update(
+ {'properties': format_columns.DictColumn(result)})
if (parsed_args.encryption_provider or
parsed_args.encryption_cipher or
parsed_args.encryption_key_size or
@@ -125,7 +159,7 @@ class CreateVolumeType(command.ShowOne):
# add encryption info in result
encryption._info.pop("volume_type_id", None)
volume_type._info.update(
- {'encryption': utils.format_dict(encryption._info)})
+ {'encryption': format_columns.DictColumn(encryption._info)})
volume_type._info.pop("os-volume-type-access:is_public", None)
return zip(*sorted(six.iteritems(volume_type._info)))
@@ -196,12 +230,7 @@ class ListVolumeType(command.Lister):
column_headers = ['ID', 'Name', 'Is Public']
data = volume_client.volume_types.list()
- def _format_encryption_info(type_id, encryption_data=None):
- encryption_data = encryption
- encryption_info = '-'
- if type_id in encryption_data.keys():
- encryption_info = encryption_data[type_id]
- return encryption_info
+ formatters = {'Extra Specs': format_columns.DictColumn}
if parsed_args.encryption_type:
encryption = {}
@@ -218,18 +247,21 @@ class ListVolumeType(command.Lister):
for key in del_key:
d._info.pop(key, None)
# save the encryption information with their volume type ID
- encryption[volume_type_id] = utils.format_dict(d._info)
+ encryption[volume_type_id] = d._info
# We need to get volume type ID, then show encryption
# information according to the ID, so use "id" to keep
# difference to the real "ID" column.
columns += ['id']
column_headers += ['Encryption']
+ _EncryptionInfoColumn = functools.partial(
+ EncryptionInfoColumn, encryption_data=encryption)
+ formatters['id'] = _EncryptionInfoColumn
+
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Extra Specs': utils.format_dict,
- 'id': _format_encryption_info},
+ formatters=formatters,
) for s in data))
@@ -340,7 +372,8 @@ class ShowVolumeType(command.ShowOne):
volume_client = self.app.client_manager.volume
volume_type = utils.find_resource(
volume_client.volume_types, parsed_args.volume_type)
- properties = utils.format_dict(volume_type._info.pop('extra_specs'))
+ properties = format_columns.DictColumn(
+ volume_type._info.pop('extra_specs'))
volume_type._info.update({'properties': properties})
if parsed_args.encryption_type:
# show encryption type information for this volume type
@@ -349,7 +382,8 @@ class ShowVolumeType(command.ShowOne):
volume_type.id)
encryption._info.pop("volume_type_id", None)
volume_type._info.update(
- {'encryption': utils.format_dict(encryption._info)})
+ {'encryption':
+ format_columns.DictColumn(encryption._info)})
except Exception as e:
LOG.error(_("Failed to display the encryption information "
"of this volume type: %s"), e)
diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py
index 0a932f8..26dd8ff 100644
--- a/openstackclient/volume/v2/consistency_group.py
+++ b/openstackclient/volume/v2/consistency_group.py
@@ -16,6 +16,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -238,7 +239,7 @@ class ListConsistencyGroup(command.Lister):
return (columns, (
utils.get_item_properties(
s, columns,
- formatters={'Volume Types': utils.format_list})
+ formatters={'Volume Types': format_columns.ListColumn})
for s in consistency_groups))
diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py
index c716058..3037d34 100644
--- a/openstackclient/volume/v2/qos_specs.py
+++ b/openstackclient/volume/v2/qos_specs.py
@@ -17,6 +17,7 @@
import logging
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -96,7 +97,8 @@ class CreateQos(command.ShowOne):
qos_spec = volume_client.qos_specs.create(parsed_args.name, specs)
qos_spec._info.update(
- {'properties': utils.format_dict(qos_spec._info.pop('specs'))}
+ {'properties':
+ format_columns.DictColumn(qos_spec._info.pop('specs'))}
)
return zip(*sorted(six.iteritems(qos_spec._info)))
@@ -210,8 +212,8 @@ class ListQos(command.Lister):
(utils.get_dict_properties(
s._info, columns,
formatters={
- 'Specs': utils.format_dict,
- 'Associations': utils.format_list
+ 'Specs': format_columns.DictColumn,
+ 'Associations': format_columns.ListColumn
},
) for s in qos_specs_list))
@@ -267,10 +269,11 @@ class ShowQos(command.ShowOne):
associations = [association.name
for association in qos_associations]
qos_spec._info.update({
- 'associations': utils.format_list(associations)
+ 'associations': format_columns.ListColumn(associations)
})
qos_spec._info.update(
- {'properties': utils.format_dict(qos_spec._info.pop('specs'))})
+ {'properties':
+ format_columns.DictColumn(qos_spec._info.pop('specs'))})
return zip(*sorted(six.iteritems(qos_spec._info)))
diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py
deleted file mode 100644
index 82b3103..0000000
--- a/openstackclient/volume/v2/snapshot.py
+++ /dev/null
@@ -1,351 +0,0 @@
-#
-# 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.
-#
-
-# TODO(Huanxuan Ao): Remove this file and "snapshot create", "snapshot delete",
-# "snapshot set", "snapshot show" and "snapshot unset"
-# commands two cycles after Ocata.
-
-"""Volume v2 snapshot action implementations"""
-
-import copy
-import logging
-
-from osc_lib.cli import parseractions
-from osc_lib.command import command
-from osc_lib import exceptions
-from osc_lib import utils
-import six
-
-from openstackclient.i18n import _
-
-
-deprecated = True
-LOG_DEP = logging.getLogger('deprecated')
-LOG = logging.getLogger(__name__)
-
-
-class CreateSnapshot(command.ShowOne):
- _description = _("Create new snapshot")
-
- def get_parser(self, prog_name):
- parser = super(CreateSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- "volume",
- metavar="<volume>",
- help=_("Volume to snapshot (name or ID)")
- )
- parser.add_argument(
- "--name",
- metavar="<name>",
- help=_("Name of the snapshot")
- )
- parser.add_argument(
- "--description",
- metavar="<description>",
- help=_("Description of the snapshot")
- )
- parser.add_argument(
- "--force",
- action="store_true",
- default=False,
- help=_("Create a snapshot attached to an instance. "
- "Default is False")
- )
- parser.add_argument(
- "--property",
- metavar="<key=value>",
- action=parseractions.KeyValueAction,
- help=_("Set a property to this snapshot "
- "(repeat option to set multiple properties)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot create" instead.'))
- volume_client = self.app.client_manager.volume
- volume_id = utils.find_resource(
- volume_client.volumes, parsed_args.volume).id
- snapshot = volume_client.volume_snapshots.create(
- volume_id,
- force=parsed_args.force,
- name=parsed_args.name,
- description=parsed_args.description,
- metadata=parsed_args.property,
- )
- snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
- )
- return zip(*sorted(six.iteritems(snapshot._info)))
-
-
-class DeleteSnapshot(command.Command):
- _description = _("Delete volume snapshot(s)")
-
- def get_parser(self, prog_name):
- parser = super(DeleteSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- "snapshots",
- metavar="<snapshot>",
- nargs="+",
- help=_("Snapshot(s) to delete (name or ID)")
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot delete" instead.'))
- volume_client = self.app.client_manager.volume
- result = 0
-
- for i in parsed_args.snapshots:
- try:
- snapshot_id = utils.find_resource(
- volume_client.volume_snapshots, i).id
- volume_client.volume_snapshots.delete(snapshot_id)
- except Exception as e:
- result += 1
- LOG.error(_("Failed to delete snapshot with "
- "name or ID '%(snapshot)s': %(e)s")
- % {'snapshot': i, 'e': e})
-
- if result > 0:
- total = len(parsed_args.snapshots)
- msg = (_("%(result)s of %(total)s snapshots failed "
- "to delete.") % {'result': result, 'total': total})
- raise exceptions.CommandError(msg)
-
-
-class ListSnapshot(command.Lister):
- _description = _("List snapshots")
-
- def get_parser(self, prog_name):
- parser = super(ListSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- '--all-projects',
- action='store_true',
- default=False,
- help=_('Include all projects (admin only)'),
- )
- parser.add_argument(
- '--long',
- action='store_true',
- default=False,
- help=_('List additional fields in output'),
- )
- parser.add_argument(
- '--marker',
- metavar='<snapshot>',
- help=_('The last snapshot ID of the previous page'),
- )
- parser.add_argument(
- '--limit',
- type=int,
- action=parseractions.NonNegativeAction,
- metavar='<num-snapshots>',
- help=_('Maximum number of snapshots to display'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot list" instead.'))
-
- def _format_volume_id(volume_id):
- """Return a volume name if available
-
- :param volume_id: a volume ID
- :rtype: either the volume ID or name
- """
-
- volume = volume_id
- if volume_id in volume_cache.keys():
- volume = volume_cache[volume_id].name
- return volume
-
- if parsed_args.long:
- columns = ['ID', 'Name', 'Description', 'Status',
- 'Size', 'Created At', 'Volume ID', 'Metadata']
- column_headers = copy.deepcopy(columns)
- column_headers[6] = 'Volume'
- column_headers[7] = 'Properties'
- else:
- columns = ['ID', 'Name', 'Description', 'Status', 'Size']
- column_headers = copy.deepcopy(columns)
-
- # Cache the volume list
- volume_cache = {}
- try:
- for s in self.app.client_manager.volume.volumes.list():
- volume_cache[s.id] = s
- except Exception:
- # Just forget it if there's any trouble
- pass
-
- search_opts = {
- 'all_tenants': parsed_args.all_projects,
- }
-
- data = self.app.client_manager.volume.volume_snapshots.list(
- search_opts=search_opts,
- marker=parsed_args.marker,
- limit=parsed_args.limit,
- )
- return (column_headers,
- (utils.get_item_properties(
- s, columns,
- formatters={'Metadata': utils.format_dict,
- 'Volume ID': _format_volume_id},
- ) for s in data))
-
-
-class SetSnapshot(command.Command):
- _description = _("Set snapshot properties")
-
- def get_parser(self, prog_name):
- parser = super(SetSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'snapshot',
- metavar='<snapshot>',
- help=_('Snapshot to modify (name or ID)')
- )
- parser.add_argument(
- '--name',
- metavar='<name>',
- help=_('New snapshot name')
- )
- parser.add_argument(
- '--description',
- metavar='<description>',
- help=_('New snapshot description')
- )
- parser.add_argument(
- '--property',
- metavar='<key=value>',
- action=parseractions.KeyValueAction,
- help=_('Property to add/change for this snapshot '
- '(repeat option to set multiple properties)'),
- )
- parser.add_argument(
- '--state',
- metavar='<state>',
- choices=['available', 'error', 'creating', 'deleting',
- 'error-deleting'],
- help=_('New snapshot state. ("available", "error", "creating", '
- '"deleting", or "error_deleting") (admin only) '
- '(This option simply changes the state of the snapshot '
- 'in the database with no regard to actual status, '
- 'exercise caution when using)'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot set" instead.'))
- volume_client = self.app.client_manager.volume
- snapshot = utils.find_resource(volume_client.volume_snapshots,
- parsed_args.snapshot)
-
- result = 0
- if parsed_args.property:
- try:
- volume_client.volume_snapshots.set_metadata(
- snapshot.id, parsed_args.property)
- except Exception as e:
- LOG.error(_("Failed to set snapshot property: %s"), e)
- result += 1
-
- if parsed_args.state:
- try:
- volume_client.volume_snapshots.reset_state(
- snapshot.id, parsed_args.state)
- except Exception as e:
- LOG.error(_("Failed to set snapshot state: %s"), e)
- result += 1
-
- kwargs = {}
- if parsed_args.name:
- kwargs['name'] = parsed_args.name
- if parsed_args.description:
- kwargs['description'] = parsed_args.description
- if kwargs:
- try:
- volume_client.volume_snapshots.update(
- snapshot.id, **kwargs)
- except Exception as e:
- LOG.error(_("Failed to update snapshot name "
- "or description: %s"), e)
- result += 1
-
- if result > 0:
- raise exceptions.CommandError(_("One or more of the "
- "set operations failed"))
-
-
-class ShowSnapshot(command.ShowOne):
- _description = _("Display snapshot details")
-
- def get_parser(self, prog_name):
- parser = super(ShowSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- "snapshot",
- metavar="<snapshot>",
- help=_("Snapshot to display (name or ID)")
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot show" instead.'))
- volume_client = self.app.client_manager.volume
- snapshot = utils.find_resource(
- volume_client.volume_snapshots, parsed_args.snapshot)
- snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
- )
- return zip(*sorted(six.iteritems(snapshot._info)))
-
-
-class UnsetSnapshot(command.Command):
- _description = _("Unset snapshot properties")
-
- def get_parser(self, prog_name):
- parser = super(UnsetSnapshot, self).get_parser(prog_name)
- parser.add_argument(
- 'snapshot',
- metavar='<snapshot>',
- help=_('Snapshot to modify (name or ID)'),
- )
- parser.add_argument(
- '--property',
- metavar='<key>',
- action='append',
- default=[],
- help=_('Property to remove from snapshot '
- '(repeat option to remove multiple properties)'),
- )
- return parser
-
- def take_action(self, parsed_args):
- LOG_DEP.warning(_('This command has been deprecated. '
- 'Please use "volume snapshot unset" instead.'))
- volume_client = self.app.client_manager.volume
- snapshot = utils.find_resource(
- volume_client.volume_snapshots, parsed_args.snapshot)
-
- if parsed_args.property:
- volume_client.volume_snapshots.delete_metadata(
- snapshot.id,
- parsed_args.property,
- )
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index fa587b5..17ccd3d 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -16,8 +16,11 @@
import argparse
import copy
+import functools
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -31,6 +34,37 @@ from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
+class AttachmentsColumn(cliff_columns.FormattableColumn):
+ """Formattable column for attachments column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes server_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(AttachmentsColumn, server_cache)``.
+ """
+
+ def __init__(self, value, server_cache=None):
+ super(AttachmentsColumn, self).__init__(value)
+ self._server_cache = server_cache or {}
+
+ def human_readable(self):
+ """Return a formatted string of a volume's attached instances
+
+ :rtype: a string of formatted instances
+ """
+
+ msg = ''
+ for attachment in self._value:
+ server = attachment['server_id']
+ if server in self._server_cache.keys():
+ server = self._server_cache[server].name
+ device = attachment['device']
+ msg += 'Attached to %s on %s ' % (server, device)
+ return msg
+
+
def _check_size_arg(args):
"""Check whether --size option is required or not.
@@ -94,16 +128,6 @@ class CreateVolume(command.ShowOne):
help=_("Volume description"),
)
parser.add_argument(
- '--user',
- metavar='<user>',
- help=argparse.SUPPRESS,
- )
- parser.add_argument(
- '--project',
- metavar='<project>',
- help=argparse.SUPPRESS,
- )
- parser.add_argument(
"--availability-zone",
metavar="<availability-zone>",
help=_("Create volume in <availability-zone>"),
@@ -127,12 +151,6 @@ class CreateVolume(command.ShowOne):
help=_("Arbitrary scheduler hint key-value pairs to help boot "
"an instance (repeat option to set multiple hints)"),
)
- parser.add_argument(
- "--multi-attach",
- action="store_true",
- help=_("Allow volume to be attached more than once "
- "(default to False)")
- )
bootable_group = parser.add_mutually_exclusive_group()
bootable_group.add_argument(
"--bootable",
@@ -196,26 +214,6 @@ class CreateVolume(command.ShowOne):
# snapshot size.
size = max(size or 0, snapshot_obj.size)
- # NOTE(abishop): Cinder's volumes.create() has 'project_id' and
- # 'user_id' args, but they're not wired up to anything. The only way
- # to specify an alternate project or user for the volume is to use
- # the identity overrides (e.g. "--os-project-id").
- #
- # Now, if the project or user arg is specified then the command is
- # rejected. Otherwise, Cinder would actually create a volume, but
- # without the specified property.
- if parsed_args.project:
- raise exceptions.CommandError(
- _("ERROR: --project is deprecated, please use"
- " --os-project-name or --os-project-id instead."))
- if parsed_args.user:
- raise exceptions.CommandError(
- _("ERROR: --user is deprecated, please use"
- " --os-username instead."))
- if parsed_args.multi_attach:
- LOG.warning(_("'--multi-attach' option is no longer supported by "
- "the block storage service."))
-
volume = volume_client.volumes.create(
size=size,
snapshot_id=snapshot,
@@ -248,7 +246,8 @@ class CreateVolume(command.ShowOne):
# Remove key links from being displayed
volume._info.update(
{
- 'properties': utils.format_dict(volume._info.pop('metadata')),
+ 'properties':
+ format_columns.DictColumn(volume._info.pop('metadata')),
'type': volume._info.pop('volume_type')
}
)
@@ -367,22 +366,6 @@ class ListVolume(command.Lister):
compute_client = self.app.client_manager.compute
identity_client = self.app.client_manager.identity
- def _format_attach(attachments):
- """Return a formatted string of a volume's attached instances
-
- :param attachments: a volume.attachments field
- :rtype: a string of formatted instances
- """
-
- msg = ''
- for attachment in attachments:
- server = attachment['server_id']
- if server in server_cache:
- server = server_cache[server].name
- device = attachment['device']
- msg += 'Attached to %s on %s ' % (server, device)
- return msg
-
if parsed_args.long:
columns = [
'ID',
@@ -417,6 +400,8 @@ class ListVolume(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ AttachmentsColumnWithCache = functools.partial(
+ AttachmentsColumn, server_cache=server_cache)
project_id = None
if parsed_args.project:
@@ -453,8 +438,8 @@ class ListVolume(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Metadata': utils.format_dict,
- 'Attachments': _format_attach},
+ formatters={'Metadata': format_columns.DictColumn,
+ 'Attachments': AttachmentsColumnWithCache},
) for s in data))
@@ -758,7 +743,8 @@ class ShowVolume(command.ShowOne):
# 'volume_type' --> 'type'
volume._info.update(
{
- 'properties': utils.format_dict(volume._info.pop('metadata')),
+ 'properties':
+ format_columns.DictColumn(volume._info.pop('metadata')),
'type': volume._info.pop('volume_type'),
},
)
diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/volume_backup.py
index d4aec8d..4d0d54c 100644
--- a/openstackclient/volume/v2/backup.py
+++ b/openstackclient/volume/v2/volume_backup.py
@@ -15,8 +15,10 @@
"""Volume v2 Backup action implementations"""
import copy
+import functools
import logging
+from cliff import columns as cliff_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -29,6 +31,33 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
+class VolumeIdColumn(cliff_columns.FormattableColumn):
+ """Formattable column for volume ID column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes volume_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(VolumeIdColumn, volume_cache)``.
+ """
+
+ def __init__(self, value, volume_cache=None):
+ super(VolumeIdColumn, self).__init__(value)
+ self._volume_cache = volume_cache or {}
+
+ def human_readable(self):
+ """Return a volume name if available
+
+ :rtype: either the volume ID or name
+ """
+ volume_id = self._value
+ volume = volume_id
+ if volume_id in self._volume_cache.keys():
+ volume = self._volume_cache[volume_id].name
+ return volume
+
+
class CreateVolumeBackup(command.ShowOne):
_description = _("Create new volume backup")
@@ -94,23 +123,6 @@ class CreateVolumeBackup(command.ShowOne):
return zip(*sorted(six.iteritems(backup._info)))
-class CreateBackup(CreateVolumeBackup):
- _description = _("Create new backup")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup create`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup create" instead.'))
- return super(CreateBackup, self).take_action(parsed_args)
-
-
class DeleteVolumeBackup(command.Command):
_description = _("Delete volume backup(s)")
@@ -152,23 +164,6 @@ class DeleteVolumeBackup(command.Command):
raise exceptions.CommandError(msg)
-class DeleteBackup(DeleteVolumeBackup):
- _description = _("Delete backup(s)")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup delete`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup delete" instead.'))
- return super(DeleteBackup, self).take_action(parsed_args)
-
-
class ListVolumeBackup(command.Lister):
_description = _("List volume backups")
@@ -223,18 +218,6 @@ class ListVolumeBackup(command.Lister):
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- def _format_volume_id(volume_id):
- """Return a volume name if available
-
- :param volume_id: a volume ID
- :rtype: either the volume ID or name
- """
-
- volume = volume_id
- if volume_id in volume_cache.keys():
- volume = volume_cache[volume_id].name
- return volume
-
if parsed_args.long:
columns = ['ID', 'Name', 'Description', 'Status', 'Size',
'Availability Zone', 'Volume ID', 'Container']
@@ -252,6 +235,8 @@ class ListVolumeBackup(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ _VolumeIdColumn = functools.partial(VolumeIdColumn,
+ volume_cache=volume_cache)
filter_volume_id = None
if parsed_args.volume:
@@ -276,27 +261,10 @@ class ListVolumeBackup(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Volume ID': _format_volume_id},
+ formatters={'Volume ID': _VolumeIdColumn},
) for s in data))
-class ListBackup(ListVolumeBackup):
- _description = _("List backups")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup list`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup list" instead.'))
- return super(ListBackup, self).take_action(parsed_args)
-
-
class RestoreVolumeBackup(command.ShowOne):
_description = _("Restore volume backup")
@@ -324,23 +292,6 @@ class RestoreVolumeBackup(command.ShowOne):
return zip(*sorted(six.iteritems(backup._info)))
-class RestoreBackup(RestoreVolumeBackup):
- _description = _("Restore backup")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup restore`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup restore" instead.'))
- return super(RestoreBackup, self).take_action(parsed_args)
-
-
class SetVolumeBackup(command.Command):
_description = _("Set volume backup properties")
@@ -421,20 +372,3 @@ class ShowVolumeBackup(command.ShowOne):
parsed_args.backup)
backup._info.pop("links", None)
return zip(*sorted(six.iteritems(backup._info)))
-
-
-class ShowBackup(ShowVolumeBackup):
- _description = _("Display backup details")
-
- # TODO(Huanxuan Ao): Remove this class and ``backup show`` command
- # two cycles after Newton.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action(self, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "volume backup show" instead.'))
- return super(ShowBackup, self).take_action(parsed_args)
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
index fe96941..2b26ae3 100644
--- a/openstackclient/volume/v2/volume_snapshot.py
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -15,8 +15,11 @@
"""Volume v2 snapshot action implementations"""
import copy
+import functools
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -30,6 +33,33 @@ from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
+class VolumeIdColumn(cliff_columns.FormattableColumn):
+ """Formattable column for volume ID column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes volume_cache as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(VolumeIdColumn, volume_cache)``.
+ """
+
+ def __init__(self, value, volume_cache=None):
+ super(VolumeIdColumn, self).__init__(value)
+ self._volume_cache = volume_cache or {}
+
+ def human_readable(self):
+ """Return a volume name if available
+
+ :rtype: either the volume ID or name
+ """
+ volume_id = self._value
+ volume = volume_id
+ if volume_id in self._volume_cache.keys():
+ volume = self._volume_cache[volume_id].name
+ return volume
+
+
class CreateVolumeSnapshot(command.ShowOne):
_description = _("Create new volume snapshot")
@@ -107,7 +137,8 @@ class CreateVolumeSnapshot(command.ShowOne):
metadata=parsed_args.property,
)
snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ {'properties':
+ format_columns.DictColumn(snapshot._info.pop('metadata'))}
)
return zip(*sorted(six.iteritems(snapshot._info)))
@@ -216,18 +247,6 @@ class ListVolumeSnapshot(command.Lister):
volume_client = self.app.client_manager.volume
identity_client = self.app.client_manager.identity
- def _format_volume_id(volume_id):
- """Return a volume name if available
-
- :param volume_id: a volume ID
- :rtype: either the volume ID or name
- """
-
- volume = volume_id
- if volume_id in volume_cache.keys():
- volume = volume_cache[volume_id].name
- return volume
-
if parsed_args.long:
columns = ['ID', 'Name', 'Description', 'Status',
'Size', 'Created At', 'Volume ID', 'Metadata']
@@ -246,6 +265,8 @@ class ListVolumeSnapshot(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
+ _VolumeIdColumn = functools.partial(VolumeIdColumn,
+ volume_cache=volume_cache)
volume_id = None
if parsed_args.volume:
@@ -279,8 +300,8 @@ class ListVolumeSnapshot(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Metadata': utils.format_dict,
- 'Volume ID': _format_volume_id},
+ formatters={'Metadata': format_columns.DictColumn,
+ 'Volume ID': _VolumeIdColumn},
) for s in data))
@@ -402,7 +423,8 @@ class ShowVolumeSnapshot(command.ShowOne):
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
snapshot._info.update(
- {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ {'properties':
+ format_columns.DictColumn(snapshot._info.pop('metadata'))}
)
return zip(*sorted(six.iteritems(snapshot._info)))
diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py
index 2f531dc..4c4741b 100644
--- a/openstackclient/volume/v2/volume_transfer_request.py
+++ b/openstackclient/volume/v2/volume_transfer_request.py
@@ -14,7 +14,6 @@
"""Volume v2 transfer action implementations"""
-import argparse
import logging
from osc_lib.command import command
@@ -39,14 +38,9 @@ class AcceptTransferRequest(command.ShowOne):
help=_('Volume transfer request to accept (ID only)'),
)
parser.add_argument(
- 'old_auth_key',
- metavar="<key>",
- nargs="?",
- help=argparse.SUPPRESS,
- )
- parser.add_argument(
'--auth-key',
metavar="<key>",
+ required=True,
help=_('Volume transfer request authentication key'),
)
return parser
@@ -64,21 +58,6 @@ class AcceptTransferRequest(command.ShowOne):
# move on and attempt with the user-supplied information
transfer_request_id = parsed_args.transfer_request
- # Remain backward-compatible for the previous command layout
- # TODO(dtroyer): Remove this back-compat in 4.0 or Oct 2017
- if not parsed_args.auth_key:
- if parsed_args.old_auth_key:
- # Move the old one into the correct place
- parsed_args.auth_key = parsed_args.old_auth_key
- self.log.warning(_(
- 'Specifying the auth-key as a positional argument '
- 'has been deprecated. Please use the --auth-key '
- 'option in the future.'
- ))
- else:
- msg = _("argument --auth-key is required")
- raise exceptions.CommandError(msg)
-
transfer_accept = volume_client.transfers.accept(
transfer_request_id,
parsed_args.auth_key,
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 71e94a2..54b1f49 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -14,8 +14,11 @@
"""Volume v2 Type action implementations"""
+import functools
import logging
+from cliff import columns as cliff_columns
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -29,6 +32,36 @@ from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
+class EncryptionInfoColumn(cliff_columns.FormattableColumn):
+ """Formattable column for encryption info column.
+
+ Unlike the parent FormattableColumn class, the initializer of the
+ class takes encryption_data as the second argument.
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
+ object with a single parameter "column value", so you need to pass
+ a partially initialized class like
+ ``functools.partial(EncryptionInfoColumn encryption_data)``.
+ """
+
+ def __init__(self, value, encryption_data=None):
+ super(EncryptionInfoColumn, self).__init__(value)
+ self._encryption_data = encryption_data or {}
+
+ def _get_encryption_info(self):
+ type_id = self._value
+ return self._encryption_data.get(type_id)
+
+ def human_readable(self):
+ encryption_info = self._get_encryption_info()
+ if encryption_info:
+ return utils.format_dict(encryption_info)
+ else:
+ return '-'
+
+ def machine_readable(self):
+ return self._get_encryption_info()
+
+
def _create_encryption_type(volume_client, volume_type, parsed_args):
if not parsed_args.encryption_provider:
msg = _("'--encryption-provider' should be specified while "
@@ -183,7 +216,8 @@ class CreateVolumeType(command.ShowOne):
LOG.error(msg % {'project': parsed_args.project, 'e': e})
if parsed_args.property:
result = volume_type.set_keys(parsed_args.property)
- volume_type._info.update({'properties': utils.format_dict(result)})
+ volume_type._info.update(
+ {'properties': format_columns.DictColumn(result)})
if (parsed_args.encryption_provider or
parsed_args.encryption_cipher or
parsed_args.encryption_key_size or
@@ -198,7 +232,7 @@ class CreateVolumeType(command.ShowOne):
# add encryption info in result
encryption._info.pop("volume_type_id", None)
volume_type._info.update(
- {'encryption': utils.format_dict(encryption._info)})
+ {'encryption': format_columns.DictColumn(encryption._info)})
volume_type._info.pop("os-volume-type-access:is_public", None)
return zip(*sorted(six.iteritems(volume_type._info)))
@@ -296,12 +330,7 @@ class ListVolumeType(command.Lister):
data = volume_client.volume_types.list(
is_public=is_public)
- def _format_encryption_info(type_id, encryption_data=None):
- encryption_data = encryption
- encryption_info = '-'
- if type_id in encryption_data.keys():
- encryption_info = encryption_data[type_id]
- return encryption_info
+ formatters = {'Extra Specs': format_columns.DictColumn}
if parsed_args.encryption_type:
encryption = {}
@@ -318,18 +347,21 @@ class ListVolumeType(command.Lister):
for key in del_key:
d._info.pop(key, None)
# save the encryption information with their volume type ID
- encryption[volume_type_id] = utils.format_dict(d._info)
+ encryption[volume_type_id] = d._info
# We need to get volume type ID, then show encryption
# information according to the ID, so use "id" to keep
# difference to the real "ID" column.
columns += ['id']
column_headers += ['Encryption']
+ _EncryptionInfoColumn = functools.partial(
+ EncryptionInfoColumn, encryption_data=encryption)
+ formatters['id'] = _EncryptionInfoColumn
+
return (column_headers,
(utils.get_item_properties(
s, columns,
- formatters={'Extra Specs': utils.format_dict,
- 'id': _format_encryption_info},
+ formatters=formatters,
) for s in data))
@@ -490,7 +522,7 @@ class ShowVolumeType(command.ShowOne):
volume_client = self.app.client_manager.volume
volume_type = utils.find_resource(
volume_client.volume_types, parsed_args.volume_type)
- properties = utils.format_dict(
+ properties = format_columns.DictColumn(
volume_type._info.pop('extra_specs', {}))
volume_type._info.update({'properties': properties})
access_project_ids = None
@@ -501,8 +533,8 @@ class ShowVolumeType(command.ShowOne):
project_ids = [utils.get_field(item, 'project_id')
for item in volume_type_access]
# TODO(Rui Chen): This format list case can be removed after
- # patch https://review.openstack.org/#/c/330223/ merged.
- access_project_ids = utils.format_list(project_ids)
+ # patch https://review.opendev.org/#/c/330223/ merged.
+ access_project_ids = format_columns.ListColumn(project_ids)
except Exception as e:
msg = _('Failed to get access project list for volume type '
'%(type)s: %(e)s')
@@ -515,7 +547,8 @@ class ShowVolumeType(command.ShowOne):
volume_type.id)
encryption._info.pop("volume_type_id", None)
volume_type._info.update(
- {'encryption': utils.format_dict(encryption._info)})
+ {'encryption':
+ format_columns.DictColumn(encryption._info)})
except Exception as e:
LOG.error(_("Failed to display the encryption information "
"of this volume type: %s"), e)
diff --git a/python_openstackclient.egg-info/PKG-INFO b/python_openstackclient.egg-info/PKG-INFO
index 4b18353..74dc726 100644
--- a/python_openstackclient.egg-info/PKG-INFO
+++ b/python_openstackclient.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: python-openstackclient
-Version: 3.19.0
+Version: 4.0.0
Summary: OpenStack Command-line Client
Home-page: https://docs.openstack.org/python-openstackclient/latest/
Author: OpenStack
@@ -24,8 +24,8 @@ Description: ========================
:alt: Latest Version
OpenStackClient (aka OSC) is a command-line client for OpenStack that brings
- the command set for Compute, Identity, Image, Object Store and Block Storage
- APIs together in a single shell with a uniform command structure.
+ the command set for Compute, Identity, Image, Network, Object Store and Block
+ Storage APIs together in a single shell with a uniform command structure.
The primary goal is to provide a unified shell command structure and a common
language to describe operations in OpenStack.
@@ -109,18 +109,6 @@ Description: ========================
If a password is not provided above (in plaintext), you will be interactively
prompted to provide one securely.
- Authentication may also be performed using an already-acquired token
- and a URL pointing directly to the service API that presumably was acquired
- from the Service Catalog::
-
- export OS_TOKEN=<token>
- export OS_URL=<url-to-openstack-service>
-
- The corresponding command-line options look very similar::
-
- --os-token <token>
- --os-url <url-to-openstack-service>
-
Platform: UNKNOWN
Classifier: Environment :: OpenStack
@@ -133,3 +121,4 @@ Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
diff --git a/python_openstackclient.egg-info/SOURCES.txt b/python_openstackclient.egg-info/SOURCES.txt
index 3519ced..18ad6b9 100644
--- a/python_openstackclient.egg-info/SOURCES.txt
+++ b/python_openstackclient.egg-info/SOURCES.txt
@@ -33,7 +33,6 @@ doc/source/cli/command-objects/address-scope.rst
doc/source/cli/command-objects/aggregate.rst
doc/source/cli/command-objects/application-credentials.rst
doc/source/cli/command-objects/availability-zone.rst
-doc/source/cli/command-objects/backup.rst
doc/source/cli/command-objects/catalog.rst
doc/source/cli/command-objects/command.rst
doc/source/cli/command-objects/complete.rst
@@ -55,6 +54,7 @@ doc/source/cli/command-objects/extension.rst
doc/source/cli/command-objects/federation-protocol.rst
doc/source/cli/command-objects/flavor.rst
doc/source/cli/command-objects/floating-ip-pool.rst
+doc/source/cli/command-objects/floating-ip-port-forwarding.rst
doc/source/cli/command-objects/floating-ip.rst
doc/source/cli/command-objects/group.rst
doc/source/cli/command-objects/host.rst
@@ -64,7 +64,6 @@ doc/source/cli/command-objects/identity-provider.rst
doc/source/cli/command-objects/image.rst
doc/source/cli/command-objects/implied_role.rst
doc/source/cli/command-objects/ip-availability.rst
-doc/source/cli/command-objects/ip-fixed.rst
doc/source/cli/command-objects/keypair.rst
doc/source/cli/command-objects/limit.rst
doc/source/cli/command-objects/limits.rst
@@ -106,13 +105,11 @@ doc/source/cli/command-objects/server-image.rst
doc/source/cli/command-objects/server.rst
doc/source/cli/command-objects/service-provider.rst
doc/source/cli/command-objects/service.rst
-doc/source/cli/command-objects/snapshot.rst
doc/source/cli/command-objects/subnet-pool.rst
doc/source/cli/command-objects/subnet.rst
doc/source/cli/command-objects/token.rst
doc/source/cli/command-objects/trust.rst
doc/source/cli/command-objects/usage.rst
-doc/source/cli/command-objects/user-role.rst
doc/source/cli/command-objects/user.rst
doc/source/cli/command-objects/versions.rst
doc/source/cli/command-objects/volume-backend.rst
@@ -152,14 +149,12 @@ openstackclient/i18n.py
openstackclient/shell.py
openstackclient/api/__init__.py
openstackclient/api/api.py
-openstackclient/api/auth_plugin.py
openstackclient/api/compute_v2.py
openstackclient/api/image_v1.py
openstackclient/api/image_v2.py
openstackclient/api/object_store_v1.py
openstackclient/common/__init__.py
openstackclient/common/availability_zone.py
-openstackclient/common/client_config.py
openstackclient/common/clientmanager.py
openstackclient/common/configuration.py
openstackclient/common/extension.py
@@ -174,9 +169,7 @@ openstackclient/compute/v2/__init__.py
openstackclient/compute/v2/agent.py
openstackclient/compute/v2/aggregate.py
openstackclient/compute/v2/console.py
-openstackclient/compute/v2/fixedip.py
openstackclient/compute/v2/flavor.py
-openstackclient/compute/v2/floatingip.py
openstackclient/compute/v2/host.py
openstackclient/compute/v2/hypervisor.py
openstackclient/compute/v2/hypervisor_stats.py
@@ -246,6 +239,7 @@ openstackclient/network/v2/_tag.py
openstackclient/network/v2/address_scope.py
openstackclient/network/v2/floating_ip.py
openstackclient/network/v2/floating_ip_pool.py
+openstackclient/network/v2/floating_ip_port_forwarding.py
openstackclient/network/v2/ip_availability.py
openstackclient/network/v2/network.py
openstackclient/network/v2/network_agent.py
@@ -277,6 +271,7 @@ openstackclient/tests/__init__.py
openstackclient/tests/functional/__init__.py
openstackclient/tests/functional/base.py
openstackclient/tests/functional/common/__init__.py
+openstackclient/tests/functional/common/test_args.py
openstackclient/tests/functional/common/test_availability_zone.py
openstackclient/tests/functional/common/test_configuration.py
openstackclient/tests/functional/common/test_extension.py
@@ -372,19 +367,19 @@ openstackclient/tests/functional/volume/v1/test_volume.py
openstackclient/tests/functional/volume/v1/test_volume_type.py
openstackclient/tests/functional/volume/v2/__init__.py
openstackclient/tests/functional/volume/v2/common.py
-openstackclient/tests/functional/volume/v2/test_backup.py
openstackclient/tests/functional/volume/v2/test_qos.py
openstackclient/tests/functional/volume/v2/test_service.py
-openstackclient/tests/functional/volume/v2/test_snapshot.py
openstackclient/tests/functional/volume/v2/test_transfer_request.py
openstackclient/tests/functional/volume/v2/test_volume.py
+openstackclient/tests/functional/volume/v2/test_volume_backup.py
+openstackclient/tests/functional/volume/v2/test_volume_snapshot.py
openstackclient/tests/functional/volume/v2/test_volume_type.py
openstackclient/tests/functional/volume/v3/__init__.py
openstackclient/tests/functional/volume/v3/common.py
openstackclient/tests/functional/volume/v3/test_qos.py
-openstackclient/tests/functional/volume/v3/test_snapshot.py
openstackclient/tests/functional/volume/v3/test_transfer_request.py
openstackclient/tests/functional/volume/v3/test_volume.py
+openstackclient/tests/functional/volume/v3/test_volume_snapshot.py
openstackclient/tests/functional/volume/v3/test_volume_type.py
openstackclient/tests/unit/__init__.py
openstackclient/tests/unit/fakes.py
@@ -487,6 +482,7 @@ openstackclient/tests/unit/network/v2/test_floating_ip_compute.py
openstackclient/tests/unit/network/v2/test_floating_ip_network.py
openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py
openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py
+openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
openstackclient/tests/unit/network/v2/test_ip_availability.py
openstackclient/tests/unit/network/v2/test_network.py
openstackclient/tests/unit/network/v2/test_network_agent.py
@@ -522,49 +518,45 @@ openstackclient/tests/unit/volume/__init__.py
openstackclient/tests/unit/volume/test_find_resource.py
openstackclient/tests/unit/volume/v1/__init__.py
openstackclient/tests/unit/volume/v1/fakes.py
-openstackclient/tests/unit/volume/v1/test_backup.py
openstackclient/tests/unit/volume/v1/test_qos_specs.py
openstackclient/tests/unit/volume/v1/test_service.py
-openstackclient/tests/unit/volume/v1/test_snapshot.py
openstackclient/tests/unit/volume/v1/test_transfer_request.py
openstackclient/tests/unit/volume/v1/test_type.py
openstackclient/tests/unit/volume/v1/test_volume.py
+openstackclient/tests/unit/volume/v1/test_volume_backup.py
openstackclient/tests/unit/volume/v2/__init__.py
openstackclient/tests/unit/volume/v2/fakes.py
-openstackclient/tests/unit/volume/v2/test_backup.py
openstackclient/tests/unit/volume/v2/test_backup_record.py
openstackclient/tests/unit/volume/v2/test_consistency_group.py
openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py
openstackclient/tests/unit/volume/v2/test_qos_specs.py
openstackclient/tests/unit/volume/v2/test_service.py
-openstackclient/tests/unit/volume/v2/test_snapshot.py
openstackclient/tests/unit/volume/v2/test_transfer_request.py
openstackclient/tests/unit/volume/v2/test_type.py
openstackclient/tests/unit/volume/v2/test_volume.py
openstackclient/tests/unit/volume/v2/test_volume_backend.py
+openstackclient/tests/unit/volume/v2/test_volume_backup.py
openstackclient/tests/unit/volume/v2/test_volume_host.py
openstackclient/tests/unit/volume/v3/__init__.py
openstackclient/volume/__init__.py
openstackclient/volume/client.py
openstackclient/volume/v1/__init__.py
-openstackclient/volume/v1/backup.py
openstackclient/volume/v1/qos_specs.py
openstackclient/volume/v1/service.py
-openstackclient/volume/v1/snapshot.py
openstackclient/volume/v1/volume.py
+openstackclient/volume/v1/volume_backup.py
openstackclient/volume/v1/volume_snapshot.py
openstackclient/volume/v1/volume_transfer_request.py
openstackclient/volume/v1/volume_type.py
openstackclient/volume/v2/__init__.py
-openstackclient/volume/v2/backup.py
openstackclient/volume/v2/backup_record.py
openstackclient/volume/v2/consistency_group.py
openstackclient/volume/v2/consistency_group_snapshot.py
openstackclient/volume/v2/qos_specs.py
openstackclient/volume/v2/service.py
-openstackclient/volume/v2/snapshot.py
openstackclient/volume/v2/volume.py
openstackclient/volume/v2/volume_backend.py
+openstackclient/volume/v2/volume_backup.py
openstackclient/volume/v2/volume_host.py
openstackclient/volume/v2/volume_snapshot.py
openstackclient/volume/v2/volume_transfer_request.py
@@ -584,7 +576,9 @@ releasenotes/notes/add-community-option-to-image-list-ac0651eb2e5d632f.yaml
releasenotes/notes/add-device_id-to-port-list-0c658db51ce43c9e.yaml
releasenotes/notes/add-disable-reason-6e0f28459a09a60d.yaml
releasenotes/notes/add-dns-nameserver-overwrite-option-b866baeae12f9460.yaml
+releasenotes/notes/add-fip-portforwarding-commands-6e4d8ace698ee308.yaml
releasenotes/notes/add-ha-to-router-update-6a38a73cc112b2fc.yaml
+releasenotes/notes/add-host-and-hypervisor-hostname-flag-to-create-server-cb8b39a9f9311d42.yaml
releasenotes/notes/add-image-member-list-1630ead5988348c2.yaml
releasenotes/notes/add-image-tag-filter-support-5cb039416b07caab.yaml
releasenotes/notes/add-implied-role-0cdafb131fbd7453.yaml
@@ -615,18 +609,23 @@ releasenotes/notes/add-server-add-network-98ede8ff6079eb23.yaml
releasenotes/notes/add-server-backup-e63feaebb6140f83.yaml
releasenotes/notes/add-server-create-image-property-ef76af26233b472b.yaml
releasenotes/notes/add-server-group-quotas-b67fcba98619f0c9.yaml
+releasenotes/notes/add-server-migrate-with-host-4884a71903c5c8a9.yaml
releasenotes/notes/add-server-remove-network-fb09c53d5b0c0068.yaml
+releasenotes/notes/add-server-resize-confirm-revert-commands-98854ca98965432a.yaml
releasenotes/notes/add-subnet-list-command-970f4b397469bdc6.yaml
releasenotes/notes/add-virtio-forwarder-vnic-type-bad939c6a868b9e9.yaml
releasenotes/notes/add-volume-host-failover-8fc77b24533b7fed.yaml
releasenotes/notes/address-scope-delete-multi-31c3af73feb31265.yaml
releasenotes/notes/allow-custom-logging-12d55f8ed859ff8e.yaml
+releasenotes/notes/allow-port-create-with-extra-dhcp-options-c2c40e4002b52e2a.yaml
releasenotes/notes/allow-port-list-with-ip-address-substr-14c5805b241e402f.yaml
releasenotes/notes/allow-to-add-remove-vm-ports-273593d7cc1982de.yaml
releasenotes/notes/allow-to-create-legacy-router-cb4dcb44dde74684.yaml
releasenotes/notes/allow-to-specify-vm-ip-to-publish-85f7207740c0cc8d.yaml
releasenotes/notes/allow-to-vm-ip-to-add-7721ba64b863fa77.yaml
releasenotes/notes/backup_list_all-projects_option-4bb23e0b9b075cac.yaml
+releasenotes/notes/better-ipv6-address-suppport-security-group-rules-95272847349982e5.yaml
+releasenotes/notes/bp-add-locked-reason-425efd2def1144f1.yaml
releasenotes/notes/bp-allow-overwrite-set-options-190a9c6904d53dab.yaml
releasenotes/notes/bp-application-credential-a7031a043efc4a25.yaml
releasenotes/notes/bp-backup-snapshot-renamed-for-volume-resource-2d2d13ea8489a61f.yaml
@@ -659,6 +658,7 @@ releasenotes/notes/bp-routed-networks-3eea4978c93aa126.yaml
releasenotes/notes/bp-routed-networks-86a24f34d86fca53.yaml
releasenotes/notes/bp-support-multi-add-remove-9516f72cfacea11a.yaml
releasenotes/notes/bp-support-no-property-in-aggregate-b74a42e00a65d14a.yaml
+releasenotes/notes/bp-support-specifying-az-when-restore-shelved-server-16e864223d51b50a.yaml
releasenotes/notes/bp-unified-limits-58f166401534a4ff.yaml
releasenotes/notes/bp-unified-limits-6c5fdb1c26805d86.yaml
releasenotes/notes/bug-1204956-af47c7f34ecc19c3.yaml
@@ -887,6 +887,7 @@ releasenotes/notes/bug-1704097-8ff1ce1444b81b04.yaml
releasenotes/notes/bug-1711301-472b577f074edd43.yaml
releasenotes/notes/bug-1712242-934bbe2f2378f5bd.yaml
releasenotes/notes/bug-1714878-46806jv2yv13q054.yaml
+releasenotes/notes/bug-1716789-abfae897b7e61246.yaml
releasenotes/notes/bug-1717130-029211b60f74b4c4.yaml
releasenotes/notes/bug-1717829-c1de1d777d3abaf9.yaml
releasenotes/notes/bug-1719413-0401d05c91cc9094.yaml
@@ -913,6 +914,7 @@ releasenotes/notes/bug-volume-list-with-project-2dc867c5e8346a66.yaml
releasenotes/notes/change-098377fd53cce7a0.yaml
releasenotes/notes/cliff-2.3.0-7ead18fae9ceea80.yaml
releasenotes/notes/compute-agent-deff48988e81b30e.yaml
+releasenotes/notes/config-show-00512dc60882e5c0.yaml
releasenotes/notes/credential_list_user_type-c809e5b8014d6275.yaml
releasenotes/notes/fix-network-rbac-create-d1f4de77ad2dd421.yaml
releasenotes/notes/flavor-add-description-b618abd4a7fb6545.yaml
@@ -942,6 +944,11 @@ releasenotes/notes/neutron_mtu-d87e53e2d76f8612.yaml
releasenotes/notes/no-project-usage-list-e88eb49aa2e96cf7.yaml
releasenotes/notes/object-stdout-db76cc500948b0e8.yaml
releasenotes/notes/osc-included-image-signing-a7021a4dbdcf6336.yaml
+releasenotes/notes/osc4-compute-09246008eff260cb.yaml
+releasenotes/notes/osc4-identity-6564257c67d43106.yaml
+releasenotes/notes/osc4-image-e922ee6f8e028648.yaml
+releasenotes/notes/osc4-network-db2aed696d964ca6.yaml
+releasenotes/notes/osc4-volume-470422e5a453310e.yaml
releasenotes/notes/rbac-add-security-group-35370701a06ac906.yaml
releasenotes/notes/recursive-container-delete-983361aa9c35ffed.yaml
releasenotes/notes/remove-ip-floating-commands-d5363f313e09249a.yaml
@@ -961,6 +968,9 @@ releasenotes/notes/server-set-state-214b12ec2161de4d.yaml
releasenotes/notes/service-set-option-61772a8940ad0778.yaml
releasenotes/notes/skip-name-lookups-9f499927173c1eee.yaml
releasenotes/notes/speedup-object-save-6bd59e678a31c3e8.yaml
+releasenotes/notes/story-2005349-compute-service-set-2.53-3d2db875154e633a.yaml
+releasenotes/notes/story-2006302-add-boot-from-volume-cd411b1ca776bb3c.yaml
+releasenotes/notes/story-2006302-support-bdm-type-image-0becfb63bd4fb969.yaml
releasenotes/notes/subnet-service-type-8d9c414732e474a4.yaml
releasenotes/notes/subnet-set-bbc26ecc16929302.yaml
releasenotes/notes/subnet-set-segment-id-4440e433b170f9f3.yaml
@@ -972,10 +982,12 @@ releasenotes/notes/support-uplink_status_propagation-4d37452bcf03e0f8.yaml
releasenotes/notes/unlock-volume-a6990fc3bf1f5f67.yaml
releasenotes/notes/unset-router-7b0cbd9518bb1de6.yaml
releasenotes/notes/unset-subnet-pool-333052dd85b95653.yaml
+releasenotes/notes/usage-list-all-49ca7a62c50e71d3.yaml
releasenotes/notes/versions-show-12a2443624c83048.yaml
releasenotes/notes/volume-backend-c5faae0b31556a24.yaml
releasenotes/notes/volume-backup-record-9f5987c45e294dc6.yaml
releasenotes/notes/volume-migrate-command-52cf6edd62fe17a7.yaml
+releasenotes/notes/volume-v3-default-0ffa9bebb43b5057.yaml
releasenotes/notes/volume_snapshot_list_project-e7dcc07f98d44182.yaml
releasenotes/source/20_releases.rst
releasenotes/source/conf.py
diff --git a/python_openstackclient.egg-info/entry_points.txt b/python_openstackclient.egg-info/entry_points.txt
index 85f69b0..98221fb 100644
--- a/python_openstackclient.egg-info/entry_points.txt
+++ b/python_openstackclient.egg-info/entry_points.txt
@@ -1,9 +1,6 @@
[console_scripts]
openstack = openstackclient.shell:main
-[keystoneauth1.plugin]
-token_endpoint = openstackclient.api.auth_plugin:TokenEndpoint
-
[openstack.cli]
command_list = openstackclient.common.module:ListCommand
module_list = openstackclient.common.module:ListModule
@@ -58,10 +55,6 @@ host_show = openstackclient.compute.v2.host:ShowHost
hypervisor_list = openstackclient.compute.v2.hypervisor:ListHypervisor
hypervisor_show = openstackclient.compute.v2.hypervisor:ShowHypervisor
hypervisor_stats_show = openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats
-ip_fixed_add = openstackclient.compute.v2.fixedip:AddFixedIP
-ip_fixed_remove = openstackclient.compute.v2.fixedip:RemoveFixedIP
-ip_floating_add = openstackclient.compute.v2.floatingip:AddFloatingIP
-ip_floating_remove = openstackclient.compute.v2.floatingip:RemoveFloatingIP
keypair_create = openstackclient.compute.v2.keypair:CreateKeypair
keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair
keypair_list = openstackclient.compute.v2.keypair:ListKeypair
@@ -97,6 +90,8 @@ server_remove_security_group = openstackclient.compute.v2.server:RemoveServerSec
server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume
server_rescue = openstackclient.compute.v2.server:RescueServer
server_resize = openstackclient.compute.v2.server:ResizeServer
+server_resize_confirm = openstackclient.compute.v2.server:ResizeConfirm
+server_resize_revert = openstackclient.compute.v2.server:ResizeRevert
server_restore = openstackclient.compute.v2.server:RestoreServer
server_resume = openstackclient.compute.v2.server:ResumeServer
server_set = openstackclient.compute.v2.server:SetServer
@@ -147,7 +142,6 @@ token_revoke = openstackclient.identity.v2_0.token:RevokeToken
user_create = openstackclient.identity.v2_0.user:CreateUser
user_delete = openstackclient.identity.v2_0.user:DeleteUser
user_list = openstackclient.identity.v2_0.user:ListUser
-user_role_list = openstackclient.identity.v2_0.role:ListUserRole
user_set = openstackclient.identity.v2_0.user:SetUser
user_show = openstackclient.identity.v2_0.user:ShowUser
@@ -308,6 +302,11 @@ floating_ip_create = openstackclient.network.v2.floating_ip:CreateFloatingIP
floating_ip_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP
floating_ip_list = openstackclient.network.v2.floating_ip:ListFloatingIP
floating_ip_pool_list = openstackclient.network.v2.floating_ip_pool:ListFloatingIPPool
+floating_ip_port_forwarding_create = openstackclient.network.v2.floating_ip_port_forwarding:CreateFloatingIPPortForwarding
+floating_ip_port_forwarding_delete = openstackclient.network.v2.floating_ip_port_forwarding:DeleteFloatingIPPortForwarding
+floating_ip_port_forwarding_list = openstackclient.network.v2.floating_ip_port_forwarding:ListFloatingIPPortForwarding
+floating_ip_port_forwarding_set = openstackclient.network.v2.floating_ip_port_forwarding:SetFloatingIPPortForwarding
+floating_ip_port_forwarding_show = openstackclient.network.v2.floating_ip_port_forwarding:ShowFloatingIPPortForwarding
floating_ip_set = openstackclient.network.v2.floating_ip:SetFloatingIP
floating_ip_show = openstackclient.network.v2.floating_ip:ShowFloatingIP
floating_ip_unset = openstackclient.network.v2.floating_ip:UnsetFloatingIP
@@ -436,22 +435,11 @@ object_store_account_unset = openstackclient.object.v1.account:UnsetAccount
object_unset = openstackclient.object.v1.object:UnsetObject
[openstack.volume.v1]
-backup_create = openstackclient.volume.v1.backup:CreateBackup
-backup_delete = openstackclient.volume.v1.backup:DeleteBackup
-backup_list = openstackclient.volume.v1.backup:ListBackup
-backup_restore = openstackclient.volume.v1.backup:RestoreBackup
-backup_show = openstackclient.volume.v1.backup:ShowBackup
-snapshot_create = openstackclient.volume.v1.snapshot:CreateSnapshot
-snapshot_delete = openstackclient.volume.v1.snapshot:DeleteSnapshot
-snapshot_list = openstackclient.volume.v1.snapshot:ListSnapshot
-snapshot_set = openstackclient.volume.v1.snapshot:SetSnapshot
-snapshot_show = openstackclient.volume.v1.snapshot:ShowSnapshot
-snapshot_unset = openstackclient.volume.v1.snapshot:UnsetSnapshot
-volume_backup_create = openstackclient.volume.v1.backup:CreateVolumeBackup
-volume_backup_delete = openstackclient.volume.v1.backup:DeleteVolumeBackup
-volume_backup_list = openstackclient.volume.v1.backup:ListVolumeBackup
-volume_backup_restore = openstackclient.volume.v1.backup:RestoreVolumeBackup
-volume_backup_show = openstackclient.volume.v1.backup:ShowVolumeBackup
+volume_backup_create = openstackclient.volume.v1.volume_backup:CreateVolumeBackup
+volume_backup_delete = openstackclient.volume.v1.volume_backup:DeleteVolumeBackup
+volume_backup_list = openstackclient.volume.v1.volume_backup:ListVolumeBackup
+volume_backup_restore = openstackclient.volume.v1.volume_backup:RestoreVolumeBackup
+volume_backup_show = openstackclient.volume.v1.volume_backup:ShowVolumeBackup
volume_create = openstackclient.volume.v1.volume:CreateVolume
volume_delete = openstackclient.volume.v1.volume:DeleteVolume
volume_list = openstackclient.volume.v1.volume:ListVolume
@@ -488,11 +476,6 @@ volume_type_unset = openstackclient.volume.v1.volume_type:UnsetVolumeType
volume_unset = openstackclient.volume.v1.volume:UnsetVolume
[openstack.volume.v2]
-backup_create = openstackclient.volume.v2.backup:CreateBackup
-backup_delete = openstackclient.volume.v2.backup:DeleteBackup
-backup_list = openstackclient.volume.v2.backup:ListBackup
-backup_restore = openstackclient.volume.v2.backup:RestoreBackup
-backup_show = openstackclient.volume.v2.backup:ShowBackup
consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup
consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup
consistency_group_delete = openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup
@@ -504,22 +487,16 @@ consistency_group_snapshot_create = openstackclient.volume.v2.consistency_group_
consistency_group_snapshot_delete = openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot
consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot
consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot
-snapshot_create = openstackclient.volume.v2.snapshot:CreateSnapshot
-snapshot_delete = openstackclient.volume.v2.snapshot:DeleteSnapshot
-snapshot_list = openstackclient.volume.v2.snapshot:ListSnapshot
-snapshot_set = openstackclient.volume.v2.snapshot:SetSnapshot
-snapshot_show = openstackclient.volume.v2.snapshot:ShowSnapshot
-snapshot_unset = openstackclient.volume.v2.snapshot:UnsetSnapshot
volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability
volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool
-volume_backup_create = openstackclient.volume.v2.backup:CreateVolumeBackup
-volume_backup_delete = openstackclient.volume.v2.backup:DeleteVolumeBackup
-volume_backup_list = openstackclient.volume.v2.backup:ListVolumeBackup
+volume_backup_create = openstackclient.volume.v2.volume_backup:CreateVolumeBackup
+volume_backup_delete = openstackclient.volume.v2.volume_backup:DeleteVolumeBackup
+volume_backup_list = openstackclient.volume.v2.volume_backup:ListVolumeBackup
volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord
volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord
-volume_backup_restore = openstackclient.volume.v2.backup:RestoreVolumeBackup
-volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
-volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
+volume_backup_restore = openstackclient.volume.v2.volume_backup:RestoreVolumeBackup
+volume_backup_set = openstackclient.volume.v2.volume_backup:SetVolumeBackup
+volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup
volume_create = openstackclient.volume.v2.volume:CreateVolume
volume_delete = openstackclient.volume.v2.volume:DeleteVolume
volume_host_failover = openstackclient.volume.v2.volume_host:FailoverVolumeHost
@@ -569,12 +546,12 @@ consistency_group_snapshot_create = openstackclient.volume.v2.consistency_group_
consistency_group_snapshot_delete = openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot
consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot
consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot
-volume_backup_create = openstackclient.volume.v2.backup:CreateVolumeBackup
-volume_backup_delete = openstackclient.volume.v2.backup:DeleteVolumeBackup
-volume_backup_list = openstackclient.volume.v2.backup:ListVolumeBackup
-volume_backup_restore = openstackclient.volume.v2.backup:RestoreVolumeBackup
-volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
-volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
+volume_backup_create = openstackclient.volume.v2.volume_backup:CreateVolumeBackup
+volume_backup_delete = openstackclient.volume.v2.volume_backup:DeleteVolumeBackup
+volume_backup_list = openstackclient.volume.v2.volume_backup:ListVolumeBackup
+volume_backup_restore = openstackclient.volume.v2.volume_backup:RestoreVolumeBackup
+volume_backup_set = openstackclient.volume.v2.volume_backup:SetVolumeBackup
+volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup
volume_create = openstackclient.volume.v2.volume:CreateVolume
volume_delete = openstackclient.volume.v2.volume:DeleteVolume
volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost
diff --git a/python_openstackclient.egg-info/pbr.json b/python_openstackclient.egg-info/pbr.json
index 2ad4f18..4742cf4 100644
--- a/python_openstackclient.egg-info/pbr.json
+++ b/python_openstackclient.egg-info/pbr.json
@@ -1 +1 @@
-{"git_version": "57dec524", "is_release": true} \ No newline at end of file
+{"git_version": "aa64eb6b", "is_release": true} \ No newline at end of file
diff --git a/python_openstackclient.egg-info/requires.txt b/python_openstackclient.egg-info/requires.txt
index 5918252..fe6b8f8 100644
--- a/python_openstackclient.egg-info/requires.txt
+++ b/python_openstackclient.egg-info/requires.txt
@@ -4,10 +4,10 @@ Babel!=2.4.0,>=2.3.4
cliff!=2.9.0,>=2.8.0
keystoneauth1>=3.6.2
openstacksdk>=0.17.0
-osc-lib>=1.10.0
+osc-lib>=1.14.0
oslo.i18n>=3.15.3
oslo.utils>=3.33.0
python-glanceclient>=2.8.0
python-keystoneclient>=3.17.0
-python-novaclient>=10.0.0
+python-novaclient>=15.0.0
python-cinderclient>=3.3.0
diff --git a/releasenotes/notes/add-fip-portforwarding-commands-6e4d8ace698ee308.yaml b/releasenotes/notes/add-fip-portforwarding-commands-6e4d8ace698ee308.yaml
new file mode 100644
index 0000000..87175e4
--- /dev/null
+++ b/releasenotes/notes/add-fip-portforwarding-commands-6e4d8ace698ee308.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Add floating IP Port Forwarding commands:
+ ``floating ip port forwarding create``,
+ ``floating ip port forwarding delete``,
+ ``floating ip port forwarding list``,
+ ``floating ip port forwarding set`` and
+ ``floating ip port forwarding show``.
diff --git a/releasenotes/notes/add-host-and-hypervisor-hostname-flag-to-create-server-cb8b39a9f9311d42.yaml b/releasenotes/notes/add-host-and-hypervisor-hostname-flag-to-create-server-cb8b39a9f9311d42.yaml
new file mode 100644
index 0000000..fbd4c70
--- /dev/null
+++ b/releasenotes/notes/add-host-and-hypervisor-hostname-flag-to-create-server-cb8b39a9f9311d42.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add ``--host`` and ``--hypervisor-hostname`` options to
+ ``server create`` command.
+ [Blueprint `add-host-and-hypervisor-hostname-flag-to-create-server <https://blueprints.launchpad.net/nova/+spec/add-host-and-hypervisor-hostname-flag-to-create-server>`_] \ No newline at end of file
diff --git a/releasenotes/notes/add-server-migrate-with-host-4884a71903c5c8a9.yaml b/releasenotes/notes/add-server-migrate-with-host-4884a71903c5c8a9.yaml
new file mode 100644
index 0000000..8da704a
--- /dev/null
+++ b/releasenotes/notes/add-server-migrate-with-host-4884a71903c5c8a9.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Add ``--host`` option to ``server migrate`` command
+ (cold migration) to specify the target host of the migration.
+ Requires ``--os-compute-api-version`` 2.56 or greater to target a
+ specific host for the (cold) migration.
+ [Story `2003325 <https://storyboard.openstack.org/#!/story/2003325>`_]
diff --git a/releasenotes/notes/add-server-resize-confirm-revert-commands-98854ca98965432a.yaml b/releasenotes/notes/add-server-resize-confirm-revert-commands-98854ca98965432a.yaml
new file mode 100644
index 0000000..2399a1a
--- /dev/null
+++ b/releasenotes/notes/add-server-resize-confirm-revert-commands-98854ca98965432a.yaml
@@ -0,0 +1,12 @@
+---
+features:
+ - |
+ Add ``server resize confirm`` and ``server resize revert`` commands.
+ These replace the now deprecated ``--confirm`` and ``--revert``
+ options to the ``server resize`` commands, respectively.
+deprecations:
+ - |
+ Deprecate the ``--confirm`` and ``--revert`` options for the
+ ``server resize`` command. They have been replaced with the
+ ``server resize confirm`` and `server resize revert`` commands,
+ respectively.
diff --git a/releasenotes/notes/allow-port-create-with-extra-dhcp-options-c2c40e4002b52e2a.yaml b/releasenotes/notes/allow-port-create-with-extra-dhcp-options-c2c40e4002b52e2a.yaml
new file mode 100644
index 0000000..ed4fa1b
--- /dev/null
+++ b/releasenotes/notes/allow-port-create-with-extra-dhcp-options-c2c40e4002b52e2a.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add ``--extra-dhcp-options`` parameter to the ``port create`` command. The
+ neutronclient ``port-create`` command can accept extra DHCP options, add
+ it to the openstackclient in order to be consistent. \ No newline at end of file
diff --git a/releasenotes/notes/better-ipv6-address-suppport-security-group-rules-95272847349982e5.yaml b/releasenotes/notes/better-ipv6-address-suppport-security-group-rules-95272847349982e5.yaml
new file mode 100644
index 0000000..7177873
--- /dev/null
+++ b/releasenotes/notes/better-ipv6-address-suppport-security-group-rules-95272847349982e5.yaml
@@ -0,0 +1,14 @@
+---
+features:
+ - |
+ Add ``--ethertype`` option to ``security group rule list`` command.
+ Valid values are ``ipv4`` and ``ipv6``.
+upgrade:
+ - |
+ Security group rule listings now have the ``Ethertype`` field displayed
+ by default to more easily differentiate between IPv4 and IPv6 rules.
+ In addition, the ``IP Range`` field of a security group will be
+ changed to ``0.0.0.0/0`` for IPv4 and ``::/0`` for IPv6 if no
+ value is returned for the address, based on the Ethertype field of
+ the rule. For further information see
+ [Bug `1735575 <https://bugs.launchpad.net/bugs/1735575>`_]
diff --git a/releasenotes/notes/bp-add-locked-reason-425efd2def1144f1.yaml b/releasenotes/notes/bp-add-locked-reason-425efd2def1144f1.yaml
new file mode 100644
index 0000000..e9f6cc6
--- /dev/null
+++ b/releasenotes/notes/bp-add-locked-reason-425efd2def1144f1.yaml
@@ -0,0 +1,13 @@
+---
+features:
+ - Add ``--reason`` option to the ``server lock`` command to specify a reason
+ when locking a server.
+ Requires ``–os-compute-api-version`` 2.73 or greater.
+ - Add ``--locked`` option to the ``server list`` command to list only locked
+ servers.
+ Requires ``–os-compute-api-version`` 2.73 or greater.
+ - Add ``--unlocked`` option to the ``server list`` command list only unlocked
+ servers.
+ Requires ``–os-compute-api-version`` 2.73 or greater.
+ [Blueprint `add-locked-reason <https://blueprints.launchpad.net/nova/+spec/add-locked-reason>`_]
+ \ No newline at end of file
diff --git a/releasenotes/notes/bp-support-specifying-az-when-restore-shelved-server-16e864223d51b50a.yaml b/releasenotes/notes/bp-support-specifying-az-when-restore-shelved-server-16e864223d51b50a.yaml
new file mode 100644
index 0000000..adb55b1
--- /dev/null
+++ b/releasenotes/notes/bp-support-specifying-az-when-restore-shelved-server-16e864223d51b50a.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - Add ``--availability-zone`` option to ``server unshelve``
+ command to enable users to specify an availability zone during
+ unshelve of a shelved offloaded server. Note that it requires
+ ``--os-compute-api-version 2.77`` or greater.
+ [Blueprint `support-specifying-az-when-restore-shelved-server <https://blueprints.launchpad.net/nova/+spec/support-specifying-az-when-restore-shelved-server>`_]
diff --git a/releasenotes/notes/bug-1716789-abfae897b7e61246.yaml b/releasenotes/notes/bug-1716789-abfae897b7e61246.yaml
new file mode 100644
index 0000000..4177f80
--- /dev/null
+++ b/releasenotes/notes/bug-1716789-abfae897b7e61246.yaml
@@ -0,0 +1,10 @@
+---
+fixes:
+ - |
+ Change the default value of ``--protocol`` option to ``any`` in
+ ``security group rule create`` command when using the Neutron v2 API.
+ [Bug `1716789 <https://bugs.launchpad.net/bugs/1716789>`_]
+upgrade:
+ - |
+ Commands that assumed the default value of ``--protocol`` to be ``tcp``
+ now must include ``--protocol tcp`` explicitly in Network commands.
diff --git a/releasenotes/notes/config-show-00512dc60882e5c0.yaml b/releasenotes/notes/config-show-00512dc60882e5c0.yaml
new file mode 100644
index 0000000..da57dff
--- /dev/null
+++ b/releasenotes/notes/config-show-00512dc60882e5c0.yaml
@@ -0,0 +1,6 @@
+---
+other:
+ - |
+ The ``configuration show`` command no longer requires authentication.
+ This is useful in debugging cloud configurations, particularly
+ auth configurations.
diff --git a/releasenotes/notes/osc4-compute-09246008eff260cb.yaml b/releasenotes/notes/osc4-compute-09246008eff260cb.yaml
new file mode 100644
index 0000000..ab7baa6
--- /dev/null
+++ b/releasenotes/notes/osc4-compute-09246008eff260cb.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - |
+ Remove deprecated ``ip fixed add|remove`` commands.
+ Use ``server add|remove fixed ip`` commands instead.
+ - |
+ Remove deprecated ``ip floating add|remove`` commands.
+ Use ``server add|remove floating ip`` commands instead.
diff --git a/releasenotes/notes/osc4-identity-6564257c67d43106.yaml b/releasenotes/notes/osc4-identity-6564257c67d43106.yaml
new file mode 100644
index 0000000..a5105c8
--- /dev/null
+++ b/releasenotes/notes/osc4-identity-6564257c67d43106.yaml
@@ -0,0 +1,12 @@
+---
+upgrade:
+ - |
+ Remove deprecated ``role list`` options ``--project`` and ``--user``.
+ Use ``role assignment list`` options ``--project`` and ``--user`` instead.
+ - |
+ Remove deprecated ``user role list`` command.
+ Use ``role assignment list`` options ``--project`` and ``--user`` instead.
+ - |
+ Remove deprecated ``service create`` option ``--type``.
+ The type is supplied as a positional argument in The
+ ``service create --name <service-name> type`` command.
diff --git a/releasenotes/notes/osc4-image-e922ee6f8e028648.yaml b/releasenotes/notes/osc4-image-e922ee6f8e028648.yaml
new file mode 100644
index 0000000..eab3f70
--- /dev/null
+++ b/releasenotes/notes/osc4-image-e922ee6f8e028648.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - |
+ Remove ``image create|set`` option ``--owner``.
+ Use ``--project`` option instead.
diff --git a/releasenotes/notes/osc4-network-db2aed696d964ca6.yaml b/releasenotes/notes/osc4-network-db2aed696d964ca6.yaml
new file mode 100644
index 0000000..7ae3001
--- /dev/null
+++ b/releasenotes/notes/osc4-network-db2aed696d964ca6.yaml
@@ -0,0 +1,12 @@
+---
+upgrade:
+ - |
+ Remove deprecated ``port create|set`` options ``--device-id`` and ``--host-id``.
+ Use ``--device`` and ``--host`` options instead.
+ - |
+ Remove deprecated ``router set`` option ``--clear-routes``.
+ Use ``--no-route`` option instead.
+ - |
+ Remove deprecated ``security group rule create`` options ``--src-ip`` and
+ ``--src-group``.
+ Use ``--remote-ip`` and ``--remote-group`` options instead.
diff --git a/releasenotes/notes/osc4-volume-470422e5a453310e.yaml b/releasenotes/notes/osc4-volume-470422e5a453310e.yaml
new file mode 100644
index 0000000..e03f0fd
--- /dev/null
+++ b/releasenotes/notes/osc4-volume-470422e5a453310e.yaml
@@ -0,0 +1,14 @@
+---
+upgrade:
+ - |
+ Remove deprecated ``backup`` commands.
+ Use ``volume backup`` commands instead.
+ - |
+ Remove deprecated ``snapshot`` commands.
+ Use ``volume snapshot`` commands instead.
+ - |
+ Remove deprecated ``volume create`` options ``--project``, ``--user``
+ and ``--multi-attach``.
+ - |
+ Change ``volume transfer request accept`` to use new required option
+ ``--auth-key`` rather than a second positional argument.
diff --git a/releasenotes/notes/story-2005349-compute-service-set-2.53-3d2db875154e633a.yaml b/releasenotes/notes/story-2005349-compute-service-set-2.53-3d2db875154e633a.yaml
new file mode 100644
index 0000000..cff3196
--- /dev/null
+++ b/releasenotes/notes/story-2005349-compute-service-set-2.53-3d2db875154e633a.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ The ``compute service set`` command now properly handles
+ ``--os-compute-api-version`` 2.53 and greater.
+ [Story `2005349 <https://storyboard.openstack.org/#!/story/2005349>`_]
diff --git a/releasenotes/notes/story-2006302-add-boot-from-volume-cd411b1ca776bb3c.yaml b/releasenotes/notes/story-2006302-add-boot-from-volume-cd411b1ca776bb3c.yaml
new file mode 100644
index 0000000..1176c88
--- /dev/null
+++ b/releasenotes/notes/story-2006302-add-boot-from-volume-cd411b1ca776bb3c.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Add ``--boot-from-volume`` option to the ``server create`` command
+ to create a volume-backed server from the specified image with the
+ specified size when used in conjunction with the ``--image`` or
+ ``--image-property`` options.
+ [Story `2006302 <https://storyboard.openstack.org/#!/story/2006302>`_]
diff --git a/releasenotes/notes/story-2006302-support-bdm-type-image-0becfb63bd4fb969.yaml b/releasenotes/notes/story-2006302-support-bdm-type-image-0becfb63bd4fb969.yaml
new file mode 100644
index 0000000..6e8e8ba
--- /dev/null
+++ b/releasenotes/notes/story-2006302-support-bdm-type-image-0becfb63bd4fb969.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ The ``server create --block-device-mapping`` option now supports
+ an ``image`` type in addition to ``volume`` and ``snapshot``. When
+ specifying an image block device the compute service will create a volume
+ from the image of the specified size and attach it to the server.
+ [Story `2006302 <https://storyboard.openstack.org/#!/story/2006302>`_]
diff --git a/releasenotes/notes/usage-list-all-49ca7a62c50e71d3.yaml b/releasenotes/notes/usage-list-all-49ca7a62c50e71d3.yaml
new file mode 100644
index 0000000..76a09cc
--- /dev/null
+++ b/releasenotes/notes/usage-list-all-49ca7a62c50e71d3.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ Compute API v2.40+ returns all matching entities rather than being
+ limited to the API server configured maximum (``CONF.api.max_limit``).
+ [Story `2005099 <https://storyboard.openstack.org/#!/story/2005099>`_]
diff --git a/releasenotes/notes/volume-v3-default-0ffa9bebb43b5057.yaml b/releasenotes/notes/volume-v3-default-0ffa9bebb43b5057.yaml
new file mode 100644
index 0000000..6b08407
--- /dev/null
+++ b/releasenotes/notes/volume-v3-default-0ffa9bebb43b5057.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+ - |
+ Volume commands now default to Volume API 3. On older clouds
+ that do not support Volume 3.x ``--os-volume-api-version 2``
+ or the adition of ``volume_api_version: '2'`` in ``clouds.yaml``
+ will be required.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 9e2d8ce..c9cc7a2 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -27,6 +27,8 @@ OpenStack release was made is shown below:
================= =======================
OpenStack Release OpenStackClient Release
================= =======================
+Train 4.0.0
+Stein 3.18.0
Rocky 3.16.0
Queens 3.14.0
Pike 3.12.0
diff --git a/requirements.txt b/requirements.txt
index aa5debf..67139bb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,10 +8,10 @@ Babel!=2.4.0,>=2.3.4 # BSD
cliff!=2.9.0,>=2.8.0 # Apache-2.0
keystoneauth1>=3.6.2 # Apache-2.0
openstacksdk>=0.17.0 # Apache-2.0
-osc-lib>=1.10.0 # Apache-2.0
+osc-lib>=1.14.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0
python-glanceclient>=2.8.0 # Apache-2.0
python-keystoneclient>=3.17.0 # Apache-2.0
-python-novaclient>=10.0.0 # Apache-2.0
+python-novaclient>=15.0.0 # Apache-2.0
python-cinderclient>=3.3.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index 3c98940..555ffc9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -17,6 +17,7 @@ classifier =
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
+ Programming Language :: Python :: 3.7
[files]
packages =
@@ -25,8 +26,6 @@ packages =
[entry_points]
console_scripts =
openstack = openstackclient.shell:main
-keystoneauth1.plugin =
- token_endpoint = openstackclient.api.auth_plugin:TokenEndpoint
openstack.cli =
command_list = openstackclient.common.module:ListCommand
module_list = openstackclient.common.module:ListModule
@@ -86,12 +85,6 @@ openstack.compute.v2 =
hypervisor_stats_show = openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats
- ip_fixed_add = openstackclient.compute.v2.fixedip:AddFixedIP
- ip_fixed_remove = openstackclient.compute.v2.fixedip:RemoveFixedIP
-
- ip_floating_add = openstackclient.compute.v2.floatingip:AddFloatingIP
- ip_floating_remove = openstackclient.compute.v2.floatingip:RemoveFloatingIP
-
keypair_create = openstackclient.compute.v2.keypair:CreateKeypair
keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair
keypair_list = openstackclient.compute.v2.keypair:ListKeypair
@@ -119,6 +112,8 @@ openstack.compute.v2 =
server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume
server_rescue = openstackclient.compute.v2.server:RescueServer
server_resize = openstackclient.compute.v2.server:ResizeServer
+ server_resize_confirm = openstackclient.compute.v2.server:ResizeConfirm
+ server_resize_revert = openstackclient.compute.v2.server:ResizeRevert
server_restore = openstackclient.compute.v2.server:RestoreServer
server_resume = openstackclient.compute.v2.server:ResumeServer
server_set = openstackclient.compute.v2.server:SetServer
@@ -191,8 +186,6 @@ openstack.identity.v2 =
user_list = openstackclient.identity.v2_0.user:ListUser
user_set = openstackclient.identity.v2_0.user:SetUser
user_show = openstackclient.identity.v2_0.user:ShowUser
-
- user_role_list = openstackclient.identity.v2_0.role:ListUserRole
openstack.identity.v3 =
access_token_create = openstackclient.identity.v3.token:CreateAccessToken
@@ -379,6 +372,12 @@ openstack.network.v2 =
floating_ip_pool_list = openstackclient.network.v2.floating_ip_pool:ListFloatingIPPool
+ floating_ip_port_forwarding_create = openstackclient.network.v2.floating_ip_port_forwarding:CreateFloatingIPPortForwarding
+ floating_ip_port_forwarding_delete = openstackclient.network.v2.floating_ip_port_forwarding:DeleteFloatingIPPortForwarding
+ floating_ip_port_forwarding_list = openstackclient.network.v2.floating_ip_port_forwarding:ListFloatingIPPortForwarding
+ floating_ip_port_forwarding_set = openstackclient.network.v2.floating_ip_port_forwarding:SetFloatingIPPortForwarding
+ floating_ip_port_forwarding_show = openstackclient.network.v2.floating_ip_port_forwarding:ShowFloatingIPPortForwarding
+
ip_availability_list = openstackclient.network.v2.ip_availability:ListIPAvailability
ip_availability_show = openstackclient.network.v2.ip_availability:ShowIPAvailability
@@ -522,19 +521,6 @@ openstack.object_store.v1 =
object_show = openstackclient.object.v1.object:ShowObject
object_unset = openstackclient.object.v1.object:UnsetObject
openstack.volume.v1 =
- backup_create = openstackclient.volume.v1.backup:CreateBackup
- backup_delete = openstackclient.volume.v1.backup:DeleteBackup
- backup_list = openstackclient.volume.v1.backup:ListBackup
- backup_restore = openstackclient.volume.v1.backup:RestoreBackup
- backup_show = openstackclient.volume.v1.backup:ShowBackup
-
- snapshot_create = openstackclient.volume.v1.snapshot:CreateSnapshot
- snapshot_delete = openstackclient.volume.v1.snapshot:DeleteSnapshot
- snapshot_list = openstackclient.volume.v1.snapshot:ListSnapshot
- snapshot_set = openstackclient.volume.v1.snapshot:SetSnapshot
- snapshot_show = openstackclient.volume.v1.snapshot:ShowSnapshot
- snapshot_unset = openstackclient.volume.v1.snapshot:UnsetSnapshot
-
volume_create = openstackclient.volume.v1.volume:CreateVolume
volume_delete = openstackclient.volume.v1.volume:DeleteVolume
volume_list = openstackclient.volume.v1.volume:ListVolume
@@ -543,11 +529,11 @@ openstack.volume.v1 =
volume_show = openstackclient.volume.v1.volume:ShowVolume
volume_unset = openstackclient.volume.v1.volume:UnsetVolume
- volume_backup_create = openstackclient.volume.v1.backup:CreateVolumeBackup
- volume_backup_delete = openstackclient.volume.v1.backup:DeleteVolumeBackup
- volume_backup_list = openstackclient.volume.v1.backup:ListVolumeBackup
- volume_backup_restore = openstackclient.volume.v1.backup:RestoreVolumeBackup
- volume_backup_show = openstackclient.volume.v1.backup:ShowVolumeBackup
+ volume_backup_create = openstackclient.volume.v1.volume_backup:CreateVolumeBackup
+ volume_backup_delete = openstackclient.volume.v1.volume_backup:DeleteVolumeBackup
+ volume_backup_list = openstackclient.volume.v1.volume_backup:ListVolumeBackup
+ volume_backup_restore = openstackclient.volume.v1.volume_backup:RestoreVolumeBackup
+ volume_backup_show = openstackclient.volume.v1.volume_backup:ShowVolumeBackup
volume_snapshot_create = openstackclient.volume.v1.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_delete = openstackclient.volume.v1.volume_snapshot:DeleteVolumeSnapshot
@@ -581,12 +567,6 @@ openstack.volume.v1 =
volume_transfer_request_list = openstackclient.volume.v1.volume_transfer_request:ListTransferRequest
volume_transfer_request_show = openstackclient.volume.v1.volume_transfer_request:ShowTransferRequest
openstack.volume.v2 =
- backup_create = openstackclient.volume.v2.backup:CreateBackup
- backup_delete = openstackclient.volume.v2.backup:DeleteBackup
- backup_list = openstackclient.volume.v2.backup:ListBackup
- backup_restore = openstackclient.volume.v2.backup:RestoreBackup
- backup_show = openstackclient.volume.v2.backup:ShowBackup
-
consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup
consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup
consistency_group_delete = openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup
@@ -600,13 +580,6 @@ openstack.volume.v2 =
consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot
consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot
- snapshot_create = openstackclient.volume.v2.snapshot:CreateSnapshot
- snapshot_delete = openstackclient.volume.v2.snapshot:DeleteSnapshot
- snapshot_list = openstackclient.volume.v2.snapshot:ListSnapshot
- snapshot_set = openstackclient.volume.v2.snapshot:SetSnapshot
- snapshot_show = openstackclient.volume.v2.snapshot:ShowSnapshot
- snapshot_unset = openstackclient.volume.v2.snapshot:UnsetSnapshot
-
volume_create = openstackclient.volume.v2.volume:CreateVolume
volume_delete = openstackclient.volume.v2.volume:DeleteVolume
volume_list = openstackclient.volume.v2.volume:ListVolume
@@ -615,12 +588,12 @@ openstack.volume.v2 =
volume_show = openstackclient.volume.v2.volume:ShowVolume
volume_unset = openstackclient.volume.v2.volume:UnsetVolume
- volume_backup_create = openstackclient.volume.v2.backup:CreateVolumeBackup
- volume_backup_delete = openstackclient.volume.v2.backup:DeleteVolumeBackup
- volume_backup_list = openstackclient.volume.v2.backup:ListVolumeBackup
- volume_backup_restore = openstackclient.volume.v2.backup:RestoreVolumeBackup
- volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
- volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
+ volume_backup_create = openstackclient.volume.v2.volume_backup:CreateVolumeBackup
+ volume_backup_delete = openstackclient.volume.v2.volume_backup:DeleteVolumeBackup
+ volume_backup_list = openstackclient.volume.v2.volume_backup:ListVolumeBackup
+ volume_backup_restore = openstackclient.volume.v2.volume_backup:RestoreVolumeBackup
+ volume_backup_set = openstackclient.volume.v2.volume_backup:SetVolumeBackup
+ volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup
volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord
volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord
@@ -684,12 +657,12 @@ openstack.volume.v3 =
volume_show = openstackclient.volume.v2.volume:ShowVolume
volume_unset = openstackclient.volume.v2.volume:UnsetVolume
- volume_backup_create = openstackclient.volume.v2.backup:CreateVolumeBackup
- volume_backup_delete = openstackclient.volume.v2.backup:DeleteVolumeBackup
- volume_backup_list = openstackclient.volume.v2.backup:ListVolumeBackup
- volume_backup_restore = openstackclient.volume.v2.backup:RestoreVolumeBackup
- volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
- volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
+ volume_backup_create = openstackclient.volume.v2.volume_backup:CreateVolumeBackup
+ volume_backup_delete = openstackclient.volume.v2.volume_backup:DeleteVolumeBackup
+ volume_backup_list = openstackclient.volume.v2.volume_backup:ListVolumeBackup
+ volume_backup_restore = openstackclient.volume.v2.volume_backup:RestoreVolumeBackup
+ volume_backup_set = openstackclient.volume.v2.volume_backup:SetVolumeBackup
+ volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup
volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost
diff --git a/test-requirements.txt b/test-requirements.txt
index 4cb66cf..4db30c7 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,8 +1,7 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
-
+hacking>=1.1.0,<1.2.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
flake8-import-order==0.13 # LGPLv3
@@ -39,4 +38,4 @@ python-searchlightclient>=1.0.0 #Apache-2.0
python-senlinclient>=1.1.0 # Apache-2.0
python-troveclient>=2.2.0 # Apache-2.0
python-zaqarclient>=1.0.0 # Apache-2.0
-python-zunclient>=1.3.0 # Apache-2.0
+python-zunclient>=3.4.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 8145179..a973b10 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
minversion = 2.3
-envlist = py36,py27,pep8
+envlist = py27,py37,pep8
skipdist = True
[testenv]
@@ -11,7 +11,7 @@ setenv = VIRTUAL_ENV={envdir}
OS_STDERR_CAPTURE=1
OS_TEST_TIMEOUT=60
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt
commands = stestr run {posargs}
@@ -88,7 +88,7 @@ commands =
[testenv:venv]
basepython = python3
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands = {posargs}
@@ -112,7 +112,7 @@ commands =
[testenv:docs]
basepython = python3
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands =
@@ -122,7 +122,7 @@ commands =
[testenv:releasenotes]
basepython = python3
deps =
- -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/upper-constraints.txt}
+ -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -132,9 +132,8 @@ show-source = True
# H203: Use assertIs(Not)None to check for None
enable-extensions = H203
exclude = .git,.tox,dist,doc,*lib/python*,*egg,build,tools
-# If 'ignore' is not set there are default errors and warnings that are set
-# Doc: http://flake8.readthedocs.org/en/latest/config.html#default
-ignore = __
+# W504 is disabled since you must choose between this or W503
+ignore = W504
import-order-style = pep8
application_import_names = openstackclient