summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Bryant <corey.bryant@canonical.com>2022-09-08 18:25:53 -0400
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2022-09-08 22:34:10 +0000
commitfad5650933d0a7320db7ded8df77cdbc9267c7ac (patch)
tree493ab1059740f6933d8adcf63d1c16c5ff93d349
parent4c7c05ece5d38fb781c721d1f21af502060b897a (diff)
Imported using git-ubuntu import.
Notes
Notes: * New upstream release for OpenStack Zed. * d/control: Bump debhelper compat to 13. * d/control: Update standards version to 4.6.1.
-rw-r--r--.pre-commit-config.yaml1
-rw-r--r--.zuul.yaml19
-rw-r--r--AUTHORS6
-rw-r--r--ChangeLog44
-rw-r--r--Dockerfile4
-rw-r--r--PKG-INFO7
-rw-r--r--debian/changelog8
-rw-r--r--debian/control5
-rw-r--r--doc/source/cli/command-objects/block-storage-cluster.rst8
-rw-r--r--doc/source/cli/command-objects/block-storage-resource-filter.rst8
-rw-r--r--doc/source/cli/commands.rst2
-rw-r--r--doc/source/cli/data/cinder.csv10
-rw-r--r--doc/source/cli/data/nova.csv2
-rw-r--r--lower-constraints.txt97
-rw-r--r--openstackclient/common/progressbar.py2
-rw-r--r--openstackclient/common/quota.py9
-rw-r--r--openstackclient/compute/v2/server.py417
-rw-r--r--openstackclient/compute/v2/server_event.py38
-rw-r--r--openstackclient/compute/v2/server_group.py103
-rw-r--r--openstackclient/compute/v2/server_migration.py485
-rw-r--r--openstackclient/identity/v3/endpoint_group.py2
-rw-r--r--openstackclient/image/v2/image.py768
-rw-r--r--openstackclient/network/v2/address_group.py2
-rw-r--r--openstackclient/network/v2/address_scope.py2
-rw-r--r--openstackclient/network/v2/floating_ip.py2
-rw-r--r--openstackclient/network/v2/floating_ip_port_forwarding.py2
-rw-r--r--openstackclient/network/v2/ip_availability.py2
-rw-r--r--openstackclient/network/v2/l3_conntrack_helper.py2
-rw-r--r--openstackclient/network/v2/local_ip.py12
-rw-r--r--openstackclient/network/v2/local_ip_association.py20
-rw-r--r--openstackclient/network/v2/network.py1
-rw-r--r--openstackclient/network/v2/network_agent.py2
-rw-r--r--openstackclient/network/v2/network_auto_allocated_topology.py2
-rw-r--r--openstackclient/network/v2/network_flavor.py2
-rw-r--r--openstackclient/network/v2/network_flavor_profile.py4
-rw-r--r--openstackclient/network/v2/network_meter.py2
-rw-r--r--openstackclient/network/v2/network_meter_rule.py2
-rw-r--r--openstackclient/network/v2/network_qos_policy.py2
-rw-r--r--openstackclient/network/v2/network_qos_rule.py42
-rw-r--r--openstackclient/network/v2/network_qos_rule_type.py4
-rw-r--r--openstackclient/network/v2/network_rbac.py2
-rw-r--r--openstackclient/network/v2/network_segment.py2
-rw-r--r--openstackclient/network/v2/network_segment_range.py2
-rw-r--r--openstackclient/network/v2/port.py2
-rw-r--r--openstackclient/network/v2/security_group.py1
-rw-r--r--openstackclient/network/v2/security_group_rule.py2
-rw-r--r--openstackclient/network/v2/subnet.py9
-rw-r--r--openstackclient/network/v2/subnet_pool.py2
-rw-r--r--openstackclient/tests/functional/base.py25
-rw-r--r--openstackclient/tests/functional/common/test_quota.py48
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server.py33
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server_event.py35
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server_group.py24
-rw-r--r--openstackclient/tests/functional/image/v2/test_image.py8
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_qos_rule.py67
-rw-r--r--openstackclient/tests/functional/volume/v1/test_volume_type.py4
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_type.py4
-rw-r--r--openstackclient/tests/functional/volume/v3/test_volume_type.py4
-rw-r--r--openstackclient/tests/unit/common/test_availability_zone.py3
-rw-r--r--openstackclient/tests/unit/common/test_quota.py1
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py97
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py894
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_group.py285
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_migration.py1028
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py235
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py1594
-rw-r--r--openstackclient/tests/unit/network/v2/test_address_group.py20
-rw-r--r--openstackclient/tests/unit/network/v2/test_address_scope.py32
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_network.py16
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_ip_availability.py10
-rw-r--r--openstackclient/tests/unit/network/v2/test_local_ip.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_local_ip_association.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py97
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_agent.py63
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py12
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_flavor.py72
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_flavor_profile.py54
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_qos_rule.py327
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py50
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_segment.py19
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_segment_range.py18
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py98
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py15
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule_network.py2
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py10
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet_pool.py6
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume_backup.py52
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume_backup.py74
-rw-r--r--openstackclient/tests/unit/volume/v3/fakes.py109
-rw-r--r--openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py434
-rw-r--r--openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py144
-rw-r--r--openstackclient/tests/unit/volume/v3/test_volume_attachment.py3
-rw-r--r--openstackclient/volume/v1/volume_backup.py19
-rw-r--r--openstackclient/volume/v1/volume_type.py2
-rw-r--r--openstackclient/volume/v2/volume.py2
-rw-r--r--openstackclient/volume/v2/volume_backup.py58
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py2
-rw-r--r--openstackclient/volume/v3/block_storage_cluster.py281
-rw-r--r--openstackclient/volume/v3/block_storage_resource_filter.py83
-rw-r--r--openstackclient/volume/v3/volume_attachment.py23
-rw-r--r--python_openstackclient.egg-info/PKG-INFO7
-rw-r--r--python_openstackclient.egg-info/SOURCES.txt20
-rw-r--r--python_openstackclient.egg-info/entry_points.txt13
-rw-r--r--python_openstackclient.egg-info/pbr.json2
-rw-r--r--releasenotes/notes/add-block-storage-cluster-commands-fae8f686582bbbcf.yaml7
-rw-r--r--releasenotes/notes/add-more-server-list-columns-4e3b87929dd330f7.yaml16
-rw-r--r--releasenotes/notes/bug-1597189-02a8d8a402725860.yaml10
-rw-r--r--releasenotes/notes/fix-image-create-from-volume-c573e553161605c4.yaml7
-rw-r--r--releasenotes/notes/image-list-multiple-tags-a394799c7807f031.yaml5
-rw-r--r--releasenotes/notes/qos-min-pps-rule-bfe22cea1966c4a0.yaml7
-rw-r--r--releasenotes/notes/quota-network-force-920913981b45ba1a.yaml5
-rw-r--r--releasenotes/notes/server-list-host-status-1f542a5bc4292a62.yaml6
-rw-r--r--releasenotes/notes/server-migration-by-uuid-59f8272f63abee5d.yaml6
-rw-r--r--releasenotes/notes/volume-attachment-create-output-fix-56515b8fcdd260b9.yaml6
-rw-r--r--releasenotes/source/index.rst1
-rw-r--r--releasenotes/source/yoga.rst6
-rw-r--r--requirements.txt4
-rw-r--r--setup.cfg20
-rw-r--r--tox.ini12
120 files changed, 5731 insertions, 3209 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f91d10b..28ed93d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,6 +16,7 @@ repos:
- id: debug-statements
- id: check-yaml
files: .*\.(yaml|yml)$
+ args: ['--unsafe']
- repo: local
hooks:
- id: flake8
diff --git a/.zuul.yaml b/.zuul.yaml
index 9ed506c..98e2ed7 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -18,8 +18,8 @@
zuul_work_dir: src/opendev.org/openstack/python-openstackclient
- job:
- name: osc-tox-py36-tips
- parent: openstack-tox-py36
+ name: osc-tox-py38-tips
+ parent: openstack-tox-py38
description: |
Run unit tests for OpenStackClient with master branch of important libs.
@@ -189,8 +189,8 @@
description: Build Docker images.
allowed-projects: openstack/python-openstackclient
requires:
- - python-builder-3.7-container-image
- - python-base-3.7-container-image
+ - python-builder-3.9-container-image
+ - python-base-3.9-container-image
provides: osc-container-image
vars: &osc_image_vars
docker_images:
@@ -203,8 +203,8 @@
description: Build Docker images and upload to Docker Hub.
allowed-projects: openstack/python-openstackclient
requires:
- - python-builder-3.7-container-image
- - python-base-3.7-container-image
+ - python-builder-3.9-container-image
+ - python-base-3.9-container-image
provides: osc-container-image
secrets:
- name: docker_credentials
@@ -229,18 +229,17 @@
name: osc-tox-unit-tips
check:
jobs:
- - osc-tox-py36-tips
+ - osc-tox-py38-tips
gate:
jobs:
- - osc-tox-py36-tips
+ - osc-tox-py38-tips
- project:
templates:
- openstackclient-plugin-jobs
- osc-tox-unit-tips
- openstack-cover-jobs
- - openstack-lower-constraints-jobs
- - openstack-python3-yoga-jobs
+ - openstack-python3-zed-jobs
- publish-openstack-docs-pti
- check-requirements
- release-notes-jobs-python3
diff --git a/AUTHORS b/AUTHORS
index 40c44d8..3205fb8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -171,6 +171,7 @@ James E. Blair <jeblair@hp.com>
James E. Blair <jeblair@redhat.com>
Jamie Lennox <jamielennox@redhat.com>
Jan Gutter <jan.gutter@netronome.com>
+Jan Hartkopf <jhartkopf@inovex.de>
Jas <singhj@us.ibm.com>
Jaspreet Singh Rawel <jaspreetsinghrawel@gmail.com>
Javier Pena <jpena@redhat.com>
@@ -287,6 +288,7 @@ Petr Blaho <pblaho@redhat.com>
Pierre Hanselmann <pierre.hanselmann@gmail.com>
Pierre Prinetti <pierreprinetti@redhat.com>
Pierre Riteau <pierre@stackhpc.com>
+Przemyslaw Szczerbik <przemyslaw.szczerbik@est.tech>
Qiu Yu <qiuyu@ebaysf.com>
Radoslaw Smigielski <radoslaw.smigielski@nokia.com>
Radosław Piliszek <radoslaw.piliszek@gmail.com>
@@ -342,6 +344,7 @@ SongmingYan <yan.songming@zte.com.cn>
Stephen Finucane <sfinucan@redhat.com>
Stephen Finucane <stephen.finucane@intel.com>
Stephen Finucane <stephen@that.guru>
+Stephen Finucane <stephenfin@redhat.com>
Steve Martinelli <s.martinelli@gmail.com>
Steve Martinelli <stevemar@ca.ibm.com>
Steven Hardy <shardy@redhat.com>
@@ -357,6 +360,7 @@ Telles Nobrega <tenobreg@redhat.com>
Terry Howe <terrylhowe@gmail.com>
Terry Howe <thowe@hp.com>
TerryHowe <terrylhowe@gmail.com>
+Thobias Salazar Trevisan <thobiast@gmail.com>
Thomas Goirand <zigo@debian.org>
Thrivikram Mudunuri <mthrivikram+opendev@gmail.com>
Tim Burke <tim.burke@gmail.com>
@@ -374,6 +378,7 @@ Vasyl Saienko <vsaienko@mirantis.com>
Victor Silva <victsou@gmail.com>
Vijendra Soni <vijendra.soni@cognizant.com>
Vincent Legoll <vincent.legoll@idgrilles.fr>
+Violet Kurtz <vikurtz@osuosl.org>
Vishakha Agarwal <agarwalvishakha18@gmail.com>
Vladimir Eremin <yottatsa@yandex-team.ru>
Vu Cong Tuan <tuanvc@vn.fujitsu.com>
@@ -389,6 +394,7 @@ Yang Youseok <ileixe@gmail.com>
Yejia Xu <yejia@unitedstack.com>
Yi Zhao <zhaoyi@cmss.chinamobile.com>
Yongli He <yongli.he@intel.com>
+Yoonsoo Lim <msdbtjd123@naver.com>
YuehuiLei <leiyuehui@inspur.com>
Yunpeng Li <yunpeng.li@live.cn>
Zane Bitter <zbitter@redhat.com>
diff --git a/ChangeLog b/ChangeLog
index 33abd4e..ee708aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,10 +1,50 @@
CHANGES
=======
+6.0.0
+-----
+
+* Migrate server\_groups to the new API
+* Update the Nova CLI Mapping Guide
+* Drop support for Python 3.6, 3.7
+* pre-commit: Allow unsafe YAML
+* Add support for CRUD operations for QoS minimum packet rate rule
+* image: Split image creation depending on service
+* image: Make better use of argparse
+* Fix: create image from volume command
+* image: Trivial style changes
+* Skip test\_quota\_network\_set\_with\_force func test
+* Don't show tenant\_id in network objects
+* Allow users to list all images
+* Migrate osc-tox-py3N-tips to Python 3.8
+* Add 'Host Status' to 'server list --long' with >= v2.16
+* volume: Correct output of 'volume attachment create'
+* Allow to filter multiple tags for image list
+* Stop testing lower-constraints
+* volume: Add 'block storage resource filter list' command
+* volume: Add 'block storage cluster \*' commands
+* Refactor network fakes to sdk properties PART 4
+* Stop using private \_is\_uuid\_like method
+* Add more filter option of columns for server list -c COLUMN
+* Allow "--force" flag in quota network commands
+* Refactor network fakes to sdk properties PART 3
+* compute: Add 'Security Groups' for 'server list'
+* Refactor network fakes to sdk properties PART 2
+* Refactor network fakes to sdk properties PART 1
+* Refactor "volume backup restore" command
+* compute: Move server migrations commands to their own file
+* compute: Allow retrieval of migration by UUID
+* volume: fix backup list of deleted volume
+* Add Python3 zed unit tests
+* Update master for stable/yoga
+* Fix metavars and typos in local\_ip
+
5.8.0
-----
* Add support for setting extra DHCP options on existing ports
+* volume list: don't fail when there's no compute service
+* Fix 'server event list|show' for deleted servers
* docs: Document additional 'server migration' commands
* Add support for 'remote-managed' vnic type
* Don't fail on extlinks warnings for reno build
@@ -19,6 +59,7 @@ CHANGES
* compute: Fix weird option definition for 'server ssh'
* compute: Return information about fixed IP
* Switch add fixed IP to SDK
+* tests: Improve logging for executed commands
* Allow setting gateway when creating a router
* Fix RemoveServerVolume
* Add network update quota "limit\_check" parameter
@@ -45,8 +86,10 @@ CHANGES
-----
* compute: Return details of attached volumes
+* compute: Use correct command class for 'show migration'
* compute: Don't warn if disk overcommit params unset
* Switch server backup to sdk
+* Fix typos
* Remove remnants of 'six'
* Remove 'get\_osc\_show\_columns\_for\_sdk\_resource' duplicates
* tox: Ignore virtualenvs for pep8 environment
@@ -94,6 +137,7 @@ CHANGES
* docs: Update novaclient comparison doc
* docs: Update cinderclient comparison doc
* Include hosts in aggregate list --long
+* Changed minversion in tox to 3.18.0
* compute: Fix typo
* setup.cfg: Replace dashes with underscores
* Make functional Neutron tests running fine on ML2/OVN environments
diff --git a/Dockerfile b/Dockerfile
index bf5de3c..90f7fd3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,12 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FROM docker.io/opendevorg/python-builder:3.7 as builder
+FROM docker.io/opendevorg/python-builder:3.9 as builder
COPY . /tmp/src
RUN assemble
-FROM docker.io/opendevorg/python-base:3.7
+FROM docker.io/opendevorg/python-base:3.9
COPY --from=builder /output/ /output
RUN /output/install-from-bindep
diff --git a/PKG-INFO b/PKG-INFO
index 7868b65..0a41b07 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: python-openstackclient
-Version: 5.8.0
+Version: 6.0.0
Summary: OpenStack Command-line Client
Home-page: https://docs.openstack.org/python-openstackclient/latest/
Author: OpenStack
@@ -154,7 +154,6 @@ Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
-Requires-Python: >=3.6
+Classifier: Programming Language :: Python :: 3.9
+Requires-Python: >=3.8
diff --git a/debian/changelog b/debian/changelog
index 21a3e15..166a5ea 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+python-openstackclient (6.0.0-0ubuntu1) kinetic; urgency=medium
+
+ * New upstream release for OpenStack Zed.
+ * d/control: Bump debhelper compat to 13.
+ * d/control: Update standards version to 4.6.1.
+
+ -- Corey Bryant <corey.bryant@canonical.com> Thu, 08 Sep 2022 18:25:53 -0400
+
python-openstackclient (5.8.0-0ubuntu1) jammy; urgency=medium
* New upstream release for OpenStack Yoga.
diff --git a/debian/control b/debian/control
index 81b14b9..3d33561 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Uploaders:
gustavo panizzo <gfa@zumbi.com.ar>,
Thomas Goirand <zigo@debian.org>,
Build-Depends:
- debhelper-compat (= 12),
+ debhelper-compat (= 13),
dh-python,
openstack-pkg-tools,
python3-all,
@@ -65,8 +65,7 @@ Build-Depends-Indep:
python3-zaqarclient (>= 1.0.0),
python3-zunclient (>= 3.6.0),
subunit,
-Standards-Version: 4.5.0
-Vcs-Browser: https://git.launchpad.net/~ubuntu-openstack-dev/ubuntu/+source/python-openstackclient
+Standards-Version: 4.6.1
Vcs-Git: https://git.launchpad.net/~ubuntu-openstack-dev/ubuntu/+source/python-openstackclient
Homepage: https://opendev.org/openstack/python-openstackclient
Testsuite: autopkgtest-pkg-python
diff --git a/doc/source/cli/command-objects/block-storage-cluster.rst b/doc/source/cli/command-objects/block-storage-cluster.rst
new file mode 100644
index 0000000..318419e
--- /dev/null
+++ b/doc/source/cli/command-objects/block-storage-cluster.rst
@@ -0,0 +1,8 @@
+=====================
+block storage cluster
+=====================
+
+Block Storage v3
+
+.. autoprogram-cliff:: openstack.volume.v3
+ :command: block storage cluster *
diff --git a/doc/source/cli/command-objects/block-storage-resource-filter.rst b/doc/source/cli/command-objects/block-storage-resource-filter.rst
new file mode 100644
index 0000000..3e4c33b
--- /dev/null
+++ b/doc/source/cli/command-objects/block-storage-resource-filter.rst
@@ -0,0 +1,8 @@
+=============================
+block storage resource filter
+=============================
+
+Block Storage v3
+
+.. autoprogram-cliff:: openstack.volume.v3
+ :command: block storage resource filter *
diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst
index 7e59215..d789ece 100644
--- a/doc/source/cli/commands.rst
+++ b/doc/source/cli/commands.rst
@@ -76,6 +76,8 @@ referring to both Compute and Volume quotas.
* ``address scope``: (**Network**) a scope of IPv4 or IPv6 addresses
* ``aggregate``: (**Compute**) a grouping of compute hosts
* ``availability zone``: (**Compute**, **Network**, **Volume**) a logical partition of hosts or block storage or network services
+* ``block storage cluster``: (**Volume**) clusters of volume services
+* ``block storage resource filter``: (**Volume**) filters for volume service resources
* ``catalog``: (**Identity**) service catalog
* ``command``: (**Internal**) installed commands in the OSC process
* ``compute agent``: (**Compute**) a cloud Compute agent available to a hypervisor
diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv
index 9d79d1b..eeaaeac 100644
--- a/doc/source/cli/data/cinder.csv
+++ b/doc/source/cli/data/cinder.csv
@@ -20,10 +20,10 @@ cgsnapshot-create,consistency group snapshot create,Creates a cgsnapshot.
cgsnapshot-delete,consistency group snapshot delete,Removes one or more cgsnapshots.
cgsnapshot-list,consistency group snapshot list,Lists all cgsnapshots.
cgsnapshot-show,consistency group snapshot show,Shows cgsnapshot details.
-cluster-disable,,Disables clustered services. (Supported by API versions 3.7 - 3.latest)
-cluster-enable,,Enables clustered services. (Supported by API versions 3.7 - 3.latest)
-cluster-list,,Lists clustered services with optional filtering. (Supported by API versions 3.7 - 3.latest)
-cluster-show,,Show detailed information on a clustered service. (Supported by API versions 3.7 - 3.latest)
+cluster-disable,block storage cluster set --disable,Disables clustered services. (Supported by API versions 3.7 - 3.latest)
+cluster-enable,block storage cluster set --enable,Enables clustered services. (Supported by API versions 3.7 - 3.latest)
+cluster-list,block storage cluster list,Lists clustered services with optional filtering. (Supported by API versions 3.7 - 3.latest)
+cluster-show,block storage cluster show,Show detailed information on a clustered service. (Supported by API versions 3.7 - 3.latest)
consisgroup-create,consistency group create,Creates a consistency group.
consisgroup-create-from-src,consistency group create --consistency-group-snapshot,Creates a consistency group from a cgsnapshot or a source CG
consisgroup-delete,consistency group delete,Removes one or more consistency groups.
@@ -69,7 +69,7 @@ group-update,volume group set,Updates a group. (Supported by API versions 3.13 -
image-metadata,volume set --image-property,Sets or deletes volume image metadata.
image-metadata-show,volume show,Shows volume image metadata.
list,volume list,Lists all volumes.
-list-filters,,List enabled filters. (Supported by API versions 3.33 - 3.latest)
+list-filters,block storage resource filter list,List enabled filters. (Supported by API versions 3.33 - 3.latest)
manage,volume create --remote-source k=v,Manage an existing volume.
manageable-list,,Lists all manageable volumes. (Supported by API versions 3.8 - 3.latest)
message-delete,volume message delete,Removes one or more messages. (Supported by API versions 3.3 - 3.latest)
diff --git a/doc/source/cli/data/nova.csv b/doc/source/cli/data/nova.csv
index 0ab2b2f..4645916 100644
--- a/doc/source/cli/data/nova.csv
+++ b/doc/source/cli/data/nova.csv
@@ -47,7 +47,7 @@ hypervisor-stats,hypervisor stats show,Get hypervisor statistics over all comput
hypervisor-uptime,,Display the uptime of the specified hypervisor.
image-create,server image create,Create a new image by taking a snapshot of a running server.
instance-action,server event show,Show an action.
-instance-action-list,,List actions on a server.
+instance-action-list,server event list,List actions on a server.
instance-usage-audit-log,,List/Get server usage audits.
interface-attach,server add port / server add floating ip / server add fixed ip,Attach a network interface to a server.
interface-detach,server remove port,Detach a network interface from a server.
diff --git a/lower-constraints.txt b/lower-constraints.txt
deleted file mode 100644
index 495b102..0000000
--- a/lower-constraints.txt
+++ /dev/null
@@ -1,97 +0,0 @@
-amqp==2.1.1
-appdirs==1.3.0
-asn1crypto==0.23.0
-Babel==2.6.0
-bcrypt==3.2.0
-cachetools==2.0.0
-cffi==1.14.0
-cliff==3.5.0
-cmd2==0.8.0
-coverage==4.0
-cryptography==2.7
-ddt==1.0.1
-debtcollector==1.2.0
-decorator==4.4.1
-dogpile.cache==0.6.5
-eventlet==0.18.2
-extras==1.0.0
-fasteners==0.7.0
-fixtures==3.0.0
-future==0.16.0
-futurist==2.1.0
-greenlet==0.4.15
-importlib-metadata==3.1.1
-iso8601==0.1.11
-Jinja2==2.10
-jmespath==0.9.0
-jsonpatch==1.16
-jsonpointer==1.13
-jsonschema==2.6.0
-keystoneauth1==3.18.0
-kombu==4.0.0
-linecache2==1.0.0
-MarkupSafe==1.1.1
-mock==4.0.2
-monotonic==0.6
-mox3==0.20.0
-msgpack-python==0.4.0
-munch==2.1.0
-netaddr==0.7.18
-netifaces==0.10.4
-openstacksdk==0.61.0
-os-client-config==2.1.0
-os-service-types==1.7.0
-osc-lib==2.3.0
-oslo.concurrency==3.26.0
-oslo.config==5.2.0
-oslo.context==2.19.2
-oslo.i18n==3.15.3
-oslo.log==3.36.0
-oslo.messaging==5.29.0
-oslo.middleware==3.31.0
-oslo.serialization==2.18.0
-oslo.service==1.24.0
-oslo.utils==3.33.0
-oslotest==3.2.0
-osprofiler==1.4.0
-paramiko==2.7.1
-Paste==2.0.2
-PasteDeploy==1.5.0
-pbr==2.0.0
-pika==0.10.0
-pika-pool==0.1.3
-prettytable==0.7.2
-pycparser==2.18
-pyinotify==0.9.6
-PyNaCl==1.4.0
-pyparsing==2.1.0
-pyperclip==1.5.27
-python-cinderclient==3.3.0
-python-dateutil==2.5.3
-python-keystoneclient==3.22.0
-python-mimeparse==1.6.0
-python-novaclient==17.0.0
-python-subunit==1.0.0
-pytz==2013.6
-PyYAML==3.13
-repoze.lru==0.7
-requests==2.14.2
-requests-mock==1.2.0
-requestsexceptions==1.2.0
-rfc3986==0.3.1
-Routes==2.3.1
-simplejson==3.5.1
-statsd==3.2.1
-stestr==1.0.0
-stevedore==2.0.1
-tempest==17.1.0
-tenacity==3.2.1
-testrepository==0.0.18
-testtools==2.2.0
-traceback2==1.4.0
-unittest2==1.1.0
-urllib3==1.21.1
-vine==1.1.4
-WebOb==1.7.1
-wrapt==1.7.0
-zipp==3.4.0
diff --git a/openstackclient/common/progressbar.py b/openstackclient/common/progressbar.py
index ef767a9..7678ace 100644
--- a/openstackclient/common/progressbar.py
+++ b/openstackclient/common/progressbar.py
@@ -17,7 +17,7 @@ import sys
class _ProgressBarBase(object):
- """A progress bar provider for a wrapped obect.
+ """A progress bar provider for a wrapped object.
Base abstract class used by specific class wrapper to show
a progress bar when the wrapped object are consumed.
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index 677cba0..4448236 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -205,7 +205,7 @@ class BaseQuota(object):
class ListQuota(command.Lister, BaseQuota):
_description = _(
"List quotas for all projects with non-default quota values or "
- "list detailed quota informations for requested project")
+ "list detailed quota information for requested project")
def _get_detailed_quotas(self, parsed_args):
columns = (
@@ -230,7 +230,7 @@ class ListQuota(command.Lister, BaseQuota):
result = []
for resource, values in quotas.items():
# NOTE(slaweq): there is no detailed quotas info for some resources
- # and it should't be displayed here
+ # and it shouldn't be displayed here
if type(values) is dict:
result.append({
'resource': resource,
@@ -533,7 +533,8 @@ class SetQuota(common.NetDetectionMixin, command.Command):
parser.add_argument(
'--force',
action='store_true',
- help=_('Force quota update (only supported by compute)')
+ help=_('Force quota update (only supported by compute and '
+ 'network)')
)
parser.add_argument(
'--check-limit',
@@ -569,6 +570,8 @@ class SetQuota(common.NetDetectionMixin, command.Command):
network_kwargs = {}
if parsed_args.check_limit:
network_kwargs['check_limit'] = True
+ if parsed_args.force:
+ network_kwargs['force'] = True
if self.app.client_manager.is_network_endpoint_enabled():
for k, v in NETWORK_QUOTAS.items():
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index a46fb90..b02ee6f 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -2455,6 +2455,30 @@ class ListServer(command.Lister):
if c in ('Created At', 'created_at'):
columns += ('created',)
column_headers += ('Created At',)
+ if c in ('Security Groups', 'security_groups'):
+ columns += ('security_groups_name',)
+ column_headers += ('Security Groups',)
+ if c in ("Task State", "task_state"):
+ columns += ('OS-EXT-STS:task_state',)
+ column_headers += ('Task State',)
+ if c in ("Power State", "power_state"):
+ columns += ('OS-EXT-STS:power_state',)
+ column_headers += ('Power State',)
+ if c in ("Image ID", "image_id"):
+ columns += ('Image ID',)
+ column_headers += ('Image ID',)
+ if c in ("Flavor ID", "flavor_id"):
+ columns += ('Flavor ID',)
+ column_headers += ('Flavor ID',)
+ if c in ('Availability Zone', "availability_zone"):
+ columns += ('OS-EXT-AZ:availability_zone',)
+ column_headers += ('Availability Zone',)
+ if c in ('Host', "host"):
+ columns += ('OS-EXT-SRV-ATTR:host',)
+ column_headers += ('Host',)
+ if c in ('Properties', "properties"):
+ columns += ('Metadata',)
+ column_headers += ('Properties',)
# convert back to tuple
column_headers = tuple(column_headers)
@@ -2534,7 +2558,7 @@ class ListServer(command.Lister):
# and 'image' missing in the server response during
# infrastructure failure situations.
# For those servers with partial constructs we just skip the
- # processing of the image and flavor informations.
+ # processing of the image and flavor information.
if not hasattr(s, 'image') or not hasattr(s, 'flavor'):
continue
@@ -2559,6 +2583,28 @@ class ListServer(command.Lister):
else:
s.flavor_name = s.flavor['original_name']
+ # Add a list with security group name as attribute
+ for s in data:
+ if hasattr(s, 'security_groups'):
+ s.security_groups_name = [x["name"] for x in s.security_groups]
+ else:
+ s.security_groups_name = []
+
+ # The host_status field contains the status of the compute host the
+ # server is on. It is only returned by the API when the nova-api
+ # policy allows. Users can look at the host_status field when, for
+ # example, their server has status ACTIVE but is unresponsive. The
+ # host_status field can indicate a possible problem on the host
+ # it's on, providing useful information to a user in this
+ # situation.
+ if (
+ compute_client.api_version >= api_versions.APIVersion('2.16') and
+ parsed_args.long
+ ):
+ if any([hasattr(s, 'host_status') for s in data]):
+ columns += ('Host Status',)
+ column_headers += ('Host Status',)
+
table = (
column_headers,
(
@@ -2574,6 +2620,7 @@ class ListServer(command.Lister):
'OS-EXT-STS:power_state': PowerStateColumn,
'networks': format_columns.DictListColumn,
'metadata': format_columns.DictColumn,
+ 'security_groups_name': format_columns.ListColumn,
},
) for s in data
),
@@ -2811,372 +2858,6 @@ revert to release the new server and restart the old one.""")
raise SystemExit
-class ListMigration(command.Lister):
- _description = _("""List server migrations""")
-
- def get_parser(self, prog_name):
- parser = super(ListMigration, self).get_parser(prog_name)
- parser.add_argument(
- '--server',
- metavar='<server>',
- help=_(
- 'Filter migrations by server (name or ID)'
- )
- )
- parser.add_argument(
- '--host',
- metavar='<host>',
- help=_(
- 'Filter migrations by source or destination host'
- ),
- )
- parser.add_argument(
- '--status',
- metavar='<status>',
- help=_('Filter migrations by status')
- )
- parser.add_argument(
- '--type',
- metavar='<type>',
- choices=[
- 'evacuation', 'live-migration', 'cold-migration', 'resize',
- ],
- help=_('Filter migrations by type'),
- )
- parser.add_argument(
- '--marker',
- metavar='<marker>',
- help=_(
- "The last migration of the previous page; displays list "
- "of migrations after 'marker'. Note that the marker is "
- "the migration UUID. "
- "(supported with --os-compute-api-version 2.59 or above)"
- ),
- )
- parser.add_argument(
- '--limit',
- metavar='<limit>',
- type=int,
- help=_(
- "Maximum number of migrations to display. Note that there "
- "is a configurable max limit on the server, and the limit "
- "that is used will be the minimum of what is requested "
- "here and what is configured in the server. "
- "(supported with --os-compute-api-version 2.59 or above)"
- ),
- )
- parser.add_argument(
- '--changes-since',
- dest='changes_since',
- metavar='<changes-since>',
- help=_(
- "List only migrations changed later or equal to a certain "
- "point of time. The provided time should be an ISO 8061 "
- "formatted time, e.g. ``2016-03-04T06:27:59Z``. "
- "(supported with --os-compute-api-version 2.59 or above)"
- ),
- )
- parser.add_argument(
- '--changes-before',
- dest='changes_before',
- metavar='<changes-before>',
- help=_(
- "List only migrations changed earlier or equal to a "
- "certain point of time. The provided time should be an ISO "
- "8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
- "(supported with --os-compute-api-version 2.66 or above)"
- ),
- )
- parser.add_argument(
- '--project',
- metavar='<project>',
- help=_(
- "Filter migrations by project (name or ID) "
- "(supported with --os-compute-api-version 2.80 or above)"
- ),
- )
- identity_common.add_project_domain_option_to_parser(parser)
- parser.add_argument(
- '--user',
- metavar='<user>',
- help=_(
- "Filter migrations by user (name or ID) "
- "(supported with --os-compute-api-version 2.80 or above)"
- ),
- )
- identity_common.add_user_domain_option_to_parser(parser)
- return parser
-
- def print_migrations(self, parsed_args, compute_client, migrations):
- column_headers = [
- 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
- 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor',
- 'Created At', 'Updated At',
- ]
-
- # Response fields coming back from the REST API are not always exactly
- # the same as the column header names.
- columns = [
- 'source_node', 'dest_node', 'source_compute', 'dest_compute',
- 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
- 'new_instance_type_id', 'created_at', 'updated_at',
- ]
-
- # Insert migrations UUID after ID
- if compute_client.api_version >= api_versions.APIVersion("2.59"):
- column_headers.insert(0, "UUID")
- columns.insert(0, "uuid")
-
- if compute_client.api_version >= api_versions.APIVersion("2.23"):
- column_headers.insert(0, "Id")
- columns.insert(0, "id")
- column_headers.insert(len(column_headers) - 2, "Type")
- columns.insert(len(columns) - 2, "migration_type")
-
- if compute_client.api_version >= api_versions.APIVersion("2.80"):
- if parsed_args.project:
- column_headers.insert(len(column_headers) - 2, "Project")
- columns.insert(len(columns) - 2, "project_id")
- if parsed_args.user:
- column_headers.insert(len(column_headers) - 2, "User")
- columns.insert(len(columns) - 2, "user_id")
-
- return (
- column_headers,
- (utils.get_item_properties(mig, columns) for mig in migrations),
- )
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- identity_client = self.app.client_manager.identity
-
- search_opts = {
- 'host': parsed_args.host,
- 'status': parsed_args.status,
- }
-
- if parsed_args.server:
- search_opts['instance_uuid'] = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).id
-
- if parsed_args.type:
- migration_type = parsed_args.type
- # we're using an alias because the default value is confusing
- if migration_type == 'cold-migration':
- migration_type = 'migration'
- search_opts['migration_type'] = migration_type
-
- if parsed_args.marker:
- if compute_client.api_version < api_versions.APIVersion('2.59'):
- msg = _(
- '--os-compute-api-version 2.59 or greater is required to '
- 'support the --marker option'
- )
- raise exceptions.CommandError(msg)
- search_opts['marker'] = parsed_args.marker
-
- if parsed_args.limit:
- if compute_client.api_version < api_versions.APIVersion('2.59'):
- msg = _(
- '--os-compute-api-version 2.59 or greater is required to '
- 'support the --limit option'
- )
- raise exceptions.CommandError(msg)
- search_opts['limit'] = parsed_args.limit
-
- if parsed_args.changes_since:
- if compute_client.api_version < api_versions.APIVersion('2.59'):
- msg = _(
- '--os-compute-api-version 2.59 or greater is required to '
- 'support the --changes-since option'
- )
- raise exceptions.CommandError(msg)
- search_opts['changes_since'] = parsed_args.changes_since
-
- if parsed_args.changes_before:
- if compute_client.api_version < api_versions.APIVersion('2.66'):
- msg = _(
- '--os-compute-api-version 2.66 or greater is required to '
- 'support the --changes-before option'
- )
- raise exceptions.CommandError(msg)
- search_opts['changes_before'] = parsed_args.changes_before
-
- if parsed_args.project:
- if compute_client.api_version < api_versions.APIVersion('2.80'):
- msg = _(
- '--os-compute-api-version 2.80 or greater is required to '
- 'support the --project option'
- )
- raise exceptions.CommandError(msg)
-
- search_opts['project_id'] = identity_common.find_project(
- identity_client,
- parsed_args.project,
- parsed_args.project_domain,
- ).id
-
- if parsed_args.user:
- if compute_client.api_version < api_versions.APIVersion('2.80'):
- msg = _(
- '--os-compute-api-version 2.80 or greater is required to '
- 'support the --user option'
- )
- raise exceptions.CommandError(msg)
-
- search_opts['user_id'] = identity_common.find_user(
- identity_client,
- parsed_args.user,
- parsed_args.user_domain,
- ).id
-
- migrations = compute_client.migrations.list(**search_opts)
-
- return self.print_migrations(parsed_args, compute_client, migrations)
-
-
-class ShowMigration(command.Command):
- """Show a migration for a given server."""
-
- def get_parser(self, prog_name):
- parser = super().get_parser(prog_name)
- parser.add_argument(
- 'server',
- metavar='<server>',
- help=_('Server (name or ID)'),
- )
- parser.add_argument(
- 'migration',
- metavar='<migration>',
- help=_("Migration (ID)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
-
- if compute_client.api_version < api_versions.APIVersion('2.24'):
- msg = _(
- '--os-compute-api-version 2.24 or greater is required to '
- 'support the server migration show command'
- )
- raise exceptions.CommandError(msg)
-
- server = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- )
- server_migration = compute_client.server_migrations.get(
- server.id, parsed_args.migration,
- )
-
- columns = (
- 'ID',
- 'Server UUID',
- 'Status',
- 'Source Compute',
- 'Source Node',
- 'Dest Compute',
- 'Dest Host',
- 'Dest Node',
- 'Memory Total Bytes',
- 'Memory Processed Bytes',
- 'Memory Remaining Bytes',
- 'Disk Total Bytes',
- 'Disk Processed Bytes',
- 'Disk Remaining Bytes',
- 'Created At',
- 'Updated At',
- )
-
- if compute_client.api_version >= api_versions.APIVersion('2.59'):
- columns += ('UUID',)
-
- if compute_client.api_version >= api_versions.APIVersion('2.80'):
- columns += ('User ID', 'Project ID')
-
- data = utils.get_item_properties(server_migration, columns)
- return columns, data
-
-
-class AbortMigration(command.Command):
- """Cancel an ongoing live migration.
-
- This command requires ``--os-compute-api-version`` 2.24 or greater.
- """
-
- def get_parser(self, prog_name):
- parser = super(AbortMigration, self).get_parser(prog_name)
- parser.add_argument(
- 'server',
- metavar='<server>',
- help=_('Server (name or ID)'),
- )
- parser.add_argument(
- 'migration',
- metavar='<migration>',
- help=_("Migration (ID)"),
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
-
- if compute_client.api_version < api_versions.APIVersion('2.24'):
- msg = _(
- '--os-compute-api-version 2.24 or greater is required to '
- 'support the server migration abort command'
- )
- raise exceptions.CommandError(msg)
-
- server = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- )
- compute_client.server_migrations.live_migration_abort(
- server.id, parsed_args.migration)
-
-
-class ForceCompleteMigration(command.Command):
- """Force an ongoing live migration to complete.
-
- This command requires ``--os-compute-api-version`` 2.22 or greater.
- """
-
- def get_parser(self, prog_name):
- parser = super(ForceCompleteMigration, self).get_parser(prog_name)
- parser.add_argument(
- 'server',
- metavar='<server>',
- help=_('Server (name or ID)'),
- )
- parser.add_argument(
- 'migration',
- metavar='<migration>',
- help=_('Migration (ID)')
- )
- return parser
-
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
-
- if compute_client.api_version < api_versions.APIVersion('2.22'):
- msg = _(
- '--os-compute-api-version 2.22 or greater is required to '
- 'support the server migration force complete command'
- )
- raise exceptions.CommandError(msg)
-
- server = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- )
- compute_client.server_migrations.live_migrate_force_complete(
- server.id, parsed_args.migration)
-
-
class PauseServer(command.Command):
_description = _("Pause server(s)")
@@ -4375,7 +4056,7 @@ class ShelveServer(command.Command):
server_obj.shelve()
- # if we don't hav to wait, either because it was requested explicitly
+ # if we don't have to wait, either because it was requested explicitly
# or is required implicitly, then our job is done
if not parsed_args.wait and not parsed_args.offload:
return
diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py
index 1b971e5..ebf0d52 100644
--- a/openstackclient/compute/v2/server_event.py
+++ b/openstackclient/compute/v2/server_event.py
@@ -22,6 +22,7 @@ from novaclient import api_versions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
+from oslo_utils import uuidutils
from openstackclient.i18n import _
@@ -33,7 +34,7 @@ class ListServerEvent(command.Lister):
"""List recent events of a server.
Specify ``--os-compute-api-version 2.21`` or higher to show events for a
- deleted server.
+ deleted server, specified by ID only.
"""
def get_parser(self, prog_name):
@@ -143,9 +144,18 @@ class ListServerEvent(command.Lister):
kwargs['changes_before'] = parsed_args.changes_before
- server_id = utils.find_resource(
- compute_client.servers, parsed_args.server,
- ).id
+ try:
+ server_id = utils.find_resource(
+ compute_client.servers, parsed_args.server,
+ ).id
+ except exceptions.CommandError:
+ # If we fail to find the resource, it is possible the server is
+ # deleted. Try once more using the <server> arg directly if it is a
+ # UUID.
+ if uuidutils.is_uuid_like(parsed_args.server):
+ server_id = parsed_args.server
+ else:
+ raise
data = compute_client.instance_action.list(server_id, **kwargs)
@@ -184,8 +194,9 @@ class ShowServerEvent(command.ShowOne):
"""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.
+ for a deleted server, specified by ID only. Specify
+ ``--os-compute-api-version 2.51`` or higher to show event details for
+ non-admin users.
"""
def get_parser(self, prog_name):
@@ -205,9 +216,18 @@ class ShowServerEvent(command.ShowOne):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- server_id = utils.find_resource(
- compute_client.servers, parsed_args.server,
- ).id
+ try:
+ server_id = utils.find_resource(
+ compute_client.servers, parsed_args.server,
+ ).id
+ except exceptions.CommandError:
+ # If we fail to find the resource, it is possible the server is
+ # deleted. Try once more using the <server> arg directly if it is a
+ # UUID.
+ if uuidutils.is_uuid_like(parsed_args.server):
+ server_id = parsed_args.server
+ else:
+ raise
action_detail = compute_client.instance_action.get(
server_id, parsed_args.request_id
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
index 32dd193..eadc3ff 100644
--- a/openstackclient/compute/v2/server_group.py
+++ b/openstackclient/compute/v2/server_group.py
@@ -17,7 +17,7 @@
import logging
-from novaclient import api_versions
+from openstack import utils as sdk_utils
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
@@ -31,19 +31,24 @@ LOG = logging.getLogger(__name__)
_formatters = {
- 'members': format_columns.ListColumn,
+ 'member_ids': format_columns.ListColumn,
'policies': format_columns.ListColumn,
'rules': format_columns.DictColumn,
}
-def _get_columns(info):
- columns = list(info.keys())
- if 'metadata' in columns:
- # NOTE(RuiChen): The metadata of server group is always empty since API
- # compatible, so hide it in order to avoid confusion.
- columns.remove('metadata')
- return tuple(sorted(columns))
+def _get_server_group_columns(item, client):
+ column_map = {'member_ids': 'members'}
+ hidden_columns = ['metadata', 'location']
+
+ if sdk_utils.supports_microversion(client, '2.64'):
+ hidden_columns.append('policies')
+ else:
+ hidden_columns.append('policy')
+ hidden_columns.append('rules')
+
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
class CreateServerGroup(command.ShowOne):
@@ -54,7 +59,7 @@ class CreateServerGroup(command.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help=_("New server group name")
+ help=_("New server group name"),
)
parser.add_argument(
'--policy',
@@ -87,11 +92,10 @@ class CreateServerGroup(command.ShowOne):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- info = {}
+ compute_client = self.app.client_manager.sdk_connection.compute
if parsed_args.policy in ('soft-affinity', 'soft-anti-affinity'):
- if compute_client.api_version < api_versions.APIVersion('2.15'):
+ if not sdk_utils.supports_microversion(compute_client, '2.15'):
msg = _(
'--os-compute-api-version 2.15 or greater is required to '
'support the %s policy'
@@ -99,30 +103,39 @@ class CreateServerGroup(command.ShowOne):
raise exceptions.CommandError(msg % parsed_args.policy)
if parsed_args.rules:
- if compute_client.api_version < api_versions.APIVersion('2.64'):
+ if not sdk_utils.supports_microversion(compute_client, '2.64'):
msg = _(
'--os-compute-api-version 2.64 or greater is required to '
'support the --rule option'
)
raise exceptions.CommandError(msg)
- if compute_client.api_version < api_versions.APIVersion('2.64'):
- kwargs = {'policies': [parsed_args.policy]}
+ if not sdk_utils.supports_microversion(compute_client, '2.64'):
+ kwargs = {
+ 'name': parsed_args.name,
+ 'policies': [parsed_args.policy],
+ }
else:
kwargs = {
+ 'name': parsed_args.name,
'policy': parsed_args.policy,
- 'rules': parsed_args.rules or None,
}
- server_group = compute_client.server_groups.create(
- name=parsed_args.name, **kwargs)
+ if parsed_args.rules:
+ kwargs['rules'] = parsed_args.rules
- info.update(server_group._info)
+ server_group = compute_client.create_server_group(**kwargs)
- columns = _get_columns(info)
- data = utils.get_dict_properties(
- info, columns, formatters=_formatters)
- return columns, data
+ display_columns, columns = _get_server_group_columns(
+ server_group,
+ compute_client,
+ )
+ data = utils.get_item_properties(
+ server_group,
+ columns,
+ formatters=_formatters,
+ )
+ return display_columns, data
class DeleteServerGroup(command.Command):
@@ -134,18 +147,17 @@ class DeleteServerGroup(command.Command):
'server_group',
metavar='<server-group>',
nargs='+',
- help=_("server group(s) to delete (name or ID)")
+ help=_("server group(s) to delete (name or ID)"),
)
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
result = 0
for group in parsed_args.server_group:
try:
- group_obj = utils.find_resource(compute_client.server_groups,
- group)
- compute_client.server_groups.delete(group_obj.id)
+ group_obj = compute_client.find_server_group(group)
+ compute_client.delete_server_group(group_obj.id)
# Catch all exceptions in order to avoid to block the next deleting
except Exception as e:
result += 1
@@ -169,13 +181,13 @@ class ListServerGroup(command.Lister):
'--all-projects',
action='store_true',
default=False,
- help=_("Display information from all projects (admin only)")
+ help=_("Display information from all projects (admin only)"),
)
parser.add_argument(
'--long',
action='store_true',
default=False,
- help=_("List additional fields in output")
+ help=_("List additional fields in output"),
)
# TODO(stephenfin): This should really be a --marker option, but alas
# the API doesn't support that for some reason
@@ -204,7 +216,7 @@ class ListServerGroup(command.Lister):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
kwargs = {}
@@ -217,10 +229,10 @@ class ListServerGroup(command.Lister):
if parsed_args.limit:
kwargs['limit'] = parsed_args.limit
- data = compute_client.server_groups.list(**kwargs)
+ data = compute_client.server_groups(**kwargs)
policy_key = 'Policies'
- if compute_client.api_version >= api_versions.APIVersion("2.64"):
+ if sdk_utils.supports_microversion(compute_client, '2.64'):
policy_key = 'Policy'
columns = (
@@ -235,7 +247,7 @@ class ListServerGroup(command.Lister):
)
if parsed_args.long:
columns += (
- 'members',
+ 'member_ids',
'project_id',
'user_id',
)
@@ -263,17 +275,18 @@ class ShowServerGroup(command.ShowOne):
parser.add_argument(
'server_group',
metavar='<server-group>',
- help=_("server group to display (name or ID)")
+ help=_("server group to display (name or ID)"),
)
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- group = utils.find_resource(compute_client.server_groups,
- parsed_args.server_group)
- info = {}
- info.update(group._info)
- columns = _get_columns(info)
- data = utils.get_dict_properties(
- info, columns, formatters=_formatters)
- return columns, data
+ compute_client = self.app.client_manager.sdk_connection.compute
+ group = compute_client.find_server_group(parsed_args.server_group)
+ display_columns, columns = _get_server_group_columns(
+ group,
+ compute_client,
+ )
+ data = utils.get_item_properties(
+ group, columns, formatters=_formatters
+ )
+ return display_columns, data
diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py
new file mode 100644
index 0000000..919b67b
--- /dev/null
+++ b/openstackclient/compute/v2/server_migration.py
@@ -0,0 +1,485 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Compute v2 Server Migration action implementations"""
+
+import uuid
+
+from novaclient import api_versions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
+
+
+class ListMigration(command.Lister):
+ _description = _("""List server migrations""")
+
+ def get_parser(self, prog_name):
+ parser = super(ListMigration, self).get_parser(prog_name)
+ parser.add_argument(
+ '--server',
+ metavar='<server>',
+ help=_(
+ 'Filter migrations by server (name or ID)'
+ )
+ )
+ parser.add_argument(
+ '--host',
+ metavar='<host>',
+ help=_(
+ 'Filter migrations by source or destination host'
+ ),
+ )
+ parser.add_argument(
+ '--status',
+ metavar='<status>',
+ help=_('Filter migrations by status')
+ )
+ parser.add_argument(
+ '--type',
+ metavar='<type>',
+ choices=[
+ 'evacuation', 'live-migration', 'cold-migration', 'resize',
+ ],
+ help=_('Filter migrations by type'),
+ )
+ parser.add_argument(
+ '--marker',
+ metavar='<marker>',
+ help=_(
+ "The last migration of the previous page; displays list "
+ "of migrations after 'marker'. Note that the marker is "
+ "the migration UUID. "
+ "(supported with --os-compute-api-version 2.59 or above)"
+ ),
+ )
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ type=int,
+ help=_(
+ "Maximum number of migrations to display. Note that there "
+ "is a configurable max limit on the server, and the limit "
+ "that is used will be the minimum of what is requested "
+ "here and what is configured in the server. "
+ "(supported with --os-compute-api-version 2.59 or above)"
+ ),
+ )
+ parser.add_argument(
+ '--changes-since',
+ dest='changes_since',
+ metavar='<changes-since>',
+ help=_(
+ "List only migrations changed later or equal to a certain "
+ "point of time. The provided time should be an ISO 8061 "
+ "formatted time, e.g. ``2016-03-04T06:27:59Z``. "
+ "(supported with --os-compute-api-version 2.59 or above)"
+ ),
+ )
+ parser.add_argument(
+ '--changes-before',
+ dest='changes_before',
+ metavar='<changes-before>',
+ help=_(
+ "List only migrations changed earlier or equal to a "
+ "certain point of time. The provided time should be an ISO "
+ "8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
+ "(supported with --os-compute-api-version 2.66 or above)"
+ ),
+ )
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_(
+ "Filter migrations by project (name or ID) "
+ "(supported with --os-compute-api-version 2.80 or above)"
+ ),
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ parser.add_argument(
+ '--user',
+ metavar='<user>',
+ help=_(
+ "Filter migrations by user (name or ID) "
+ "(supported with --os-compute-api-version 2.80 or above)"
+ ),
+ )
+ identity_common.add_user_domain_option_to_parser(parser)
+ return parser
+
+ def print_migrations(self, parsed_args, compute_client, migrations):
+ column_headers = [
+ 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
+ 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor',
+ 'Created At', 'Updated At',
+ ]
+
+ # Response fields coming back from the REST API are not always exactly
+ # the same as the column header names.
+ columns = [
+ 'source_node', 'dest_node', 'source_compute', 'dest_compute',
+ 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
+ 'new_instance_type_id', 'created_at', 'updated_at',
+ ]
+
+ # Insert migrations UUID after ID
+ if compute_client.api_version >= api_versions.APIVersion("2.59"):
+ column_headers.insert(0, "UUID")
+ columns.insert(0, "uuid")
+
+ if compute_client.api_version >= api_versions.APIVersion("2.23"):
+ column_headers.insert(0, "Id")
+ columns.insert(0, "id")
+ column_headers.insert(len(column_headers) - 2, "Type")
+ columns.insert(len(columns) - 2, "migration_type")
+
+ if compute_client.api_version >= api_versions.APIVersion("2.80"):
+ if parsed_args.project:
+ column_headers.insert(len(column_headers) - 2, "Project")
+ columns.insert(len(columns) - 2, "project_id")
+ if parsed_args.user:
+ column_headers.insert(len(column_headers) - 2, "User")
+ columns.insert(len(columns) - 2, "user_id")
+
+ return (
+ column_headers,
+ (utils.get_item_properties(mig, columns) for mig in migrations),
+ )
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ identity_client = self.app.client_manager.identity
+
+ search_opts = {
+ 'host': parsed_args.host,
+ 'status': parsed_args.status,
+ }
+
+ if parsed_args.server:
+ search_opts['instance_uuid'] = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ ).id
+
+ if parsed_args.type:
+ migration_type = parsed_args.type
+ # we're using an alias because the default value is confusing
+ if migration_type == 'cold-migration':
+ migration_type = 'migration'
+ search_opts['migration_type'] = migration_type
+
+ if parsed_args.marker:
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'support the --marker option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['marker'] = parsed_args.marker
+
+ if parsed_args.limit:
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'support the --limit option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['limit'] = parsed_args.limit
+
+ if parsed_args.changes_since:
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'support the --changes-since option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['changes_since'] = parsed_args.changes_since
+
+ if parsed_args.changes_before:
+ if compute_client.api_version < api_versions.APIVersion('2.66'):
+ msg = _(
+ '--os-compute-api-version 2.66 or greater is required to '
+ 'support the --changes-before option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['changes_before'] = parsed_args.changes_before
+
+ if parsed_args.project:
+ if compute_client.api_version < api_versions.APIVersion('2.80'):
+ msg = _(
+ '--os-compute-api-version 2.80 or greater is required to '
+ 'support the --project option'
+ )
+ raise exceptions.CommandError(msg)
+
+ search_opts['project_id'] = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+
+ if parsed_args.user:
+ if compute_client.api_version < api_versions.APIVersion('2.80'):
+ msg = _(
+ '--os-compute-api-version 2.80 or greater is required to '
+ 'support the --user option'
+ )
+ raise exceptions.CommandError(msg)
+
+ search_opts['user_id'] = identity_common.find_user(
+ identity_client,
+ parsed_args.user,
+ parsed_args.user_domain,
+ ).id
+
+ migrations = compute_client.migrations.list(**search_opts)
+
+ return self.print_migrations(parsed_args, compute_client, migrations)
+
+
+def _get_migration_by_uuid(compute_client, server_id, migration_uuid):
+ for migration in compute_client.server_migrations.list(server_id):
+ if migration.uuid == migration_uuid:
+ return migration
+ break
+ else:
+ msg = _(
+ 'In-progress live migration %s is not found for server %s.'
+ )
+ raise exceptions.CommandError(msg % (migration_uuid, server_id))
+
+
+class ShowMigration(command.ShowOne):
+ """Show an in-progress live migration for a given server.
+
+ Note that it is not possible to show cold migrations or completed
+ live-migrations. Use 'openstack server migration list' to get details for
+ these.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help=_('Server (name or ID)'),
+ )
+ parser.add_argument(
+ 'migration',
+ metavar='<migration>',
+ help=_("Migration (ID)"),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+
+ if compute_client.api_version < api_versions.APIVersion('2.24'):
+ msg = _(
+ '--os-compute-api-version 2.24 or greater is required to '
+ 'support the server migration show command'
+ )
+ raise exceptions.CommandError(msg)
+
+ if not parsed_args.migration.isdigit():
+ try:
+ uuid.UUID(parsed_args.migration)
+ except ValueError:
+ msg = _(
+ 'The <migration> argument must be an ID or UUID'
+ )
+ raise exceptions.CommandError(msg)
+
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'retrieve server migrations by UUID'
+ )
+ raise exceptions.CommandError(msg)
+
+ server = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ )
+
+ # the nova API doesn't currently allow retrieval by UUID but it's a
+ # reasonably common operation so emulate this behavior by listing
+ # migrations - the responses are identical
+ if not parsed_args.migration.isdigit():
+ server_migration = _get_migration_by_uuid(
+ compute_client, server.id, parsed_args.migration,
+ )
+ else:
+ server_migration = compute_client.server_migrations.get(
+ server.id, parsed_args.migration,
+ )
+
+ columns = (
+ 'ID',
+ 'Server UUID',
+ 'Status',
+ 'Source Compute',
+ 'Source Node',
+ 'Dest Compute',
+ 'Dest Host',
+ 'Dest Node',
+ 'Memory Total Bytes',
+ 'Memory Processed Bytes',
+ 'Memory Remaining Bytes',
+ 'Disk Total Bytes',
+ 'Disk Processed Bytes',
+ 'Disk Remaining Bytes',
+ 'Created At',
+ 'Updated At',
+ )
+
+ if compute_client.api_version >= api_versions.APIVersion('2.59'):
+ columns += ('UUID',)
+
+ if compute_client.api_version >= api_versions.APIVersion('2.80'):
+ columns += ('User ID', 'Project ID')
+
+ data = utils.get_item_properties(server_migration, columns)
+ return columns, data
+
+
+class AbortMigration(command.Command):
+ """Cancel an ongoing live migration.
+
+ This command requires ``--os-compute-api-version`` 2.24 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super(AbortMigration, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help=_('Server (name or ID)'),
+ )
+ parser.add_argument(
+ 'migration',
+ metavar='<migration>',
+ help=_("Migration (ID)"),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+
+ if compute_client.api_version < api_versions.APIVersion('2.24'):
+ msg = _(
+ '--os-compute-api-version 2.24 or greater is required to '
+ 'support the server migration abort command'
+ )
+ raise exceptions.CommandError(msg)
+
+ if not parsed_args.migration.isdigit():
+ try:
+ uuid.UUID(parsed_args.migration)
+ except ValueError:
+ msg = _(
+ 'The <migration> argument must be an ID or UUID'
+ )
+ raise exceptions.CommandError(msg)
+
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'abort server migrations by UUID'
+ )
+ raise exceptions.CommandError(msg)
+
+ server = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ )
+
+ # the nova API doesn't currently allow retrieval by UUID but it's a
+ # reasonably common operation so emulate this behavior by listing
+ # migrations - the responses are identical
+ migration_id = parsed_args.migration
+ if not parsed_args.migration.isdigit():
+ migration_id = _get_migration_by_uuid(
+ compute_client, server.id, parsed_args.migration,
+ ).id
+
+ compute_client.server_migrations.live_migration_abort(
+ server.id, migration_id,
+ )
+
+
+class ForceCompleteMigration(command.Command):
+ """Force an ongoing live migration to complete.
+
+ This command requires ``--os-compute-api-version`` 2.22 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super(ForceCompleteMigration, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help=_('Server (name or ID)'),
+ )
+ parser.add_argument(
+ 'migration',
+ metavar='<migration>',
+ help=_('Migration (ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+
+ if compute_client.api_version < api_versions.APIVersion('2.22'):
+ msg = _(
+ '--os-compute-api-version 2.22 or greater is required to '
+ 'support the server migration force complete command'
+ )
+ raise exceptions.CommandError(msg)
+
+ if not parsed_args.migration.isdigit():
+ try:
+ uuid.UUID(parsed_args.migration)
+ except ValueError:
+ msg = _(
+ 'The <migration> argument must be an ID or UUID'
+ )
+ raise exceptions.CommandError(msg)
+
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'abort server migrations by UUID'
+ )
+ raise exceptions.CommandError(msg)
+
+ server = utils.find_resource(
+ compute_client.servers,
+ parsed_args.server,
+ )
+
+ # the nova API doesn't currently allow retrieval by UUID but it's a
+ # reasonably common operation so emulate this behavior by listing
+ # migrations - the responses are identical
+ migration_id = parsed_args.migration
+ if not parsed_args.migration.isdigit():
+ migration_id = _get_migration_by_uuid(
+ compute_client, server.id, parsed_args.migration,
+ ).id
+
+ compute_client.server_migrations.live_migrate_force_complete(
+ server.id, migration_id,
+ )
diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py
index cbe27ed..9bb026a 100644
--- a/openstackclient/identity/v3/endpoint_group.py
+++ b/openstackclient/identity/v3/endpoint_group.py
@@ -268,7 +268,7 @@ class SetEndpointGroup(command.Command, _FiltersReader):
parser.add_argument(
'--name',
metavar='<name>',
- help=_('New enpoint group name'),
+ help=_('New endpoint group name'),
)
parser.add_argument(
'--filters',
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 407c129..2342fd3 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -21,7 +21,7 @@ import logging
import os
import sys
-import openstack.cloud._utils
+from cinderclient import api_versions
from openstack.image import image_signer
from osc_lib.api import utils as api_utils
from osc_lib.cli import format_columns
@@ -29,6 +29,7 @@ from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
+from oslo_utils import uuidutils
from openstackclient.common import progressbar
from openstackclient.i18n import _
@@ -43,8 +44,19 @@ else:
CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"]
DEFAULT_CONTAINER_FORMAT = 'bare'
DEFAULT_DISK_FORMAT = 'raw'
-DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx",
- "vdi", "iso", "ploop"]
+DISK_CHOICES = [
+ "ami",
+ "ari",
+ "aki",
+ "vhd",
+ "vmdk",
+ "raw",
+ "qcow2",
+ "vhdx",
+ "vdi",
+ "iso",
+ "ploop",
+]
MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"]
@@ -58,10 +70,25 @@ def _format_image(image, human_readable=False):
properties = {}
# the only fields we're not including is "links", "tags" and the properties
- fields_to_show = ['status', 'name', 'container_format', 'created_at',
- 'size', 'disk_format', 'updated_at', 'visibility',
- 'min_disk', 'protected', 'id', 'file', 'checksum',
- 'owner', 'virtual_size', 'min_ram', 'schema']
+ fields_to_show = [
+ 'status',
+ 'name',
+ 'container_format',
+ 'created_at',
+ 'size',
+ 'disk_format',
+ 'updated_at',
+ 'visibility',
+ 'min_disk',
+ 'protected',
+ 'id',
+ 'file',
+ 'checksum',
+ 'owner',
+ 'virtual_size',
+ 'min_ram',
+ 'schema',
+ ]
# TODO(gtema/anybody): actually it should be possible to drop this method,
# since SDK already delivers a proper object
@@ -98,12 +125,12 @@ _formatters = {
def _get_member_columns(item):
- column_map = {
- 'image_id': 'image_id'
- }
+ column_map = {'image_id': 'image_id'}
hidden_columns = ['id', 'location', 'name']
return utils.get_osc_show_columns_for_sdk_resource(
- item.to_dict(), column_map, hidden_columns,
+ item.to_dict(),
+ column_map,
+ hidden_columns,
)
@@ -141,7 +168,7 @@ class AddProjectToImage(command.ShowOne):
_description = _("Associate project with image")
def get_parser(self, prog_name):
- parser = super(AddProjectToImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
@@ -159,16 +186,18 @@ class AddProjectToImage(command.ShowOne):
image_client = self.app.client_manager.image
identity_client = self.app.client_manager.identity
- if openstack.cloud._utils._is_uuid_like(parsed_args.project):
+ if uuidutils.is_uuid_like(parsed_args.project):
project_id = parsed_args.project
else:
project_id = common.find_project(
identity_client,
parsed_args.project,
- parsed_args.project_domain).id
+ parsed_args.project_domain,
+ ).id
- image = image_client.find_image(parsed_args.image,
- ignore_missing=False)
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False
+ )
obj = image_client.add_member(
image=image.id,
@@ -187,7 +216,7 @@ class CreateImage(command.ShowOne):
deadopts = ('size', 'location', 'copy-from', 'checksum', 'store')
def get_parser(self, prog_name):
- parser = super(CreateImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
# TODO(bunting): There are additional arguments that v1 supported
# that v2 either doesn't support or supports weirdly.
# --checksum - could be faked clientside perhaps?
@@ -210,19 +239,28 @@ class CreateImage(command.ShowOne):
default=DEFAULT_CONTAINER_FORMAT,
choices=CONTAINER_CHOICES,
metavar="<container-format>",
- help=(_("Image container format. "
+ help=(
+ _(
+ "Image container format. "
"The supported options are: %(option_list)s. "
- "The default format is: %(default_opt)s") %
- {'option_list': ', '.join(CONTAINER_CHOICES),
- 'default_opt': DEFAULT_CONTAINER_FORMAT})
+ "The default format is: %(default_opt)s"
+ )
+ % {
+ 'option_list': ', '.join(CONTAINER_CHOICES),
+ 'default_opt': DEFAULT_CONTAINER_FORMAT,
+ }
+ ),
)
parser.add_argument(
"--disk-format",
default=DEFAULT_DISK_FORMAT,
choices=DISK_CHOICES,
metavar="<disk-format>",
- help=_("Image disk format. The supported options are: %s. "
- "The default format is: raw") % ', '.join(DISK_CHOICES)
+ help=_(
+ "Image disk format. The supported options are: %s. "
+ "The default format is: raw"
+ )
+ % ', '.join(DISK_CHOICES),
)
parser.add_argument(
"--min-disk",
@@ -252,8 +290,10 @@ class CreateImage(command.ShowOne):
dest='force',
action='store_true',
default=False,
- help=_("Force image creation if volume is in use "
- "(only meaningful with --volume)"),
+ help=_(
+ "Force image creation if volume is in use "
+ "(only meaningful with --volume)"
+ ),
)
parser.add_argument(
"--progress",
@@ -265,48 +305,64 @@ class CreateImage(command.ShowOne):
'--sign-key-path',
metavar="<sign-key-path>",
default=[],
- help=_("Sign the image using the specified private key. "
- "Only use in combination with --sign-cert-id")
+ help=_(
+ "Sign the image using the specified private key. "
+ "Only use in combination with --sign-cert-id"
+ ),
)
parser.add_argument(
'--sign-cert-id',
metavar="<sign-cert-id>",
default=[],
- help=_("The specified certificate UUID is a reference to "
- "the certificate in the key manager that corresponds "
- "to the public key and is used for signature validation. "
- "Only use in combination with --sign-key-path")
+ help=_(
+ "The specified certificate UUID is a reference to "
+ "the certificate in the key manager that corresponds "
+ "to the public key and is used for signature validation. "
+ "Only use in combination with --sign-key-path"
+ ),
)
protected_group = parser.add_mutually_exclusive_group()
protected_group.add_argument(
"--protected",
action="store_true",
+ dest="is_protected",
+ default=None,
help=_("Prevent image from being deleted"),
)
protected_group.add_argument(
"--unprotected",
- action="store_true",
+ action="store_false",
+ dest="is_protected",
+ default=None,
help=_("Allow image to be deleted (default)"),
)
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
"--public",
- action="store_true",
+ action="store_const",
+ const="public",
+ dest="visibility",
help=_("Image is accessible to the public"),
)
public_group.add_argument(
"--private",
- action="store_true",
+ action="store_const",
+ const="private",
+ dest="visibility",
help=_("Image is inaccessible to the public (default)"),
)
public_group.add_argument(
"--community",
- action="store_true",
+ action="store_const",
+ const="community",
+ dest="visibility",
help=_("Image is accessible to the community"),
)
public_group.add_argument(
"--shared",
- action="store_true",
+ action="store_const",
+ const="shared",
+ dest="visibility",
help=_("Image can be shared"),
)
parser.add_argument(
@@ -314,16 +370,20 @@ class CreateImage(command.ShowOne):
dest="properties",
metavar="<key=value>",
action=parseractions.KeyValueAction,
- help=_("Set a property on this image "
- "(repeat option to set multiple properties)"),
+ help=_(
+ "Set a property on this image "
+ "(repeat option to set multiple properties)"
+ ),
)
parser.add_argument(
"--tag",
dest="tags",
metavar="<tag>",
action='append',
- help=_("Set a tag on this image "
- "(repeat option to set multiple tags)"),
+ help=_(
+ "Set a tag on this image "
+ "(repeat option to set multiple tags)"
+ ),
)
parser.add_argument(
"--project",
@@ -335,8 +395,8 @@ class CreateImage(command.ShowOne):
dest="use_import",
action="store_true",
help=_(
- "Force the use of glance image import instead of"
- " direct upload")
+ "Force the use of glance image import instead of direct upload"
+ ),
)
common.add_project_domain_option_to_parser(parser)
for deadopt in self.deadopts:
@@ -348,22 +408,23 @@ class CreateImage(command.ShowOne):
)
return parser
- def take_action(self, parsed_args):
+ def _take_action_image(self, parsed_args):
identity_client = self.app.client_manager.identity
image_client = self.app.client_manager.image
- for deadopt in self.deadopts:
- if getattr(parsed_args, deadopt.replace('-', '_'), None):
- raise exceptions.CommandError(
- _("ERROR: --%s was given, which is an Image v1 option"
- " that is no longer supported in Image v2") % deadopt)
-
# Build an attribute dict from the parsed args, only include
# attributes that were actually set on the command line
kwargs = {'allow_duplicates': True}
- copy_attrs = ('name', 'id',
- 'container_format', 'disk_format',
- 'min_disk', 'min_ram', 'tags', 'visibility')
+ copy_attrs = (
+ 'name',
+ 'id',
+ 'container_format',
+ 'disk_format',
+ 'min_disk',
+ 'min_ram',
+ 'tags',
+ 'visibility',
+ )
for attr in copy_attrs:
if attr in parsed_args:
val = getattr(parsed_args, attr, None)
@@ -383,18 +444,12 @@ class CreateImage(command.ShowOne):
# a single value for the pair of options because the default must be
# to do nothing when no options are present as opposed to always
# setting a default.
- if parsed_args.protected:
- kwargs['is_protected'] = True
- if parsed_args.unprotected:
- kwargs['is_protected'] = False
- if parsed_args.public:
- kwargs['visibility'] = 'public'
- if parsed_args.private:
- kwargs['visibility'] = 'private'
- if parsed_args.community:
- kwargs['visibility'] = 'community'
- if parsed_args.shared:
- kwargs['visibility'] = 'shared'
+ if parsed_args.is_protected is not None:
+ kwargs['is_protected'] = parsed_args.is_protected
+
+ if parsed_args.visibility is not None:
+ kwargs['visibility'] = parsed_args.visibility
+
if parsed_args.project:
kwargs['owner_id'] = common.find_project(
identity_client,
@@ -408,16 +463,19 @@ class CreateImage(command.ShowOne):
# open the file first to ensure any failures are handled before the
# image is created. Get the file name (if it is file, and not stdin)
# for easier further handling.
- (fp, fname) = get_data_file(parsed_args)
- info = {}
+ fp, fname = get_data_file(parsed_args)
if fp is not None and parsed_args.volume:
- raise exceptions.CommandError(_("Uploading data and using "
- "container are not allowed at "
- "the same time"))
+ msg = _(
+ "Uploading data and using container are not allowed at "
+ "the same time"
+ )
+ raise exceptions.CommandError(msg)
+
if fp is None and parsed_args.file:
LOG.warning(_("Failed to get an image file."))
return {}, {}
+
if fp is not None and parsed_args.progress:
filesize = os.path.getsize(fname)
if filesize is not None:
@@ -432,76 +490,145 @@ class CreateImage(command.ShowOne):
# 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:
- msg = (_("signing an image requires the --file option, "
- "passing files via stdin when signing is not "
- "supported."))
+ msg = _(
+ "signing an image requires the --file option, "
+ "passing files via stdin when signing is not "
+ "supported."
+ )
raise exceptions.CommandError(msg)
- if (len(parsed_args.sign_key_path) < 1 or
- len(parsed_args.sign_cert_id) < 1):
- msg = (_("'sign-key-path' and 'sign-cert-id' must both be "
- "specified when attempting to sign an image."))
+
+ if (
+ len(parsed_args.sign_key_path) < 1 or
+ len(parsed_args.sign_cert_id) < 1
+ ):
+ msg = _(
+ "'sign-key-path' and 'sign-cert-id' must both be "
+ "specified when attempting to sign an image."
+ )
raise exceptions.CommandError(msg)
- else:
- sign_key_path = parsed_args.sign_key_path
- sign_cert_id = parsed_args.sign_cert_id
- signer = image_signer.ImageSigner()
- try:
- pw = utils.get_password(
- self.app.stdin,
- prompt=("Please enter private key password, leave "
- "empty if none: "),
- confirm=False)
-
- if not pw or len(pw) < 1:
- pw = None
- else:
- # load_private_key() requires the password to be
- # passed as bytes
- pw = pw.encode()
-
- signer.load_private_key(
- sign_key_path,
- password=pw)
- except Exception:
- msg = (_("Error during sign operation: private key "
- "could not be loaded."))
- raise exceptions.CommandError(msg)
-
- signature = signer.generate_signature(fp)
- signature_b64 = b64encode(signature)
- kwargs['img_signature'] = signature_b64
- kwargs['img_signature_certificate_uuid'] = sign_cert_id
- kwargs['img_signature_hash_method'] = signer.hash_method
- if signer.padding_method:
- kwargs['img_signature_key_type'] = \
- signer.padding_method
-
- # If a volume is specified.
- if parsed_args.volume:
- volume_client = self.app.client_manager.volume
- source_volume = utils.find_resource(
- volume_client.volumes,
- parsed_args.volume,
- )
- response, body = volume_client.volumes.upload_to_image(
- source_volume.id,
- parsed_args.force,
- parsed_args.name,
- parsed_args.container_format,
- parsed_args.disk_format,
- visibility=kwargs.get('visibility', 'private'),
- protected=True if parsed_args.protected else False
- )
- info = body['os-volume_upload_image']
+
+ sign_key_path = parsed_args.sign_key_path
+ sign_cert_id = parsed_args.sign_cert_id
+ signer = image_signer.ImageSigner()
try:
- info['volume_type'] = info['volume_type']['name']
- except TypeError:
- info['volume_type'] = None
+ pw = utils.get_password(
+ self.app.stdin,
+ prompt=(
+ "Please enter private key password, leave "
+ "empty if none: "
+ ),
+ confirm=False,
+ )
+
+ if not pw or len(pw) < 1:
+ pw = None
+ else:
+ # load_private_key() requires the password to be
+ # passed as bytes
+ pw = pw.encode()
+
+ signer.load_private_key(sign_key_path, password=pw)
+ except Exception:
+ msg = _(
+ "Error during sign operation: private key "
+ "could not be loaded."
+ )
+ raise exceptions.CommandError(msg)
+
+ signature = signer.generate_signature(fp)
+ signature_b64 = b64encode(signature)
+ kwargs['img_signature'] = signature_b64
+ kwargs['img_signature_certificate_uuid'] = sign_cert_id
+ kwargs['img_signature_hash_method'] = signer.hash_method
+ if signer.padding_method:
+ kwargs['img_signature_key_type'] = signer.padding_method
+
+ image = image_client.create_image(**kwargs)
+ return _format_image(image)
+
+ def _take_action_volume(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ unsupported_opts = {
+ # 'name', # 'name' is a positional argument and will always exist
+ 'id',
+ 'min_disk',
+ 'min_ram',
+ 'file',
+ 'force',
+ 'progress',
+ 'sign_key_path',
+ 'sign_cert_id',
+ 'properties',
+ 'tags',
+ 'project',
+ 'use_import',
+ }
+ for unsupported_opt in unsupported_opts:
+ if getattr(parsed_args, unsupported_opt, None):
+ opt_name = unsupported_opt.replace('-', '_')
+ if unsupported_opt == 'use_import':
+ opt_name = 'import'
+ msg = _(
+ "'--%s' was given, which is not supported when "
+ "creating an image from a volume. "
+ "This will be an error in a future version."
+ )
+ # TODO(stephenfin): These should be an error in a future
+ # version
+ LOG.warning(msg % opt_name)
+
+ source_volume = utils.find_resource(
+ volume_client.volumes,
+ parsed_args.volume,
+ )
+ kwargs = {}
+ if volume_client.api_version < api_versions.APIVersion('3.1'):
+ if (
+ parsed_args.visibility or
+ parsed_args.is_protected is not None
+ ):
+ msg = _(
+ '--os-volume-api-version 3.1 or greater is required '
+ 'to support the --public, --private, --community, '
+ '--shared or --protected option.'
+ )
+ raise exceptions.CommandError(msg)
else:
- image = image_client.create_image(**kwargs)
+ kwargs.update(
+ visibility=parsed_args.visibility or 'private',
+ protected=parsed_args.is_protected or False,
+ )
+
+ response, body = volume_client.volumes.upload_to_image(
+ source_volume.id,
+ parsed_args.force,
+ parsed_args.name,
+ parsed_args.container_format,
+ parsed_args.disk_format,
+ **kwargs
+ )
+ info = body['os-volume_upload_image']
+ try:
+ info['volume_type'] = info['volume_type']['name']
+ except TypeError:
+ info['volume_type'] = None
- if not info:
- info = _format_image(image)
+ return info
+
+ def take_action(self, parsed_args):
+ for deadopt in self.deadopts:
+ if getattr(parsed_args, deadopt.replace('-', '_'), None):
+ msg = _(
+ "ERROR: --%s was given, which is an Image v1 option "
+ "that is no longer supported in Image v2"
+ )
+ raise exceptions.CommandError(msg % deadopt)
+
+ if parsed_args.volume:
+ info = self._take_action_volume(parsed_args)
+ else:
+ info = self._take_action_image(parsed_args)
return zip(*sorted(info.items()))
@@ -510,7 +637,7 @@ class DeleteImage(command.Command):
_description = _("Delete image(s)")
def get_parser(self, prog_name):
- parser = super(DeleteImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"images",
metavar="<image>",
@@ -525,19 +652,24 @@ class DeleteImage(command.Command):
image_client = self.app.client_manager.image
for image in parsed_args.images:
try:
- image_obj = image_client.find_image(image,
- ignore_missing=False)
+ image_obj = image_client.find_image(
+ image, ignore_missing=False
+ )
image_client.delete_image(image_obj.id)
except Exception as e:
del_result += 1
- LOG.error(_("Failed to delete image with name or "
- "ID '%(image)s': %(e)s"),
- {'image': image, 'e': e})
+ msg = _(
+ "Failed to delete image with name or "
+ "ID '%(image)s': %(e)s"
+ )
+ LOG.error(msg, {'image': image, 'e': e})
total = len(parsed_args.images)
- if (del_result > 0):
- msg = (_("Failed to delete %(dresult)s of %(total)s images.")
- % {'dresult': del_result, 'total': total})
+ if del_result > 0:
+ msg = _("Failed to delete %(dresult)s of %(total)s images.") % {
+ 'dresult': del_result,
+ 'total': total,
+ }
raise exceptions.CommandError(msg)
@@ -545,54 +677,63 @@ class ListImage(command.Lister):
_description = _("List available images")
def get_parser(self, prog_name):
- parser = super(ListImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
"--public",
- dest="public",
- action="store_true",
- default=False,
+ action="store_const",
+ const="public",
+ dest="visibility",
help=_("List only public images"),
)
public_group.add_argument(
"--private",
- dest="private",
- action="store_true",
- default=False,
+ action="store_const",
+ const="private",
+ dest="visibility",
help=_("List only private images"),
)
public_group.add_argument(
"--community",
- dest="community",
- action="store_true",
- default=False,
+ action="store_const",
+ const="community",
+ dest="visibility",
help=_("List only community images"),
)
public_group.add_argument(
"--shared",
- dest="shared",
- action="store_true",
- default=False,
+ action="store_const",
+ const="shared",
+ dest="visibility",
help=_("List only shared images"),
)
+ public_group.add_argument(
+ "--all",
+ action="store_const",
+ const="all",
+ dest="visibility",
+ help=_("List all images"),
+ )
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help=_('Filter output based on property '
- '(repeat option to filter on multiple properties)'),
+ help=_(
+ 'Filter output based on property '
+ '(repeat option to filter on multiple properties)'
+ ),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
- help=_("Filter images based on name.")
+ help=_("Filter images based on name."),
)
parser.add_argument(
'--status',
metavar='<status>',
default=None,
- help=_("Filter images based on status.")
+ help=_("Filter images based on status."),
)
parser.add_argument(
'--member-status',
@@ -600,25 +741,34 @@ class ListImage(command.Lister):
default=None,
type=lambda s: s.lower(),
choices=MEMBER_STATUS_CHOICES,
- help=(_("Filter images based on member status. "
- "The supported options are: %s. ") %
- ', '.join(MEMBER_STATUS_CHOICES))
+ help=(
+ _(
+ "Filter images based on member status. "
+ "The supported options are: %s. "
+ )
+ % ', '.join(MEMBER_STATUS_CHOICES)
+ ),
)
parser.add_argument(
'--project',
metavar='<project>',
- help=_("Search by project (admin only) (name or ID)")
+ help=_("Search by project (admin only) (name or ID)"),
)
common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--tag',
metavar='<tag>',
- default=None,
- help=_('Filter images based on tag.'),
+ action='append',
+ default=[],
+ help=_(
+ 'Filter images based on tag. '
+ '(repeat option to filter on multiple tags)'
+ ),
)
parser.add_argument(
'--hidden',
action='store_true',
+ dest='is_hidden',
default=False,
help=_('List hidden images'),
)
@@ -640,9 +790,11 @@ class ListImage(command.Lister):
'--sort',
metavar="<key>[:<direction>]",
default='name:asc',
- help=_("Sort output by selected keys and directions(asc or desc) "
- "(default: name:asc), multiple keys and directions can be "
- "specified separated by comma"),
+ help=_(
+ "Sort output by selected keys and directions (asc or desc) "
+ "(default: name:asc), multiple keys and directions can be "
+ "specified separated by comma"
+ ),
)
parser.add_argument(
"--limit",
@@ -654,9 +806,11 @@ class ListImage(command.Lister):
'--marker',
metavar='<image>',
default=None,
- help=_("The last image of the previous page. Display "
- "list of images after marker. Display all images if not "
- "specified. (name or ID)"),
+ help=_(
+ "The last image of the previous page. Display "
+ "list of images after marker. Display all images if not "
+ "specified. (name or ID)"
+ ),
)
return parser
@@ -665,14 +819,8 @@ class ListImage(command.Lister):
image_client = self.app.client_manager.image
kwargs = {}
- if parsed_args.public:
- kwargs['visibility'] = 'public'
- if parsed_args.private:
- kwargs['visibility'] = 'private'
- if parsed_args.community:
- kwargs['visibility'] = 'community'
- if parsed_args.shared:
- kwargs['visibility'] = 'shared'
+ if parsed_args.visibility is not None:
+ kwargs['visibility'] = parsed_args.visibility
if parsed_args.limit:
kwargs['limit'] = parsed_args.limit
if parsed_args.marker:
@@ -693,8 +841,8 @@ class ListImage(command.Lister):
parsed_args.project_domain,
).id
kwargs['owner'] = project_id
- if parsed_args.hidden:
- kwargs['is_hidden'] = True
+ if parsed_args.is_hidden:
+ kwargs['is_hidden'] = parsed_args.is_hidden
if parsed_args.long:
columns = (
'ID',
@@ -745,11 +893,14 @@ class ListImage(command.Lister):
return (
column_headers,
- (utils.get_item_properties(
- s,
- columns,
- formatters=_formatters,
- ) for s in data)
+ (
+ utils.get_item_properties(
+ s,
+ columns,
+ formatters=_formatters,
+ )
+ for s in data
+ ),
)
@@ -757,7 +908,7 @@ class ListImageProjects(command.Lister):
_description = _("List projects associated with image")
def get_parser(self, prog_name):
- parser = super(ListImageProjects, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
@@ -768,27 +919,29 @@ class ListImageProjects(command.Lister):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- columns = (
- "Image ID",
- "Member ID",
- "Status"
- )
+ columns = ("Image ID", "Member ID", "Status")
image_id = image_client.find_image(parsed_args.image).id
data = image_client.members(image=image_id)
- return (columns,
- (utils.get_item_properties(
- s, columns,
- ) for s in data))
+ return (
+ columns,
+ (
+ utils.get_item_properties(
+ s,
+ columns,
+ )
+ for s in data
+ ),
+ )
class RemoveProjectImage(command.Command):
_description = _("Disassociate project with image")
def get_parser(self, prog_name):
- parser = super(RemoveProjectImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
@@ -806,23 +959,22 @@ class RemoveProjectImage(command.Command):
image_client = self.app.client_manager.image
identity_client = self.app.client_manager.identity
- project_id = common.find_project(identity_client,
- parsed_args.project,
- parsed_args.project_domain).id
+ project_id = common.find_project(
+ identity_client, parsed_args.project, parsed_args.project_domain
+ ).id
- image = image_client.find_image(parsed_args.image,
- ignore_missing=False)
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False
+ )
- image_client.remove_member(
- member=project_id,
- image=image.id)
+ image_client.remove_member(member=project_id, image=image.id)
class SaveImage(command.Command):
_description = _("Save an image locally")
def get_parser(self, prog_name):
- parser = super(SaveImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"--file",
metavar="<filename>",
@@ -852,7 +1004,7 @@ class SetImage(command.Command):
deadopts = ('visibility',)
def get_parser(self, prog_name):
- parser = super(SetImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
# TODO(bunting): There are additional arguments that v1 supported
# --size - does not exist in v2
# --store - does not exist in v2
@@ -864,20 +1016,16 @@ class SetImage(command.Command):
# --checksum - maybe could be done client side
# --stdin - could be implemented
parser.add_argument(
- "image",
- metavar="<image>",
- help=_("Image to modify (name or ID)")
+ "image", metavar="<image>", help=_("Image to modify (name or ID)")
)
parser.add_argument(
- "--name",
- metavar="<name>",
- help=_("New image name")
+ "--name", metavar="<name>", help=_("New image name")
)
parser.add_argument(
"--min-disk",
type=int,
metavar="<disk-gb>",
- help=_("Minimum disk size needed to boot image, in gigabytes")
+ help=_("Minimum disk size needed to boot image, in gigabytes"),
)
parser.add_argument(
"--min-ram",
@@ -889,46 +1037,58 @@ class SetImage(command.Command):
"--container-format",
metavar="<container-format>",
choices=CONTAINER_CHOICES,
- help=_("Image container format. The supported options are: %s") %
- ', '.join(CONTAINER_CHOICES)
+ help=_("Image container format. The supported options are: %s")
+ % ', '.join(CONTAINER_CHOICES),
)
parser.add_argument(
"--disk-format",
metavar="<disk-format>",
choices=DISK_CHOICES,
- help=_("Image disk format. The supported options are: %s") %
- ', '.join(DISK_CHOICES)
+ help=_("Image disk format. The supported options are: %s")
+ % ', '.join(DISK_CHOICES),
)
protected_group = parser.add_mutually_exclusive_group()
protected_group.add_argument(
"--protected",
action="store_true",
+ dest="is_protected",
+ default=None,
help=_("Prevent image from being deleted"),
)
protected_group.add_argument(
"--unprotected",
- action="store_true",
+ action="store_false",
+ dest="is_protected",
+ default=None,
help=_("Allow image to be deleted (default)"),
)
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
"--public",
- action="store_true",
+ action="store_const",
+ const="public",
+ dest="visibility",
help=_("Image is accessible to the public"),
)
public_group.add_argument(
"--private",
- action="store_true",
+ action="store_const",
+ const="private",
+ dest="visibility",
help=_("Image is inaccessible to the public (default)"),
)
public_group.add_argument(
"--community",
- action="store_true",
+ action="store_const",
+ const="community",
+ dest="visibility",
help=_("Image is accessible to the community"),
)
public_group.add_argument(
"--shared",
- action="store_true",
+ action="store_const",
+ const="shared",
+ dest="visibility",
help=_("Image can be shared"),
)
parser.add_argument(
@@ -936,8 +1096,10 @@ class SetImage(command.Command):
dest="properties",
metavar="<key=value>",
action=parseractions.KeyValueAction,
- help=_("Set a property on this image "
- "(repeat option to set multiple properties)"),
+ help=_(
+ "Set a property on this image "
+ "(repeat option to set multiple properties)"
+ ),
)
parser.add_argument(
"--tag",
@@ -945,8 +1107,10 @@ class SetImage(command.Command):
metavar="<tag>",
default=None,
action='append',
- help=_("Set a tag on this image "
- "(repeat option to set multiple tags)"),
+ help=_(
+ "Set a tag on this image "
+ "(repeat option to set multiple tags)"
+ ),
)
parser.add_argument(
"--architecture",
@@ -1005,7 +1169,7 @@ class SetImage(command.Command):
parser.add_argument(
"--%s" % deadopt,
metavar="<%s>" % deadopt,
- dest=deadopt.replace('-', '_'),
+ dest=f"dead_{deadopt.replace('-', '_')}",
help=argparse.SUPPRESS,
)
@@ -1038,14 +1202,14 @@ class SetImage(command.Command):
hidden_group = parser.add_mutually_exclusive_group()
hidden_group.add_argument(
"--hidden",
- dest='hidden',
+ dest="is_hidden",
default=None,
action="store_true",
help=_("Hide the image"),
)
hidden_group.add_argument(
"--unhidden",
- dest='hidden',
+ dest="is_hidden",
default=None,
action="store_false",
help=_("Unhide the image"),
@@ -1057,13 +1221,18 @@ class SetImage(command.Command):
image_client = self.app.client_manager.image
for deadopt in self.deadopts:
- if getattr(parsed_args, deadopt.replace('-', '_'), None):
+ if getattr(parsed_args, f"dead_{deadopt.replace('-', '_')}", None):
raise exceptions.CommandError(
- _("ERROR: --%s was given, which is an Image v1 option"
- " that is no longer supported in Image v2") % deadopt)
+ _(
+ "ERROR: --%s was given, which is an Image v1 option"
+ " that is no longer supported in Image v2"
+ )
+ % deadopt
+ )
image = image_client.find_image(
- parsed_args.image, ignore_missing=False,
+ parsed_args.image,
+ ignore_missing=False,
)
project_id = None
if parsed_args.project:
@@ -1100,10 +1269,25 @@ class SetImage(command.Command):
# handle everything else
kwargs = {}
- copy_attrs = ('architecture', 'container_format', 'disk_format',
- 'file', 'instance_id', 'kernel_id', 'locations',
- 'min_disk', 'min_ram', 'name', 'os_distro', 'os_version',
- 'prefix', 'progress', 'ramdisk_id', 'tags', 'visibility')
+ copy_attrs = (
+ 'architecture',
+ 'container_format',
+ 'disk_format',
+ 'file',
+ 'instance_id',
+ 'kernel_id',
+ 'locations',
+ 'min_disk',
+ 'min_ram',
+ 'name',
+ 'os_distro',
+ 'os_version',
+ 'prefix',
+ 'progress',
+ 'ramdisk_id',
+ 'tags',
+ 'visibility',
+ )
for attr in copy_attrs:
if attr in parsed_args:
val = getattr(parsed_args, attr, None)
@@ -1123,33 +1307,31 @@ class SetImage(command.Command):
# a single value for the pair of options because the default must be
# to do nothing when no options are present as opposed to always
# setting a default.
- if parsed_args.protected:
- kwargs['is_protected'] = True
- if parsed_args.unprotected:
- kwargs['is_protected'] = False
- if parsed_args.public:
- kwargs['visibility'] = 'public'
- if parsed_args.private:
- kwargs['visibility'] = 'private'
- if parsed_args.community:
- kwargs['visibility'] = 'community'
- if parsed_args.shared:
- kwargs['visibility'] = 'shared'
+ if parsed_args.is_protected is not None:
+ kwargs['is_protected'] = parsed_args.is_protected
+
+ if parsed_args.visibility is not None:
+ kwargs['visibility'] = parsed_args.visibility
+
if parsed_args.project:
# We already did the project lookup above
kwargs['owner_id'] = project_id
+
if parsed_args.tags:
# Tags should be extended, but duplicates removed
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
- if parsed_args.hidden is not None:
- kwargs['is_hidden'] = parsed_args.hidden
+
+ if parsed_args.is_hidden is not None:
+ kwargs['is_hidden'] = parsed_args.is_hidden
try:
image = image_client.update_image(image.id, **kwargs)
except Exception:
if activation_status is not None:
- LOG.info(_("Image %(id)s was %(status)s."),
- {'id': image.id, 'status': activation_status})
+ LOG.info(
+ _("Image %(id)s was %(status)s."),
+ {'id': image.id, 'status': activation_status},
+ )
raise
@@ -1157,7 +1339,7 @@ class ShowImage(command.ShowOne):
_description = _("Display image details")
def get_parser(self, prog_name):
- parser = super(ShowImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"--human-readable",
default=False,
@@ -1174,8 +1356,9 @@ class ShowImage(command.ShowOne):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = image_client.find_image(parsed_args.image,
- ignore_missing=False)
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False
+ )
info = _format_image(image, parsed_args.human_readable)
return zip(*sorted(info.items()))
@@ -1185,7 +1368,7 @@ class UnsetImage(command.Command):
_description = _("Unset image tags and properties")
def get_parser(self, prog_name):
- parser = super(UnsetImage, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"image",
metavar="<image>",
@@ -1197,8 +1380,10 @@ class UnsetImage(command.Command):
metavar="<tag>",
default=[],
action='append',
- help=_("Unset a tag on this image "
- "(repeat option to unset multiple tags)"),
+ help=_(
+ "Unset a tag on this image "
+ "(repeat option to unset multiple tags)"
+ ),
)
parser.add_argument(
"--property",
@@ -1206,15 +1391,18 @@ class UnsetImage(command.Command):
metavar="<property-key>",
default=[],
action='append',
- help=_("Unset a property on this image "
- "(repeat option to unset multiple properties)"),
+ help=_(
+ "Unset a property on this image "
+ "(repeat option to unset multiple properties)"
+ ),
)
return parser
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = image_client.find_image(parsed_args.image,
- ignore_missing=False)
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False
+ )
kwargs = {}
tagret = 0
@@ -1224,8 +1412,9 @@ class UnsetImage(command.Command):
try:
image_client.remove_tag(image.id, k)
except Exception:
- LOG.error(_("tag unset failed, '%s' is a "
- "nonexistent tag "), k)
+ LOG.error(
+ _("tag unset failed, '%s' is a " "nonexistent tag "), k
+ )
tagret += 1
if parsed_args.properties:
@@ -1237,35 +1426,46 @@ class UnsetImage(command.Command):
# pass modified properties object, so that SDK can figure
# out, what was changed inside
# NOTE: ping gtema to improve that in SDK
- new_props = kwargs.get('properties',
- image.get('properties').copy())
+ new_props = kwargs.get(
+ 'properties', image.get('properties').copy()
+ )
new_props.pop(k, None)
kwargs['properties'] = new_props
else:
- LOG.error(_("property unset failed, '%s' is a "
- "nonexistent property "), k)
+ LOG.error(
+ _(
+ "property unset failed, '%s' is a "
+ "nonexistent property "
+ ),
+ k,
+ )
propret += 1
# We must give to update a current image for the reference on what
# has changed
- image_client.update_image(
- image,
- **kwargs)
+ image_client.update_image(image, **kwargs)
tagtotal = len(parsed_args.tags)
proptotal = len(parsed_args.properties)
- if (tagret > 0 and propret > 0):
- msg = (_("Failed to unset %(tagret)s of %(tagtotal)s tags,"
- "Failed to unset %(propret)s of %(proptotal)s properties.")
- % {'tagret': tagret, 'tagtotal': tagtotal,
- 'propret': propret, 'proptotal': proptotal})
+ if tagret > 0 and propret > 0:
+ msg = _(
+ "Failed to unset %(tagret)s of %(tagtotal)s tags,"
+ "Failed to unset %(propret)s of %(proptotal)s properties."
+ ) % {
+ 'tagret': tagret,
+ 'tagtotal': tagtotal,
+ 'propret': propret,
+ 'proptotal': proptotal,
+ }
raise exceptions.CommandError(msg)
elif tagret > 0:
- msg = (_("Failed to unset %(tagret)s of %(tagtotal)s tags.")
- % {'tagret': tagret, 'tagtotal': tagtotal})
+ msg = _("Failed to unset %(tagret)s of %(tagtotal)s tags.") % {
+ 'tagret': tagret,
+ 'tagtotal': tagtotal,
+ }
raise exceptions.CommandError(msg)
elif propret > 0:
- msg = (_("Failed to unset %(propret)s of %(proptotal)s"
- " properties.")
- % {'propret': propret, 'proptotal': proptotal})
+ msg = _(
+ "Failed to unset %(propret)s of %(proptotal)s" " properties."
+ ) % {'propret': propret, 'proptotal': proptotal}
raise exceptions.CommandError(msg)
diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py
index b08fa87..b22fd8a 100644
--- a/openstackclient/network/v2/address_group.py
+++ b/openstackclient/network/v2/address_group.py
@@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 488e160..91f581b 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -30,7 +30,7 @@ def _get_columns(item):
column_map = {
'is_shared': 'shared',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 4c03074..22096bc 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -28,7 +28,7 @@ _formatters = {
def _get_network_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py
index b33633d..bcd5add 100644
--- a/openstackclient/network/v2/floating_ip_port_forwarding.py
+++ b/openstackclient/network/v2/floating_ip_port_forwarding.py
@@ -27,7 +27,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index 9e56c5b..b065ab1 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -27,7 +27,7 @@ _formatters = {
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['id', 'name', 'location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py
index 1de5b78..df153dd 100644
--- a/openstackclient/network/v2/l3_conntrack_helper.py
+++ b/openstackclient/network/v2/l3_conntrack_helper.py
@@ -26,7 +26,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py
index 109f64c..e8fb5f8 100644
--- a/openstackclient/network/v2/local_ip.py
+++ b/openstackclient/network/v2/local_ip.py
@@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item, column_map, hidden_columns)
@@ -87,17 +87,17 @@ class CreateLocalIP(command.ShowOne):
)
parser.add_argument(
'--local-port',
- metavar='<local_port>',
+ metavar='<local-port>',
help=_("Port to allocate Local IP (name or ID)")
)
parser.add_argument(
"--local-ip-address",
- metavar="<local_ip_address>",
+ metavar="<local-ip-address>",
help=_("IP address or CIDR "),
)
parser.add_argument(
'--ip-mode',
- metavar='<ip_mode>',
+ metavar='<ip-mode>',
help=_("local IP ip mode")
)
@@ -212,13 +212,13 @@ class ListLocalIP(command.Lister):
)
parser.add_argument(
'--local-port',
- metavar='<local_port>',
+ metavar='<local-port>',
help=_("List Local IP(s) according to "
"given port (name or ID)")
)
parser.add_argument(
'--local-ip-address',
- metavar='<local_ip_address>',
+ metavar='<local-ip-address>',
help=_("List Local IP(s) according to "
"given Local IP Address")
)
diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py
index aa0747c..4cd7707 100644
--- a/openstackclient/network/v2/local_ip_association.py
+++ b/openstackclient/network/v2/local_ip_association.py
@@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location', 'name', 'id']
+ hidden_columns = ['location', 'name', 'id', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item, column_map, hidden_columns)
@@ -43,16 +43,16 @@ class CreateLocalIPAssociation(command.ShowOne):
'local_ip',
metavar='<local-ip>',
help=_("Local IP that the port association belongs to "
- "(IP address or ID)")
+ "(Name or ID)")
)
parser.add_argument(
'fixed_port',
- metavar='<fixed_port>',
+ metavar='<fixed-port>',
help=_("The ID or Name of Port to allocate Local IP Association")
)
parser.add_argument(
'--fixed-ip',
- metavar='<fixed_ip>',
+ metavar='<fixed-ip>',
help=_("Fixed IP for Local IP Association")
)
@@ -87,14 +87,14 @@ class DeleteLocalIPAssociation(command.Command):
parser = super().get_parser(prog_name)
parser.add_argument(
'local_ip',
- metavar="<local_ip>",
+ metavar="<local-ip>",
help=_("Local IP that the port association belongs to "
- "(IP address or ID)")
+ "(Name or ID)")
)
parser.add_argument(
'fixed_port_id',
nargs="+",
- metavar="<fixed_port_id>",
+ metavar="<fixed-port-id>",
help=_("The fixed port ID of Local IP Association")
)
return parser
@@ -136,18 +136,18 @@ class ListLocalIPAssociation(command.Lister):
parser.add_argument(
'local_ip',
- metavar='<local_ip>',
+ metavar='<local-ip>',
help=_("Local IP that port associations belongs to")
)
parser.add_argument(
'--fixed-port',
- metavar='<fixed_port>',
+ metavar='<fixed-port>',
help=_("Filter the list result by the ID or name of "
"the fixed port")
)
parser.add_argument(
'--fixed-ip',
- metavar='<fixed_ip>',
+ metavar='<fixed-ip>',
help=_("Filter the list result by fixed ip")
)
parser.add_argument(
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index a239e0f..d7b37eb 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -60,6 +60,7 @@ def _get_columns_network(item):
'ipv6_address_scope_id': 'ipv6_address_scope',
'tags': 'tags',
}
+ hidden_columns = ['location', 'tenant_id']
hidden_columns = ['location']
return utils.get_osc_show_columns_for_sdk_resource(
item,
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index 3024d02..d963b3b 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -50,7 +50,7 @@ def _get_network_columns(item):
'is_admin_state_up': 'admin_state_up',
'is_alive': 'alive',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'name', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py
index 496606b..c612f05 100644
--- a/openstackclient/network/v2/network_auto_allocated_topology.py
+++ b/openstackclient/network/v2/network_auto_allocated_topology.py
@@ -26,7 +26,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['name', 'location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py
index 862155c..864184c 100644
--- a/openstackclient/network/v2/network_flavor.py
+++ b/openstackclient/network/v2/network_flavor.py
@@ -31,7 +31,7 @@ def _get_columns(item):
'is_enabled': 'enabled',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py
index 719f955..66c6dcf 100644
--- a/openstackclient/network/v2/network_flavor_profile.py
+++ b/openstackclient/network/v2/network_flavor_profile.py
@@ -29,7 +29,7 @@ def _get_columns(item):
'is_enabled': 'enabled',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'name', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
@@ -166,7 +166,7 @@ class ListNetworkFlavorProfile(command.Lister):
'id',
'driver',
'is_enabled',
- 'metainfo',
+ 'meta_info',
'description',
)
column_headers = (
diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py
index b7b77fb..99b0bdd 100644
--- a/openstackclient/network/v2/network_meter.py
+++ b/openstackclient/network/v2/network_meter.py
@@ -30,7 +30,7 @@ def _get_columns(item):
column_map = {
'is_shared': 'shared',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py
index 0f42727..2c50e5a 100644
--- a/openstackclient/network/v2/network_meter_rule.py
+++ b/openstackclient/network/v2/network_meter_rule.py
@@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py
index bc25790..d77e5db 100644
--- a/openstackclient/network/v2/network_qos_policy.py
+++ b/openstackclient/network/v2/network_qos_policy.py
@@ -30,7 +30,7 @@ def _get_columns(item):
column_map = {
'is_shared': 'shared',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
index a4129b8..cb2d233 100644
--- a/openstackclient/network/v2/network_qos_rule.py
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -25,16 +25,20 @@ from openstackclient.network import common
RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit'
RULE_TYPE_DSCP_MARKING = 'dscp-marking'
RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
+RULE_TYPE_MINIMUM_PACKET_RATE = 'minimum-packet-rate'
MANDATORY_PARAMETERS = {
RULE_TYPE_MINIMUM_BANDWIDTH: {'min_kbps', 'direction'},
+ RULE_TYPE_MINIMUM_PACKET_RATE: {'min_kpps', 'direction'},
RULE_TYPE_DSCP_MARKING: {'dscp_mark'},
RULE_TYPE_BANDWIDTH_LIMIT: {'max_kbps'}}
OPTIONAL_PARAMETERS = {
RULE_TYPE_MINIMUM_BANDWIDTH: set(),
+ RULE_TYPE_MINIMUM_PACKET_RATE: set(),
RULE_TYPE_DSCP_MARKING: set(),
RULE_TYPE_BANDWIDTH_LIMIT: {'direction', 'max_burst_kbps'}}
DIRECTION_EGRESS = 'egress'
DIRECTION_INGRESS = 'ingress'
+DIRECTION_ANY = 'any'
DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
34, 36, 38, 40, 46, 48, 56]
@@ -47,7 +51,7 @@ ACTION_SHOW = 'get'
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
@@ -98,10 +102,20 @@ def _get_attrs(network_client, parsed_args, is_create=False):
attrs['dscp_mark'] = parsed_args.dscp_mark
if parsed_args.min_kbps is not None:
attrs['min_kbps'] = parsed_args.min_kbps
+ if parsed_args.min_kpps is not None:
+ attrs['min_kpps'] = parsed_args.min_kpps
if parsed_args.ingress:
- attrs['direction'] = 'ingress'
+ attrs['direction'] = DIRECTION_INGRESS
if parsed_args.egress:
- attrs['direction'] = 'egress'
+ attrs['direction'] = DIRECTION_EGRESS
+ if parsed_args.any:
+ if rule_type == RULE_TYPE_MINIMUM_PACKET_RATE:
+ attrs['direction'] = DIRECTION_ANY
+ else:
+ msg = (_('Direction "any" can only be used with '
+ '%(rule_type_min_pps)s rule type') %
+ {'rule_type_min_pps': RULE_TYPE_MINIMUM_PACKET_RATE})
+ raise exceptions.CommandError(msg)
_check_type_parameters(attrs, rule_type, is_create)
return attrs
@@ -160,6 +174,13 @@ def _add_rule_arguments(parser):
type=int,
help=_('Minimum guaranteed bandwidth in kbps')
)
+ parser.add_argument(
+ '--min-kpps',
+ dest='min_kpps',
+ metavar='<min-kpps>',
+ type=int,
+ help=_('Minimum guaranteed packet rate in kpps')
+ )
direction_group = parser.add_mutually_exclusive_group()
direction_group.add_argument(
'--ingress',
@@ -171,6 +192,12 @@ def _add_rule_arguments(parser):
action='store_true',
help=_("Egress traffic direction from the project point of view")
)
+ direction_group.add_argument(
+ '--any',
+ action='store_true',
+ help=_("Any traffic direction from the project point of view. Can be "
+ "used only with minimum packet rate rule.")
+ )
class CreateNetworkQosRule(command.ShowOne,
@@ -190,6 +217,7 @@ class CreateNetworkQosRule(command.ShowOne,
metavar='<type>',
required=True,
choices=[RULE_TYPE_MINIMUM_BANDWIDTH,
+ RULE_TYPE_MINIMUM_PACKET_RATE,
RULE_TYPE_DSCP_MARKING,
RULE_TYPE_BANDWIDTH_LIMIT],
help=(_('QoS rule type (%s)') %
@@ -200,10 +228,10 @@ class CreateNetworkQosRule(command.ShowOne,
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
- attrs = _get_attrs(network_client, parsed_args, is_create=True)
- attrs.update(
- self._parse_extra_properties(parsed_args.extra_properties))
try:
+ attrs = _get_attrs(network_client, parsed_args, is_create=True)
+ attrs.update(
+ self._parse_extra_properties(parsed_args.extra_properties))
obj = _rule_action_call(
network_client, ACTION_CREATE, parsed_args.type)(
attrs.pop('qos_policy_id'), **attrs)
@@ -270,6 +298,7 @@ class ListNetworkQosRule(command.Lister):
'max_kbps',
'max_burst_kbps',
'min_kbps',
+ 'min_kpps',
'dscp_mark',
'direction',
)
@@ -280,6 +309,7 @@ class ListNetworkQosRule(command.Lister):
'Max Kbps',
'Max Burst Kbits',
'Min Kbps',
+ 'Min Kpps',
'DSCP mark',
'Direction',
)
diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py
index 1bcfda8..9af2287 100644
--- a/openstackclient/network/v2/network_qos_rule_type.py
+++ b/openstackclient/network/v2/network_qos_rule_type.py
@@ -24,9 +24,9 @@ def _get_columns(item):
"type": "rule_type_name",
"drivers": "drivers",
}
- invisible_columns = ["id", "location", "name"]
+ hidden_columns = ["id", "location", "name", 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
- item, column_map, invisible_columns)
+ item, column_map, hidden_columns)
class ListNetworkQosRuleType(command.Lister):
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index 0066739..91cd9dc 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -30,7 +30,7 @@ def _get_columns(item):
column_map = {
'target_tenant': 'target_project_id',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'name', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py
index 0f64bd8..c6c88e3 100644
--- a/openstackclient/network/v2/network_segment.py
+++ b/openstackclient/network/v2/network_segment.py
@@ -27,7 +27,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py
index a95adb0..1291d9d 100644
--- a/openstackclient/network/v2/network_segment_range.py
+++ b/openstackclient/network/v2/network_segment_range.py
@@ -33,7 +33,7 @@ LOG = logging.getLogger(__name__)
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 16072bc..8bf14d6 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -64,7 +64,7 @@ def _get_columns(item):
'is_admin_state_up': 'admin_state_up',
'is_port_security_enabled': 'port_security_enabled',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index d8c38f4..ffad998 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -35,6 +35,7 @@ def _format_network_security_group_rules(sg_rules):
for key in empty_keys:
sg_rule.pop(key)
sg_rule.pop('security_group_id', None)
+ sg_rule.pop('tenant_id', None)
sg_rule.pop('project_id', None)
return utils.format_list_of_dicts(sg_rules)
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index a112261..2179f33 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -72,7 +72,7 @@ def _format_remote_ip_prefix(rule):
def _get_columns(item):
column_map = {}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index bf6a46d..8e3a877 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -138,16 +138,17 @@ def _get_columns(item):
'is_dhcp_enabled': 'enable_dhcp',
'subnet_pool_id': 'subnetpool_id',
}
- # Do not show this column when displaying a subnet
- invisible_columns = [
+ # Do not show these columns when displaying a subnet
+ hidden_columns = [
'location',
'use_default_subnet_pool',
- 'prefix_length'
+ 'prefix_length',
+ 'tenant_id',
]
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
- invisible_columns
+ hidden_columns
)
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index b4142f3..2369960 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -37,7 +37,7 @@ def _get_columns(item):
'maximum_prefix_length': 'max_prefixlen',
'minimum_prefix_length': 'min_prefixlen',
}
- hidden_columns = ['location']
+ hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py
index 0ed7dff..b6867f8 100644
--- a/openstackclient/tests/functional/base.py
+++ b/openstackclient/tests/functional/base.py
@@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import logging
import os
import shlex
import subprocess
@@ -18,22 +19,30 @@ from tempest.lib.cli import output_parser
from tempest.lib import exceptions
import testtools
-
ADMIN_CLOUD = os.environ.get('OS_ADMIN_CLOUD', 'devstack-admin')
+LOG = logging.getLogger(__name__)
def execute(cmd, fail_ok=False, merge_stderr=False):
"""Executes specified command for the given action."""
+ LOG.debug('Executing: %s', cmd)
cmdlist = shlex.split(cmd)
stdout = subprocess.PIPE
stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
+
proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr)
- result, result_err = proc.communicate()
- result = result.decode('utf-8')
+
+ result_out, result_err = proc.communicate()
+ result_out = result_out.decode('utf-8')
+ LOG.debug('stdout: %s', result_out)
+ LOG.debug('stderr: %s', result_err)
+
if not fail_ok and proc.returncode != 0:
- raise exceptions.CommandFailed(proc.returncode, cmd, result,
- result_err)
- return result
+ raise exceptions.CommandFailed(
+ proc.returncode, cmd, result_out, result_err,
+ )
+
+ return result_out
class TestCase(testtools.TestCase):
@@ -42,7 +51,7 @@ class TestCase(testtools.TestCase):
def openstack(cls, cmd, cloud=ADMIN_CLOUD, fail_ok=False):
"""Executes openstackclient command for the given action
- NOTE(dtroyer): There is a subtle distinction between pasing
+ NOTE(dtroyer): There is a subtle distinction between passing
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'
@@ -61,7 +70,7 @@ class TestCase(testtools.TestCase):
fail_ok=fail_ok
)
else:
- # Execure command with an explicit cloud specified
+ # Execute command with an explicit cloud specified
return execute(
'openstack --os-cloud=' + cloud + ' ' + cmd,
fail_ok=fail_ok
diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py
index bf67101..783294d 100644
--- a/openstackclient/tests/functional/common/test_quota.py
+++ b/openstackclient/tests/functional/common/test_quota.py
@@ -169,12 +169,21 @@ class QuotaTests(base.TestCase):
self.assertTrue(cmd_output["key-pairs"] >= 0)
self.assertTrue(cmd_output["snapshots"] >= 0)
+ def _restore_quota_limit(self, resource, limit, project):
+ self.openstack('quota set --%s %s %s' % (resource, limit, project))
+
def test_quota_network_set_with_check_limit(self):
if not self.haz_network:
self.skipTest('No Network service present')
if not self.is_extension_enabled('quota-check-limit'):
self.skipTest('No "quota-check-limit" extension present')
+ cmd_output = json.loads(self.openstack(
+ 'quota list -f json --network'
+ ))
+ self.addCleanup(self._restore_quota_limit, 'network',
+ cmd_output[0]['Networks'], self.PROJECT_NAME)
+
self.openstack('quota set --networks 40 ' + self.PROJECT_NAME)
cmd_output = json.loads(self.openstack(
'quota list -f json --network'
@@ -190,3 +199,42 @@ class QuotaTests(base.TestCase):
self.assertRaises(exceptions.CommandFailed, self.openstack,
'quota set --networks 1 --check-limit ' +
self.PROJECT_NAME)
+
+ def test_quota_network_set_with_force(self):
+ self.skipTest('story 2010110')
+ if not self.haz_network:
+ self.skipTest('No Network service present')
+ # NOTE(ralonsoh): the Neutron support for the flag "check-limit" was
+ # added with the extension "quota-check-limit". The flag "force" was
+ # added too in order to change the behaviour of Neutron quota engine
+ # and mimic the Nova one: by default the engine will check the resource
+ # usage before setting the new limit; with "force", this check will be
+ # skipped (in Yoga, this behaviour is still NOT the default in
+ # Neutron).
+ if not self.is_extension_enabled('quota-check-limit'):
+ self.skipTest('No "quota-check-limit" extension present')
+
+ cmd_output = json.loads(self.openstack(
+ 'quota list -f json --network'
+ ))
+ self.addCleanup(self._restore_quota_limit, 'network',
+ cmd_output[0]['Networks'], self.PROJECT_NAME)
+
+ self.openstack('quota set --networks 40 ' + self.PROJECT_NAME)
+ cmd_output = json.loads(self.openstack(
+ 'quota list -f json --network'
+ ))
+ self.assertIsNotNone(cmd_output)
+ self.assertEqual(40, cmd_output[0]['Networks'])
+
+ # That will ensure we have at least two networks in the system.
+ for _ in range(2):
+ self.openstack('network create --project %s %s' %
+ (self.PROJECT_NAME, uuid.uuid4().hex))
+
+ self.openstack('quota set --networks 1 --force ' + self.PROJECT_NAME)
+ cmd_output = json.loads(self.openstack(
+ 'quota list -f json --network'
+ ))
+ self.assertIsNotNone(cmd_output)
+ self.assertEqual(1, cmd_output[0]['Networks'])
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index cf4bcbc..0558ef6 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -1195,19 +1195,19 @@ class ServerTests(common.ComputeTestCase):
def test_server_add_remove_volume(self):
volume_wait_for = volume_common.BaseVolumeTests.wait_for_status
- name = uuid.uuid4().hex
+ server_name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'server create -f json ' +
'--network private ' +
'--flavor ' + self.flavor_name + ' ' +
'--image ' + self.image_name + ' ' +
'--wait ' +
- name
+ server_name
))
self.assertIsNotNone(cmd_output['id'])
- self.assertEqual(name, cmd_output['name'])
- self.addCleanup(self.openstack, 'server delete --wait ' + name)
+ self.assertEqual(server_name, cmd_output['name'])
+ self.addCleanup(self.openstack, 'server delete --wait ' + server_name)
server_id = cmd_output['id']
volume_name = uuid.uuid4().hex
@@ -1225,7 +1225,7 @@ class ServerTests(common.ComputeTestCase):
cmd_output = json.loads(self.openstack(
'server add volume -f json ' +
- name + ' ' +
+ server_name + ' ' +
volume_name + ' ' +
'--tag bar'
))
@@ -1237,7 +1237,7 @@ class ServerTests(common.ComputeTestCase):
cmd_output = json.loads(self.openstack(
'server volume list -f json ' +
- name
+ server_name
))
self.assertEqual(volume_attachment_id, cmd_output[0]['ID'])
@@ -1245,8 +1245,25 @@ class ServerTests(common.ComputeTestCase):
self.assertEqual(volume_id, cmd_output[0]['Volume ID'])
volume_wait_for('volume', volume_name, 'in-use')
- self.openstack('server remove volume ' + name + ' ' + volume_name)
+
+ cmd_output = json.loads(self.openstack(
+ 'server event list -f json ' +
+ server_name
+ ))
+ self.assertEqual(2, len(cmd_output))
+ self.assertIn('attach_volume', {x['Action'] for x in cmd_output})
+
+ self.openstack(
+ 'server remove volume ' + server_name + ' ' + volume_name
+ )
volume_wait_for('volume', volume_name, 'available')
- raw_output = self.openstack('server volume list ' + name)
+ cmd_output = json.loads(self.openstack(
+ 'server event list -f json ' +
+ server_name
+ ))
+ self.assertEqual(3, len(cmd_output))
+ self.assertIn('detach_volume', {x['Action'] for x in cmd_output})
+
+ raw_output = self.openstack('server volume list ' + server_name)
self.assertEqual('\n', raw_output)
diff --git a/openstackclient/tests/functional/compute/v2/test_server_event.py b/openstackclient/tests/functional/compute/v2/test_server_event.py
index 953ade4..b4725fe 100644
--- a/openstackclient/tests/functional/compute/v2/test_server_event.py
+++ b/openstackclient/tests/functional/compute/v2/test_server_event.py
@@ -82,3 +82,38 @@ class ServerEventTests(common.ComputeTestCase):
self.assertEqual('reboot', cmd_output.get('action'))
self.assertIsNotNone(cmd_output.get('events'))
self.assertIsInstance(cmd_output.get('events'), list)
+
+ def test_server_event_list_and_show_deleted_server(self):
+ # Need to create a new server that will not attempt cleanup because we
+ # are going to delete it during the test and cleanup would fail with
+ # 404.
+ cmd_output = self.server_create(cleanup=False)
+ server_id = cmd_output['id']
+ # Delete the server
+ self.openstack('server delete --wait ' + server_id)
+ # And verify we can get the event list after it's deleted
+ # Test 'server event list' for deleting
+ cmd_output = json.loads(self.openstack(
+ '--os-compute-api-version 2.21 '
+ 'server event list -f json ' + server_id
+ ))
+ request_id = None
+ for each_event in cmd_output:
+ self.assertNotIn('Message', each_event)
+ self.assertNotIn('Project ID', each_event)
+ self.assertNotIn('User ID', each_event)
+ if each_event.get('Action') == 'delete':
+ self.assertEqual(server_id, each_event.get('Server ID'))
+ request_id = each_event.get('Request ID')
+ break
+ self.assertIsNotNone(request_id)
+ # Test 'server event show' for deleting
+ cmd_output = json.loads(self.openstack(
+ '--os-compute-api-version 2.21 '
+ 'server event show -f json ' + server_id + ' ' + request_id
+ ))
+ self.assertEqual(server_id, cmd_output.get('instance_uuid'))
+ self.assertEqual(request_id, cmd_output.get('request_id'))
+ self.assertEqual('delete', cmd_output.get('action'))
+ self.assertIsNotNone(cmd_output.get('events'))
+ self.assertIsInstance(cmd_output.get('events'), list)
diff --git a/openstackclient/tests/functional/compute/v2/test_server_group.py b/openstackclient/tests/functional/compute/v2/test_server_group.py
index 3dff3dc..daeecd2 100644
--- a/openstackclient/tests/functional/compute/v2/test_server_group.py
+++ b/openstackclient/tests/functional/compute/v2/test_server_group.py
@@ -33,8 +33,8 @@ class ServerGroupTests(base.TestCase):
cmd_output['name']
)
self.assertEqual(
- ['affinity'],
- cmd_output['policies']
+ 'affinity',
+ cmd_output['policy']
)
cmd_output = json.loads(self.openstack(
@@ -47,8 +47,8 @@ class ServerGroupTests(base.TestCase):
cmd_output['name']
)
self.assertEqual(
- ['anti-affinity'],
- cmd_output['policies']
+ 'anti-affinity',
+ cmd_output['policy']
)
del_output = self.openstack(
@@ -60,7 +60,7 @@ class ServerGroupTests(base.TestCase):
name1 = uuid.uuid4().hex
name2 = uuid.uuid4().hex
- # test server gorup show
+ # test server group show
cmd_output = json.loads(self.openstack(
'server group create -f json ' +
'--policy affinity ' +
@@ -74,8 +74,8 @@ class ServerGroupTests(base.TestCase):
cmd_output['name']
)
self.assertEqual(
- ['affinity'],
- cmd_output['policies']
+ 'affinity',
+ cmd_output['policy']
)
cmd_output = json.loads(self.openstack(
@@ -91,8 +91,8 @@ class ServerGroupTests(base.TestCase):
cmd_output['name']
)
self.assertEqual(
- ['anti-affinity'],
- cmd_output['policies']
+ 'anti-affinity',
+ cmd_output['policy']
)
# test server group list
@@ -101,6 +101,6 @@ class ServerGroupTests(base.TestCase):
names = [x["Name"] for x in cmd_output]
self.assertIn(name1, names)
self.assertIn(name2, names)
- policies = [x["Policies"] for x in cmd_output]
- self.assertIn(['affinity'], policies)
- self.assertIn(['anti-affinity'], policies)
+ policies = [x["Policy"] for x in cmd_output]
+ self.assertIn('affinity', policies)
+ self.assertIn('anti-affinity', policies)
diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py
index 0a3a736..5b07470 100644
--- a/openstackclient/tests/functional/image/v2/test_image.py
+++ b/openstackclient/tests/functional/image/v2/test_image.py
@@ -26,6 +26,7 @@ class ImageTests(base.BaseImageTests):
self.name = uuid.uuid4().hex
self.image_tag = 'my_tag'
+ self.image_tag1 = 'random'
json_output = json.loads(self.openstack(
'--os-image-api-version 2 '
'image create -f json --tag {tag} {name}'.format(
@@ -78,13 +79,18 @@ class ImageTests(base.BaseImageTests):
def test_image_list_with_tag_filter(self):
json_output = json.loads(self.openstack(
- 'image list --tag ' + self.image_tag + ' --long -f json'
+ 'image list --tag ' + self.image_tag + ' --tag ' +
+ self.image_tag1 + ' --long -f json'
))
for taglist in [img['Tags'] for img in json_output]:
self.assertIn(
self.image_tag,
taglist
)
+ self.assertIn(
+ self.image_tag1,
+ taglist
+ )
def test_image_attributes(self):
"""Test set, unset, show on attributes, tags and properties"""
diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py
index 98e588e..fd411b3 100644
--- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py
+++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py
@@ -85,6 +85,73 @@ class NetworkQosRuleTestsMinimumBandwidth(common.NetworkTests):
self.assertEqual(7500, cmd_output['min_kbps'])
+class NetworkQosRuleTestsMinimumPacketRate(common.NetworkTests):
+ """Functional tests for QoS minimum packet rate rule"""
+
+ def setUp(self):
+ super(NetworkQosRuleTestsMinimumPacketRate, self).setUp()
+ # Nothing in this class works with Nova Network
+ if not self.haz_network:
+ self.skipTest("No Network service present")
+
+ self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex
+
+ self.openstack(
+ 'network qos policy create %s' % self.QOS_POLICY_NAME
+ )
+ self.addCleanup(self.openstack,
+ 'network qos policy delete %s' % self.QOS_POLICY_NAME)
+ cmd_output = json.loads(self.openstack(
+ 'network qos rule create -f json '
+ '--type minimum-packet-rate '
+ '--min-kpps 2800 '
+ '--egress %s' %
+ self.QOS_POLICY_NAME
+ ))
+ self.RULE_ID = cmd_output['id']
+ self.addCleanup(self.openstack,
+ 'network qos rule delete %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
+ self.assertTrue(self.RULE_ID)
+
+ def test_qos_rule_create_delete(self):
+ # This is to check the output of qos rule delete
+ policy_name = uuid.uuid4().hex
+ self.openstack('network qos policy create -f json %s' % policy_name)
+ self.addCleanup(self.openstack,
+ 'network qos policy delete %s' % policy_name)
+ rule = json.loads(self.openstack(
+ 'network qos rule create -f json '
+ '--type minimum-packet-rate '
+ '--min-kpps 2800 '
+ '--egress %s' % policy_name
+ ))
+ raw_output = self.openstack(
+ 'network qos rule delete %s %s' %
+ (policy_name, rule['id']))
+ self.assertEqual('', raw_output)
+
+ def test_qos_rule_list(self):
+ cmd_output = json.loads(self.openstack(
+ 'network qos rule list -f json %s' % self.QOS_POLICY_NAME))
+ self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output])
+
+ def test_qos_rule_show(self):
+ cmd_output = json.loads(self.openstack(
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
+ self.assertEqual(self.RULE_ID, cmd_output['id'])
+
+ def test_qos_rule_set(self):
+ self.openstack('network qos rule set --min-kpps 7500 --any %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
+ cmd_output = json.loads(self.openstack(
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
+ self.assertEqual(7500, cmd_output['min_kpps'])
+ self.assertEqual('any', cmd_output['direction'])
+
+
class NetworkQosRuleTestsDSCPMarking(common.NetworkTests):
"""Functional tests for QoS DSCP marking rule"""
diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py
index fb8dabd..7434b5b 100644
--- a/openstackclient/tests/functional/volume/v1/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v1/test_volume_type.py
@@ -118,10 +118,10 @@ class VolumeTypeTests(common.BaseVolumeTests):
raw_output = self.openstack(cmd)
self.assertOutput('', raw_output)
- # NOTE: Add some basic funtional tests with the old format to
+ # NOTE: Add some basic functional tests with the old format to
# make sure the command works properly, need to change
# these to new test format when beef up all tests for
- # volume tye commands.
+ # volume type commands.
def test_encryption_type(self):
encryption_type = uuid.uuid4().hex
# test create new encryption type
diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py
index 3f1a6ea..861c393 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py
@@ -139,10 +139,10 @@ class VolumeTypeTests(common.BaseVolumeTests):
raw_output = self.openstack(cmd)
self.assertOutput('', raw_output)
- # NOTE: Add some basic funtional tests with the old format to
+ # NOTE: Add some basic functional tests with the old format to
# make sure the command works properly, need to change
# these to new test format when beef up all tests for
- # volume tye commands.
+ # volume type commands.
def test_encryption_type(self):
name = uuid.uuid4().hex
encryption_type = uuid.uuid4().hex
diff --git a/openstackclient/tests/functional/volume/v3/test_volume_type.py b/openstackclient/tests/functional/volume/v3/test_volume_type.py
index 79d4096..165c625 100644
--- a/openstackclient/tests/functional/volume/v3/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v3/test_volume_type.py
@@ -139,10 +139,10 @@ class VolumeTypeTests(common.BaseVolumeTests):
raw_output = self.openstack(cmd)
self.assertOutput('', raw_output)
- # NOTE: Add some basic funtional tests with the old format to
+ # NOTE: Add some basic functional tests with the old format to
# make sure the command works properly, need to change
# these to new test format when beef up all tests for
- # volume tye commands.
+ # volume type commands.
def test_encryption_type(self):
name = uuid.uuid4().hex
encryption_type = uuid.uuid4().hex
diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py
index 8733b51..e5348ec 100644
--- a/openstackclient/tests/unit/common/test_availability_zone.py
+++ b/openstackclient/tests/unit/common/test_availability_zone.py
@@ -115,8 +115,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
compute_fakes.FakeAvailabilityZone.create_availability_zones()
volume_azs = \
volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1)
- network_azs = \
- network_fakes.FakeAvailabilityZone.create_availability_zones()
+ network_azs = network_fakes.create_availability_zones()
short_columnslist = ('Zone Name', 'Zone Status')
long_columnslist = (
diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py
index 896a63a..70fd143 100644
--- a/openstackclient/tests/unit/common/test_quota.py
+++ b/openstackclient/tests/unit/common/test_quota.py
@@ -935,6 +935,7 @@ class TestQuotaSet(TestQuota):
}
kwargs_network = {
'subnet': network_fakes.QUOTA['subnet'],
+ 'force': True,
}
self.compute_quotas_mock.update.assert_called_once_with(
self.projects[0].id,
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 55572cd..d77797a 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -21,6 +21,7 @@ import uuid
from novaclient import api_versions
from openstack.compute.v2 import flavor as _flavor
from openstack.compute.v2 import server
+from openstack.compute.v2 import server_group as _server_group
from openstack.compute.v2 import server_interface as _server_interface
from openstack.compute.v2 import service
from openstack.compute.v2 import volume_attachment
@@ -1290,72 +1291,6 @@ class FakeHost(object):
return host_info
-class FakeServerGroup(object):
- """Fake one server group"""
-
- @staticmethod
- def _create_one_server_group(attrs=None):
- """Create a fake server group
-
- :param dict attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id and other attributes
- """
- if attrs is None:
- attrs = {}
-
- # Set default attributes.
- server_group_info = {
- 'id': 'server-group-id-' + uuid.uuid4().hex,
- 'members': [],
- 'metadata': {},
- 'name': 'server-group-name-' + uuid.uuid4().hex,
- 'project_id': 'server-group-project-id-' + uuid.uuid4().hex,
- 'user_id': 'server-group-user-id-' + uuid.uuid4().hex,
- }
-
- # Overwrite default attributes.
- server_group_info.update(attrs)
-
- server_group = fakes.FakeResource(
- info=copy.deepcopy(server_group_info),
- loaded=True)
- return server_group
-
- @staticmethod
- def create_one_server_group(attrs=None):
- """Create a fake server group
-
- :param dict attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id and other attributes
- """
- if attrs is None:
- attrs = {}
- attrs.setdefault('policies', ['policy1', 'policy2'])
- return FakeServerGroup._create_one_server_group(attrs)
-
-
-class FakeServerGroupV264(object):
- """Fake one server group fo API >= 2.64"""
-
- @staticmethod
- def create_one_server_group(attrs=None):
- """Create a fake server group
-
- :param dict attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id and other attributes
- """
- if attrs is None:
- attrs = {}
- attrs.setdefault('policy', 'policy1')
- return FakeServerGroup._create_one_server_group(attrs)
-
-
class FakeUsage(object):
"""Fake one or more usage."""
@@ -1454,7 +1389,7 @@ class FakeQuota(object):
@staticmethod
def create_one_default_comp_quota(attrs=None):
- """Crate one quota"""
+ """Create one quota"""
attrs = attrs or {}
@@ -1860,6 +1795,34 @@ class FakeVolumeAttachment(object):
return volume_attachments
+def create_one_server_group(attrs=None):
+ """Create a fake server group
+
+ :param dict attrs:
+ A dictionary with all attributes
+ :return:
+ A fake ServerGroup object, with id and other attributes
+ """
+ if attrs is None:
+ attrs = {}
+
+ # Set default attributes.
+ server_group_info = {
+ 'id': 'server-group-id-' + uuid.uuid4().hex,
+ 'member_ids': '',
+ 'metadata': {},
+ 'name': 'server-group-name-' + uuid.uuid4().hex,
+ 'project_id': 'server-group-project-id-' + uuid.uuid4().hex,
+ 'user_id': 'server-group-user-id-' + uuid.uuid4().hex,
+ }
+
+ # Overwrite default attributes.
+ server_group_info.update(attrs)
+
+ server_group = _server_group.ServerGroup(**server_group_info)
+ return server_group
+
+
def create_one_server_interface(attrs=None):
"""Create a fake SDK ServerInterface.
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 480dcfe..004f3a0 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -31,7 +31,6 @@ from osc_lib import utils as common_utils
from openstackclient.compute.v2 import server
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
-from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit.image.v2 import fakes as image_fakes
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils
@@ -593,7 +592,7 @@ class TestServerAddFloatingIPNetwork(
def test_server_add_floating_ip(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
_floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
self.network.find_ip = mock.Mock(return_value=_floating_ip)
self.network.ports = mock.Mock(return_value=[_port])
@@ -662,7 +661,7 @@ class TestServerAddFloatingIPNetwork(
def test_server_add_floating_ip_no_external_gateway(self, success=False):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
_floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
self.network.find_ip = mock.Mock(return_value=_floating_ip)
return_value = [_port]
@@ -718,7 +717,7 @@ class TestServerAddFloatingIPNetwork(
def test_server_add_floating_ip_with_fixed_ip(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
_floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
self.network.find_ip = mock.Mock(return_value=_floating_ip)
self.network.ports = mock.Mock(return_value=[_port])
@@ -760,7 +759,7 @@ class TestServerAddFloatingIPNetwork(
def test_server_add_floating_ip_with_fixed_ip_no_port_found(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
_floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
self.network.find_ip = mock.Mock(return_value=_floating_ip)
self.network.ports = mock.Mock(return_value=[_port])
@@ -4486,6 +4485,14 @@ class TestServerList(_TestServerList):
'-c', 'Project ID',
'-c', 'User ID',
'-c', 'Created At',
+ '-c', 'Security Groups',
+ '-c', 'Task State',
+ '-c', 'Power State',
+ '-c', 'Image ID',
+ '-c', 'Flavor ID',
+ '-c', 'Availability Zone',
+ '-c', 'Host',
+ '-c', 'Properties',
'--long'
]
verifylist = [
@@ -4499,6 +4506,14 @@ class TestServerList(_TestServerList):
self.assertIn('Project ID', columns)
self.assertIn('User ID', columns)
self.assertIn('Created At', columns)
+ self.assertIn('Security Groups', columns)
+ self.assertIn('Task State', columns)
+ self.assertIn('Power State', columns)
+ self.assertIn('Image ID', columns)
+ self.assertIn('Flavor ID', columns)
+ self.assertIn('Availability Zone', columns)
+ self.assertIn('Host', columns)
+ self.assertIn('Properties', columns)
def test_server_list_no_name_lookup_option(self):
self.data = tuple(
@@ -4884,6 +4899,99 @@ class TestServerList(_TestServerList):
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
+ def test_server_list_long_with_host_status_v216(self):
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.16')
+
+ self.data1 = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ getattr(s, 'OS-EXT-STS:task_state'),
+ server.PowerStateColumn(
+ getattr(s, 'OS-EXT-STS:power_state')
+ ),
+ format_columns.DictListColumn(s.networks),
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
+ s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
+ self.flavor.name,
+ s.flavor['id'],
+ getattr(s, 'OS-EXT-AZ:availability_zone'),
+ getattr(s, 'OS-EXT-SRV-ATTR:host'),
+ s.Metadata,
+ ) for s in self.servers)
+
+ arglist = [
+ '--long'
+ ]
+ verifylist = [
+ ('long', True),
+ ]
+
+ # First test without host_status in the data -- the column should not
+ # be present in this case.
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns_long, columns)
+ self.assertEqual(tuple(self.data1), tuple(data))
+
+ # Next test with host_status in the data -- the column should be
+ # present in this case.
+ self.servers_mock.reset_mock()
+
+ self.attrs['host_status'] = 'UP'
+ servers = self.setup_servers_mock(3)
+ self.servers_mock.list.return_value = servers
+
+ # Make sure the returned image and flavor IDs match the servers.
+ Image = collections.namedtuple('Image', 'id name')
+ self.images_mock.return_value = [
+ Image(id=s.image['id'], name=self.image.name)
+ # Image will be an empty string if boot-from-volume
+ for s in servers if s.image
+ ]
+
+ Flavor = collections.namedtuple('Flavor', 'id name')
+ self.flavors_mock.list.return_value = [
+ Flavor(id=s.flavor['id'], name=self.flavor.name)
+ for s in servers
+ ]
+
+ # Add the expected host_status column and data.
+ columns_long = self.columns_long + ('Host Status',)
+ self.data2 = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ getattr(s, 'OS-EXT-STS:task_state'),
+ server.PowerStateColumn(
+ getattr(s, 'OS-EXT-STS:power_state')
+ ),
+ format_columns.DictListColumn(s.networks),
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
+ s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
+ self.flavor.name,
+ s.flavor['id'],
+ getattr(s, 'OS-EXT-AZ:availability_zone'),
+ getattr(s, 'OS-EXT-SRV-ATTR:host'),
+ s.Metadata,
+ s.host_status,
+ ) for s in servers)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(columns_long, columns)
+ self.assertEqual(tuple(self.data2), tuple(data))
+
class TestServerListV273(_TestServerList):
@@ -5559,782 +5667,6 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
-class TestListMigration(TestServer):
- """Test fetch all migrations."""
-
- MIGRATION_COLUMNS = [
- 'Source Node', 'Dest Node', 'Source Compute',
- 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
- 'Old Flavor', 'New Flavor', 'Created At', 'Updated At'
- ]
-
- # These are the fields that come back in the response from the REST API.
- MIGRATION_FIELDS = [
- 'source_node', 'dest_node', 'source_compute', 'dest_compute',
- 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
- 'new_instance_type_id', 'created_at', 'updated_at'
- ]
-
- def setUp(self):
- super(TestListMigration, self).setUp()
-
- self.server = compute_fakes.FakeServer.create_one_server()
- self.servers_mock.get.return_value = self.server
-
- self.migrations = compute_fakes.FakeMigration.create_migrations(
- count=3)
- self.migrations_mock.list.return_value = self.migrations
-
- self.data = (common_utils.get_item_properties(
- s, self.MIGRATION_FIELDS) for s in self.migrations)
-
- # Get the command object to test
- self.cmd = server.ListMigration(self.app, None)
-
- def test_server_migration_list_no_options(self):
- arglist = []
- verifylist = []
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': None,
- 'host': None,
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- def test_server_migration_list(self):
- arglist = [
- '--server', 'server1',
- '--host', 'host1',
- '--status', 'migrating',
- '--type', 'cold-migration',
- ]
- verifylist = [
- ('server', 'server1'),
- ('host', 'host1'),
- ('status', 'migrating'),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'host': 'host1',
- 'instance_uuid': self.server.id,
- 'migration_type': 'migration',
- }
-
- self.servers_mock.get.assert_called_with('server1')
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
-
-class TestListMigrationV223(TestListMigration):
- """Test fetch all migrations. """
-
- MIGRATION_COLUMNS = [
- 'Id', 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
- 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor',
- 'Type', 'Created At', 'Updated At'
- ]
-
- # These are the fields that come back in the response from the REST API.
- MIGRATION_FIELDS = [
- 'id', 'source_node', 'dest_node', 'source_compute', 'dest_compute',
- 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
- 'new_instance_type_id', 'migration_type', 'created_at', 'updated_at'
- ]
-
- def setUp(self):
- super(TestListMigrationV223, self).setUp()
-
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.23')
-
- def test_server_migration_list(self):
- arglist = [
- '--status', 'migrating'
- ]
- verifylist = [
- ('status', 'migrating')
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'host': None,
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
-
-class TestListMigrationV259(TestListMigration):
- """Test fetch all migrations. """
-
- MIGRATION_COLUMNS = [
- 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
- 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
- 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
- ]
-
- # These are the fields that come back in the response from the REST API.
- MIGRATION_FIELDS = [
- 'id', 'uuid', 'source_node', 'dest_node', 'source_compute',
- 'dest_compute', 'dest_host', 'status', 'instance_uuid',
- 'old_instance_type_id', 'new_instance_type_id', 'migration_type',
- 'created_at', 'updated_at'
- ]
-
- def setUp(self):
- super(TestListMigrationV259, self).setUp()
-
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.59')
-
- def test_server_migration_list(self):
- arglist = [
- '--status', 'migrating',
- '--limit', '1',
- '--marker', 'test_kp',
- '--changes-since', '2019-08-09T08:03:25Z'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('limit', 1),
- ('marker', 'test_kp'),
- ('changes_since', '2019-08-09T08:03:25Z')
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'limit': 1,
- 'marker': 'test_kp',
- 'host': None,
- 'changes_since': '2019-08-09T08:03:25Z',
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- def test_server_migration_list_with_limit_pre_v259(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.58')
- arglist = [
- '--status', 'migrating',
- '--limit', '1'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('limit', 1)
- ]
- 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.59 or greater is required',
- str(ex))
-
- def test_server_migration_list_with_marker_pre_v259(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.58')
- arglist = [
- '--status', 'migrating',
- '--marker', 'test_kp'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('marker', 'test_kp')
- ]
- 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.59 or greater is required',
- str(ex))
-
- def test_server_migration_list_with_changes_since_pre_v259(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.58')
- arglist = [
- '--status', 'migrating',
- '--changes-since', '2019-08-09T08:03:25Z'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('changes_since', '2019-08-09T08:03:25Z')
- ]
- 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.59 or greater is required',
- str(ex))
-
-
-class TestListMigrationV266(TestListMigration):
- """Test fetch all migrations by changes-before. """
-
- MIGRATION_COLUMNS = [
- 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
- 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
- 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
- ]
-
- # These are the fields that come back in the response from the REST API.
- MIGRATION_FIELDS = [
- 'id', 'uuid', 'source_node', 'dest_node', 'source_compute',
- 'dest_compute', 'dest_host', 'status', 'instance_uuid',
- 'old_instance_type_id', 'new_instance_type_id', 'migration_type',
- 'created_at', 'updated_at'
- ]
-
- def setUp(self):
- super(TestListMigrationV266, self).setUp()
-
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.66')
-
- def test_server_migration_list_with_changes_before(self):
- arglist = [
- '--status', 'migrating',
- '--limit', '1',
- '--marker', 'test_kp',
- '--changes-since', '2019-08-07T08:03:25Z',
- '--changes-before', '2019-08-09T08:03:25Z'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('limit', 1),
- ('marker', 'test_kp'),
- ('changes_since', '2019-08-07T08:03:25Z'),
- ('changes_before', '2019-08-09T08:03:25Z')
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'limit': 1,
- 'marker': 'test_kp',
- 'host': None,
- 'changes_since': '2019-08-07T08:03:25Z',
- 'changes_before': '2019-08-09T08:03:25Z',
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
-
- def test_server_migration_list_with_changes_before_pre_v266(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.65')
- arglist = [
- '--status', 'migrating',
- '--changes-before', '2019-08-09T08:03:25Z'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('changes_before', '2019-08-09T08:03:25Z')
- ]
- 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.66 or greater is required',
- str(ex))
-
-
-class TestListMigrationV280(TestListMigration):
- """Test fetch all migrations by user-id and/or project-id. """
-
- MIGRATION_COLUMNS = [
- 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
- 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
- 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
- ]
-
- # These are the fields that come back in the response from the REST API.
- MIGRATION_FIELDS = [
- 'id', 'uuid', 'source_node', 'dest_node', 'source_compute',
- 'dest_compute', 'dest_host', 'status', 'instance_uuid',
- 'old_instance_type_id', 'new_instance_type_id', 'migration_type',
- 'created_at', 'updated_at'
- ]
-
- project = identity_fakes.FakeProject.create_one_project()
- user = identity_fakes.FakeUser.create_one_user()
-
- def setUp(self):
- super(TestListMigrationV280, self).setUp()
-
- self.projects_mock = self.app.client_manager.identity.projects
- self.projects_mock.reset_mock()
-
- self.users_mock = self.app.client_manager.identity.users
- self.users_mock.reset_mock()
-
- self.projects_mock.get.return_value = self.project
- self.users_mock.get.return_value = self.user
-
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.80')
-
- def test_server_migration_list_with_project(self):
- arglist = [
- '--status', 'migrating',
- '--limit', '1',
- '--marker', 'test_kp',
- '--changes-since', '2019-08-07T08:03:25Z',
- '--changes-before', '2019-08-09T08:03:25Z',
- '--project', self.project.id
- ]
- verifylist = [
- ('status', 'migrating'),
- ('limit', 1),
- ('marker', 'test_kp'),
- ('changes_since', '2019-08-07T08:03:25Z'),
- ('changes_before', '2019-08-09T08:03:25Z'),
- ('project', self.project.id)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'limit': 1,
- 'marker': 'test_kp',
- 'host': None,
- 'project_id': self.project.id,
- 'changes_since': '2019-08-07T08:03:25Z',
- 'changes_before': "2019-08-09T08:03:25Z",
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.MIGRATION_COLUMNS.insert(
- len(self.MIGRATION_COLUMNS) - 2, "Project")
- self.MIGRATION_FIELDS.insert(
- len(self.MIGRATION_FIELDS) - 2, "project_id")
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
- # Clean up global variables MIGRATION_COLUMNS
- self.MIGRATION_COLUMNS.remove('Project')
- # Clean up global variables MIGRATION_FIELDS
- self.MIGRATION_FIELDS.remove('project_id')
-
- def test_get_migrations_with_project_pre_v280(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.79')
- arglist = [
- '--status', 'migrating',
- '--changes-before', '2019-08-09T08:03:25Z',
- '--project', '0c2accde-644a-45fa-8c10-e76debc7fbc3'
- ]
- verifylist = [
- ('status', 'migrating'),
- ('changes_before', '2019-08-09T08:03:25Z'),
- ('project', '0c2accde-644a-45fa-8c10-e76debc7fbc3')
- ]
- 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.80 or greater is required',
- str(ex))
-
- def test_server_migration_list_with_user(self):
- arglist = [
- '--status', 'migrating',
- '--limit', '1',
- '--marker', 'test_kp',
- '--changes-since', '2019-08-07T08:03:25Z',
- '--changes-before', '2019-08-09T08:03:25Z',
- '--user', self.user.id,
- ]
- verifylist = [
- ('status', 'migrating'),
- ('limit', 1),
- ('marker', 'test_kp'),
- ('changes_since', '2019-08-07T08:03:25Z'),
- ('changes_before', '2019-08-09T08:03:25Z'),
- ('user', self.user.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'limit': 1,
- 'marker': 'test_kp',
- 'host': None,
- 'user_id': self.user.id,
- 'changes_since': '2019-08-07T08:03:25Z',
- 'changes_before': "2019-08-09T08:03:25Z",
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.MIGRATION_COLUMNS.insert(
- len(self.MIGRATION_COLUMNS) - 2, "User")
- self.MIGRATION_FIELDS.insert(
- len(self.MIGRATION_FIELDS) - 2, "user_id")
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
- # Clean up global variables MIGRATION_COLUMNS
- self.MIGRATION_COLUMNS.remove('User')
- # Clean up global variables MIGRATION_FIELDS
- self.MIGRATION_FIELDS.remove('user_id')
-
- def test_get_migrations_with_user_pre_v280(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.79')
- arglist = [
- '--status', 'migrating',
- '--changes-before', '2019-08-09T08:03:25Z',
- '--user', self.user.id,
- ]
- verifylist = [
- ('status', 'migrating'),
- ('changes_before', '2019-08-09T08:03:25Z'),
- ('user', self.user.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.80 or greater is required',
- str(ex))
-
- def test_server_migration_list_with_project_and_user(self):
- arglist = [
- '--status', 'migrating',
- '--limit', '1',
- '--changes-since', '2019-08-07T08:03:25Z',
- '--changes-before', '2019-08-09T08:03:25Z',
- '--project', self.project.id,
- '--user', self.user.id,
- ]
- verifylist = [
- ('status', 'migrating'),
- ('limit', 1),
- ('changes_since', '2019-08-07T08:03:25Z'),
- ('changes_before', '2019-08-09T08:03:25Z'),
- ('project', self.project.id),
- ('user', self.user.id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'status': 'migrating',
- 'limit': 1,
- 'host': None,
- 'project_id': self.project.id,
- 'user_id': self.user.id,
- 'changes_since': '2019-08-07T08:03:25Z',
- 'changes_before': "2019-08-09T08:03:25Z",
- }
-
- self.migrations_mock.list.assert_called_with(**kwargs)
-
- self.MIGRATION_COLUMNS.insert(
- len(self.MIGRATION_COLUMNS) - 2, "Project")
- self.MIGRATION_FIELDS.insert(
- len(self.MIGRATION_FIELDS) - 2, "project_id")
- self.MIGRATION_COLUMNS.insert(
- len(self.MIGRATION_COLUMNS) - 2, "User")
- self.MIGRATION_FIELDS.insert(
- len(self.MIGRATION_FIELDS) - 2, "user_id")
- self.assertEqual(self.MIGRATION_COLUMNS, columns)
- self.assertEqual(tuple(self.data), tuple(data))
- # Clean up global variables MIGRATION_COLUMNS
- self.MIGRATION_COLUMNS.remove('Project')
- self.MIGRATION_FIELDS.remove('project_id')
- self.MIGRATION_COLUMNS.remove('User')
- self.MIGRATION_FIELDS.remove('user_id')
-
- def test_get_migrations_with_project_and_user_pre_v280(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.79')
- arglist = [
- '--status', 'migrating',
- '--changes-before', '2019-08-09T08:03:25Z',
- '--project', self.project.id,
- '--user', self.user.id,
- ]
- verifylist = [
- ('status', 'migrating'),
- ('changes_before', '2019-08-09T08:03:25Z'),
- ('project', self.project.id),
- ('user', self.user.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.80 or greater is required',
- str(ex))
-
-
-class TestServerMigrationShow(TestServer):
-
- def setUp(self):
- super().setUp()
-
- self.server = compute_fakes.FakeServer.create_one_server()
- self.servers_mock.get.return_value = self.server
-
- self.server_migration = compute_fakes.FakeServerMigration\
- .create_one_server_migration()
- self.server_migrations_mock.get.return_value = self.server_migration
-
- self.columns = (
- 'ID',
- 'Server UUID',
- 'Status',
- 'Source Compute',
- 'Source Node',
- 'Dest Compute',
- 'Dest Host',
- 'Dest Node',
- 'Memory Total Bytes',
- 'Memory Processed Bytes',
- 'Memory Remaining Bytes',
- 'Disk Total Bytes',
- 'Disk Processed Bytes',
- 'Disk Remaining Bytes',
- 'Created At',
- 'Updated At',
- )
-
- self.data = (
- self.server_migration.id,
- self.server_migration.server_uuid,
- self.server_migration.status,
- self.server_migration.source_compute,
- self.server_migration.source_node,
- self.server_migration.dest_compute,
- self.server_migration.dest_host,
- self.server_migration.dest_node,
- self.server_migration.memory_total_bytes,
- self.server_migration.memory_processed_bytes,
- self.server_migration.memory_remaining_bytes,
- self.server_migration.disk_total_bytes,
- self.server_migration.disk_processed_bytes,
- self.server_migration.disk_remaining_bytes,
- self.server_migration.created_at,
- self.server_migration.updated_at,
- )
-
- # Get the command object to test
- self.cmd = server.ShowMigration(self.app, None)
-
- def _test_server_migration_show(self):
- arglist = [
- self.server.id,
- '2', # arbitrary migration ID
- ]
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- columns, data = self.cmd.take_action(parsed_args)
-
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
-
- self.servers_mock.get.assert_called_with(self.server.id)
- self.server_migrations_mock.get.assert_called_with(
- self.server.id, '2',)
-
- def test_server_migration_show(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.24')
-
- self._test_server_migration_show()
-
- def test_server_migration_show_v259(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.59')
-
- self.columns += ('UUID',)
- self.data += (self.server_migration.uuid,)
-
- self._test_server_migration_show()
-
- def test_server_migration_show_v280(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.80')
-
- self.columns += ('UUID', 'User ID', 'Project ID')
- self.data += (
- self.server_migration.uuid,
- self.server_migration.user_id,
- self.server_migration.project_id,
- )
-
- self._test_server_migration_show()
-
- def test_server_migration_show_pre_v224(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.23')
-
- arglist = [
- self.server.id,
- '2', # arbitrary migration ID
- ]
- verifylist = []
- 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.24 or greater is required',
- str(ex))
-
-
-class TestServerMigrationAbort(TestServer):
-
- def setUp(self):
- super(TestServerMigrationAbort, self).setUp()
-
- self.server = compute_fakes.FakeServer.create_one_server()
-
- # Return value for utils.find_resource for server.
- self.servers_mock.get.return_value = self.server
-
- # Get the command object to test
- self.cmd = server.AbortMigration(self.app, None)
-
- def test_migration_abort(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.24')
-
- arglist = [
- self.server.id,
- '2', # arbitrary migration ID
- ]
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.servers_mock.get.assert_called_with(self.server.id)
- self.server_migrations_mock.live_migration_abort.assert_called_with(
- self.server.id, '2',)
- self.assertIsNone(result)
-
- def test_migration_abort_pre_v224(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.23')
-
- arglist = [
- self.server.id,
- '2', # arbitrary migration ID
- ]
- verifylist = []
- 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.24 or greater is required',
- str(ex))
-
-
-class TestServerMigrationForceComplete(TestServer):
-
- def setUp(self):
- super(TestServerMigrationForceComplete, self).setUp()
-
- self.server = compute_fakes.FakeServer.create_one_server()
-
- # Return value for utils.find_resource for server.
- self.servers_mock.get.return_value = self.server
-
- # Get the command object to test
- self.cmd = server.ForceCompleteMigration(self.app, None)
-
- def test_migration_force_complete(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.22')
-
- arglist = [
- self.server.id,
- '2', # arbitrary migration ID
- ]
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.take_action(parsed_args)
-
- self.servers_mock.get.assert_called_with(self.server.id)
- self.server_migrations_mock.live_migrate_force_complete\
- .assert_called_with(self.server.id, '2',)
- self.assertIsNone(result)
-
- def test_migration_force_complete_pre_v222(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.21')
-
- arglist = [
- self.server.id,
- '2', # arbitrary migration ID
- ]
- verifylist = []
- 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.22 or greater is required',
- str(ex))
-
-
class TestServerPause(TestServer):
def setUp(self):
diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py
index 3ed19e2..655366a 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_group.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_group.py
@@ -15,10 +15,9 @@
from unittest import mock
-from novaclient import api_versions
+from openstack import utils as sdk_utils
from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.compute.v2 import server_group
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -27,38 +26,7 @@ from openstackclient.tests.unit import utils as tests_utils
class TestServerGroup(compute_fakes.TestComputev2):
- fake_server_group = compute_fakes.FakeServerGroup.create_one_server_group()
-
- columns = (
- 'id',
- 'members',
- 'name',
- 'policies',
- 'project_id',
- 'user_id',
- )
-
- data = (
- fake_server_group.id,
- format_columns.ListColumn(fake_server_group.members),
- fake_server_group.name,
- format_columns.ListColumn(fake_server_group.policies),
- fake_server_group.project_id,
- fake_server_group.user_id,
- )
-
- def setUp(self):
- super(TestServerGroup, self).setUp()
-
- # Get a shortcut to the ServerGroupsManager Mock
- self.server_groups_mock = self.app.client_manager.compute.server_groups
- self.server_groups_mock.reset_mock()
-
-
-class TestServerGroupV264(TestServerGroup):
-
- fake_server_group = \
- compute_fakes.FakeServerGroupV264.create_one_server_group()
+ fake_server_group = compute_fakes.create_one_server_group()
columns = (
'id',
@@ -66,31 +34,40 @@ class TestServerGroupV264(TestServerGroup):
'name',
'policy',
'project_id',
+ 'rules',
'user_id',
)
data = (
fake_server_group.id,
- format_columns.ListColumn(fake_server_group.members),
+ format_columns.ListColumn(fake_server_group.member_ids),
fake_server_group.name,
fake_server_group.policy,
fake_server_group.project_id,
+ format_columns.DictColumn(fake_server_group.rules),
fake_server_group.user_id,
)
def setUp(self):
- super(TestServerGroupV264, self).setUp()
+ super().setUp()
+
+ # Create and get a shortcut to the compute client mock
+ self.app.client_manager.sdk_connection = mock.Mock()
+ self.sdk_client = self.app.client_manager.sdk_connection.compute
+ self.sdk_client.reset_mock()
class TestServerGroupCreate(TestServerGroup):
def setUp(self):
- super(TestServerGroupCreate, self).setUp()
+ super().setUp()
- self.server_groups_mock.create.return_value = self.fake_server_group
+ self.sdk_client.create_server_group.return_value = \
+ self.fake_server_group
self.cmd = server_group.CreateServerGroup(self.app, None)
- def test_server_group_create(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_create(self, sm_mock):
arglist = [
'--policy', 'anti-affinity',
'affinity_group',
@@ -101,18 +78,16 @@ class TestServerGroupCreate(TestServerGroup):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.create.assert_called_once_with(
+ self.sdk_client.create_server_group.assert_called_once_with(
name=parsed_args.name,
- policies=[parsed_args.policy],
+ policy=parsed_args.policy,
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
- def test_server_group_create_with_soft_policies(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.15')
-
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_create_with_soft_policies(self, sm_mock):
arglist = [
'--policy', 'soft-anti-affinity',
'affinity_group',
@@ -123,18 +98,16 @@ class TestServerGroupCreate(TestServerGroup):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.create.assert_called_once_with(
+ self.sdk_client.create_server_group.assert_called_once_with(
name=parsed_args.name,
- policies=[parsed_args.policy],
+ policy=parsed_args.policy,
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
- def test_server_group_create_with_soft_policies_pre_v215(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.14')
-
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False)
+ def test_server_group_create_with_soft_policies_pre_v215(self, sm_mock):
arglist = [
'--policy', 'soft-anti-affinity',
'affinity_group',
@@ -152,10 +125,8 @@ class TestServerGroupCreate(TestServerGroup):
'--os-compute-api-version 2.15 or greater is required',
str(ex))
- def test_server_group_create_with_rules(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.64')
-
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_create_with_rules(self, sm_mock):
arglist = [
'--policy', 'soft-anti-affinity',
'--rule', 'max_server_per_host=2',
@@ -168,19 +139,18 @@ class TestServerGroupCreate(TestServerGroup):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.create.assert_called_once_with(
+ self.sdk_client.create_server_group.assert_called_once_with(
name=parsed_args.name,
- policy=parsed_args.policy, # should be 'policy', not 'policies'
+ policy=parsed_args.policy,
rules=parsed_args.rules,
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
- def test_server_group_create_with_rules_pre_v264(self):
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.63')
-
+ @mock.patch.object(
+ sdk_utils, 'supports_microversion', side_effect=[True, False])
+ def test_server_group_create_with_rules_pre_v264(self, sm_mock):
arglist = [
'--policy', 'soft-anti-affinity',
'--rule', 'max_server_per_host=2',
@@ -205,9 +175,9 @@ class TestServerGroupCreate(TestServerGroup):
class TestServerGroupDelete(TestServerGroup):
def setUp(self):
- super(TestServerGroupDelete, self).setUp()
+ super().setUp()
- self.server_groups_mock.get.return_value = self.fake_server_group
+ self.sdk_client.find_server_group.return_value = self.fake_server_group
self.cmd = server_group.DeleteServerGroup(self.app, None)
def test_server_group_delete(self):
@@ -219,8 +189,10 @@ class TestServerGroupDelete(TestServerGroup):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.server_groups_mock.get.assert_called_once_with('affinity_group')
- self.server_groups_mock.delete.assert_called_once_with(
+ self.sdk_client.find_server_group.assert_called_once_with(
+ 'affinity_group'
+ )
+ self.sdk_client.delete_server_group.assert_called_once_with(
self.fake_server_group.id
)
self.assertIsNone(result)
@@ -235,13 +207,15 @@ class TestServerGroupDelete(TestServerGroup):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.server_groups_mock.get.assert_any_call('affinity_group')
- self.server_groups_mock.get.assert_any_call('anti_affinity_group')
- self.server_groups_mock.delete.assert_called_with(
+ self.sdk_client.find_server_group.assert_any_call('affinity_group')
+ self.sdk_client.find_server_group.assert_any_call(
+ 'anti_affinity_group'
+ )
+ self.sdk_client.delete_server_group.assert_called_with(
self.fake_server_group.id
)
- self.assertEqual(2, self.server_groups_mock.get.call_count)
- self.assertEqual(2, self.server_groups_mock.delete.call_count)
+ self.assertEqual(2, self.sdk_client.find_server_group.call_count)
+ self.assertEqual(2, self.sdk_client.delete_server_group.call_count)
self.assertIsNone(result)
def test_server_group_delete_no_input(self):
@@ -262,25 +236,23 @@ class TestServerGroupDelete(TestServerGroup):
('server_group', ['affinity_group', 'anti_affinity_group']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- find_mock_result = [self.fake_server_group, 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 server groups failed to delete.',
- str(e))
-
- find_mock.assert_any_call(self.server_groups_mock,
- 'affinity_group')
- find_mock.assert_any_call(self.server_groups_mock,
- 'anti_affinity_group')
-
- self.assertEqual(2, find_mock.call_count)
- self.server_groups_mock.delete.assert_called_once_with(
- self.fake_server_group.id
- )
+
+ self.sdk_client.find_server_group.side_effect = [
+ self.fake_server_group, exceptions.CommandError]
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 server groups failed to delete.', str(e))
+
+ self.sdk_client.find_server_group.assert_any_call('affinity_group')
+ self.sdk_client.find_server_group.assert_any_call(
+ 'anti_affinity_group'
+ )
+ self.assertEqual(2, self.sdk_client.find_server_group.call_count)
+ self.sdk_client.delete_server_group.assert_called_once_with(
+ self.fake_server_group.id
+ )
class TestServerGroupList(TestServerGroup):
@@ -300,28 +272,67 @@ class TestServerGroupList(TestServerGroup):
'User Id',
)
+ list_columns_v264 = (
+ 'ID',
+ 'Name',
+ 'Policy',
+ )
+
+ list_columns_v264_long = (
+ 'ID',
+ 'Name',
+ 'Policy',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+
list_data = ((
TestServerGroup.fake_server_group.id,
TestServerGroup.fake_server_group.name,
- format_columns.ListColumn(TestServerGroup.fake_server_group.policies),
+ format_columns.ListColumn(
+ TestServerGroup.fake_server_group.policies
+ ),
),)
list_data_long = ((
TestServerGroup.fake_server_group.id,
TestServerGroup.fake_server_group.name,
- format_columns.ListColumn(TestServerGroup.fake_server_group.policies),
- format_columns.ListColumn(TestServerGroup.fake_server_group.members),
+ format_columns.ListColumn(
+ TestServerGroup.fake_server_group.policies
+ ),
+ format_columns.ListColumn(
+ TestServerGroup.fake_server_group.member_ids
+ ),
+ TestServerGroup.fake_server_group.project_id,
+ TestServerGroup.fake_server_group.user_id,
+ ),)
+
+ list_data_v264 = ((
+ TestServerGroup.fake_server_group.id,
+ TestServerGroup.fake_server_group.name,
+ TestServerGroup.fake_server_group.policy,
+ ),)
+
+ list_data_v264_long = ((
+ TestServerGroup.fake_server_group.id,
+ TestServerGroup.fake_server_group.name,
+ TestServerGroup.fake_server_group.policy,
+ format_columns.ListColumn(
+ TestServerGroup.fake_server_group.member_ids
+ ),
TestServerGroup.fake_server_group.project_id,
TestServerGroup.fake_server_group.user_id,
),)
def setUp(self):
- super(TestServerGroupList, self).setUp()
+ super().setUp()
- self.server_groups_mock.list.return_value = [self.fake_server_group]
+ self.sdk_client.server_groups.return_value = [self.fake_server_group]
self.cmd = server_group.ListServerGroup(self.app, None)
- def test_server_group_list(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False)
+ def test_server_group_list(self, sm_mock):
arglist = []
verifylist = [
('all_projects', False),
@@ -332,12 +343,13 @@ class TestServerGroupList(TestServerGroup):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with()
+ self.sdk_client.server_groups.assert_called_once_with()
self.assertCountEqual(self.list_columns, columns)
self.assertCountEqual(self.list_data, tuple(data))
- def test_server_group_list_with_all_projects_and_long(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False)
+ def test_server_group_list_with_all_projects_and_long(self, sm_mock):
arglist = [
'--all-projects',
'--long',
@@ -350,13 +362,14 @@ class TestServerGroupList(TestServerGroup):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(
+ self.sdk_client.server_groups.assert_called_once_with(
all_projects=True)
self.assertCountEqual(self.list_columns_long, columns)
self.assertCountEqual(self.list_data_long, tuple(data))
- def test_server_group_list_with_limit(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_list_with_limit(self, sm_mock):
arglist = [
'--limit', '1',
]
@@ -370,9 +383,10 @@ class TestServerGroupList(TestServerGroup):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(limit=1)
+ self.sdk_client.server_groups.assert_called_once_with(limit=1)
- def test_server_group_list_with_offset(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_list_with_offset(self, sm_mock):
arglist = [
'--offset', '5',
]
@@ -386,51 +400,10 @@ class TestServerGroupList(TestServerGroup):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(offset=5)
-
-
-class TestServerGroupListV264(TestServerGroupV264):
-
- list_columns = (
- 'ID',
- 'Name',
- 'Policy',
- )
+ self.sdk_client.server_groups.assert_called_once_with(offset=5)
- list_columns_long = (
- 'ID',
- 'Name',
- 'Policy',
- 'Members',
- 'Project Id',
- 'User Id',
- )
-
- list_data = ((
- TestServerGroupV264.fake_server_group.id,
- TestServerGroupV264.fake_server_group.name,
- TestServerGroupV264.fake_server_group.policy,
- ),)
-
- list_data_long = ((
- TestServerGroupV264.fake_server_group.id,
- TestServerGroupV264.fake_server_group.name,
- TestServerGroupV264.fake_server_group.policy,
- format_columns.ListColumn(
- TestServerGroupV264.fake_server_group.members),
- TestServerGroupV264.fake_server_group.project_id,
- TestServerGroupV264.fake_server_group.user_id,
- ),)
-
- def setUp(self):
- super(TestServerGroupListV264, self).setUp()
-
- self.server_groups_mock.list.return_value = [self.fake_server_group]
- self.cmd = server_group.ListServerGroup(self.app, None)
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.64')
-
- def test_server_group_list(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_list_v264(self, sm_mock):
arglist = []
verifylist = [
('all_projects', False),
@@ -438,12 +411,13 @@ class TestServerGroupListV264(TestServerGroupV264):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with()
+ self.sdk_client.server_groups.assert_called_once_with()
- self.assertCountEqual(self.list_columns, columns)
- self.assertCountEqual(self.list_data, tuple(data))
+ self.assertCountEqual(self.list_columns_v264, columns)
+ self.assertCountEqual(self.list_data_v264, tuple(data))
- def test_server_group_list_with_all_projects_and_long(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_list_with_all_projects_and_long_v264(self, sm_mock):
arglist = [
'--all-projects',
'--long',
@@ -454,22 +428,23 @@ class TestServerGroupListV264(TestServerGroupV264):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(
+ self.sdk_client.server_groups.assert_called_once_with(
all_projects=True)
- self.assertCountEqual(self.list_columns_long, columns)
- self.assertCountEqual(self.list_data_long, tuple(data))
+ self.assertCountEqual(self.list_columns_v264_long, columns)
+ self.assertCountEqual(self.list_data_v264_long, tuple(data))
class TestServerGroupShow(TestServerGroup):
def setUp(self):
- super(TestServerGroupShow, self).setUp()
+ super().setUp()
- self.server_groups_mock.get.return_value = self.fake_server_group
+ self.sdk_client.find_server_group.return_value = self.fake_server_group
self.cmd = server_group.ShowServerGroup(self.app, None)
- def test_server_group_show(self):
+ @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True)
+ def test_server_group_show(self, sm_mock):
arglist = [
'affinity_group',
]
diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py
new file mode 100644
index 0000000..c4cbac4
--- /dev/null
+++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py
@@ -0,0 +1,1028 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from unittest import mock
+
+from novaclient import api_versions
+from osc_lib import exceptions
+from osc_lib import utils as common_utils
+
+from openstackclient.compute.v2 import server_migration
+from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
+
+
+class TestServerMigration(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super().setUp()
+
+ # Get a shortcut to the compute client ServerManager Mock
+ self.servers_mock = self.app.client_manager.compute.servers
+ self.servers_mock.reset_mock()
+
+ # Get a shortcut to the compute client ServerMigrationsManager Mock
+ self.server_migrations_mock = \
+ self.app.client_manager.compute.server_migrations
+ self.server_migrations_mock.reset_mock()
+
+ # Get a shortcut to the compute client MigrationManager mock
+ self.migrations_mock = self.app.client_manager.compute.migrations
+ self.migrations_mock.reset_mock()
+
+ self.app.client_manager.sdk_connection = mock.Mock()
+ self.app.client_manager.sdk_connection.compute = mock.Mock()
+ self.sdk_client = self.app.client_manager.sdk_connection.compute
+
+
+class TestListMigration(TestServerMigration):
+ """Test fetch all migrations."""
+
+ MIGRATION_COLUMNS = [
+ 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Created At', 'Updated At'
+ ]
+
+ # These are the fields that come back in the response from the REST API.
+ MIGRATION_FIELDS = [
+ 'source_node', 'dest_node', 'source_compute', 'dest_compute',
+ 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
+ 'new_instance_type_id', 'created_at', 'updated_at'
+ ]
+
+ def setUp(self):
+ super().setUp()
+
+ self.server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = self.server
+
+ self.migrations = compute_fakes.FakeMigration.create_migrations(
+ count=3)
+ self.migrations_mock.list.return_value = self.migrations
+
+ self.data = (common_utils.get_item_properties(
+ s, self.MIGRATION_FIELDS) for s in self.migrations)
+
+ # Get the command object to test
+ self.cmd = server_migration.ListMigration(self.app, None)
+
+ def test_server_migration_list_no_options(self):
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': None,
+ 'host': None,
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_migration_list(self):
+ arglist = [
+ '--server', 'server1',
+ '--host', 'host1',
+ '--status', 'migrating',
+ '--type', 'cold-migration',
+ ]
+ verifylist = [
+ ('server', 'server1'),
+ ('host', 'host1'),
+ ('status', 'migrating'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'host': 'host1',
+ 'instance_uuid': self.server.id,
+ 'migration_type': 'migration',
+ }
+
+ self.servers_mock.get.assert_called_with('server1')
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+
+class TestListMigrationV223(TestListMigration):
+ """Test fetch all migrations. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
+ 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor',
+ 'Type', 'Created At', 'Updated At'
+ ]
+
+ # These are the fields that come back in the response from the REST API.
+ MIGRATION_FIELDS = [
+ 'id', 'source_node', 'dest_node', 'source_compute', 'dest_compute',
+ 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
+ 'new_instance_type_id', 'migration_type', 'created_at', 'updated_at'
+ ]
+
+ def setUp(self):
+ super().setUp()
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.23')
+
+ def test_server_migration_list(self):
+ arglist = [
+ '--status', 'migrating'
+ ]
+ verifylist = [
+ ('status', 'migrating')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'host': None,
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+
+class TestListMigrationV259(TestListMigration):
+ """Test fetch all migrations. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
+ ]
+
+ # These are the fields that come back in the response from the REST API.
+ MIGRATION_FIELDS = [
+ 'id', 'uuid', 'source_node', 'dest_node', 'source_compute',
+ 'dest_compute', 'dest_host', 'status', 'instance_uuid',
+ 'old_instance_type_id', 'new_instance_type_id', 'migration_type',
+ 'created_at', 'updated_at'
+ ]
+
+ def setUp(self):
+ super().setUp()
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ def test_server_migration_list(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-09T08:03:25Z')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'changes_since': '2019-08-09T08:03:25Z',
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_migration_list_with_limit_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1)
+ ]
+ 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.59 or greater is required',
+ str(ex))
+
+ def test_server_migration_list_with_marker_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+ arglist = [
+ '--status', 'migrating',
+ '--marker', 'test_kp'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('marker', 'test_kp')
+ ]
+ 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.59 or greater is required',
+ str(ex))
+
+ def test_server_migration_list_with_changes_since_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-since', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_since', '2019-08-09T08:03:25Z')
+ ]
+ 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.59 or greater is required',
+ str(ex))
+
+
+class TestListMigrationV266(TestListMigration):
+ """Test fetch all migrations by changes-before. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
+ ]
+
+ # These are the fields that come back in the response from the REST API.
+ MIGRATION_FIELDS = [
+ 'id', 'uuid', 'source_node', 'dest_node', 'source_compute',
+ 'dest_compute', 'dest_host', 'status', 'instance_uuid',
+ 'old_instance_type_id', 'new_instance_type_id', 'migration_type',
+ 'created_at', 'updated_at'
+ ]
+
+ def setUp(self):
+ super().setUp()
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.66')
+
+ def test_server_migration_list_with_changes_before(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': '2019-08-09T08:03:25Z',
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_migration_list_with_changes_before_pre_v266(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.65')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z')
+ ]
+ 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.66 or greater is required',
+ str(ex))
+
+
+class TestListMigrationV280(TestListMigration):
+ """Test fetch all migrations by user-id and/or project-id. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
+ ]
+
+ # These are the fields that come back in the response from the REST API.
+ MIGRATION_FIELDS = [
+ 'id', 'uuid', 'source_node', 'dest_node', 'source_compute',
+ 'dest_compute', 'dest_host', 'status', 'instance_uuid',
+ 'old_instance_type_id', 'new_instance_type_id', 'migration_type',
+ 'created_at', 'updated_at'
+ ]
+
+ project = identity_fakes.FakeProject.create_one_project()
+ user = identity_fakes.FakeUser.create_one_user()
+
+ def setUp(self):
+ super().setUp()
+
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock.reset_mock()
+
+ self.users_mock = self.app.client_manager.identity.users
+ self.users_mock.reset_mock()
+
+ self.projects_mock.get.return_value = self.project
+ self.users_mock.get.return_value = self.user
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.80')
+
+ def test_server_migration_list_with_project(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', self.project.id
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project', self.project.id)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'project_id': self.project.id,
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': "2019-08-09T08:03:25Z",
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "Project")
+ self.MIGRATION_FIELDS.insert(
+ len(self.MIGRATION_FIELDS) - 2, "project_id")
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+ # Clean up global variables MIGRATION_COLUMNS
+ self.MIGRATION_COLUMNS.remove('Project')
+ # Clean up global variables MIGRATION_FIELDS
+ self.MIGRATION_FIELDS.remove('project_id')
+
+ def test_get_migrations_with_project_pre_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.79')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', '0c2accde-644a-45fa-8c10-e76debc7fbc3'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project', '0c2accde-644a-45fa-8c10-e76debc7fbc3')
+ ]
+ 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.80 or greater is required',
+ str(ex))
+
+ def test_server_migration_list_with_user(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--user', self.user.id,
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('user', self.user.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'user_id': self.user.id,
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': "2019-08-09T08:03:25Z",
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "User")
+ self.MIGRATION_FIELDS.insert(
+ len(self.MIGRATION_FIELDS) - 2, "user_id")
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+ # Clean up global variables MIGRATION_COLUMNS
+ self.MIGRATION_COLUMNS.remove('User')
+ # Clean up global variables MIGRATION_FIELDS
+ self.MIGRATION_FIELDS.remove('user_id')
+
+ def test_get_migrations_with_user_pre_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.79')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--user', self.user.id,
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('user', self.user.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.80 or greater is required',
+ str(ex))
+
+ def test_server_migration_list_with_project_and_user(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', self.project.id,
+ '--user', self.user.id,
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project', self.project.id),
+ ('user', self.user.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'host': None,
+ 'project_id': self.project.id,
+ 'user_id': self.user.id,
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': "2019-08-09T08:03:25Z",
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "Project")
+ self.MIGRATION_FIELDS.insert(
+ len(self.MIGRATION_FIELDS) - 2, "project_id")
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "User")
+ self.MIGRATION_FIELDS.insert(
+ len(self.MIGRATION_FIELDS) - 2, "user_id")
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+ # Clean up global variables MIGRATION_COLUMNS
+ self.MIGRATION_COLUMNS.remove('Project')
+ self.MIGRATION_FIELDS.remove('project_id')
+ self.MIGRATION_COLUMNS.remove('User')
+ self.MIGRATION_FIELDS.remove('user_id')
+
+ def test_get_migrations_with_project_and_user_pre_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.79')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', self.project.id,
+ '--user', self.user.id,
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project', self.project.id),
+ ('user', self.user.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.80 or greater is required',
+ str(ex))
+
+
+class TestServerMigrationShow(TestServerMigration):
+
+ def setUp(self):
+ super().setUp()
+
+ self.server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = self.server
+
+ self.server_migration = compute_fakes.FakeServerMigration\
+ .create_one_server_migration()
+ self.server_migrations_mock.get.return_value = self.server_migration
+
+ self.columns = (
+ 'ID',
+ 'Server UUID',
+ 'Status',
+ 'Source Compute',
+ 'Source Node',
+ 'Dest Compute',
+ 'Dest Host',
+ 'Dest Node',
+ 'Memory Total Bytes',
+ 'Memory Processed Bytes',
+ 'Memory Remaining Bytes',
+ 'Disk Total Bytes',
+ 'Disk Processed Bytes',
+ 'Disk Remaining Bytes',
+ 'Created At',
+ 'Updated At',
+ )
+
+ self.data = (
+ self.server_migration.id,
+ self.server_migration.server_uuid,
+ self.server_migration.status,
+ self.server_migration.source_compute,
+ self.server_migration.source_node,
+ self.server_migration.dest_compute,
+ self.server_migration.dest_host,
+ self.server_migration.dest_node,
+ self.server_migration.memory_total_bytes,
+ self.server_migration.memory_processed_bytes,
+ self.server_migration.memory_remaining_bytes,
+ self.server_migration.disk_total_bytes,
+ self.server_migration.disk_processed_bytes,
+ self.server_migration.disk_remaining_bytes,
+ self.server_migration.created_at,
+ self.server_migration.updated_at,
+ )
+
+ # Get the command object to test
+ self.cmd = server_migration.ShowMigration(self.app, None)
+
+ def _test_server_migration_show(self):
+ arglist = [
+ self.server.id,
+ '2', # arbitrary migration ID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server_migrations_mock.get.assert_called_with(
+ self.server.id, '2',)
+
+ def test_server_migration_show(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.24')
+
+ self._test_server_migration_show()
+
+ def test_server_migration_show_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ self.columns += ('UUID',)
+ self.data += (self.server_migration.uuid,)
+
+ self._test_server_migration_show()
+
+ def test_server_migration_show_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.80')
+
+ self.columns += ('UUID', 'User ID', 'Project ID')
+ self.data += (
+ self.server_migration.uuid,
+ self.server_migration.user_id,
+ self.server_migration.project_id,
+ )
+
+ self._test_server_migration_show()
+
+ def test_server_migration_show_pre_v224(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.23')
+
+ arglist = [
+ self.server.id,
+ '2', # arbitrary migration ID
+ ]
+ verifylist = []
+ 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.24 or greater is required',
+ str(ex))
+
+ def test_server_migration_show_by_uuid(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+ self.server_migrations_mock.list.return_value = [self.server_migration]
+
+ self.columns += ('UUID',)
+ self.data += (self.server_migration.uuid,)
+
+ arglist = [
+ self.server.id,
+ self.server_migration.uuid, # arbitrary migration UUID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server_migrations_mock.list.assert_called_with(self.server.id)
+ self.server_migrations_mock.get.assert_not_called()
+
+ def test_server_migration_show_by_uuid_no_matches(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+ self.server_migrations_mock.list.return_value = []
+
+ arglist = [
+ self.server.id,
+ '69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ 'In-progress live migration 69f95745-bfe3-4302-90f7-5b0022cba1ce',
+ str(ex))
+
+ def test_server_migration_show_by_uuid_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+
+ arglist = [
+ self.server.id,
+ '69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
+ ]
+ verifylist = []
+ 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.59 or greater is required',
+ str(ex))
+
+ def test_server_migration_show_invalid_id(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.24')
+
+ arglist = [
+ self.server.id,
+ 'foo', # invalid migration ID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ 'The <migration> argument must be an ID or UUID',
+ str(ex))
+
+
+class TestServerMigrationAbort(TestServerMigration):
+
+ def setUp(self):
+ super().setUp()
+
+ self.server = compute_fakes.FakeServer.create_one_server()
+
+ # Return value for utils.find_resource for server.
+ self.servers_mock.get.return_value = self.server
+
+ # Get the command object to test
+ self.cmd = server_migration.AbortMigration(self.app, None)
+
+ def test_migration_abort(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.24')
+
+ arglist = [
+ self.server.id,
+ '2', # arbitrary migration ID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server_migrations_mock.live_migration_abort.assert_called_with(
+ self.server.id, '2',)
+ self.assertIsNone(result)
+
+ def test_migration_abort_pre_v224(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.23')
+
+ arglist = [
+ self.server.id,
+ '2', # arbitrary migration ID
+ ]
+ verifylist = []
+ 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.24 or greater is required',
+ str(ex))
+
+ def test_server_migration_abort_by_uuid(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ self.server_migration = compute_fakes.FakeServerMigration\
+ .create_one_server_migration()
+ self.server_migrations_mock.list.return_value = [self.server_migration]
+
+ arglist = [
+ self.server.id,
+ self.server_migration.uuid, # arbitrary migration UUID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server_migrations_mock.list.assert_called_with(self.server.id)
+ self.server_migrations_mock.live_migration_abort.assert_called_with(
+ self.server.id, self.server_migration.id)
+ self.assertIsNone(result)
+
+ def test_server_migration_abort_by_uuid_no_matches(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ self.server_migrations_mock.list.return_value = []
+
+ arglist = [
+ self.server.id,
+ '69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ 'In-progress live migration 69f95745-bfe3-4302-90f7-5b0022cba1ce',
+ str(ex))
+
+ def test_server_migration_abort_by_uuid_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+
+ arglist = [
+ self.server.id,
+ '69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
+ ]
+ verifylist = []
+ 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.59 or greater is required',
+ str(ex))
+
+
+class TestServerMigrationForceComplete(TestServerMigration):
+
+ def setUp(self):
+ super().setUp()
+
+ self.server = compute_fakes.FakeServer.create_one_server()
+
+ # Return value for utils.find_resource for server.
+ self.servers_mock.get.return_value = self.server
+
+ # Get the command object to test
+ self.cmd = server_migration.ForceCompleteMigration(self.app, None)
+
+ def test_migration_force_complete(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.22')
+
+ arglist = [
+ self.server.id,
+ '2', # arbitrary migration ID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server_migrations_mock.live_migrate_force_complete\
+ .assert_called_with(self.server.id, '2',)
+ self.assertIsNone(result)
+
+ def test_migration_force_complete_pre_v222(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.21')
+
+ arglist = [
+ self.server.id,
+ '2', # arbitrary migration ID
+ ]
+ verifylist = []
+ 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.22 or greater is required',
+ str(ex))
+
+ def test_server_migration_force_complete_by_uuid(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ self.server_migration = compute_fakes.FakeServerMigration\
+ .create_one_server_migration()
+ self.server_migrations_mock.list.return_value = [self.server_migration]
+
+ arglist = [
+ self.server.id,
+ self.server_migration.uuid, # arbitrary migration UUID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server_migrations_mock.list.assert_called_with(self.server.id)
+ self.server_migrations_mock.live_migrate_force_complete\
+ .assert_called_with(self.server.id, self.server_migration.id)
+ self.assertIsNone(result)
+
+ def test_server_migration_force_complete_by_uuid_no_matches(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ self.server_migrations_mock.list.return_value = []
+
+ arglist = [
+ self.server.id,
+ '69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
+ ]
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ 'In-progress live migration 69f95745-bfe3-4302-90f7-5b0022cba1ce',
+ str(ex))
+
+ def test_server_migration_force_complete_by_uuid_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+
+ arglist = [
+ self.server.id,
+ '69f95745-bfe3-4302-90f7-5b0022cba1ce', # arbitrary migration UUID
+ ]
+ verifylist = []
+ 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.59 or greater is required',
+ str(ex))
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index 7ccc9f0..02f7b41 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -18,6 +18,7 @@ import os
import tempfile
from unittest import mock
+from cinderclient import api_versions
from openstack import exceptions as sdk_exceptions
from osc_lib.cli import format_columns
from osc_lib import exceptions
@@ -25,12 +26,13 @@ from osc_lib import exceptions
from openstackclient.image.v2 import image
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from openstackclient.tests.unit.image.v2 import fakes as image_fakes
+from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
-class TestImage(image_fakes.TestImagev2):
+class TestImage(image_fakes.TestImagev2, volume_fakes.TestVolume):
def setUp(self):
- super(TestImage, self).setUp()
+ super().setUp()
# Get shortcuts to mocked image client
self.client = self.app.client_manager.image
@@ -40,6 +42,13 @@ class TestImage(image_fakes.TestImagev2):
self.project_mock.reset_mock()
self.domain_mock = self.app.client_manager.identity.domains
self.domain_mock.reset_mock()
+ self.volumes_mock = self.app.client_manager.volume.volumes
+ fake_body = {
+ 'os-volume_upload_image':
+ {'volume_type': {'name': 'fake_type'}}}
+ self.volumes_mock.upload_to_image.return_value = (
+ 200, fake_body)
+ self.volumes_mock.reset_mock()
def setup_images_mock(self, count):
images = image_fakes.create_images(count=count)
@@ -53,7 +62,7 @@ class TestImageCreate(TestImage):
domain = identity_fakes.FakeDomain.create_one_domain()
def setUp(self):
- super(TestImageCreate, self).setUp()
+ super().setUp()
self.new_image = image_fakes.create_one_image()
self.client.create_image.return_value = self.new_image
@@ -125,10 +134,8 @@ class TestImageCreate(TestImage):
('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
- ('protected', self.new_image.is_protected),
- ('unprotected', not self.new_image.is_protected),
- ('public', self.new_image.visibility == 'public'),
- ('private', self.new_image.visibility == 'private'),
+ ('is_protected', self.new_image.is_protected),
+ ('visibility', self.new_image.visibility),
('project', self.new_image.owner_id),
('project_domain', self.domain.id),
('name', self.new_image.name),
@@ -179,10 +186,8 @@ class TestImageCreate(TestImage):
('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
- ('protected', True),
- ('unprotected', False),
- ('public', False),
- ('private', True),
+ ('is_protected', True),
+ ('visibility', 'private'),
('project', 'unexist_owner'),
('name', 'graven'),
]
@@ -213,10 +218,8 @@ class TestImageCreate(TestImage):
]
verifylist = [
('file', imagefile.name),
- ('protected', self.new_image.is_protected),
- ('unprotected', not self.new_image.is_protected),
- ('public', self.new_image.visibility == 'public'),
- ('private', self.new_image.visibility == 'private'),
+ ('is_protected', self.new_image.is_protected),
+ ('visibility', self.new_image.visibility),
('properties', {'Alpha': '1', 'Beta': '2'}),
('tags', self.new_image.tags),
('name', self.new_image.name),
@@ -287,6 +290,101 @@ class TestImageCreate(TestImage):
use_import=True
)
+ @mock.patch('osc_lib.utils.find_resource')
+ @mock.patch('openstackclient.image.v2.image.get_data_file')
+ def test_image_create_from_volume(self, mock_get_data_f, mock_get_vol):
+
+ fake_vol_id = 'fake-volume-id'
+ mock_get_data_f.return_value = (None, None)
+
+ class FakeVolume:
+ id = fake_vol_id
+
+ mock_get_vol.return_value = FakeVolume()
+
+ arglist = [
+ '--volume', fake_vol_id,
+ self.new_image.name,
+ ]
+ verifylist = [
+ ('name', self.new_image.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.upload_to_image.assert_called_with(
+ fake_vol_id,
+ False,
+ self.new_image.name,
+ 'bare',
+ 'raw'
+ )
+
+ @mock.patch('osc_lib.utils.find_resource')
+ @mock.patch('openstackclient.image.v2.image.get_data_file')
+ def test_image_create_from_volume_fail(self, mock_get_data_f,
+ mock_get_vol):
+
+ fake_vol_id = 'fake-volume-id'
+ mock_get_data_f.return_value = (None, None)
+
+ class FakeVolume:
+ id = fake_vol_id
+
+ mock_get_vol.return_value = FakeVolume()
+
+ arglist = [
+ '--volume', fake_vol_id,
+ self.new_image.name,
+ '--public'
+ ]
+ verifylist = [
+ ('name', self.new_image.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ @mock.patch('osc_lib.utils.find_resource')
+ @mock.patch('openstackclient.image.v2.image.get_data_file')
+ def test_image_create_from_volume_v31(self, mock_get_data_f,
+ mock_get_vol):
+
+ self.app.client_manager.volume.api_version = (
+ api_versions.APIVersion('3.1'))
+
+ fake_vol_id = 'fake-volume-id'
+ mock_get_data_f.return_value = (None, None)
+
+ class FakeVolume:
+ id = fake_vol_id
+
+ mock_get_vol.return_value = FakeVolume()
+
+ arglist = [
+ '--volume', fake_vol_id,
+ self.new_image.name,
+ '--public'
+ ]
+ verifylist = [
+ ('name', self.new_image.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.upload_to_image.assert_called_with(
+ fake_vol_id,
+ False,
+ self.new_image.name,
+ 'bare',
+ 'raw',
+ visibility='public',
+ protected=False
+ )
+
class TestAddProjectToImage(TestImage):
@@ -317,7 +415,7 @@ class TestAddProjectToImage(TestImage):
)
def setUp(self):
- super(TestAddProjectToImage, self).setUp()
+ super().setUp()
# This is the return value for utils.find_resource()
self.client.find_image.return_value = self._image
@@ -381,7 +479,7 @@ class TestAddProjectToImage(TestImage):
class TestImageDelete(TestImage):
def setUp(self):
- super(TestImageDelete, self).setUp()
+ super().setUp()
self.client.delete_image.return_value = None
@@ -472,7 +570,7 @@ class TestImageList(TestImage):
),
def setUp(self):
- super(TestImageList, self).setUp()
+ super().setUp()
self.client.images.side_effect = [[self._image], []]
@@ -482,10 +580,7 @@ class TestImageList(TestImage):
def test_image_list_no_options(self):
arglist = []
verifylist = [
- ('public', False),
- ('private', False),
- ('community', False),
- ('shared', False),
+ ('visibility', None),
('long', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -506,10 +601,7 @@ class TestImageList(TestImage):
'--public',
]
verifylist = [
- ('public', True),
- ('private', False),
- ('community', False),
- ('shared', False),
+ ('visibility', 'public'),
('long', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -530,10 +622,7 @@ class TestImageList(TestImage):
'--private',
]
verifylist = [
- ('public', False),
- ('private', True),
- ('community', False),
- ('shared', False),
+ ('visibility', 'private'),
('long', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -554,10 +643,7 @@ class TestImageList(TestImage):
'--community',
]
verifylist = [
- ('public', False),
- ('private', False),
- ('community', True),
- ('shared', False),
+ ('visibility', 'community'),
('long', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -578,10 +664,7 @@ class TestImageList(TestImage):
'--shared',
]
verifylist = [
- ('public', False),
- ('private', False),
- ('community', False),
- ('shared', True),
+ ('visibility', 'shared'),
('long', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -597,16 +680,34 @@ class TestImageList(TestImage):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.datalist, tuple(data))
+ def test_image_list_all_option(self):
+ arglist = [
+ '--all',
+ ]
+ verifylist = [
+ ('visibility', 'all'),
+ ('long', False),
+ ]
+ 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.client.images.assert_called_with(
+ visibility='all',
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertCountEqual(self.datalist, tuple(data))
+
def test_image_list_shared_member_status_option(self):
arglist = [
'--shared',
'--member-status', 'all'
]
verifylist = [
- ('public', False),
- ('private', False),
- ('community', False),
- ('shared', True),
+ ('visibility', 'shared'),
('long', False),
('member_status', 'all')
]
@@ -630,10 +731,7 @@ class TestImageList(TestImage):
'--member-status', 'ALl'
]
verifylist = [
- ('public', False),
- ('private', False),
- ('community', False),
- ('shared', True),
+ ('visibility', 'shared'),
('long', False),
('member_status', 'all')
]
@@ -823,7 +921,7 @@ class TestImageList(TestImage):
'--hidden',
]
verifylist = [
- ('hidden', True),
+ ('is_hidden', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -835,15 +933,16 @@ class TestImageList(TestImage):
def test_image_list_tag_option(self):
arglist = [
'--tag', 'abc',
+ '--tag', 'cba'
]
verifylist = [
- ('tag', 'abc'),
+ ('tag', ['abc', 'cba']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.client.images.assert_called_with(
- tag='abc'
+ tag=['abc', 'cba']
)
@@ -869,7 +968,7 @@ class TestListImageProjects(TestImage):
)]
def setUp(self):
- super(TestListImageProjects, self).setUp()
+ super().setUp()
self.client.find_image.return_value = self._image
self.client.members.return_value = [self.member]
@@ -899,7 +998,7 @@ class TestRemoveProjectImage(TestImage):
domain = identity_fakes.FakeDomain.create_one_domain()
def setUp(self):
- super(TestRemoveProjectImage, self).setUp()
+ super().setUp()
self._image = image_fakes.create_one_image()
# This is the return value for utils.find_resource()
@@ -963,7 +1062,7 @@ class TestImageSet(TestImage):
_image = image_fakes.create_one_image({'tags': []})
def setUp(self):
- super(TestImageSet, self).setUp()
+ super().setUp()
self.project_mock.get.return_value = self.project
@@ -1145,10 +1244,8 @@ class TestImageSet(TestImage):
'graven',
]
verifylist = [
- ('protected', True),
- ('unprotected', False),
- ('public', False),
- ('private', True),
+ ('is_protected', True),
+ ('visibility', 'private'),
('image', 'graven'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1173,10 +1270,8 @@ class TestImageSet(TestImage):
'graven',
]
verifylist = [
- ('protected', False),
- ('unprotected', True),
- ('public', True),
- ('private', False),
+ ('is_protected', False),
+ ('visibility', 'public'),
('image', 'graven'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1395,7 +1490,7 @@ class TestImageSet(TestImage):
'graven',
]
verifylist = [
- ('visibility', '1-mile'),
+ ('dead_visibility', '1-mile'),
('image', 'graven'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1437,9 +1532,8 @@ class TestImageSet(TestImage):
'graven',
]
verifylist = [
- ('hidden', True),
- ('public', True),
- ('private', False),
+ ('is_hidden', True),
+ ('visibility', 'public'),
('image', 'graven'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1464,9 +1558,8 @@ class TestImageSet(TestImage):
'graven',
]
verifylist = [
- ('hidden', False),
- ('public', True),
- ('private', False),
+ ('is_hidden', False),
+ ('visibility', 'public'),
('image', 'graven'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1506,7 +1599,7 @@ class TestImageShow(TestImage):
)
def setUp(self):
- super(TestImageShow, self).setUp()
+ super().setUp()
self.client.find_image = mock.Mock(return_value=self._data)
@@ -1562,7 +1655,7 @@ class TestImageShow(TestImage):
class TestImageUnset(TestImage):
def setUp(self):
- super(TestImageUnset, self).setUp()
+ super().setUp()
attrs = {}
attrs['tags'] = ['test']
@@ -1661,7 +1754,7 @@ class TestImageSave(TestImage):
image = image_fakes.create_one_image({})
def setUp(self):
- super(TestImageSave, self).setUp()
+ super().setUp()
self.client.find_image.return_value = self.image
self.client.download_image.return_value = self.image
@@ -1690,7 +1783,7 @@ class TestImageSave(TestImage):
class TestImageGetData(TestImage):
def setUp(self):
- super(TestImageGetData, self).setUp()
+ super().setUp()
self.args = mock.Mock()
def test_get_data_file_file(self):
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index 2f839d1..06c5822 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -18,8 +18,21 @@ from random import randint
from unittest import mock
import uuid
+from openstack.network.v2 import address_group as _address_group
+from openstack.network.v2 import address_scope as _address_scope
+from openstack.network.v2 import agent as network_agent
+from openstack.network.v2 import auto_allocated_topology as allocated_topology
+from openstack.network.v2 import availability_zone as _availability_zone
+from openstack.network.v2 import flavor as _flavor
from openstack.network.v2 import local_ip as _local_ip
from openstack.network.v2 import local_ip_association as _local_ip_association
+from openstack.network.v2 import network as _network
+from openstack.network.v2 import network_ip_availability as _ip_availability
+from openstack.network.v2 import network_segment_range as _segment_range
+from openstack.network.v2 import port as _port
+from openstack.network.v2 import rbac_policy as network_rbac
+from openstack.network.v2 import segment as _segment
+from openstack.network.v2 import service_profile as _flavor_profile
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
@@ -45,9 +58,11 @@ QUOTA = {
RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit'
RULE_TYPE_DSCP_MARKING = 'dscp-marking'
RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
+RULE_TYPE_MINIMUM_PACKET_RATE = 'minimum-packet-rate'
VALID_QOS_RULES = [RULE_TYPE_BANDWIDTH_LIMIT,
RULE_TYPE_DSCP_MARKING,
- RULE_TYPE_MINIMUM_BANDWIDTH]
+ RULE_TYPE_MINIMUM_BANDWIDTH,
+ RULE_TYPE_MINIMUM_PACKET_RATE]
VALID_DSCP_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
34, 36, 38, 40, 46, 48, 56]
@@ -86,271 +101,6 @@ class TestNetworkV2(utils.TestCommand):
)
-class FakeAddressGroup(object):
- """Fake one or more address groups."""
-
- @staticmethod
- def create_one_address_group(attrs=None):
- """Create a fake address group.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object with name, id, etc.
- """
- attrs = attrs or {}
-
- # Set default attributes.
- address_group_attrs = {
- 'name': 'address-group-name-' + uuid.uuid4().hex,
- 'description': 'address-group-description-' + uuid.uuid4().hex,
- 'id': 'address-group-id-' + uuid.uuid4().hex,
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'addresses': ['10.0.0.1/32'],
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- address_group_attrs.update(attrs)
-
- address_group = fakes.FakeResource(
- info=copy.deepcopy(address_group_attrs),
- loaded=True)
-
- return address_group
-
- @staticmethod
- def create_address_groups(attrs=None, count=2):
- """Create multiple fake address groups.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of address groups to fake
- :return:
- A list of FakeResource objects faking the address groups
- """
- address_groups = []
- for i in range(0, count):
- address_groups.append(
- FakeAddressGroup.create_one_address_group(attrs))
-
- return address_groups
-
- @staticmethod
- def get_address_groups(address_groups=None, count=2):
- """Get an iterable Mock object with a list of faked address groups.
-
- If address groups list is provided, then initialize the Mock object
- with the list. Otherwise create one.
-
- :param List address_groups:
- A list of FakeResource objects faking address groups
- :param int count:
- The number of address groups to fake
- :return:
- An iterable Mock object with side_effect set to a list of faked
- address groups
- """
- if address_groups is None:
- address_groups = FakeAddressGroup.create_address_groups(count)
- return mock.Mock(side_effect=address_groups)
-
-
-class FakeAddressScope(object):
- """Fake one or more address scopes."""
-
- @staticmethod
- def create_one_address_scope(attrs=None):
- """Create a fake address scope.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object with name, id, etc.
- """
- attrs = attrs or {}
-
- # Set default attributes.
- address_scope_attrs = {
- 'name': 'address-scope-name-' + uuid.uuid4().hex,
- 'id': 'address-scope-id-' + uuid.uuid4().hex,
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'shared': False,
- 'ip_version': 4,
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- address_scope_attrs.update(attrs)
-
- address_scope = fakes.FakeResource(
- info=copy.deepcopy(address_scope_attrs),
- loaded=True)
-
- # Set attributes with special mapping in OpenStack SDK.
- address_scope.is_shared = address_scope_attrs['shared']
-
- return address_scope
-
- @staticmethod
- def create_address_scopes(attrs=None, count=2):
- """Create multiple fake address scopes.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of address scopes to fake
- :return:
- A list of FakeResource objects faking the address scopes
- """
- address_scopes = []
- for i in range(0, count):
- address_scopes.append(
- FakeAddressScope.create_one_address_scope(attrs))
-
- return address_scopes
-
- @staticmethod
- def get_address_scopes(address_scopes=None, count=2):
- """Get an iterable Mock object with a list of faked address scopes.
-
- If address scopes list is provided, then initialize the Mock object
- with the list. Otherwise create one.
-
- :param List address_scopes:
- A list of FakeResource objects faking address scopes
- :param int count:
- The number of address scopes to fake
- :return:
- An iterable Mock object with side_effect set to a list of faked
- address scopes
- """
- if address_scopes is None:
- address_scopes = FakeAddressScope.create_address_scopes(count)
- return mock.Mock(side_effect=address_scopes)
-
-
-class FakeAutoAllocatedTopology(object):
- """Fake Auto Allocated Topology"""
-
- @staticmethod
- def create_one_topology(attrs=None):
- attrs = attrs or {}
-
- auto_allocated_topology_attrs = {
- 'id': 'network-id-' + uuid.uuid4().hex,
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- }
-
- auto_allocated_topology_attrs.update(attrs)
-
- auto_allocated_topology = fakes.FakeResource(
- info=copy.deepcopy(auto_allocated_topology_attrs),
- loaded=True)
-
- return auto_allocated_topology
-
-
-class FakeAvailabilityZone(object):
- """Fake one or more network availability zones (AZs)."""
-
- @staticmethod
- def create_one_availability_zone(attrs=None):
- """Create a fake AZ.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object with name, state, etc.
- """
- attrs = attrs or {}
-
- # Set default attributes.
- availability_zone = {
- 'name': uuid.uuid4().hex,
- 'state': 'available',
- 'resource': 'network',
- }
-
- # Overwrite default attributes.
- availability_zone.update(attrs)
-
- availability_zone = fakes.FakeResource(
- info=copy.deepcopy(availability_zone),
- loaded=True)
- return availability_zone
-
- @staticmethod
- def create_availability_zones(attrs=None, count=2):
- """Create multiple fake AZs.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of AZs to fake
- :return:
- A list of FakeResource objects faking the AZs
- """
- availability_zones = []
- for i in range(0, count):
- availability_zone = \
- FakeAvailabilityZone.create_one_availability_zone(attrs)
- availability_zones.append(availability_zone)
-
- return availability_zones
-
-
-class FakeIPAvailability(object):
- """Fake one or more network ip availabilities."""
-
- @staticmethod
- def create_one_ip_availability(attrs=None):
- """Create a fake list with ip availability stats of a network.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object with network_name, network_id, etc.
- """
- attrs = attrs or {}
-
- # Set default attributes.
- network_ip_attrs = {
- 'network_id': 'network-id-' + uuid.uuid4().hex,
- 'network_name': 'network-name-' + uuid.uuid4().hex,
- 'project_id': '',
- 'subnet_ip_availability': [],
- 'total_ips': 254,
- 'used_ips': 6,
- 'location': 'MUNCHMUNCHMUNCH',
- }
- network_ip_attrs.update(attrs)
-
- network_ip_availability = fakes.FakeResource(
- info=copy.deepcopy(network_ip_attrs),
- loaded=True)
-
- return network_ip_availability
-
- @staticmethod
- def create_ip_availability(count=2):
- """Create fake list of ip availability stats of multiple networks.
-
- :param int count:
- The number of networks to fake
- :return:
- A list of FakeResource objects faking network ip availability stats
- """
- network_ip_availabilities = []
- for i in range(0, count):
- network_ip_availability = \
- FakeIPAvailability.create_one_ip_availability()
- network_ip_availabilities.append(network_ip_availability)
-
- return network_ip_availabilities
-
-
class FakeExtension(object):
"""Fake one or more extension."""
@@ -384,582 +134,6 @@ class FakeExtension(object):
return extension
-class FakeNetwork(object):
- """Fake one or more networks."""
-
- @staticmethod
- def create_one_network(attrs=None):
- """Create a fake network.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id, name, etc.
- """
- attrs = attrs or {}
-
- # Set default attributes.
- network_attrs = {
- 'id': 'network-id-' + uuid.uuid4().hex,
- 'name': 'network-name-' + uuid.uuid4().hex,
- 'status': 'ACTIVE',
- 'description': 'network-description-' + uuid.uuid4().hex,
- 'dns_domain': 'example.org.',
- 'mtu': '1350',
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'admin_state_up': True,
- 'shared': False,
- 'subnets': ['a', 'b'],
- 'provider:network_type': 'vlan',
- 'provider:physical_network': 'physnet1',
- 'provider:segmentation_id': "400",
- 'router:external': True,
- 'availability_zones': [],
- 'availability_zone_hints': [],
- 'is_default': False,
- 'port_security_enabled': True,
- 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
- 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex,
- 'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex,
- 'tags': [],
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- network_attrs.update(attrs)
-
- network = fakes.FakeResource(info=copy.deepcopy(network_attrs),
- loaded=True)
-
- # Set attributes with special mapping in OpenStack SDK.
- network.is_router_external = network_attrs['router:external']
- network.is_admin_state_up = network_attrs['admin_state_up']
- network.is_port_security_enabled = \
- network_attrs['port_security_enabled']
- network.subnet_ids = network_attrs['subnets']
- network.is_shared = network_attrs['shared']
- network.is_tags = network_attrs['tags']
- network.provider_network_type = \
- network_attrs['provider:network_type']
- network.provider_physical_network = \
- network_attrs['provider:physical_network']
- network.provider_segmentation_id = \
- network_attrs['provider:segmentation_id']
- network.ipv4_address_scope_id = \
- network_attrs['ipv4_address_scope']
- network.ipv6_address_scope_id = \
- network_attrs['ipv6_address_scope']
-
- return network
-
- @staticmethod
- def create_networks(attrs=None, count=2):
- """Create multiple fake networks.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of networks to fake
- :return:
- A list of FakeResource objects faking the networks
- """
- networks = []
- for i in range(0, count):
- networks.append(FakeNetwork.create_one_network(attrs))
-
- return networks
-
- @staticmethod
- def get_networks(networks=None, count=2):
- """Get an iterable Mock object with a list of faked networks.
-
- If networks list is provided, then initialize the Mock object with the
- list. Otherwise create one.
-
- :param List networks:
- A list of FakeResource objects faking networks
- :param int count:
- The number of networks to fake
- :return:
- An iterable Mock object with side_effect set to a list of faked
- networks
- """
- if networks is None:
- networks = FakeNetwork.create_networks(count)
- return mock.Mock(side_effect=networks)
-
-
-class FakeNetworkFlavor(object):
- """Fake Network Flavor."""
-
- @staticmethod
- def create_one_network_flavor(attrs=None):
- """Create a fake network flavor.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object faking the network flavor
- """
- attrs = attrs or {}
-
- fake_uuid = uuid.uuid4().hex
- network_flavor_attrs = {
- 'description': 'network-flavor-description-' + fake_uuid,
- 'enabled': True,
- 'id': 'network-flavor-id-' + fake_uuid,
- 'name': 'network-flavor-name-' + fake_uuid,
- 'service_type': 'vpn',
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- network_flavor_attrs.update(attrs)
-
- network_flavor = fakes.FakeResource(
- info=copy.deepcopy(network_flavor_attrs),
- loaded=True
- )
-
- network_flavor.is_enabled = network_flavor_attrs['enabled']
-
- return network_flavor
-
- @staticmethod
- def create_flavor(attrs=None, count=2):
- """Create multiple fake network flavors.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of network flavors to fake
- :return:
- A list of FakeResource objects faking the network falvors
- """
- network_flavors = []
- for i in range(0, count):
- network_flavors.append(
- FakeNetworkFlavor.create_one_network_flavor(attrs)
- )
- return network_flavors
-
- @staticmethod
- def get_flavor(network_flavors=None, count=2):
- """Get a list of flavors."""
- if network_flavors is None:
- network_flavors = (FakeNetworkFlavor.create_flavor(count))
- return mock.Mock(side_effect=network_flavors)
-
-
-class FakeNetworkSegment(object):
- """Fake one or more network segments."""
-
- @staticmethod
- def create_one_network_segment(attrs=None):
- """Create a fake network segment.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object faking the network segment
- """
- attrs = attrs or {}
-
- # Set default attributes.
- fake_uuid = uuid.uuid4().hex
- network_segment_attrs = {
- 'description': 'network-segment-description-' + fake_uuid,
- 'id': 'network-segment-id-' + fake_uuid,
- 'name': 'network-segment-name-' + fake_uuid,
- 'network_id': 'network-id-' + fake_uuid,
- 'network_type': 'vlan',
- 'physical_network': 'physical-network-name-' + fake_uuid,
- 'segmentation_id': 1024,
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- network_segment_attrs.update(attrs)
-
- network_segment = fakes.FakeResource(
- info=copy.deepcopy(network_segment_attrs),
- loaded=True
- )
-
- return network_segment
-
- @staticmethod
- def create_network_segments(attrs=None, count=2):
- """Create multiple fake network segments.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of network segments to fake
- :return:
- A list of FakeResource objects faking the network segments
- """
- network_segments = []
- for i in range(0, count):
- network_segments.append(
- FakeNetworkSegment.create_one_network_segment(attrs)
- )
- return network_segments
-
-
-class FakeNetworkSegmentRange(object):
- """Fake one or more network segment ranges."""
-
- @staticmethod
- def create_one_network_segment_range(attrs=None):
- """Create a fake network segment range.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object faking the network segment range
- """
- attrs = attrs or {}
-
- # Set default attributes.
- fake_uuid = uuid.uuid4().hex
- network_segment_range_attrs = {
- 'id': 'network-segment-range-id-' + fake_uuid,
- 'name': 'network-segment-name-' + fake_uuid,
- 'default': False,
- 'shared': False,
- 'project_id': 'project-id-' + fake_uuid,
- 'network_type': 'vlan',
- 'physical_network': 'physical-network-name-' + fake_uuid,
- 'minimum': 100,
- 'maximum': 106,
- 'used': {104: '3312e4ba67864b2eb53f3f41432f8efc',
- 106: '3312e4ba67864b2eb53f3f41432f8efc'},
- 'available': [100, 101, 102, 103, 105],
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- network_segment_range_attrs.update(attrs)
-
- network_segment_range = fakes.FakeResource(
- info=copy.deepcopy(network_segment_range_attrs),
- loaded=True
- )
-
- return network_segment_range
-
- @staticmethod
- def create_network_segment_ranges(attrs=None, count=2):
- """Create multiple fake network segment ranges.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of network segment ranges to fake
- :return:
- A list of FakeResource objects faking the network segment ranges
- """
- network_segment_ranges = []
- for i in range(0, count):
- network_segment_ranges.append(
- FakeNetworkSegmentRange.create_one_network_segment_range(attrs)
- )
- return network_segment_ranges
-
-
-class FakePort(object):
- """Fake one or more ports."""
-
- @staticmethod
- def create_one_port(attrs=None):
- """Create a fake port.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id, name, etc.
- """
- attrs = attrs or {}
-
- # Set default attributes.
- port_attrs = {
- 'admin_state_up': True,
- 'allowed_address_pairs': [{}],
- 'binding:host_id': 'binding-host-id-' + uuid.uuid4().hex,
- 'binding:profile': {},
- 'binding:vif_details': {},
- 'binding:vif_type': 'ovs',
- 'binding:vnic_type': 'normal',
- 'data_plane_status': None,
- 'description': 'description-' + uuid.uuid4().hex,
- 'device_id': 'device-id-' + uuid.uuid4().hex,
- 'device_owner': 'compute:nova',
- 'device_profile': 'cyborg_device_profile_1',
- 'dns_assignment': [{}],
- 'dns_domain': 'dns-domain-' + uuid.uuid4().hex,
- 'dns_name': 'dns-name-' + uuid.uuid4().hex,
- 'extra_dhcp_opts': [{}],
- 'fixed_ips': [{'ip_address': '10.0.0.3',
- 'subnet_id': 'subnet-id-' + uuid.uuid4().hex}],
- 'id': 'port-id-' + uuid.uuid4().hex,
- 'mac_address': 'fa:16:3e:a9:4e:72',
- 'name': 'port-name-' + uuid.uuid4().hex,
- 'network_id': 'network-id-' + uuid.uuid4().hex,
- 'numa_affinity_policy': 'required',
- 'port_security_enabled': True,
- 'security_group_ids': [],
- 'status': 'ACTIVE',
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'qos_network_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
- 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
- 'tags': [],
- 'propagate_uplink_status': False,
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- # Overwrite default attributes.
- port_attrs.update(attrs)
-
- port = fakes.FakeResource(info=copy.deepcopy(port_attrs),
- loaded=True)
-
- # Set attributes with special mappings in OpenStack SDK.
- port.binding_host_id = port_attrs['binding:host_id']
- port.binding_profile = port_attrs['binding:profile']
- port.binding_vif_details = port_attrs['binding:vif_details']
- port.binding_vif_type = port_attrs['binding:vif_type']
- port.binding_vnic_type = port_attrs['binding:vnic_type']
- port.is_admin_state_up = port_attrs['admin_state_up']
- port.is_port_security_enabled = port_attrs['port_security_enabled']
-
- return port
-
- @staticmethod
- def create_ports(attrs=None, count=2):
- """Create multiple fake ports.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of ports to fake
- :return:
- A list of FakeResource objects faking the ports
- """
- ports = []
- for i in range(0, count):
- ports.append(FakePort.create_one_port(attrs))
-
- return ports
-
- @staticmethod
- def get_ports(ports=None, count=2):
- """Get an iterable Mock object with a list of faked ports.
-
- If ports list is provided, then initialize the Mock object with the
- list. Otherwise create one.
-
- :param List ports:
- A list of FakeResource objects faking ports
- :param int count:
- The number of ports to fake
- :return:
- An iterable Mock object with side_effect set to a list of faked
- ports
- """
- if ports is None:
- ports = FakePort.create_ports(count)
- return mock.Mock(side_effect=ports)
-
-
-class FakeNetworkAgent(object):
- """Fake one or more network agents."""
-
- @staticmethod
- def create_one_network_agent(attrs=None):
- """Create a fake network agent
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id, agent_type, and so on.
- """
- attrs = attrs or {}
-
- # Set default attributes
- agent_attrs = {
- 'id': 'agent-id-' + uuid.uuid4().hex,
- 'agent_type': 'agent-type-' + uuid.uuid4().hex,
- 'host': 'host-' + uuid.uuid4().hex,
- 'availability_zone': 'zone-' + uuid.uuid4().hex,
- 'alive': True,
- 'admin_state_up': True,
- 'binary': 'binary-' + uuid.uuid4().hex,
- 'configurations': {'subnet': 2, 'networks': 1},
- 'location': 'MUNCHMUNCHMUNCH',
- }
- agent_attrs.update(attrs)
- agent = fakes.FakeResource(info=copy.deepcopy(agent_attrs),
- loaded=True)
- agent.is_admin_state_up = agent_attrs['admin_state_up']
- agent.is_alive = agent_attrs['alive']
- return agent
-
- @staticmethod
- def create_network_agents(attrs=None, count=2):
- """Create multiple fake network agents.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of network agents to fake
- :return:
- A list of FakeResource objects faking the network agents
- """
- agents = []
- for i in range(0, count):
- agents.append(FakeNetworkAgent.create_one_network_agent(attrs))
-
- return agents
-
- @staticmethod
- def get_network_agents(agents=None, count=2):
- """Get an iterable Mock object with a list of faked network agents.
-
- If network agents list is provided, then initialize the Mock object
- with the list. Otherwise create one.
-
- :param List agents:
- A list of FakeResource objects faking network agents
- :param int count:
- The number of network agents to fake
- :return:
- An iterable Mock object with side_effect set to a list of faked
- network agents
- """
- if agents is None:
- agents = FakeNetworkAgent.create_network_agents(count)
- return mock.Mock(side_effect=agents)
-
-
-class FakeNetworkRBAC(object):
- """Fake one or more network rbac policies."""
-
- @staticmethod
- def create_one_network_rbac(attrs=None):
- """Create a fake network rbac
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :return:
- A FakeResource object, with id, action, target_tenant,
- project_id, type
- """
- attrs = attrs or {}
-
- # Set default attributes
- rbac_attrs = {
- 'id': 'rbac-id-' + uuid.uuid4().hex,
- 'object_type': 'network',
- 'object_id': 'object-id-' + uuid.uuid4().hex,
- 'action': 'access_as_shared',
- 'target_tenant': 'target-tenant-' + uuid.uuid4().hex,
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'location': 'MUNCHMUNCHMUNCH',
- }
- rbac_attrs.update(attrs)
- rbac = fakes.FakeResource(info=copy.deepcopy(rbac_attrs),
- loaded=True)
- # Set attributes with special mapping in OpenStack SDK.
- rbac.target_project_id = rbac_attrs['target_tenant']
- return rbac
-
- @staticmethod
- def create_network_rbacs(attrs=None, count=2):
- """Create multiple fake network rbac policies.
-
- :param Dictionary attrs:
- A dictionary with all attributes
- :param int count:
- The number of rbac policies to fake
- :return:
- A list of FakeResource objects faking the rbac policies
- """
- rbac_policies = []
- for i in range(0, count):
- rbac_policies.append(FakeNetworkRBAC.
- create_one_network_rbac(attrs))
-
- return rbac_policies
-
- @staticmethod
- def get_network_rbacs(rbac_policies=None, count=2):
- """Get an iterable Mock object with a list of faked rbac policies.
-
- If rbac policies list is provided, then initialize the Mock object
- with the list. Otherwise create one.
-
- :param List rbac_policies:
- A list of FakeResource objects faking rbac policies
- :param int count:
- The number of rbac policies to fake
- :return:
- An iterable Mock object with side_effect set to a list of faked
- rbac policies
- """
- if rbac_policies is None:
- rbac_policies = FakeNetworkRBAC.create_network_rbacs(count)
- return mock.Mock(side_effect=rbac_policies)
-
-
-class FakeNetworkFlavorProfile(object):
- """Fake network flavor profile."""
-
- @staticmethod
- def create_one_service_profile(attrs=None):
- """Create flavor profile."""
- attrs = attrs or {}
-
- flavor_profile_attrs = {
- 'id': 'flavor-profile-id' + uuid.uuid4().hex,
- 'description': 'flavor-profile-description-' + uuid.uuid4().hex,
- 'project_id': 'project-id-' + uuid.uuid4().hex,
- 'driver': 'driver-' + uuid.uuid4().hex,
- 'metainfo': 'metainfo-' + uuid.uuid4().hex,
- 'enabled': True,
- 'location': 'MUNCHMUNCHMUNCH',
- }
-
- flavor_profile_attrs.update(attrs)
-
- flavor_profile = fakes.FakeResource(
- info=copy.deepcopy(flavor_profile_attrs),
- loaded=True)
-
- flavor_profile.is_enabled = flavor_profile_attrs['enabled']
-
- return flavor_profile
-
- @staticmethod
- def create_service_profile(attrs=None, count=2):
- """Create multiple flavor profiles."""
-
- flavor_profiles = []
- for i in range(0, count):
- flavor_profiles.append(FakeNetworkFlavorProfile.
- create_one_service_profile(attrs))
- return flavor_profiles
-
- @staticmethod
- def get_service_profile(flavor_profile=None, count=2):
- """Get a list of flavor profiles."""
- if flavor_profile is None:
- flavor_profile = (FakeNetworkFlavorProfile.
- create_service_profile(count))
- return mock.Mock(side_effect=flavor_profile)
-
-
class FakeNetworkQosPolicy(object):
"""Fake one or more QoS policies."""
@@ -1102,6 +276,9 @@ class FakeNetworkQosRule(object):
elif type == RULE_TYPE_MINIMUM_BANDWIDTH:
qos_rule_attrs['min_kbps'] = randint(1, 10000)
qos_rule_attrs['direction'] = 'egress'
+ elif type == RULE_TYPE_MINIMUM_PACKET_RATE:
+ qos_rule_attrs['min_kpps'] = randint(1, 10000)
+ qos_rule_attrs['direction'] = 'egress'
# Overwrite default attributes.
qos_rule_attrs.update(attrs)
@@ -2038,6 +1215,737 @@ class FakeL3ConntrackHelper(object):
return mock.Mock(side_effect=ct_helpers)
+def create_one_address_group(attrs=None):
+ """Create a fake address group.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ An AddressGroup object with name, id, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ address_group_attrs = {
+ 'name': 'address-group-name-' + uuid.uuid4().hex,
+ 'description': 'address-group-description-' + uuid.uuid4().hex,
+ 'id': 'address-group-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'addresses': ['10.0.0.1/32'],
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ # Overwrite default attributes.
+ address_group_attrs.update(attrs)
+
+ address_group = _address_group.AddressGroup(**address_group_attrs)
+
+ return address_group
+
+
+def create_address_groups(attrs=None, count=2):
+ """Create multiple fake address groups.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of address groups to fake
+ :return:
+ A list of AddressGroup objects faking the address groups
+ """
+ address_groups = []
+ for i in range(0, count):
+ address_groups.append(create_one_address_group(attrs))
+
+ return address_groups
+
+
+def get_address_groups(address_groups=None, count=2):
+ """Get an iterable Mock object with a list of faked address groups.
+
+ If address groups list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List address_groups:
+ A list of FakeResource objects faking address groups
+ :param int count:
+ The number of address groups to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ AddressGroup objects
+ """
+ if address_groups is None:
+ address_groups = create_address_groups(count)
+ return mock.Mock(side_effect=address_groups)
+
+
+def create_one_address_scope(attrs=None):
+ """Create a fake address scope.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ An AddressScope object with name, id, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ address_scope_attrs = {
+ 'name': 'address-scope-name-' + uuid.uuid4().hex,
+ 'id': 'address-scope-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'shared': False,
+ 'ip_version': 4,
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ # Overwrite default attributes.
+ address_scope_attrs.update(attrs)
+
+ address_scope = _address_scope.AddressScope(**address_scope_attrs)
+
+ # Set attributes with special mapping in OpenStack SDK.
+ address_scope.is_shared = address_scope_attrs['shared']
+
+ return address_scope
+
+
+def create_address_scopes(attrs=None, count=2):
+ """Create multiple fake address scopes.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of address scopes to fake
+ :return:
+ A list of AddressScope objects faking the address scopes
+ """
+ address_scopes = []
+ for i in range(0, count):
+ address_scopes.append(create_one_address_scope(attrs))
+
+ return address_scopes
+
+
+def get_address_scopes(address_scopes=None, count=2):
+ """Get an iterable Mock object with a list of faked address scopes.
+
+ If address scopes list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List address_scopes:
+ A list of FakeResource objects faking address scopes
+ :param int count:
+ The number of address scopes to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ AddressScope objects
+ """
+ if address_scopes is None:
+ address_scopes = create_address_scopes(count)
+ return mock.Mock(side_effect=address_scopes)
+
+
+def create_one_topology(attrs=None):
+ attrs = attrs or {}
+
+ auto_allocated_topology_attrs = {
+ 'id': 'network-id-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ }
+
+ auto_allocated_topology_attrs.update(attrs)
+
+ auto_allocated_topology = allocated_topology.AutoAllocatedTopology(
+ **auto_allocated_topology_attrs
+ )
+
+ return auto_allocated_topology
+
+
+def create_one_availability_zone(attrs=None):
+ """Create a fake AZ.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ An AvailabilityZone object with name, state, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ availability_zone = {
+ 'name': uuid.uuid4().hex,
+ 'state': 'available',
+ 'resource': 'network',
+ }
+
+ # Overwrite default attributes.
+ availability_zone.update(attrs)
+
+ availability_zone = _availability_zone.AvailabilityZone(
+ **availability_zone
+ )
+ return availability_zone
+
+
+def create_availability_zones(attrs=None, count=2):
+ """Create multiple fake AZs.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of AZs to fake
+ :return:
+ A list of AvailabilityZone objects faking the AZs
+ """
+ availability_zones = []
+ for i in range(0, count):
+ availability_zone = create_one_availability_zone(attrs)
+ availability_zones.append(availability_zone)
+
+ return availability_zones
+
+
+def create_one_ip_availability(attrs=None):
+ """Create a fake list with ip availability stats of a network.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A NetworkIPAvailability object with network_name, network_id, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ network_ip_attrs = {
+ 'network_id': 'network-id-' + uuid.uuid4().hex,
+ 'network_name': 'network-name-' + uuid.uuid4().hex,
+ 'project_id': '',
+ 'subnet_ip_availability': [],
+ 'total_ips': 254,
+ 'used_ips': 6,
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+ network_ip_attrs.update(attrs)
+
+ network_ip_availability = _ip_availability.NetworkIPAvailability(
+ **network_ip_attrs)
+
+ return network_ip_availability
+
+
+def create_ip_availability(count=2):
+ """Create fake list of ip availability stats of multiple networks.
+
+ :param int count:
+ The number of networks to fake
+ :return:
+ A list of NetworkIPAvailability objects faking
+ network ip availability stats
+ """
+ network_ip_availabilities = []
+ for i in range(0, count):
+ network_ip_availability = create_one_ip_availability()
+ network_ip_availabilities.append(network_ip_availability)
+
+ return network_ip_availabilities
+
+
+def create_one_network(attrs=None):
+ """Create a fake network.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ An Network object, with id, name, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ network_attrs = {
+ 'created_at': '2021-11-29T10:10:23.000000',
+ 'id': 'network-id-' + uuid.uuid4().hex,
+ 'name': 'network-name-' + uuid.uuid4().hex,
+ 'status': 'ACTIVE',
+ 'description': 'network-description-' + uuid.uuid4().hex,
+ 'dns_domain': 'example.org.',
+ 'mtu': '1350',
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'admin_state_up': True,
+ 'shared': False,
+ 'subnets': ['a', 'b'],
+ 'segments': 'network-segment-' + uuid.uuid4().hex,
+ 'provider:network_type': 'vlan',
+ 'provider:physical_network': 'physnet1',
+ 'provider:segmentation_id': "400",
+ 'router:external': True,
+ 'availability_zones': [],
+ 'availability_zone_hints': [],
+ 'is_default': False,
+ 'is_vlan_transparent': True,
+ 'port_security_enabled': True,
+ 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
+ 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex,
+ 'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex,
+ 'tags': [],
+ 'location': 'MUNCHMUNCHMUNCH',
+ 'updated_at': '2021-11-29T10:10:25.000000',
+ }
+
+ # Overwrite default attributes.
+ network_attrs.update(attrs)
+
+ network = _network.Network(**network_attrs)
+
+ return network
+
+
+def create_networks(attrs=None, count=2):
+ """Create multiple fake networks.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of networks to fake
+ :return:
+ A list of Network objects faking the networks
+ """
+ networks = []
+ for i in range(0, count):
+ networks.append(create_one_network(attrs))
+
+ return networks
+
+
+def get_networks(networks=None, count=2):
+ """Get an iterable Mock object with a list of faked networks.
+
+ If networks list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List networks:
+ A list of Network objects faking networks
+ :param int count:
+ The number of networks to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ networks
+ """
+ if networks is None:
+ networks = create_networks(count)
+ return mock.Mock(side_effect=networks)
+
+
+def create_one_network_flavor(attrs=None):
+ """Create a fake network flavor.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A Flavor object faking the network flavor
+ """
+ attrs = attrs or {}
+
+ fake_uuid = uuid.uuid4().hex
+ network_flavor_attrs = {
+ 'description': 'network-flavor-description-' + fake_uuid,
+ 'is_enabled': True,
+ 'id': 'network-flavor-id-' + fake_uuid,
+ 'name': 'network-flavor-name-' + fake_uuid,
+ 'service_type': 'vpn',
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ # Overwrite default attributes.
+ network_flavor_attrs.update(attrs)
+
+ network_flavor = _flavor.Flavor(**network_flavor_attrs)
+
+ return network_flavor
+
+
+def create_flavor(attrs=None, count=2):
+ """Create multiple fake network flavors.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of network flavors to fake
+ :return:
+ A list of Flavor objects faking the network falvors
+ """
+ network_flavors = []
+ for i in range(0, count):
+ network_flavors.append(create_one_network_flavor(attrs))
+
+ return network_flavors
+
+
+def get_flavor(network_flavors=None, count=2):
+ """Get a list of flavors."""
+ if network_flavors is None:
+ network_flavors = create_flavor(count)
+ return mock.Mock(side_effect=network_flavors)
+
+
+def create_one_network_segment(attrs=None):
+ """Create a fake network segment.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ An Segment object faking the network segment
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ fake_uuid = uuid.uuid4().hex
+ network_segment_attrs = {
+ 'description': 'network-segment-description-' + fake_uuid,
+ 'id': 'network-segment-id-' + fake_uuid,
+ 'name': 'network-segment-name-' + fake_uuid,
+ 'network_id': 'network-id-' + fake_uuid,
+ 'network_type': 'vlan',
+ 'physical_network': 'physical-network-name-' + fake_uuid,
+ 'segmentation_id': 1024,
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ # Overwrite default attributes.
+ network_segment_attrs.update(attrs)
+
+ network_segment = _segment.Segment(**network_segment_attrs)
+
+ return network_segment
+
+
+def create_network_segments(attrs=None, count=2):
+ """Create multiple fake network segments.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of network segments to fake
+ :return:
+ A list of Segment objects faking the network segments
+ """
+ network_segments = []
+ for i in range(0, count):
+ network_segments.append(create_one_network_segment(attrs))
+ return network_segments
+
+
+def create_one_network_segment_range(attrs=None):
+ """Create a fake network segment range.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A NetworkSegmentRange object faking the network segment range
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ fake_uuid = uuid.uuid4().hex
+ network_segment_range_attrs = {
+ 'id': 'network-segment-range-id-' + fake_uuid,
+ 'name': 'network-segment-name-' + fake_uuid,
+ 'default': False,
+ 'shared': False,
+ 'project_id': 'project-id-' + fake_uuid,
+ 'network_type': 'vlan',
+ 'physical_network': 'physical-network-name-' + fake_uuid,
+ 'minimum': 100,
+ 'maximum': 106,
+ 'used': {104: '3312e4ba67864b2eb53f3f41432f8efc',
+ 106: '3312e4ba67864b2eb53f3f41432f8efc'},
+ 'available': [100, 101, 102, 103, 105],
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ # Overwrite default attributes.
+ network_segment_range_attrs.update(attrs)
+
+ network_segment_range = (
+ _segment_range.NetworkSegmentRange(**network_segment_range_attrs))
+
+ return network_segment_range
+
+
+def create_network_segment_ranges(attrs=None, count=2):
+ """Create multiple fake network segment ranges.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of network segment ranges to fake
+ :return:
+ A list of NetworkSegmentRange objects faking
+ the network segment ranges
+ """
+ network_segment_ranges = []
+ for i in range(0, count):
+ network_segment_ranges.append(create_one_network_segment_range(attrs))
+ return network_segment_ranges
+
+
+def create_one_port(attrs=None):
+ """Create a fake port.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A Port object, with id, name, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ port_attrs = {
+ 'is_admin_state_up': True,
+ 'allowed_address_pairs': [{}],
+ 'binding:host_id': 'binding-host-id-' + uuid.uuid4().hex,
+ 'binding:profile': {},
+ 'binding:vif_details': {},
+ 'binding:vif_type': 'ovs',
+ 'binding:vnic_type': 'normal',
+ 'data_plane_status': None,
+ 'description': 'description-' + uuid.uuid4().hex,
+ 'device_id': 'device-id-' + uuid.uuid4().hex,
+ 'device_owner': 'compute:nova',
+ 'device_profile': 'cyborg_device_profile_1',
+ 'dns_assignment': [{}],
+ 'dns_domain': 'dns-domain-' + uuid.uuid4().hex,
+ 'dns_name': 'dns-name-' + uuid.uuid4().hex,
+ 'extra_dhcp_opts': [{}],
+ 'fixed_ips': [{'ip_address': '10.0.0.3',
+ 'subnet_id': 'subnet-id-' + uuid.uuid4().hex}],
+ 'id': 'port-id-' + uuid.uuid4().hex,
+ 'mac_address': 'fa:16:3e:a9:4e:72',
+ 'name': 'port-name-' + uuid.uuid4().hex,
+ 'network_id': 'network-id-' + uuid.uuid4().hex,
+ 'numa_affinity_policy': 'required',
+ 'is_port_security_enabled': True,
+ 'security_group_ids': [],
+ 'status': 'ACTIVE',
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'qos_network_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
+ 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
+ 'tags': [],
+ 'propagate_uplink_status': False,
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ # Overwrite default attributes.
+ port_attrs.update(attrs)
+
+ port = _port.Port(**port_attrs)
+
+ return port
+
+
+def create_ports(attrs=None, count=2):
+ """Create multiple fake ports.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of ports to fake
+ :return:
+ A list of Port objects faking the ports
+ """
+ ports = []
+ for i in range(0, count):
+ ports.append(create_one_port(attrs))
+
+ return ports
+
+
+def get_ports(ports=None, count=2):
+ """Get an iterable Mock object with a list of faked ports.
+
+ If ports list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List ports:
+ A list of Port objects faking ports
+ :param int count:
+ The number of ports to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ ports
+ """
+ if ports is None:
+ ports = create_ports(count)
+ return mock.Mock(side_effect=ports)
+
+
+def create_one_network_agent(attrs=None):
+ """Create a fake network agent
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ An Agent object, with id, agent_type, and so on.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes
+ agent_attrs = {
+ 'id': 'agent-id-' + uuid.uuid4().hex,
+ 'agent_type': 'agent-type-' + uuid.uuid4().hex,
+ 'host': 'host-' + uuid.uuid4().hex,
+ 'availability_zone': 'zone-' + uuid.uuid4().hex,
+ 'alive': True,
+ 'admin_state_up': True,
+ 'binary': 'binary-' + uuid.uuid4().hex,
+ 'configurations': {'subnet': 2, 'networks': 1},
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+ agent_attrs.update(attrs)
+ agent = network_agent.Agent(**agent_attrs)
+
+ return agent
+
+
+def create_network_agents(attrs=None, count=2):
+ """Create multiple fake network agents.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of network agents to fake
+ :return:
+ A list of Agent objects faking the network agents
+ """
+ agents = []
+ for i in range(0, count):
+ agents.append(create_one_network_agent(attrs))
+
+ return agents
+
+
+def get_network_agents(agents=None, count=2):
+ """Get an iterable Mock object with a list of faked network agents.
+
+ If network agents list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List agents:
+ A list of Agent objects faking network agents
+ :param int count:
+ The number of network agents to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ network agents
+ """
+ if agents is None:
+ agents = create_network_agents(count)
+ return mock.Mock(side_effect=agents)
+
+
+def create_one_network_rbac(attrs=None):
+ """Create a fake network rbac
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A RBACPolicy object, with id, action, target_tenant,
+ project_id, type
+ """
+ attrs = attrs or {}
+
+ # Set default attributes
+ rbac_attrs = {
+ 'id': 'rbac-id-' + uuid.uuid4().hex,
+ 'object_type': 'network',
+ 'object_id': 'object-id-' + uuid.uuid4().hex,
+ 'action': 'access_as_shared',
+ 'target_tenant': 'target-tenant-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ rbac_attrs.update(attrs)
+ rbac = network_rbac.RBACPolicy(**rbac_attrs)
+
+ return rbac
+
+
+def create_network_rbacs(attrs=None, count=2):
+ """Create multiple fake network rbac policies.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of rbac policies to fake
+ :return:
+ A list of RBACPolicy objects faking the rbac policies
+ """
+ rbac_policies = []
+ for i in range(0, count):
+ rbac_policies.append(create_one_network_rbac(attrs))
+
+ return rbac_policies
+
+
+def get_network_rbacs(rbac_policies=None, count=2):
+ """Get an iterable Mock object with a list of faked rbac policies.
+
+ If rbac policies list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List rbac_policies:
+ A list of RBACPolicy objects faking rbac policies
+ :param int count:
+ The number of rbac policies to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ rbac policies
+ """
+ if rbac_policies is None:
+ rbac_policies = create_network_rbacs(count)
+ return mock.Mock(side_effect=rbac_policies)
+
+
+def create_one_service_profile(attrs=None):
+ """Create flavor profile."""
+ attrs = attrs or {}
+
+ flavor_profile_attrs = {
+ 'id': 'flavor-profile-id' + uuid.uuid4().hex,
+ 'description': 'flavor-profile-description-' + uuid.uuid4().hex,
+ 'project_id': 'project-id-' + uuid.uuid4().hex,
+ 'driver': 'driver-' + uuid.uuid4().hex,
+ 'metainfo': 'metainfo-' + uuid.uuid4().hex,
+ 'enabled': True,
+ 'location': 'MUNCHMUNCHMUNCH',
+ }
+
+ flavor_profile_attrs.update(attrs)
+
+ flavor_profile = _flavor_profile.ServiceProfile(**flavor_profile_attrs)
+
+ return flavor_profile
+
+
+def create_service_profile(attrs=None, count=2):
+ """Create multiple flavor profiles."""
+
+ flavor_profiles = []
+ for i in range(0, count):
+ flavor_profiles.append(create_one_service_profile(attrs))
+ return flavor_profiles
+
+
+def get_service_profile(flavor_profile=None, count=2):
+ """Get a list of flavor profiles."""
+ if flavor_profile is None:
+ flavor_profile = create_service_profile(count)
+
+ return mock.Mock(side_effect=flavor_profile)
+
+
def create_one_local_ip(attrs=None):
"""Create a fake local ip.
@@ -2142,7 +2050,7 @@ def create_local_ip_associations(attrs=None, count=2):
:param Dictionary attrs:
A dictionary with all attributes
:param int count:
- The number of address groups to fake
+ The number of local ip associations to fake
:return:
A list of FakeResource objects faking the local ip associations
"""
diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py
index 703ab74..f550db6 100644
--- a/openstackclient/tests/unit/network/v2/test_address_group.py
+++ b/openstackclient/tests/unit/network/v2/test_address_group.py
@@ -41,7 +41,7 @@ class TestCreateAddressGroup(TestAddressGroup):
domain = identity_fakes_v3.FakeDomain.create_one_domain()
# The new address group created.
new_address_group = (
- network_fakes.FakeAddressGroup.create_one_address_group(
+ network_fakes.create_one_address_group(
attrs={
'project_id': project.id,
}
@@ -133,16 +133,13 @@ class TestCreateAddressGroup(TestAddressGroup):
class TestDeleteAddressGroup(TestAddressGroup):
# The address group to delete.
- _address_groups = (
- network_fakes.FakeAddressGroup.create_address_groups(count=2))
+ _address_groups = network_fakes.create_address_groups(count=2)
def setUp(self):
super(TestDeleteAddressGroup, self).setUp()
self.network.delete_address_group = mock.Mock(return_value=None)
- self.network.find_address_group = (
- network_fakes.FakeAddressGroup.get_address_groups(
- address_groups=self._address_groups)
- )
+ self.network.find_address_group = network_fakes.get_address_groups(
+ address_groups=self._address_groups)
# Get the command object to test
self.cmd = address_group.DeleteAddressGroup(self.app, self.namespace)
@@ -216,8 +213,7 @@ class TestDeleteAddressGroup(TestAddressGroup):
class TestListAddressGroup(TestAddressGroup):
# The address groups to list up.
- address_groups = (
- network_fakes.FakeAddressGroup.create_address_groups(count=3))
+ address_groups = network_fakes.create_address_groups(count=3)
columns = (
'ID',
'Name',
@@ -308,7 +304,7 @@ class TestListAddressGroup(TestAddressGroup):
class TestSetAddressGroup(TestAddressGroup):
# The address group to set.
- _address_group = network_fakes.FakeAddressGroup.create_one_address_group()
+ _address_group = network_fakes.create_one_address_group()
def setUp(self):
super(TestSetAddressGroup, self).setUp()
@@ -392,7 +388,7 @@ class TestSetAddressGroup(TestAddressGroup):
class TestShowAddressGroup(TestAddressGroup):
# The address group to show.
- _address_group = network_fakes.FakeAddressGroup.create_one_address_group()
+ _address_group = network_fakes.create_one_address_group()
columns = (
'addresses',
'description',
@@ -444,7 +440,7 @@ class TestShowAddressGroup(TestAddressGroup):
class TestUnsetAddressGroup(TestAddressGroup):
# The address group to unset.
- _address_group = network_fakes.FakeAddressGroup.create_one_address_group()
+ _address_group = network_fakes.create_one_address_group()
def setUp(self):
super(TestUnsetAddressGroup, self).setUp()
diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py
index 7e7c421..d4c8352 100644
--- a/openstackclient/tests/unit/network/v2/test_address_scope.py
+++ b/openstackclient/tests/unit/network/v2/test_address_scope.py
@@ -40,12 +40,10 @@ class TestCreateAddressScope(TestAddressScope):
project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain()
# The new address scope created.
- new_address_scope = (
- network_fakes.FakeAddressScope.create_one_address_scope(
- attrs={
- 'project_id': project.id,
- }
- ))
+ new_address_scope = network_fakes.create_one_address_scope(
+ attrs={
+ 'project_id': project.id,
+ })
columns = (
'id',
'ip_version',
@@ -58,7 +56,7 @@ class TestCreateAddressScope(TestAddressScope):
new_address_scope.ip_version,
new_address_scope.name,
new_address_scope.project_id,
- new_address_scope.shared,
+ new_address_scope.is_shared,
)
def setUp(self):
@@ -153,16 +151,13 @@ class TestCreateAddressScope(TestAddressScope):
class TestDeleteAddressScope(TestAddressScope):
# The address scope to delete.
- _address_scopes = (
- network_fakes.FakeAddressScope.create_address_scopes(count=2))
+ _address_scopes = network_fakes.create_address_scopes(count=2)
def setUp(self):
super(TestDeleteAddressScope, self).setUp()
self.network.delete_address_scope = mock.Mock(return_value=None)
- self.network.find_address_scope = (
- network_fakes.FakeAddressScope.get_address_scopes(
- address_scopes=self._address_scopes)
- )
+ self.network.find_address_scope = network_fakes.get_address_scopes(
+ address_scopes=self._address_scopes)
# Get the command object to test
self.cmd = address_scope.DeleteAddressScope(self.app, self.namespace)
@@ -237,8 +232,7 @@ class TestDeleteAddressScope(TestAddressScope):
class TestListAddressScope(TestAddressScope):
# The address scopes to list up.
- address_scopes = (
- network_fakes.FakeAddressScope.create_address_scopes(count=3))
+ address_scopes = network_fakes.create_address_scopes(count=3)
columns = (
'ID',
'Name',
@@ -252,7 +246,7 @@ class TestListAddressScope(TestAddressScope):
scope.id,
scope.name,
scope.ip_version,
- scope.shared,
+ scope.is_shared,
scope.project_id,
))
@@ -377,7 +371,7 @@ class TestListAddressScope(TestAddressScope):
class TestSetAddressScope(TestAddressScope):
# The address scope to set.
- _address_scope = network_fakes.FakeAddressScope.create_one_address_scope()
+ _address_scope = network_fakes.create_one_address_scope()
def setUp(self):
super(TestSetAddressScope, self).setUp()
@@ -448,7 +442,7 @@ class TestShowAddressScope(TestAddressScope):
# The address scope to show.
_address_scope = (
- network_fakes.FakeAddressScope.create_one_address_scope())
+ network_fakes.create_one_address_scope())
columns = (
'id',
'ip_version',
@@ -461,7 +455,7 @@ class TestShowAddressScope(TestAddressScope):
_address_scope.ip_version,
_address_scope.name,
_address_scope.project_id,
- _address_scope.shared,
+ _address_scope.is_shared,
)
def setUp(self):
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
index 5b5c83a..f76dcc7 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
@@ -40,9 +40,9 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2):
class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
# Fake data for option tests.
- floating_network = network_fakes.FakeNetwork.create_one_network()
+ floating_network = network_fakes.create_one_network()
subnet = network_fakes.FakeSubnet.create_one_subnet()
- port = network_fakes.FakePort.create_one_port()
+ port = network_fakes.create_one_port()
# The floating ip created.
floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
@@ -378,10 +378,10 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
# The floating ips to list up
floating_ips = network_fakes.FakeFloatingIP.create_floating_ips(count=3)
- fake_network = network_fakes.FakeNetwork.create_one_network({
+ fake_network = network_fakes.create_one_network({
'id': 'fake_network_id',
})
- fake_port = network_fakes.FakePort.create_one_port({
+ fake_port = network_fakes.create_one_port({
'id': 'fake_port_id',
})
fake_router = network_fakes.FakeRouter.create_one_router({
@@ -700,9 +700,9 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork):
class TestSetFloatingIP(TestFloatingIPNetwork):
# Fake data for option tests.
- floating_network = network_fakes.FakeNetwork.create_one_network()
+ floating_network = network_fakes.create_one_network()
subnet = network_fakes.FakeSubnet.create_one_subnet()
- port = network_fakes.FakePort.create_one_port()
+ port = network_fakes.create_one_port()
# The floating ip to be set.
floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
@@ -932,9 +932,9 @@ class TestSetFloatingIP(TestFloatingIPNetwork):
class TestUnsetFloatingIP(TestFloatingIPNetwork):
- floating_network = network_fakes.FakeNetwork.create_one_network()
+ floating_network = network_fakes.create_one_network()
subnet = network_fakes.FakeSubnet.create_one_subnet()
- port = network_fakes.FakePort.create_one_port()
+ port = network_fakes.create_one_port()
# The floating ip to be unset.
floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
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
index 7b9e3aa..97399f4 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py
@@ -31,7 +31,7 @@ class TestFloatingIPPortForwarding(network_fakes.TestNetworkV2):
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.port = network_fakes.create_one_port()
self.project = identity_fakes_v2.FakeProject.create_one_project()
self.network.find_port = mock.Mock(return_value=self.port)
diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py
index 880cf58..fbe3b1a 100644
--- a/openstackclient/tests/unit/network/v2/test_ip_availability.py
+++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py
@@ -38,8 +38,7 @@ class TestIPAvailability(network_fakes.TestNetworkV2):
class TestListIPAvailability(TestIPAvailability):
- _ip_availability = \
- network_fakes.FakeIPAvailability.create_ip_availability(count=3)
+ _ip_availability = network_fakes.create_ip_availability(count=3)
columns = (
'Network ID',
'Network Name',
@@ -117,10 +116,9 @@ class TestListIPAvailability(TestIPAvailability):
class TestShowIPAvailability(TestIPAvailability):
- _network = network_fakes.FakeNetwork.create_one_network()
- _ip_availability = \
- network_fakes.FakeIPAvailability.create_one_ip_availability(
- attrs={'network_id': _network.id})
+ _network = network_fakes.create_one_network()
+ _ip_availability = network_fakes.create_one_ip_availability(
+ attrs={'network_id': _network.id})
columns = (
'network_id',
diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py
index 17e8dcd..97e246d 100644
--- a/openstackclient/tests/unit/network/v2/test_local_ip.py
+++ b/openstackclient/tests/unit/network/v2/test_local_ip.py
@@ -40,8 +40,8 @@ class TestLocalIP(network_fakes.TestNetworkV2):
class TestCreateLocalIP(TestLocalIP):
project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain()
- local_ip_network = network_fakes.FakeNetwork.create_one_network()
- port = network_fakes.FakePort.create_one_port()
+ local_ip_network = network_fakes.create_one_network()
+ port = network_fakes.create_one_port()
# The new local ip created.
new_local_ip = network_fakes.create_one_local_ip(
attrs={'project_id': project.id,
@@ -216,7 +216,7 @@ class TestListLocalIP(TestLocalIP):
# The local ip to list up.
local_ips = (
network_fakes.create_local_ips(count=3))
- fake_network = network_fakes.FakeNetwork.create_one_network(
+ fake_network = network_fakes.create_one_network(
{'id': 'fake_network_id'}
)
diff --git a/openstackclient/tests/unit/network/v2/test_local_ip_association.py b/openstackclient/tests/unit/network/v2/test_local_ip_association.py
index 9775930..0e45374 100644
--- a/openstackclient/tests/unit/network/v2/test_local_ip_association.py
+++ b/openstackclient/tests/unit/network/v2/test_local_ip_association.py
@@ -29,7 +29,7 @@ class TestLocalIPAssociation(network_fakes.TestNetworkV2):
super().setUp()
self.network = self.app.client_manager.network
self.local_ip = network_fakes.create_one_local_ip()
- self.fixed_port = network_fakes.FakePort.create_one_port()
+ self.fixed_port = network_fakes.create_one_port()
self.project = identity_fakes_v2.FakeProject.create_one_project()
self.network.find_port = mock.Mock(return_value=self.fixed_port)
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 2b04edf..6adb9e1 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -46,7 +46,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain()
# The new network created.
- _network = network_fakes.FakeNetwork.create_one_network(
+ _network = network_fakes.create_one_network(
attrs={
'project_id': project.id,
'availability_zone_hints': ["nova"],
@@ -59,12 +59,14 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'admin_state_up',
'availability_zone_hints',
'availability_zones',
+ 'created_at',
'description',
'dns_domain',
'id',
'ipv4_address_scope',
'ipv6_address_scope',
'is_default',
+ 'is_vlan_transparent',
'mtu',
'name',
'port_security_enabled',
@@ -76,14 +78,18 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'router:external',
'shared',
'status',
+ 'segments',
'subnets',
'tags',
+ 'revision_number',
+ 'updated_at',
)
data = (
- network.AdminStateColumn(_network.admin_state_up),
+ network.AdminStateColumn(_network.is_admin_state_up),
format_columns.ListColumn(_network.availability_zone_hints),
format_columns.ListColumn(_network.availability_zones),
+ _network.created_at,
_network.description,
_network.dns_domain,
_network.id,
@@ -99,10 +105,14 @@ class TestCreateNetworkIdentityV3(TestNetwork):
_network.provider_segmentation_id,
_network.qos_policy_id,
network.RouterExternalColumn(_network.is_router_external),
- _network.shared,
+ _network.is_shared,
+ _network.is_vlan_transparent,
_network.status,
- format_columns.ListColumn(_network.subnets),
+ _network.segments,
+ format_columns.ListColumn(_network.subnet_ids),
format_columns.ListColumn(_network.tags),
+ _network.revision_number,
+ _network.updated_at,
)
def setUp(self):
@@ -145,7 +155,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'name': self._network.name,
})
self.assertFalse(self.network.set_tags.called)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_all_options(self):
@@ -153,7 +163,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
"--disable",
"--share",
"--description", self._network.description,
- "--mtu", self._network.mtu,
+ "--mtu", str(self._network.mtu),
"--project", self.project.name,
"--project-domain", self.domain.name,
"--availability-zone-hint", "nova",
@@ -171,7 +181,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
('disable', True),
('share', True),
('description', self._network.description),
- ('mtu', self._network.mtu),
+ ('mtu', str(self._network.mtu)),
('project', self.project.name),
('project_domain', self.domain.name),
('availability_zone_hints', ["nova"]),
@@ -196,7 +206,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'name': self._network.name,
'shared': True,
'description': self._network.description,
- 'mtu': self._network.mtu,
+ 'mtu': str(self._network.mtu),
'project_id': self.project.id,
'is_default': True,
'router:external': True,
@@ -208,7 +218,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'port_security_enabled': True,
'dns_domain': 'example.org.',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_other_options(self):
@@ -235,7 +245,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'shared': False,
'port_security_enabled': False,
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def _test_create_with_tag(self, add_tags=True):
@@ -267,7 +277,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
tests_utils.CompareBySet(['red', 'blue']))
else:
self.assertFalse(self.network.set_tags.called)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_tags(self):
@@ -281,20 +291,21 @@ class TestCreateNetworkIdentityV2(TestNetwork):
project = identity_fakes_v2.FakeProject.create_one_project()
# The new network created.
- _network = network_fakes.FakeNetwork.create_one_network(
+ _network = network_fakes.create_one_network(
attrs={'project_id': project.id}
)
-
columns = (
'admin_state_up',
'availability_zone_hints',
'availability_zones',
+ 'created_at',
'description',
'dns_domain',
'id',
'ipv4_address_scope',
'ipv6_address_scope',
'is_default',
+ 'is_vlan_transparent',
'mtu',
'name',
'port_security_enabled',
@@ -306,14 +317,18 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'router:external',
'shared',
'status',
+ 'segments',
'subnets',
'tags',
+ 'revision_number',
+ 'updated_at',
)
data = (
- network.AdminStateColumn(_network.admin_state_up),
+ network.AdminStateColumn(_network.is_admin_state_up),
format_columns.ListColumn(_network.availability_zone_hints),
format_columns.ListColumn(_network.availability_zones),
+ _network.created_at,
_network.description,
_network.dns_domain,
_network.id,
@@ -329,10 +344,14 @@ class TestCreateNetworkIdentityV2(TestNetwork):
_network.provider_segmentation_id,
_network.qos_policy_id,
network.RouterExternalColumn(_network.is_router_external),
- _network.shared,
+ _network.is_shared,
+ _network.is_vlan_transparent,
_network.status,
- format_columns.ListColumn(_network.subnets),
+ _network.segments,
+ format_columns.ListColumn(_network.subnet_ids),
format_columns.ListColumn(_network.tags),
+ _network.revision_number,
+ _network.updated_at,
)
def setUp(self):
@@ -380,7 +399,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'project_id': self.project.id,
})
self.assertFalse(self.network.set_tags.called)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_domain_identityv2(self):
@@ -413,11 +432,11 @@ class TestDeleteNetwork(TestNetwork):
super(TestDeleteNetwork, self).setUp()
# The networks to delete
- self._networks = network_fakes.FakeNetwork.create_networks(count=3)
+ self._networks = network_fakes.create_networks(count=3)
self.network.delete_network = mock.Mock(return_value=None)
- self.network.find_network = network_fakes.FakeNetwork.get_networks(
+ self.network.find_network = network_fakes.get_networks(
networks=self._networks)
# Get the command object to test
@@ -495,7 +514,7 @@ class TestDeleteNetwork(TestNetwork):
class TestListNetwork(TestNetwork):
# The networks going to be listed up.
- _network = network_fakes.FakeNetwork.create_networks(count=3)
+ _network = network_fakes.create_networks(count=3)
columns = (
'ID',
@@ -521,7 +540,7 @@ class TestListNetwork(TestNetwork):
data.append((
net.id,
net.name,
- format_columns.ListColumn(net.subnets),
+ format_columns.ListColumn(net.subnet_ids),
))
data_long = []
@@ -531,9 +550,9 @@ class TestListNetwork(TestNetwork):
net.name,
net.status,
net.project_id,
- network.AdminStateColumn(net.admin_state_up),
- net.shared,
- format_columns.ListColumn(net.subnets),
+ network.AdminStateColumn(net.is_admin_state_up),
+ net.is_shared,
+ format_columns.ListColumn(net.subnet_ids),
net.provider_network_type,
network.RouterExternalColumn(net.is_router_external),
format_columns.ListColumn(net.availability_zones),
@@ -549,7 +568,7 @@ class TestListNetwork(TestNetwork):
self.network.networks = mock.Mock(return_value=self._network)
self._agent = \
- network_fakes.FakeNetworkAgent.create_one_network_agent()
+ network_fakes.create_one_network_agent()
self.network.get_agent = mock.Mock(return_value=self._agent)
self.network.dhcp_agent_hosting_networks = mock.Mock(
@@ -840,13 +859,12 @@ class TestListNetwork(TestNetwork):
('agent_id', self._agent.id),
]
- attrs = {self._agent, }
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network.dhcp_agent_hosting_networks.assert_called_once_with(
- *attrs)
+ self._agent)
self.assertEqual(self.columns, columns)
self.assertCountEqual(list(data), list(self.data))
@@ -880,7 +898,7 @@ class TestListNetwork(TestNetwork):
class TestSetNetwork(TestNetwork):
# The network to set.
- _network = network_fakes.FakeNetwork.create_one_network(
+ _network = network_fakes.create_one_network(
{'tags': ['green', 'red']})
qos_policy = (network_fakes.FakeNetworkQosPolicy.
create_one_qos_policy(attrs={'id': _network.qos_policy_id}))
@@ -1025,18 +1043,19 @@ class TestSetNetwork(TestNetwork):
class TestShowNetwork(TestNetwork):
# The network to show.
- _network = network_fakes.FakeNetwork.create_one_network()
-
+ _network = network_fakes.create_one_network()
columns = (
'admin_state_up',
'availability_zone_hints',
'availability_zones',
+ 'created_at',
'description',
'dns_domain',
'id',
'ipv4_address_scope',
'ipv6_address_scope',
'is_default',
+ 'is_vlan_transparent',
'mtu',
'name',
'port_security_enabled',
@@ -1048,14 +1067,18 @@ class TestShowNetwork(TestNetwork):
'router:external',
'shared',
'status',
+ 'segments',
'subnets',
'tags',
+ 'revision_number',
+ 'updated_at',
)
data = (
- network.AdminStateColumn(_network.admin_state_up),
+ network.AdminStateColumn(_network.is_admin_state_up),
format_columns.ListColumn(_network.availability_zone_hints),
format_columns.ListColumn(_network.availability_zones),
+ _network.created_at,
_network.description,
_network.dns_domain,
_network.id,
@@ -1071,10 +1094,14 @@ class TestShowNetwork(TestNetwork):
_network.provider_segmentation_id,
_network.qos_policy_id,
network.RouterExternalColumn(_network.is_router_external),
- _network.shared,
+ _network.is_shared,
+ _network.is_vlan_transparent,
_network.status,
- format_columns.ListColumn(_network.subnets),
+ _network.segments,
+ format_columns.ListColumn(_network.subnet_ids),
format_columns.ListColumn(_network.tags),
+ _network.revision_number,
+ _network.updated_at,
)
def setUp(self):
@@ -1106,14 +1133,14 @@ class TestShowNetwork(TestNetwork):
self.network.find_network.assert_called_once_with(
self._network.name, ignore_missing=False)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
class TestUnsetNetwork(TestNetwork):
# The network to set.
- _network = network_fakes.FakeNetwork.create_one_network(
+ _network = network_fakes.create_one_network(
{'tags': ['green', 'red']})
qos_policy = (network_fakes.FakeNetworkQosPolicy.
create_one_qos_policy(attrs={'id': _network.qos_policy_id}))
diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py
index 734a36e..15c4c5d 100644
--- a/openstackclient/tests/unit/network/v2/test_network_agent.py
+++ b/openstackclient/tests/unit/network/v2/test_network_agent.py
@@ -33,8 +33,8 @@ class TestNetworkAgent(network_fakes.TestNetworkV2):
class TestAddNetworkToAgent(TestNetworkAgent):
- net = network_fakes.FakeNetwork.create_one_network()
- agent = network_fakes.FakeNetworkAgent.create_one_network_agent()
+ net = network_fakes.create_one_network()
+ agent = network_fakes.create_one_network_agent()
def setUp(self):
super(TestAddNetworkToAgent, self).setUp()
@@ -76,7 +76,7 @@ class TestAddNetworkToAgent(TestNetworkAgent):
class TestAddRouterAgent(TestNetworkAgent):
_router = network_fakes.FakeRouter.create_one_router()
- _agent = network_fakes.FakeNetworkAgent.create_one_network_agent()
+ _agent = network_fakes.create_one_network_agent()
def setUp(self):
super(TestAddRouterAgent, self).setUp()
@@ -115,8 +115,7 @@ class TestAddRouterAgent(TestNetworkAgent):
class TestDeleteNetworkAgent(TestNetworkAgent):
- network_agents = (
- network_fakes.FakeNetworkAgent.create_network_agents(count=2))
+ network_agents = network_fakes.create_network_agents(count=2)
def setUp(self):
super(TestDeleteNetworkAgent, self).setUp()
@@ -188,8 +187,7 @@ class TestDeleteNetworkAgent(TestNetworkAgent):
class TestListNetworkAgent(TestNetworkAgent):
- network_agents = (
- network_fakes.FakeNetworkAgent.create_network_agents(count=3))
+ network_agents = network_fakes.create_network_agents(count=3)
columns = (
'ID',
@@ -207,8 +205,8 @@ class TestListNetworkAgent(TestNetworkAgent):
agent.agent_type,
agent.host,
agent.availability_zone,
- network_agent.AliveColumn(agent.alive),
- network_agent.AdminStateColumn(agent.admin_state_up),
+ network_agent.AliveColumn(agent.is_alive),
+ network_agent.AdminStateColumn(agent.is_admin_state_up),
agent.binary,
))
@@ -217,11 +215,10 @@ class TestListNetworkAgent(TestNetworkAgent):
self.network.agents = mock.Mock(
return_value=self.network_agents)
- _testagent = \
- network_fakes.FakeNetworkAgent.create_one_network_agent()
+ _testagent = network_fakes.create_one_network_agent()
self.network.get_agent = mock.Mock(return_value=_testagent)
- self._testnetwork = network_fakes.FakeNetwork.create_one_network()
+ self._testnetwork = network_fakes.create_one_network()
self.network.find_network = mock.Mock(return_value=self._testnetwork)
self.network.network_hosting_dhcp_agents = mock.Mock(
return_value=self.network_agents)
@@ -290,13 +287,11 @@ class TestListNetworkAgent(TestNetworkAgent):
('network', self._testnetwork.id),
]
- attrs = {self._testnetwork, }
-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network.network_hosting_dhcp_agents.assert_called_once_with(
- *attrs)
+ self._testnetwork)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, list(data))
@@ -343,13 +338,13 @@ class TestListNetworkAgent(TestNetworkAgent):
router_agent_data = [d + ('',) for d in self.data]
self.assertEqual(router_agent_columns, columns)
- self.assertCountEqual(router_agent_data, list(data))
+ self.assertEqual(len(router_agent_data), len(list(data)))
class TestRemoveNetworkFromAgent(TestNetworkAgent):
- net = network_fakes.FakeNetwork.create_one_network()
- agent = network_fakes.FakeNetworkAgent.create_one_network_agent()
+ net = network_fakes.create_one_network()
+ agent = network_fakes.create_one_network_agent()
def setUp(self):
super(TestRemoveNetworkFromAgent, self).setUp()
@@ -400,7 +395,7 @@ class TestRemoveNetworkFromAgent(TestNetworkAgent):
class TestRemoveRouterAgent(TestNetworkAgent):
_router = network_fakes.FakeRouter.create_one_router()
- _agent = network_fakes.FakeNetworkAgent.create_one_network_agent()
+ _agent = network_fakes.create_one_network_agent()
def setUp(self):
super(TestRemoveRouterAgent, self).setUp()
@@ -440,8 +435,7 @@ class TestRemoveRouterAgent(TestNetworkAgent):
class TestSetNetworkAgent(TestNetworkAgent):
- _network_agent = (
- network_fakes.FakeNetworkAgent.create_one_network_agent())
+ _network_agent = network_fakes.create_one_network_agent()
def setUp(self):
super(TestSetNetworkAgent, self).setUp()
@@ -517,8 +511,7 @@ class TestSetNetworkAgent(TestNetworkAgent):
class TestShowNetworkAgent(TestNetworkAgent):
- _network_agent = (
- network_fakes.FakeNetworkAgent.create_one_network_agent())
+ _network_agent = network_fakes.create_one_network_agent()
columns = (
'admin_state_up',
@@ -526,19 +519,33 @@ class TestShowNetworkAgent(TestNetworkAgent):
'alive',
'availability_zone',
'binary',
- 'configurations',
+ 'configuration',
+ 'created_at',
+ 'description',
'host',
+ 'ha_state',
'id',
+ 'last_heartbeat_at',
+ 'resources_synced',
+ 'started_at',
+ 'topic',
)
data = (
- network_agent.AdminStateColumn(_network_agent.admin_state_up),
+ network_agent.AdminStateColumn(_network_agent.is_admin_state_up),
_network_agent.agent_type,
network_agent.AliveColumn(_network_agent.is_alive),
_network_agent.availability_zone,
_network_agent.binary,
- format_columns.DictColumn(_network_agent.configurations),
+ format_columns.DictColumn(_network_agent.configuration),
+ _network_agent.created_at,
+ _network_agent.description,
+ _network_agent.ha_state,
_network_agent.host,
_network_agent.id,
+ _network_agent.last_heartbeat_at,
+ _network_agent.resources_synced,
+ _network_agent.started_at,
+ _network_agent.topic,
)
def setUp(self):
@@ -570,5 +577,5 @@ class TestShowNetworkAgent(TestNetworkAgent):
self.network.get_agent.assert_called_once_with(
self._network_agent.id)
- self.assertEqual(self.columns, columns)
- self.assertCountEqual(list(self.data), list(data))
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(len(list(self.data)), len(list(data)))
diff --git a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py
index d77d689..a5dbcde 100644
--- a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py
+++ b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py
@@ -29,9 +29,9 @@ class TestAutoAllocatedTopology(network_fakes.TestNetworkV2):
class TestCreateAutoAllocatedTopology(TestAutoAllocatedTopology):
project = identity_fakes.FakeProject.create_one_project()
- network_object = network_fakes.FakeNetwork.create_one_network()
+ network_object = network_fakes.create_one_network()
- topology = network_fakes.FakeAutoAllocatedTopology.create_one_topology(
+ topology = network_fakes.create_one_topology(
attrs={'id': network_object.id,
'project_id': project.id}
)
@@ -127,9 +127,9 @@ class TestCreateAutoAllocatedTopology(TestAutoAllocatedTopology):
class TestValidateAutoAllocatedTopology(TestAutoAllocatedTopology):
project = identity_fakes.FakeProject.create_one_project()
- network_object = network_fakes.FakeNetwork.create_one_network()
+ network_object = network_fakes.create_one_network()
- topology = network_fakes.FakeAutoAllocatedTopology.create_one_topology(
+ topology = network_fakes.create_one_topology(
attrs={'id': network_object.id,
'project_id': project.id}
)
@@ -204,9 +204,9 @@ class TestValidateAutoAllocatedTopology(TestAutoAllocatedTopology):
class TestDeleteAutoAllocatedTopology(TestAutoAllocatedTopology):
project = identity_fakes.FakeProject.create_one_project()
- network_object = network_fakes.FakeNetwork.create_one_network()
+ network_object = network_fakes.create_one_network()
- topology = network_fakes.FakeAutoAllocatedTopology.create_one_topology(
+ topology = network_fakes.create_one_topology(
attrs={'id': network_object.id,
'project_id': project.id}
)
diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py
index 20c5b9d..3149def 100644
--- a/openstackclient/tests/unit/network/v2/test_network_flavor.py
+++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py
@@ -39,10 +39,8 @@ class TestNetworkFlavor(network_fakes.TestNetworkV2):
class TestAddNetworkFlavorToProfile(TestNetworkFlavor):
- network_flavor = \
- network_fakes.FakeNetworkFlavor.create_one_network_flavor()
- service_profile = \
- network_fakes.FakeNetworkFlavorProfile.create_one_service_profile()
+ network_flavor = network_fakes.create_one_network_flavor()
+ service_profile = network_fakes.create_one_service_profile()
def setUp(self):
super(TestAddNetworkFlavorToProfile, self).setUp()
@@ -84,23 +82,22 @@ class TestCreateNetworkFlavor(TestNetworkFlavor):
project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain()
# The new network flavor created.
- new_network_flavor = (
- network_fakes.FakeNetworkFlavor.create_one_network_flavor())
+ new_network_flavor = network_fakes.create_one_network_flavor()
columns = (
'description',
'enabled',
'id',
'name',
- 'project_id',
- 'service_type'
+ 'service_type',
+ 'service_profile_ids',
)
data = (
new_network_flavor.description,
- new_network_flavor.enabled,
+ new_network_flavor.is_enabled,
new_network_flavor.id,
new_network_flavor.name,
- new_network_flavor.project_id,
new_network_flavor.service_type,
+ new_network_flavor.service_profile_ids,
)
def setUp(self):
@@ -139,14 +136,14 @@ class TestCreateNetworkFlavor(TestNetworkFlavor):
'service_type': self.new_network_flavor.service_type,
'name': self.new_network_flavor.name,
})
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(data))
def test_create_all_options(self):
arglist = [
'--description', self.new_network_flavor.description,
'--enable',
- '--project', self.new_network_flavor.project_id,
+ '--project', self.project.id,
'--project-domain', self.domain.name,
'--service-type', self.new_network_flavor.service_type,
self.new_network_flavor.name,
@@ -154,7 +151,7 @@ class TestCreateNetworkFlavor(TestNetworkFlavor):
verifylist = [
('description', self.new_network_flavor.description),
('enable', True),
- ('project', self.new_network_flavor.project_id),
+ ('project', self.project.id),
('project_domain', self.domain.name),
('service_type', self.new_network_flavor.service_type),
('name', self.new_network_flavor.name),
@@ -170,8 +167,8 @@ class TestCreateNetworkFlavor(TestNetworkFlavor):
'service_type': self.new_network_flavor.service_type,
'name': self.new_network_flavor.name,
})
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(data))
def test_create_disable(self):
arglist = [
@@ -193,23 +190,20 @@ class TestCreateNetworkFlavor(TestNetworkFlavor):
'service_type': self.new_network_flavor.service_type,
'name': self.new_network_flavor.name,
})
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(data))
class TestDeleteNetworkFlavor(TestNetworkFlavor):
# The network flavor to delete.
- _network_flavors = (
- network_fakes.FakeNetworkFlavor.create_flavor(count=2))
+ _network_flavors = network_fakes.create_flavor(count=2)
def setUp(self):
super(TestDeleteNetworkFlavor, self).setUp()
self.network.delete_flavor = mock.Mock(return_value=None)
- self.network.find_flavor = (
- network_fakes.FakeNetworkFlavor.get_flavor(
- network_flavors=self._network_flavors)
- )
+ self.network.find_flavor = network_fakes.get_flavor(
+ network_flavors=self._network_flavors)
# Get the command object to test
self.cmd = network_flavor.DeleteNetworkFlavor(self.app, self.namespace)
@@ -284,8 +278,7 @@ class TestDeleteNetworkFlavor(TestNetworkFlavor):
class TestListNetworkFlavor(TestNetworkFlavor):
# The network flavors to list up.
- _network_flavors = (
- network_fakes.FakeNetworkFlavor.create_flavor(count=2))
+ _network_flavors = network_fakes.create_flavor(count=2)
columns = (
'ID',
'Name',
@@ -298,7 +291,7 @@ class TestListNetworkFlavor(TestNetworkFlavor):
data.append((
flavor.id,
flavor.name,
- flavor.enabled,
+ flavor.is_enabled,
flavor.service_type,
flavor.description,
))
@@ -319,16 +312,14 @@ class TestListNetworkFlavor(TestNetworkFlavor):
columns, data = self.cmd.take_action(parsed_args)
self.network.flavors.assert_called_once_with(**{})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertEqual(self.data, list(data))
class TestRemoveNetworkFlavorFromProfile(TestNetworkFlavor):
- network_flavor = \
- network_fakes.FakeNetworkFlavor.create_one_network_flavor()
- service_profile = \
- network_fakes.FakeNetworkFlavorProfile.create_one_service_profile()
+ network_flavor = network_fakes.create_one_network_flavor()
+ service_profile = network_fakes.create_one_service_profile()
def setUp(self):
super(TestRemoveNetworkFlavorFromProfile, self).setUp()
@@ -368,23 +359,22 @@ class TestRemoveNetworkFlavorFromProfile(TestNetworkFlavor):
class TestShowNetworkFlavor(TestNetworkFlavor):
# The network flavor to show.
- new_network_flavor = (
- network_fakes.FakeNetworkFlavor.create_one_network_flavor())
+ new_network_flavor = network_fakes.create_one_network_flavor()
columns = (
'description',
'enabled',
'id',
'name',
- 'project_id',
- 'service_type'
+ 'service_type',
+ 'service_profile_ids',
)
data = (
new_network_flavor.description,
- new_network_flavor.enabled,
+ new_network_flavor.is_enabled,
new_network_flavor.id,
new_network_flavor.name,
- new_network_flavor.project_id,
new_network_flavor.service_type,
+ new_network_flavor.service_profile_ids,
)
def setUp(self):
@@ -416,15 +406,15 @@ class TestShowNetworkFlavor(TestNetworkFlavor):
self.network.find_flavor.assert_called_once_with(
self.new_network_flavor.name, ignore_missing=False)
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertEqual(set(self.columns), set(columns))
+ self.assertEqual(set(self.data), set(data))
class TestSetNetworkFlavor(TestNetworkFlavor):
# The network flavor to set.
new_network_flavor = (
- network_fakes.FakeNetworkFlavor.create_one_network_flavor())
+ network_fakes.create_one_network_flavor())
def setUp(self):
super(TestSetNetworkFlavor, self).setUp()
diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py
index 1cbe30b..5c2b9e2 100644
--- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py
+++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py
@@ -34,25 +34,23 @@ class TestFlavorProfile(network_fakes.TestNetworkV2):
class TestCreateFlavorProfile(TestFlavorProfile):
project = identity_fakes_v3.FakeProject.create_one_project()
domain = identity_fakes_v3.FakeDomain.create_one_domain()
- new_flavor_profile = (
- network_fakes.FakeNetworkFlavorProfile.
- create_one_service_profile()
- )
+ new_flavor_profile = network_fakes.create_one_service_profile()
+
columns = (
'description',
'driver',
'enabled',
'id',
- 'metainfo',
+ 'meta_info',
'project_id',
)
data = (
new_flavor_profile.description,
new_flavor_profile.driver,
- new_flavor_profile.enabled,
+ new_flavor_profile.is_enabled,
new_flavor_profile.id,
- new_flavor_profile.metainfo,
+ new_flavor_profile.meta_info,
new_flavor_profile.project_id,
)
@@ -72,7 +70,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
'--project-domain', self.domain.name,
"--enable",
"--driver", self.new_flavor_profile.driver,
- "--metainfo", self.new_flavor_profile.metainfo,
+ "--metainfo", self.new_flavor_profile.meta_info,
]
verifylist = [
@@ -81,7 +79,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
('project_domain', self.domain.name),
('enable', True),
('driver', self.new_flavor_profile.driver),
- ('metainfo', self.new_flavor_profile.metainfo)
+ ('metainfo', self.new_flavor_profile.meta_info)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -90,9 +88,9 @@ class TestCreateFlavorProfile(TestFlavorProfile):
self.network.create_service_profile.assert_called_once_with(
**{'description': self.new_flavor_profile.description,
'project_id': self.project.id,
- 'enabled': self.new_flavor_profile.enabled,
+ 'enabled': self.new_flavor_profile.is_enabled,
'driver': self.new_flavor_profile.driver,
- 'metainfo': self.new_flavor_profile.metainfo}
+ 'metainfo': self.new_flavor_profile.meta_info}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -103,7 +101,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
"--project", self.new_flavor_profile.project_id,
'--project-domain', self.domain.name,
"--enable",
- "--metainfo", self.new_flavor_profile.metainfo,
+ "--metainfo", self.new_flavor_profile.meta_info,
]
verifylist = [
@@ -111,7 +109,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
('project', self.new_flavor_profile.project_id),
('project_domain', self.domain.name),
('enable', True),
- ('metainfo', self.new_flavor_profile.metainfo)
+ ('metainfo', self.new_flavor_profile.meta_info)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -120,8 +118,8 @@ class TestCreateFlavorProfile(TestFlavorProfile):
self.network.create_service_profile.assert_called_once_with(
**{'description': self.new_flavor_profile.description,
'project_id': self.project.id,
- 'enabled': self.new_flavor_profile.enabled,
- 'metainfo': self.new_flavor_profile.metainfo}
+ 'enabled': self.new_flavor_profile.is_enabled,
+ 'metainfo': self.new_flavor_profile.meta_info}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -149,7 +147,7 @@ class TestCreateFlavorProfile(TestFlavorProfile):
self.network.create_service_profile.assert_called_once_with(
**{'description': self.new_flavor_profile.description,
'project_id': self.project.id,
- 'enabled': self.new_flavor_profile.enabled,
+ 'enabled': self.new_flavor_profile.is_enabled,
'driver': self.new_flavor_profile.driver,
}
)
@@ -203,14 +201,13 @@ class TestCreateFlavorProfile(TestFlavorProfile):
class TestDeleteFlavorProfile(TestFlavorProfile):
# The network flavor_profiles to delete.
- _network_flavor_profiles = (
- network_fakes.FakeNetworkFlavorProfile.create_service_profile(count=2))
+ _network_flavor_profiles = network_fakes.create_service_profile(count=2)
def setUp(self):
super(TestDeleteFlavorProfile, self).setUp()
self.network.delete_service_profile = mock.Mock(return_value=None)
self.network.find_service_profile = (
- network_fakes.FakeNetworkFlavorProfile.get_service_profile(
+ network_fakes.get_service_profile(
flavor_profile=self._network_flavor_profiles)
)
@@ -290,8 +287,7 @@ class TestDeleteFlavorProfile(TestFlavorProfile):
class TestListFlavorProfile(TestFlavorProfile):
# The network flavor profiles list
- _network_flavor_profiles = (
- network_fakes.FakeNetworkFlavorProfile.create_service_profile(count=2))
+ _network_flavor_profiles = network_fakes.create_service_profile(count=2)
columns = (
'ID',
@@ -305,8 +301,8 @@ class TestListFlavorProfile(TestFlavorProfile):
data.append((
flavor_profile.id,
flavor_profile.driver,
- flavor_profile.enabled,
- flavor_profile.metainfo,
+ flavor_profile.is_enabled,
+ flavor_profile.meta_info,
flavor_profile.description,
))
@@ -334,22 +330,21 @@ class TestListFlavorProfile(TestFlavorProfile):
class TestShowFlavorProfile(TestFlavorProfile):
# The network flavor profile to show.
- network_flavor_profile = (
- network_fakes.FakeNetworkFlavorProfile.create_one_service_profile())
+ network_flavor_profile = network_fakes.create_one_service_profile()
columns = (
'description',
'driver',
'enabled',
'id',
- 'metainfo',
+ 'meta_info',
'project_id',
)
data = (
network_flavor_profile.description,
network_flavor_profile.driver,
- network_flavor_profile.enabled,
+ network_flavor_profile.is_enabled,
network_flavor_profile.id,
- network_flavor_profile.metainfo,
+ network_flavor_profile.meta_info,
network_flavor_profile.project_id,
)
@@ -382,8 +377,7 @@ class TestShowFlavorProfile(TestFlavorProfile):
class TestSetFlavorProfile(TestFlavorProfile):
# The network flavor profile to set.
- network_flavor_profile = (
- network_fakes.FakeNetworkFlavorProfile.create_one_service_profile())
+ network_flavor_profile = network_fakes.create_one_service_profile()
def setUp(self):
super(TestSetFlavorProfile, self).setUp()
diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py
index 217e481..c7de816 100644
--- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py
+++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py
@@ -25,6 +25,7 @@ from openstackclient.tests.unit import utils as tests_utils
RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit'
RULE_TYPE_DSCP_MARKING = 'dscp-marking'
RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
+RULE_TYPE_MINIMUM_PACKET_RATE = 'minimum-packet-rate'
DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
34, 36, 38, 40, 46, 48, 56]
@@ -126,8 +127,101 @@ class TestCreateNetworkQosRuleMinimumBandwidth(TestNetworkQosRule):
try:
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
- msg = ('"Create" rule command for type "minimum-bandwidth" '
- 'requires arguments: direction, min_kbps')
+ msg = ('Failed to create Network QoS rule: "Create" rule command '
+ 'for type "minimum-bandwidth" requires arguments: '
+ 'direction, min_kbps')
+ self.assertEqual(msg, str(e))
+
+
+class TestCreateNetworkQosRuleMinimumPacketRate(TestNetworkQosRule):
+
+ def test_check_type_parameters(self):
+ pass
+
+ def setUp(self):
+ super(TestCreateNetworkQosRuleMinimumPacketRate, self).setUp()
+ attrs = {'qos_policy_id': self.qos_policy.id,
+ 'type': RULE_TYPE_MINIMUM_PACKET_RATE}
+ self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
+ attrs)
+ self.columns = (
+ 'direction',
+ 'id',
+ 'min_kpps',
+ 'project_id',
+ 'qos_policy_id',
+ 'type'
+ )
+
+ self.data = (
+ self.new_rule.direction,
+ self.new_rule.id,
+ self.new_rule.min_kpps,
+ self.new_rule.project_id,
+ self.new_rule.qos_policy_id,
+ self.new_rule.type,
+ )
+ self.network.create_qos_minimum_packet_rate_rule = mock.Mock(
+ return_value=self.new_rule)
+
+ # Get the command object to test
+ self.cmd = network_qos_rule.CreateNetworkQosRule(self.app,
+ self.namespace)
+
+ 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_default_options(self):
+ arglist = [
+ '--type', RULE_TYPE_MINIMUM_PACKET_RATE,
+ '--min-kpps', str(self.new_rule.min_kpps),
+ '--egress',
+ self.new_rule.qos_policy_id,
+ ]
+
+ verifylist = [
+ ('type', RULE_TYPE_MINIMUM_PACKET_RATE),
+ ('min_kpps', self.new_rule.min_kpps),
+ ('egress', True),
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_qos_minimum_packet_rate_rule.\
+ assert_called_once_with(
+ self.qos_policy.id,
+ **{'min_kpps': self.new_rule.min_kpps,
+ 'direction': self.new_rule.direction})
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_wrong_options(self):
+ arglist = [
+ '--type', RULE_TYPE_MINIMUM_PACKET_RATE,
+ '--min-kbps', '10000',
+ self.new_rule.qos_policy_id,
+ ]
+
+ verifylist = [
+ ('type', RULE_TYPE_MINIMUM_PACKET_RATE),
+ ('min_kbps', 10000),
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ try:
+ self.cmd.take_action(parsed_args)
+ except exceptions.CommandError as e:
+ msg = ('Failed to create Network QoS rule: "Create" rule command '
+ 'for type "minimum-packet-rate" requires arguments: '
+ 'direction, min_kpps')
self.assertEqual(msg, str(e))
@@ -212,8 +306,8 @@ class TestCreateNetworkQosRuleDSCPMarking(TestNetworkQosRule):
try:
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
- msg = ('"Create" rule command for type "dscp-marking" '
- 'requires arguments: dscp_mark')
+ msg = ('Failed to create Network QoS rule: "Create" rule command '
+ 'for type "dscp-marking" requires arguments: dscp_mark')
self.assertEqual(msg, str(e))
@@ -351,8 +445,8 @@ class TestCreateNetworkQosRuleBandwidtLimit(TestNetworkQosRule):
try:
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
- msg = ('"Create" rule command for type "bandwidth-limit" '
- 'requires arguments: max_kbps')
+ msg = ('Failed to create Network QoS rule: "Create" rule command '
+ 'for type "bandwidth-limit" requires arguments: max_kbps')
self.assertEqual(msg, str(e))
@@ -415,6 +509,65 @@ class TestDeleteNetworkQosRuleMinimumBandwidth(TestNetworkQosRule):
self.assertEqual(msg, str(e))
+class TestDeleteNetworkQosRuleMinimumPacketRate(TestNetworkQosRule):
+
+ def setUp(self):
+ super(TestDeleteNetworkQosRuleMinimumPacketRate, self).setUp()
+ attrs = {'qos_policy_id': self.qos_policy.id,
+ 'type': RULE_TYPE_MINIMUM_PACKET_RATE}
+ self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
+ attrs)
+ self.qos_policy.rules = [self.new_rule]
+ self.network.delete_qos_minimum_packet_rate_rule = mock.Mock(
+ return_value=None)
+ self.network.find_qos_minimum_packet_rate_rule = (
+ network_fakes.FakeNetworkQosRule.get_qos_rules(
+ qos_rules=self.new_rule)
+ )
+
+ # Get the command object to test
+ self.cmd = network_qos_rule.DeleteNetworkQosRule(self.app,
+ self.namespace)
+
+ def test_qos_policy_delete(self):
+ arglist = [
+ self.new_rule.qos_policy_id,
+ self.new_rule.id,
+ ]
+ verifylist = [
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ('id', self.new_rule.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.network.find_qos_policy.assert_called_once_with(
+ self.qos_policy.id, ignore_missing=False)
+ self.network.delete_qos_minimum_packet_rate_rule.\
+ assert_called_once_with(self.new_rule.id, self.qos_policy.id)
+ self.assertIsNone(result)
+
+ def test_qos_policy_delete_error(self):
+ arglist = [
+ self.new_rule.qos_policy_id,
+ self.new_rule.id,
+ ]
+ verifylist = [
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ('id', self.new_rule.id),
+ ]
+
+ self.network.delete_qos_minimum_packet_rate_rule.side_effect = \
+ Exception('Error message')
+ try:
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+ except exceptions.CommandError as e:
+ msg = ('Failed to delete Network QoS rule ID "%(rule)s": %(e)s' %
+ {'rule': self.new_rule.id, 'e': 'Error message'})
+ self.assertEqual(msg, str(e))
+
+
class TestDeleteNetworkQosRuleDSCPMarking(TestNetworkQosRule):
def setUp(self):
@@ -627,6 +780,100 @@ class TestSetNetworkQosRuleMinimumBandwidth(TestNetworkQosRule):
self.assertEqual(msg, str(e))
+class TestSetNetworkQosRuleMinimumPacketRate(TestNetworkQosRule):
+
+ def setUp(self):
+ super(TestSetNetworkQosRuleMinimumPacketRate, self).setUp()
+ attrs = {'qos_policy_id': self.qos_policy.id,
+ 'type': RULE_TYPE_MINIMUM_PACKET_RATE}
+ self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
+ attrs=attrs)
+ self.qos_policy.rules = [self.new_rule]
+ self.network.update_qos_minimum_packet_rate_rule = mock.Mock(
+ return_value=None)
+ self.network.find_qos_minimum_packet_rate_rule = mock.Mock(
+ return_value=self.new_rule)
+ self.network.find_qos_policy = mock.Mock(
+ return_value=self.qos_policy)
+
+ # Get the command object to test
+ self.cmd = (network_qos_rule.SetNetworkQosRule(self.app,
+ self.namespace))
+
+ def test_set_nothing(self):
+ arglist = [
+ self.new_rule.qos_policy_id,
+ self.new_rule.id,
+ ]
+ verifylist = [
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ('id', self.new_rule.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.update_qos_minimum_packet_rate_rule.assert_called_with(
+ self.new_rule, self.qos_policy.id)
+ self.assertIsNone(result)
+
+ def test_set_min_kpps(self):
+ self._set_min_kpps()
+
+ def test_set_min_kpps_to_zero(self):
+ self._set_min_kpps(min_kpps=0)
+
+ def _set_min_kpps(self, min_kpps=None):
+ if min_kpps:
+ previous_min_kpps = self.new_rule.min_kpps
+ self.new_rule.min_kpps = min_kpps
+
+ arglist = [
+ '--min-kpps', str(self.new_rule.min_kpps),
+ self.new_rule.qos_policy_id,
+ self.new_rule.id,
+ ]
+ verifylist = [
+ ('min_kpps', self.new_rule.min_kpps),
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ('id', self.new_rule.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'min_kpps': self.new_rule.min_kpps,
+ }
+ self.network.update_qos_minimum_packet_rate_rule.assert_called_with(
+ self.new_rule, self.qos_policy.id, **attrs)
+ self.assertIsNone(result)
+
+ if min_kpps:
+ self.new_rule.min_kpps = previous_min_kpps
+
+ def test_set_wrong_options(self):
+ arglist = [
+ '--min-kbps', str(10000),
+ self.new_rule.qos_policy_id,
+ self.new_rule.id,
+ ]
+ verifylist = [
+ ('min_kbps', 10000),
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ('id', self.new_rule.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ try:
+ self.cmd.take_action(parsed_args)
+ except exceptions.CommandError as e:
+ msg = ('Failed to set Network QoS rule ID "%(rule)s": Rule type '
+ '"minimum-packet-rate" only requires arguments: direction, '
+ 'min_kpps' % {'rule': self.new_rule.id})
+ self.assertEqual(msg, str(e))
+
+
class TestSetNetworkQosRuleDSCPMarking(TestNetworkQosRule):
def setUp(self):
@@ -893,6 +1140,9 @@ class TestListNetworkQosRule(TestNetworkQosRule):
'type': RULE_TYPE_MINIMUM_BANDWIDTH}
self.new_rule_min_bw = (network_fakes.FakeNetworkQosRule.
create_one_qos_rule(attrs=attrs))
+ attrs['type'] = RULE_TYPE_MINIMUM_PACKET_RATE
+ self.new_rule_min_pps = (network_fakes.FakeNetworkQosRule.
+ create_one_qos_rule(attrs=attrs))
attrs['type'] = RULE_TYPE_DSCP_MARKING
self.new_rule_dscp_mark = (network_fakes.FakeNetworkQosRule.
create_one_qos_rule(attrs=attrs))
@@ -900,10 +1150,13 @@ class TestListNetworkQosRule(TestNetworkQosRule):
self.new_rule_max_bw = (network_fakes.FakeNetworkQosRule.
create_one_qos_rule(attrs=attrs))
self.qos_policy.rules = [self.new_rule_min_bw,
+ self.new_rule_min_pps,
self.new_rule_dscp_mark,
self.new_rule_max_bw]
self.network.find_qos_minimum_bandwidth_rule = mock.Mock(
return_value=self.new_rule_min_bw)
+ self.network.find_qos_minimum_packet_rate_rule = mock.Mock(
+ return_value=self.new_rule_min_pps)
self.network.find_qos_dscp_marking_rule = mock.Mock(
return_value=self.new_rule_dscp_mark)
self.network.find_qos_bandwidth_limit_rule = mock.Mock(
@@ -915,6 +1168,7 @@ class TestListNetworkQosRule(TestNetworkQosRule):
'Max Kbps',
'Max Burst Kbits',
'Min Kbps',
+ 'Min Kpps',
'DSCP mark',
'Direction',
)
@@ -927,6 +1181,7 @@ class TestListNetworkQosRule(TestNetworkQosRule):
getattr(self.qos_policy.rules[index], 'max_kbps', ''),
getattr(self.qos_policy.rules[index], 'max_burst_kbps', ''),
getattr(self.qos_policy.rules[index], 'min_kbps', ''),
+ getattr(self.qos_policy.rules[index], 'min_kpps', ''),
getattr(self.qos_policy.rules[index], 'dscp_mark', ''),
getattr(self.qos_policy.rules[index], 'direction', ''),
))
@@ -1014,6 +1269,66 @@ class TestShowNetworkQosRuleMinimumBandwidth(TestNetworkQosRule):
self.assertEqual(list(self.data), list(data))
+class TestShowNetworkQosRuleMinimumPacketRate(TestNetworkQosRule):
+
+ def setUp(self):
+ super(TestShowNetworkQosRuleMinimumPacketRate, self).setUp()
+ attrs = {'qos_policy_id': self.qos_policy.id,
+ 'type': RULE_TYPE_MINIMUM_PACKET_RATE}
+ self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
+ attrs)
+ self.qos_policy.rules = [self.new_rule]
+ self.columns = (
+ 'direction',
+ 'id',
+ 'min_kpps',
+ 'project_id',
+ 'qos_policy_id',
+ 'type'
+ )
+ self.data = (
+ self.new_rule.direction,
+ self.new_rule.id,
+ self.new_rule.min_kpps,
+ self.new_rule.project_id,
+ self.new_rule.qos_policy_id,
+ self.new_rule.type,
+ )
+
+ self.network.get_qos_minimum_packet_rate_rule = mock.Mock(
+ return_value=self.new_rule)
+
+ # Get the command object to test
+ self.cmd = network_qos_rule.ShowNetworkQosRule(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_all_options(self):
+ arglist = [
+ self.new_rule.qos_policy_id,
+ self.new_rule.id,
+ ]
+ verifylist = [
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ('id', self.new_rule.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.get_qos_minimum_packet_rate_rule.assert_called_once_with(
+ self.new_rule.id, self.qos_policy.id)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(list(self.data), list(data))
+
+
class TestShowNetworkQosDSCPMarking(TestNetworkQosRule):
def setUp(self):
diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py
index e20efc8..b0bc7e8 100644
--- a/openstackclient/tests/unit/network/v2/test_network_rbac.py
+++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py
@@ -37,14 +37,14 @@ class TestNetworkRBAC(network_fakes.TestNetworkV2):
@ddt.ddt
class TestCreateNetworkRBAC(TestNetworkRBAC):
- network_object = network_fakes.FakeNetwork.create_one_network()
+ network_object = network_fakes.create_one_network()
qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group()
- as_object = network_fakes.FakeAddressScope.create_one_address_scope()
+ as_object = network_fakes.create_one_address_scope()
snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool()
- ag_object = network_fakes.FakeAddressGroup.create_one_address_group()
+ ag_object = network_fakes.create_one_address_group()
project = identity_fakes_v3.FakeProject.create_one_project()
- rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac(
+ rbac_policy = network_fakes.create_one_network_rbac(
attrs={'project_id': project.id,
'target_tenant': project.id,
'object_id': network_object.id}
@@ -65,7 +65,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
rbac_policy.object_id,
rbac_policy.object_type,
rbac_policy.project_id,
- rbac_policy.target_tenant,
+ rbac_policy.target_project_id,
]
def setUp(self):
@@ -120,13 +120,13 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
arglist = [
'--action', self.rbac_policy.action,
'--type', 'invalid_type',
- '--target-project', self.rbac_policy.target_tenant,
+ '--target-project', self.rbac_policy.target_project_id,
self.rbac_policy.object_id,
]
verifylist = [
('action', self.rbac_policy.action),
('type', 'invalid_type'),
- ('target-project', self.rbac_policy.target_tenant),
+ ('target-project', self.rbac_policy.target_project_id),
('rbac_policy', self.rbac_policy.id),
]
@@ -137,13 +137,13 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
arglist = [
'--type', self.rbac_policy.object_type,
'--action', 'invalid_action',
- '--target-project', self.rbac_policy.target_tenant,
+ '--target-project', self.rbac_policy.target_project_id,
self.rbac_policy.object_id,
]
verifylist = [
('type', self.rbac_policy.object_type),
('action', 'invalid_action'),
- ('target-project', self.rbac_policy.target_tenant),
+ ('target-project', self.rbac_policy.target_project_id),
('rbac_policy', self.rbac_policy.id),
]
@@ -154,13 +154,13 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
arglist = [
'--type', self.rbac_policy.object_type,
'--action', self.rbac_policy.action,
- '--target-project', self.rbac_policy.target_tenant,
+ '--target-project', self.rbac_policy.target_project_id,
self.rbac_policy.object_id,
]
verifylist = [
('type', self.rbac_policy.object_type),
('action', self.rbac_policy.action),
- ('target_project', self.rbac_policy.target_tenant),
+ ('target_project', self.rbac_policy.target_project_id),
('rbac_object', self.rbac_policy.object_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -172,7 +172,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
'object_id': self.rbac_policy.object_id,
'object_type': self.rbac_policy.object_type,
'action': self.rbac_policy.action,
- 'target_tenant': self.rbac_policy.target_tenant,
+ 'target_tenant': self.rbac_policy.target_project_id,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -205,7 +205,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
arglist = [
'--type', self.rbac_policy.object_type,
'--action', self.rbac_policy.action,
- '--target-project', self.rbac_policy.target_tenant,
+ '--target-project', self.rbac_policy.target_project_id,
'--project', self.rbac_policy.project_id,
'--project-domain', self.project.domain_id,
'--target-project-domain', self.project.domain_id,
@@ -214,7 +214,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
verifylist = [
('type', self.rbac_policy.object_type),
('action', self.rbac_policy.action),
- ('target_project', self.rbac_policy.target_tenant),
+ ('target_project', self.rbac_policy.target_project_id),
('project', self.rbac_policy.project_id),
('project_domain', self.project.domain_id),
('target_project_domain', self.project.domain_id),
@@ -229,7 +229,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
'object_id': self.rbac_policy.object_id,
'object_type': self.rbac_policy.object_type,
'action': self.rbac_policy.action,
- 'target_tenant': self.rbac_policy.target_tenant,
+ 'target_tenant': self.rbac_policy.target_project_id,
'project_id': self.rbac_policy.project_id,
})
self.assertEqual(self.columns, columns)
@@ -251,13 +251,13 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
arglist = [
'--type', obj_type,
'--action', self.rbac_policy.action,
- '--target-project', self.rbac_policy.target_tenant,
+ '--target-project', self.rbac_policy.target_project_id,
obj_fake.name,
]
verifylist = [
('type', obj_type),
('action', self.rbac_policy.action),
- ('target_project', self.rbac_policy.target_tenant),
+ ('target_project', self.rbac_policy.target_project_id),
('rbac_object', obj_fake.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -269,7 +269,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
'object_id': obj_fake.id,
'object_type': obj_type,
'action': self.rbac_policy.action,
- 'target_tenant': self.rbac_policy.target_tenant,
+ 'target_tenant': self.rbac_policy.target_project_id,
})
self.data = [
self.rbac_policy.action,
@@ -277,7 +277,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
obj_fake.id,
obj_type,
self.rbac_policy.project_id,
- self.rbac_policy.target_tenant,
+ self.rbac_policy.target_project_id,
]
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -285,13 +285,13 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
class TestDeleteNetworkRBAC(TestNetworkRBAC):
- rbac_policies = network_fakes.FakeNetworkRBAC.create_network_rbacs(count=2)
+ rbac_policies = network_fakes.create_network_rbacs(count=2)
def setUp(self):
super(TestDeleteNetworkRBAC, self).setUp()
self.network.delete_rbac_policy = mock.Mock(return_value=None)
self.network.find_rbac_policy = (
- network_fakes.FakeNetworkRBAC.get_network_rbacs(
+ network_fakes.get_network_rbacs(
rbac_policies=self.rbac_policies)
)
@@ -368,7 +368,7 @@ class TestDeleteNetworkRBAC(TestNetworkRBAC):
class TestListNetworkRABC(TestNetworkRBAC):
# The network rbac policies going to be listed up.
- rbac_policies = network_fakes.FakeNetworkRBAC.create_network_rbacs(count=3)
+ rbac_policies = network_fakes.create_network_rbacs(count=3)
columns = (
'ID',
@@ -470,7 +470,7 @@ class TestListNetworkRABC(TestNetworkRBAC):
class TestSetNetworkRBAC(TestNetworkRBAC):
project = identity_fakes_v3.FakeProject.create_one_project()
- rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac(
+ rbac_policy = network_fakes.create_one_network_rbac(
attrs={'target_tenant': project.id})
def setUp(self):
@@ -525,7 +525,7 @@ class TestSetNetworkRBAC(TestNetworkRBAC):
class TestShowNetworkRBAC(TestNetworkRBAC):
- rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac()
+ rbac_policy = network_fakes.create_one_network_rbac()
columns = (
'action',
@@ -542,7 +542,7 @@ class TestShowNetworkRBAC(TestNetworkRBAC):
rbac_policy.object_id,
rbac_policy.object_type,
rbac_policy.project_id,
- rbac_policy.target_tenant,
+ rbac_policy.target_project_id,
]
def setUp(self):
diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py
index 6cd948e..b41b619 100644
--- a/openstackclient/tests/unit/network/v2/test_network_segment.py
+++ b/openstackclient/tests/unit/network/v2/test_network_segment.py
@@ -33,9 +33,8 @@ class TestNetworkSegment(network_fakes.TestNetworkV2):
class TestCreateNetworkSegment(TestNetworkSegment):
# The network segment to create along with associated network.
- _network_segment = \
- network_fakes.FakeNetworkSegment.create_one_network_segment()
- _network = network_fakes.FakeNetwork.create_one_network({
+ _network_segment = network_fakes.create_one_network_segment()
+ _network = network_fakes.create_one_network({
'id': _network_segment.network_id,
})
@@ -156,8 +155,7 @@ class TestCreateNetworkSegment(TestNetworkSegment):
class TestDeleteNetworkSegment(TestNetworkSegment):
# The network segments to delete.
- _network_segments = \
- network_fakes.FakeNetworkSegment.create_network_segments()
+ _network_segments = network_fakes.create_network_segments()
def setUp(self):
super(TestDeleteNetworkSegment, self).setUp()
@@ -240,9 +238,8 @@ class TestDeleteNetworkSegment(TestNetworkSegment):
class TestListNetworkSegment(TestNetworkSegment):
- _network = network_fakes.FakeNetwork.create_one_network()
- _network_segments = \
- network_fakes.FakeNetworkSegment.create_network_segments(count=3)
+ _network = network_fakes.create_one_network()
+ _network_segments = network_fakes.create_network_segments(count=3)
columns = (
'ID',
@@ -338,8 +335,7 @@ class TestListNetworkSegment(TestNetworkSegment):
class TestSetNetworkSegment(TestNetworkSegment):
# The network segment to show.
- _network_segment = \
- network_fakes.FakeNetworkSegment.create_one_network_segment()
+ _network_segment = network_fakes.create_one_network_segment()
def setUp(self):
super(TestSetNetworkSegment, self).setUp()
@@ -398,8 +394,7 @@ class TestSetNetworkSegment(TestNetworkSegment):
class TestShowNetworkSegment(TestNetworkSegment):
# The network segment to show.
- _network_segment = \
- network_fakes.FakeNetworkSegment.create_one_network_segment()
+ _network_segment = network_fakes.create_one_network_segment()
columns = (
'description',
diff --git a/openstackclient/tests/unit/network/v2/test_network_segment_range.py b/openstackclient/tests/unit/network/v2/test_network_segment_range.py
index b60f171..20d34bf 100644
--- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py
+++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py
@@ -50,8 +50,7 @@ class TestNetworkSegmentRange(network_fakes.TestNetworkV2):
class TestCreateNetworkSegmentRange(TestNetworkSegmentRange):
# The network segment range to create.
- _network_segment_range = network_fakes.FakeNetworkSegmentRange.\
- create_one_network_segment_range()
+ _network_segment_range = network_fakes.create_one_network_segment_range()
columns = (
'available',
@@ -310,8 +309,7 @@ class TestCreateNetworkSegmentRange(TestNetworkSegmentRange):
class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange):
# The network segment ranges to delete.
- _network_segment_ranges = \
- network_fakes.FakeNetworkSegmentRange.create_network_segment_ranges()
+ _network_segment_ranges = network_fakes.create_network_segment_ranges()
def setUp(self):
super(TestDeleteNetworkSegmentRange, self).setUp()
@@ -396,8 +394,8 @@ class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange):
class TestListNetworkSegmentRange(TestNetworkSegmentRange):
- _network_segment_ranges = network_fakes.FakeNetworkSegmentRange.\
- create_network_segment_ranges(count=3)
+ _network_segment_ranges = (
+ network_fakes.create_network_segment_ranges(count=3))
columns = (
'ID',
@@ -496,14 +494,13 @@ class TestListNetworkSegmentRange(TestNetworkSegmentRange):
class TestSetNetworkSegmentRange(TestNetworkSegmentRange):
# The network segment range to set.
- _network_segment_range = network_fakes.FakeNetworkSegmentRange.\
- create_one_network_segment_range()
+ _network_segment_range = network_fakes.create_one_network_segment_range()
# The network segment range updated.
minimum_updated = _network_segment_range.minimum - 5
maximum_updated = _network_segment_range.maximum + 5
available_updated = (list(range(minimum_updated, 104)) + [105] +
list(range(107, maximum_updated + 1)))
- _network_segment_range_updated = network_fakes.FakeNetworkSegmentRange.\
+ _network_segment_range_updated = network_fakes.\
create_one_network_segment_range(
attrs={'minimum': minimum_updated,
'maximum': maximum_updated,
@@ -577,8 +574,7 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange):
class TestShowNetworkSegmentRange(TestNetworkSegmentRange):
# The network segment range to show.
- _network_segment_range = network_fakes.FakeNetworkSegmentRange.\
- create_one_network_segment_range()
+ _network_segment_range = network_fakes.create_one_network_segment_range()
columns = (
'available',
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index 6f830aa..04412c5 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -50,6 +50,7 @@ class TestPort(network_fakes.TestNetworkV2):
'binding_vif_details',
'binding_vif_type',
'binding_vnic_type',
+ 'created_at',
'data_plane_status',
'description',
'device_id',
@@ -61,6 +62,7 @@ class TestPort(network_fakes.TestNetworkV2):
'extra_dhcp_opts',
'fixed_ips',
'id',
+ 'ip_allocation',
'mac_address',
'name',
'network_id',
@@ -68,21 +70,26 @@ class TestPort(network_fakes.TestNetworkV2):
'port_security_enabled',
'project_id',
'propagate_uplink_status',
+ 'resource_request',
+ 'revision_number',
'qos_network_policy_id',
'qos_policy_id',
'security_group_ids',
'status',
'tags',
+ 'trunk_details',
+ 'updated_at',
)
data = (
- port.AdminStateColumn(fake_port.admin_state_up),
+ port.AdminStateColumn(fake_port.is_admin_state_up),
format_columns.ListDictColumn(fake_port.allowed_address_pairs),
fake_port.binding_host_id,
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.created_at,
fake_port.data_plane_status,
fake_port.description,
fake_port.device_id,
@@ -94,18 +101,23 @@ class TestPort(network_fakes.TestNetworkV2):
format_columns.ListDictColumn(fake_port.extra_dhcp_opts),
format_columns.ListDictColumn(fake_port.fixed_ips),
fake_port.id,
+ fake_port.ip_allocation,
fake_port.mac_address,
fake_port.name,
fake_port.network_id,
fake_port.numa_affinity_policy,
- fake_port.port_security_enabled,
+ fake_port.is_port_security_enabled,
fake_port.project_id,
fake_port.propagate_uplink_status,
+ fake_port.resource_request,
+ fake_port.revision_number,
fake_port.qos_network_policy_id,
fake_port.qos_policy_id,
format_columns.ListColumn(fake_port.security_group_ids),
fake_port.status,
format_columns.ListColumn(fake_port.tags),
+ fake_port.trunk_details,
+ fake_port.updated_at,
)
return columns, data
@@ -113,7 +125,7 @@ class TestPort(network_fakes.TestNetworkV2):
class TestCreatePort(TestPort):
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
columns, data = TestPort._get_common_cols_data(_port)
def setUp(self):
@@ -121,7 +133,7 @@ class TestCreatePort(TestPort):
self.network.create_port = mock.Mock(return_value=self._port)
self.network.set_tags = mock.Mock(return_value=None)
- fake_net = network_fakes.FakeNetwork.create_one_network({
+ fake_net = network_fakes.create_one_network({
'id': self._port.network_id,
})
self.network.find_network = mock.Mock(return_value=fake_net)
@@ -152,7 +164,7 @@ class TestCreatePort(TestPort):
})
self.assertFalse(self.network.set_tags.called)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_full_options(self):
@@ -210,7 +222,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_invalid_json_binding_profile(self):
@@ -261,7 +273,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_security_group(self):
@@ -290,7 +302,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_port_with_dns_name(self):
@@ -316,7 +328,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_security_groups(self):
@@ -346,7 +358,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_no_security_groups(self):
@@ -372,7 +384,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_no_fixed_ips(self):
@@ -398,7 +410,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_port_with_allowed_address_pair_ipaddr(self):
@@ -428,7 +440,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_port_with_allowed_address_pair(self):
@@ -464,7 +476,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_port_with_qos(self):
@@ -492,7 +504,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_port_security_enabled(self):
@@ -601,7 +613,7 @@ class TestCreatePort(TestPort):
else:
self.assertFalse(self.network.set_tags.called)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_tags(self):
@@ -644,7 +656,7 @@ class TestCreatePort(TestPort):
'name': 'test-port',
})
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_uplink_status_propagation_enabled(self):
@@ -724,7 +736,7 @@ class TestCreatePort(TestPort):
create_args['numa_affinity_policy'] = numa_affinity_policy
self.network.create_port.assert_called_once_with(**create_args)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
def test_create_with_numa_affinity_policy_required(self):
@@ -763,20 +775,20 @@ class TestCreatePort(TestPort):
'device_profile': 'cyborg_device_profile_1',
}
self.network.create_port.assert_called_once_with(**create_args)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
class TestDeletePort(TestPort):
# Ports to delete.
- _ports = network_fakes.FakePort.create_ports(count=2)
+ _ports = network_fakes.create_ports(count=2)
def setUp(self):
super(TestDeletePort, self).setUp()
self.network.delete_port = mock.Mock(return_value=None)
- self.network.find_port = network_fakes.FakePort.get_ports(
+ self.network.find_port = network_fakes.get_ports(
ports=self._ports)
# Get the command object to test
self.cmd = port.DeletePort(self.app, self.namespace)
@@ -848,7 +860,7 @@ class TestDeletePort(TestPort):
class TestListPort(TestPort):
- _ports = network_fakes.FakePort.create_ports(count=3)
+ _ports = network_fakes.create_ports(count=3)
columns = (
'ID',
@@ -901,7 +913,7 @@ class TestListPort(TestPort):
fake_router = network_fakes.FakeRouter.create_one_router({
'id': 'fake-router-id',
})
- fake_network = network_fakes.FakeNetwork.create_one_network({
+ fake_network = network_fakes.create_one_network({
'id': 'fake-network-id',
})
self.network.find_router = mock.Mock(return_value=fake_router)
@@ -1317,7 +1329,7 @@ class TestListPort(TestPort):
class TestSetPort(TestPort):
- _port = network_fakes.FakePort.create_one_port({'tags': ['green', 'red']})
+ _port = network_fakes.create_one_port({'tags': ['green', 'red']})
def setUp(self):
super(TestSetPort, self).setUp()
@@ -1345,7 +1357,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_set_port_fixed_ip(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'fixed_ips': [{'ip_address': '0.0.0.1'}]})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1369,7 +1381,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_set_port_fixed_ip_clear(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'fixed_ips': [{'ip_address': '0.0.0.1'}]})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1412,7 +1424,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_set_port_overwrite_binding_profile(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'binding_profile': {'lok_i': 'visi_on'}})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1434,7 +1446,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_overwrite_mac_address(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'mac_address': '11:22:33:44:55:66'})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1578,7 +1590,7 @@ class TestSetPort(TestPort):
sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group()
sg_3 = network_fakes.FakeSecurityGroup.create_one_security_group()
self.network.find_security_group = mock.Mock(side_effect=[sg_2, sg_3])
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'security_group_ids': [sg_1.id]})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1620,7 +1632,7 @@ class TestSetPort(TestPort):
def test_set_port_security_group_replace(self):
sg1 = network_fakes.FakeSecurityGroup.create_one_security_group()
sg2 = network_fakes.FakeSecurityGroup.create_one_security_group()
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'security_group_ids': [sg1.id]})
self.network.find_port = mock.Mock(return_value=_testport)
self.network.find_security_group = mock.Mock(return_value=sg2)
@@ -1662,7 +1674,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_set_port_append_allowed_address_pair(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1685,7 +1697,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_set_port_overwrite_allowed_address_pair(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1787,7 +1799,7 @@ class TestSetPort(TestPort):
def test_set_port_with_qos(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
self.network.find_qos_policy = mock.Mock(return_value=qos_policy)
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'qos_policy_id': None})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1809,7 +1821,7 @@ class TestSetPort(TestPort):
self.assertIsNone(result)
def test_set_port_data_plane_status(self):
- _testport = network_fakes.FakePort.create_one_port(
+ _testport = network_fakes.create_one_port(
{'data_plane_status': None})
self.network.find_port = mock.Mock(return_value=_testport)
arglist = [
@@ -1900,7 +1912,7 @@ class TestSetPort(TestPort):
class TestShowPort(TestPort):
# The port to show.
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
columns, data = TestPort._get_common_cols_data(_port)
def setUp(self):
@@ -1932,7 +1944,7 @@ class TestShowPort(TestPort):
self.network.find_port.assert_called_once_with(
self._port.name, ignore_missing=False)
- self.assertEqual(self.columns, columns)
+ self.assertEqual(set(self.columns), set(columns))
self.assertCountEqual(self.data, data)
@@ -1940,7 +1952,7 @@ class TestUnsetPort(TestPort):
def setUp(self):
super(TestUnsetPort, self).setUp()
- self._testport = network_fakes.FakePort.create_one_port(
+ self._testport = network_fakes.create_one_port(
{'fixed_ips': [{'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152',
'ip_address': '0.0.0.1'},
{'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152',
@@ -2024,7 +2036,7 @@ class TestUnsetPort(TestPort):
def test_unset_security_group(self):
_fake_sg1 = network_fakes.FakeSecurityGroup.create_one_security_group()
_fake_sg2 = network_fakes.FakeSecurityGroup.create_one_security_group()
- _fake_port = network_fakes.FakePort.create_one_port(
+ _fake_port = network_fakes.create_one_port(
{'security_group_ids': [_fake_sg1.id, _fake_sg2.id]})
self.network.find_port = mock.Mock(return_value=_fake_port)
self.network.find_security_group = mock.Mock(return_value=_fake_sg2)
@@ -2049,7 +2061,7 @@ class TestUnsetPort(TestPort):
def test_unset_port_security_group_not_existent(self):
_fake_sg1 = network_fakes.FakeSecurityGroup.create_one_security_group()
_fake_sg2 = network_fakes.FakeSecurityGroup.create_one_security_group()
- _fake_port = network_fakes.FakePort.create_one_port(
+ _fake_port = network_fakes.create_one_port(
{'security_group_ids': [_fake_sg1.id]})
self.network.find_security_group = mock.Mock(return_value=_fake_sg2)
arglist = [
@@ -2066,7 +2078,7 @@ class TestUnsetPort(TestPort):
parsed_args)
def test_unset_port_allowed_address_pair(self):
- _fake_port = network_fakes.FakePort.create_one_port(
+ _fake_port = network_fakes.create_one_port(
{'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]})
self.network.find_port = mock.Mock(return_value=_fake_port)
arglist = [
@@ -2088,7 +2100,7 @@ class TestUnsetPort(TestPort):
self.assertIsNone(result)
def test_unset_port_allowed_address_pair_not_existent(self):
- _fake_port = network_fakes.FakePort.create_one_port(
+ _fake_port = network_fakes.create_one_port(
{'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]})
self.network.find_port = mock.Mock(return_value=_fake_port)
arglist = [
@@ -2105,7 +2117,7 @@ class TestUnsetPort(TestPort):
parsed_args)
def test_unset_port_data_plane_status(self):
- _fake_port = network_fakes.FakePort.create_one_port(
+ _fake_port = network_fakes.create_one_port(
{'data_plane_status': 'ACTIVE'})
self.network.find_port = mock.Mock(return_value=_fake_port)
arglist = [
@@ -2156,7 +2168,7 @@ class TestUnsetPort(TestPort):
self._test_unset_tags(with_tags=False)
def test_unset_numa_affinity_policy(self):
- _fake_port = network_fakes.FakePort.create_one_port(
+ _fake_port = network_fakes.create_one_port(
{'numa_affinity_policy': 'required'})
self.network.find_port = mock.Mock(return_value=_fake_port)
arglist = [
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 4d03507..fb9673c 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -36,7 +36,7 @@ class TestRouter(network_fakes.TestNetworkV2):
class TestAddPortToRouter(TestRouter):
'''Add port to Router '''
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
_router = network_fakes.FakeRouter.create_one_router(
attrs={'port': _port.id})
@@ -187,7 +187,7 @@ class TestCreateRouter(TestRouter):
self.assertCountEqual(self.data, data)
def test_create_with_gateway(self):
- _network = network_fakes.FakeNetwork.create_one_network()
+ _network = network_fakes.create_one_network()
_subnet = network_fakes.FakeSubnet.create_one_subnet()
self.network.find_network = mock.Mock(return_value=_network)
self.network.find_subnet = mock.Mock(return_value=_subnet)
@@ -512,8 +512,7 @@ class TestListRouter(TestRouter):
self.network.routers = mock.Mock(return_value=self.routers)
self.network.find_extension = mock.Mock(return_value=self._extensions)
self.network.find_router = mock.Mock(return_value=self.routers[0])
- self._testagent = \
- network_fakes.FakeNetworkAgent.create_one_network_agent()
+ self._testagent = network_fakes.create_one_network_agent()
self.network.get_agent = mock.Mock(return_value=self._testagent)
self.network.get_router = mock.Mock(return_value=self.routers[0])
@@ -739,7 +738,7 @@ class TestListRouter(TestRouter):
class TestRemovePortFromRouter(TestRouter):
'''Remove port from a Router '''
- _port = network_fakes.FakePort.create_one_port()
+ _port = network_fakes.create_one_port()
_router = network_fakes.FakeRouter.create_one_router(
attrs={'port': _port.id})
@@ -957,7 +956,7 @@ class TestSetRouter(TestRouter):
# The router to set.
_default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'}
- _network = network_fakes.FakeNetwork.create_one_network()
+ _network = network_fakes.create_one_network()
_subnet = network_fakes.FakeSubnet.create_one_subnet()
_router = network_fakes.FakeRouter.create_one_router(
attrs={'routes': [_default_route],
@@ -1364,7 +1363,7 @@ class TestShowRouter(TestRouter):
# The router to set.
_router = network_fakes.FakeRouter.create_one_router()
- _port = network_fakes.FakePort.create_one_port({
+ _port = network_fakes.create_one_port({
'device_owner': 'network:router_interface',
'device_id': _router.id
})
@@ -1485,7 +1484,7 @@ class TestUnsetRouter(TestRouter):
def setUp(self):
super(TestUnsetRouter, self).setUp()
- self.fake_network = network_fakes.FakeNetwork.create_one_network()
+ self.fake_network = network_fakes.create_one_network()
self.fake_qos_policy = (
network_fakes.FakeNetworkQosPolicy.create_one_qos_policy())
self._testrouter = network_fakes.FakeRouter.create_one_router(
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 4e765f3..3c90f28 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
@@ -47,7 +47,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
network_fakes.FakeSecurityGroup.create_one_security_group()
# The address group to be used in security group rules
- _address_group = network_fakes.FakeAddressGroup.create_one_address_group()
+ _address_group = network_fakes.create_one_address_group()
expected_columns = (
'description',
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index dcac2ef..6b3ab2c 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -95,7 +95,7 @@ class TestCreateSubnet(TestSubnet):
)
# The network to be returned from find_network
- self._network = network_fakes.FakeNetwork.create_one_network(
+ self._network = network_fakes.create_one_network(
attrs={
'id': self._subnet.network_id,
}
@@ -103,7 +103,7 @@ class TestCreateSubnet(TestSubnet):
# The network segment to be returned from find_segment
self._network_segment = \
- network_fakes.FakeNetworkSegment.create_one_network_segment(
+ network_fakes.create_one_network_segment(
attrs={
'network_id': self._subnet.network_id,
}
@@ -828,7 +828,7 @@ class TestListSubnet(TestSubnet):
self.assertCountEqual(self.data, list(data))
def test_subnet_list_network(self):
- network = network_fakes.FakeNetwork.create_one_network()
+ network = network_fakes.create_one_network()
self.network.find_network = mock.Mock(return_value=network)
arglist = [
'--network', network.id,
@@ -1185,8 +1185,8 @@ class TestSetSubnet(TestSubnet):
self._test_set_tags(with_tags=False)
def test_set_segment(self):
- _net = network_fakes.FakeNetwork.create_one_network()
- _segment = network_fakes.FakeNetworkSegment.create_one_network_segment(
+ _net = network_fakes.create_one_network()
+ _segment = network_fakes.create_one_network_segment(
attrs={'network_id': _net.id})
_subnet = network_fakes.FakeSubnet.create_one_subnet(
{'host_routes': [{'destination': '10.20.20.0/24',
diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py
index 55d2cc2..b24906b 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py
@@ -44,7 +44,7 @@ class TestCreateSubnetPool(TestSubnetPool):
# The new subnet pool to create.
_subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool()
- _address_scope = network_fakes.FakeAddressScope.create_one_address_scope()
+ _address_scope = network_fakes.create_one_address_scope()
columns = (
'address_scope_id',
@@ -614,7 +614,7 @@ class TestListSubnetPool(TestSubnetPool):
self.assertCountEqual(self.data, list(data))
def test_subnet_pool_list_address_scope(self):
- addr_scope = network_fakes.FakeAddressScope.create_one_address_scope()
+ addr_scope = network_fakes.create_one_address_scope()
self.network.find_address_scope = mock.Mock(return_value=addr_scope)
arglist = [
'--address-scope', addr_scope.id,
@@ -665,7 +665,7 @@ class TestSetSubnetPool(TestSubnetPool):
'tags': ['green', 'red']}
)
- _address_scope = network_fakes.FakeAddressScope.create_one_address_scope()
+ _address_scope = network_fakes.create_one_address_scope()
def setUp(self):
super(TestSetSubnetPool, self).setUp()
diff --git a/openstackclient/tests/unit/volume/v1/test_volume_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py
index f25a5ff..2f56892 100644
--- a/openstackclient/tests/unit/volume/v1/test_volume_backup.py
+++ b/openstackclient/tests/unit/volume/v1/test_volume_backup.py
@@ -319,29 +319,69 @@ class TestBackupRestore(TestBackup):
attrs={'volume_id': volume.id})
def setUp(self):
- super(TestBackupRestore, self).setUp()
+ super().setUp()
self.backups_mock.get.return_value = self.backup
self.volumes_mock.get.return_value = self.volume
- self.restores_mock.restore.return_value = None
+ self.restores_mock.restore.return_value = (
+ volume_fakes.FakeVolume.create_one_volume(
+ {'id': self.volume['id']},
+ )
+ )
# Get the command object to mock
self.cmd = volume_backup.RestoreVolumeBackup(self.app, None)
def test_backup_restore(self):
arglist = [
self.backup.id,
- self.backup.volume_id
]
verifylist = [
("backup", self.backup.id),
- ("volume", self.backup.volume_id)
+ ("volume", None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.restores_mock.restore.assert_called_with(self.backup.id,
- self.backup.volume_id)
- self.assertIsNone(result)
+ None)
+ self.assertIsNotNone(result)
+
+ def test_backup_restore_with_existing_volume(self):
+ arglist = [
+ self.backup.id,
+ self.backup.volume_id,
+ ]
+ verifylist = [
+ ("backup", self.backup.id),
+ ("volume", self.backup.volume_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.restores_mock.restore.assert_called_with(
+ self.backup.id, self.backup.volume_id,
+ )
+ self.assertIsNotNone(result)
+
+ def test_backup_restore_with_invalid_volume(self):
+ arglist = [
+ self.backup.id,
+ "unexist_volume",
+ ]
+ verifylist = [
+ ("backup", self.backup.id),
+ ("volume", "unexist_volume"),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ with mock.patch.object(
+ utils, 'find_resource',
+ side_effect=exceptions.CommandError(),
+ ):
+ self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args,
+ )
class TestBackupShow(TestBackup):
diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py
index 4b9212d..ffd8490 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py
@@ -458,35 +458,95 @@ class TestBackupRestore(TestBackup):
volume = volume_fakes.FakeVolume.create_one_volume()
backup = volume_fakes.FakeBackup.create_one_backup(
- attrs={'volume_id': volume.id})
+ attrs={'volume_id': volume.id},
+ )
def setUp(self):
- super(TestBackupRestore, self).setUp()
+ super().setUp()
self.backups_mock.get.return_value = self.backup
self.volumes_mock.get.return_value = self.volume
self.restores_mock.restore.return_value = (
volume_fakes.FakeVolume.create_one_volume(
- {'id': self.volume['id']}))
+ {'id': self.volume['id']},
+ )
+ )
# Get the command object to mock
self.cmd = volume_backup.RestoreVolumeBackup(self.app, None)
def test_backup_restore(self):
+ self.volumes_mock.get.side_effect = exceptions.CommandError()
+ self.volumes_mock.find.side_effect = exceptions.CommandError()
+ arglist = [
+ self.backup.id
+ ]
+ verifylist = [
+ ("backup", self.backup.id),
+ ("volume", None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.restores_mock.restore.assert_called_with(
+ self.backup.id, None, None,
+ )
+ self.assertIsNotNone(result)
+
+ def test_backup_restore_with_volume(self):
+ self.volumes_mock.get.side_effect = exceptions.CommandError()
+ self.volumes_mock.find.side_effect = exceptions.CommandError()
+ arglist = [
+ self.backup.id,
+ self.backup.volume_id,
+ ]
+ verifylist = [
+ ("backup", self.backup.id),
+ ("volume", self.backup.volume_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.restores_mock.restore.assert_called_with(
+ self.backup.id, None, self.backup.volume_id,
+ )
+ self.assertIsNotNone(result)
+
+ def test_backup_restore_with_volume_force(self):
arglist = [
+ "--force",
self.backup.id,
- self.backup.volume_id
+ self.volume.name,
]
verifylist = [
+ ("force", True),
("backup", self.backup.id),
- ("volume", self.backup.volume_id)
+ ("volume", self.volume.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.restores_mock.restore.assert_called_with(self.backup.id,
- self.backup.volume_id)
+ self.restores_mock.restore.assert_called_with(
+ self.backup.id, self.volume.id, None,
+ )
self.assertIsNotNone(result)
+ def test_backup_restore_with_volume_existing(self):
+ arglist = [
+ self.backup.id,
+ self.volume.name,
+ ]
+ verifylist = [
+ ("backup", self.backup.id),
+ ("volume", self.volume.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args,
+ )
+
class TestBackupSet(TestBackup):
diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py
index 9040b2b..e27d7fc 100644
--- a/openstackclient/tests/unit/volume/v3/fakes.py
+++ b/openstackclient/tests/unit/volume/v3/fakes.py
@@ -32,6 +32,8 @@ class FakeVolumeClient(object):
self.attachments = mock.Mock()
self.attachments.resource_class = fakes.FakeResource(None, {})
+ self.clusters = mock.Mock()
+ self.clusters.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
self.groups.resource_class = fakes.FakeResource(None, {})
self.group_snapshots = mock.Mock()
@@ -40,6 +42,8 @@ class FakeVolumeClient(object):
self.group_types.resource_class = fakes.FakeResource(None, {})
self.messages = mock.Mock()
self.messages.resource_class = fakes.FakeResource(None, {})
+ self.resource_filters = mock.Mock()
+ self.resource_filters.resource_class = fakes.FakeResource(None, {})
self.volumes = mock.Mock()
self.volumes.resource_class = fakes.FakeResource(None, {})
self.volume_types = mock.Mock()
@@ -70,6 +74,105 @@ FakeVolume = volume_v2_fakes.FakeVolume
FakeVolumeType = volume_v2_fakes.FakeVolumeType
+class FakeCluster:
+ """Fake one or more clusters."""
+
+ @staticmethod
+ def create_one_cluster(attrs=None):
+ """Create a fake service cluster.
+
+ :param attrs: A dictionary with all attributes of service cluster
+ :return: A FakeResource object with id, name, status, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attribute
+ cluster_info = {
+ 'name': f'cluster-{uuid.uuid4().hex}',
+ 'binary': f'binary-{uuid.uuid4().hex}',
+ 'state': random.choice(['up', 'down']),
+ 'status': random.choice(['enabled', 'disabled']),
+ 'disabled_reason': None,
+ 'num_hosts': random.randint(1, 64),
+ 'num_down_hosts': random.randint(1, 64),
+ 'last_heartbeat': '2015-09-16T09:28:52.000000',
+ 'created_at': '2015-09-16T09:28:52.000000',
+ 'updated_at': '2015-09-16T09:28:52.000000',
+ 'replication_status': None,
+ 'frozen': False,
+ 'active_backend_id': None,
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ cluster_info.update(attrs)
+
+ return fakes.FakeResource(
+ None,
+ cluster_info,
+ loaded=True)
+
+ @staticmethod
+ def create_clusters(attrs=None, count=2):
+ """Create multiple fake service clusters.
+
+ :param attrs: A dictionary with all attributes of service cluster
+ :param count: The number of service clusters to be faked
+ :return: A list of FakeResource objects
+ """
+ clusters = []
+ for n in range(0, count):
+ clusters.append(FakeCluster.create_one_cluster(attrs))
+
+ return clusters
+
+
+class FakeResourceFilter:
+ """Fake one or more resource filters."""
+
+ @staticmethod
+ def create_one_resource_filter(attrs=None):
+ """Create a fake resource filter.
+
+ :param attrs: A dictionary with all attributes of resource filter
+ :return: A FakeResource object with id, name, status, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attribute
+
+ resource_filter_info = {
+ 'filters': [
+ 'name',
+ 'status',
+ 'image_metadata',
+ 'bootable',
+ 'migration_status',
+ ],
+ 'resource': 'volume',
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ resource_filter_info.update(attrs)
+
+ return fakes.FakeResource(None, resource_filter_info, loaded=True)
+
+ @staticmethod
+ def create_resource_filters(attrs=None, count=2):
+ """Create multiple fake resource filters.
+
+ :param attrs: A dictionary with all attributes of resource filter
+ :param count: The number of resource filters to be faked
+ :return: A list of FakeResource objects
+ """
+ resource_filters = []
+ for n in range(0, count):
+ resource_filters.append(
+ FakeResourceFilter.create_one_resource_filter(attrs)
+ )
+
+ return resource_filters
+
+
class FakeVolumeGroup:
"""Fake one or more volume groups."""
@@ -255,11 +358,10 @@ class FakeVolumeMessage:
# Overwrite default attributes if there are some attributes set
message_info.update(attrs)
- message = fakes.FakeResource(
+ return fakes.FakeResource(
None,
message_info,
loaded=True)
- return message
@staticmethod
def create_volume_messages(attrs=None, count=2):
@@ -348,11 +450,10 @@ class FakeVolumeAttachment:
# Overwrite default attributes if there are some attributes set
attachment_info.update(attrs)
- attachment = fakes.FakeResource(
+ return fakes.FakeResource(
None,
attachment_info,
loaded=True)
- return attachment
@staticmethod
def create_volume_attachments(attrs=None, count=2):
diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py b/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py
new file mode 100644
index 0000000..d87a946
--- /dev/null
+++ b/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py
@@ -0,0 +1,434 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from cinderclient import api_versions
+from osc_lib import exceptions
+
+from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
+from openstackclient.volume.v3 import block_storage_cluster
+
+
+class TestBlockStorageCluster(volume_fakes.TestVolume):
+
+ def setUp(self):
+ super().setUp()
+
+ # Get a shortcut to the BlockStorageClusterManager Mock
+ self.cluster_mock = self.app.client_manager.volume.clusters
+ self.cluster_mock.reset_mock()
+
+
+class TestBlockStorageClusterList(TestBlockStorageCluster):
+
+ # The cluster to be listed
+ fake_clusters = volume_fakes.FakeCluster.create_clusters()
+
+ def setUp(self):
+ super().setUp()
+
+ self.cluster_mock.list.return_value = self.fake_clusters
+
+ # Get the command object to test
+ self.cmd = \
+ block_storage_cluster.ListBlockStorageCluster(self.app, None)
+
+ def test_cluster_list(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ ]
+ verifylist = [
+ ('cluster', None),
+ ('binary', None),
+ ('is_up', None),
+ ('is_disabled', None),
+ ('num_hosts', None),
+ ('num_down_hosts', None),
+ ('long', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = ('Name', 'Binary', 'State', 'Status')
+ expected_data = tuple(
+ (
+ cluster.name,
+ cluster.binary,
+ cluster.state,
+ cluster.status,
+ ) for cluster in self.fake_clusters
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, tuple(data))
+
+ # checking if proper call was made to list clusters
+ self.cluster_mock.list.assert_called_with(
+ name=None,
+ binary=None,
+ is_up=None,
+ disabled=None,
+ num_hosts=None,
+ num_down_hosts=None,
+ detailed=False,
+ )
+
+ def test_cluster_list_with_full_options(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ '--cluster', 'foo',
+ '--binary', 'bar',
+ '--up',
+ '--disabled',
+ '--num-hosts', '5',
+ '--num-down-hosts', '0',
+ '--long',
+ ]
+ verifylist = [
+ ('cluster', 'foo'),
+ ('binary', 'bar'),
+ ('is_up', True),
+ ('is_disabled', True),
+ ('num_hosts', 5),
+ ('num_down_hosts', 0),
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = (
+ 'Name',
+ 'Binary',
+ 'State',
+ 'Status',
+ 'Num Hosts',
+ 'Num Down Hosts',
+ 'Last Heartbeat',
+ 'Disabled Reason',
+ 'Created At',
+ 'Updated At',
+ )
+ expected_data = tuple(
+ (
+ cluster.name,
+ cluster.binary,
+ cluster.state,
+ cluster.status,
+ cluster.num_hosts,
+ cluster.num_down_hosts,
+ cluster.last_heartbeat,
+ cluster.disabled_reason,
+ cluster.created_at,
+ cluster.updated_at,
+ ) for cluster in self.fake_clusters
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, tuple(data))
+
+ # checking if proper call was made to list clusters
+ self.cluster_mock.list.assert_called_with(
+ name='foo',
+ binary='bar',
+ is_up=True,
+ disabled=True,
+ num_hosts=5,
+ num_down_hosts=0,
+ detailed=True,
+ )
+
+ def test_cluster_list_pre_v37(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.6')
+
+ arglist = [
+ ]
+ verifylist = [
+ ('cluster', None),
+ ('binary', None),
+ ('is_up', None),
+ ('is_disabled', None),
+ ('num_hosts', None),
+ ('num_down_hosts', None),
+ ('long', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-volume-api-version 3.7 or greater is required', str(exc))
+
+
+class TestBlockStorageClusterSet(TestBlockStorageCluster):
+
+ cluster = volume_fakes.FakeCluster.create_one_cluster()
+ columns = (
+ 'Name',
+ 'Binary',
+ 'State',
+ 'Status',
+ 'Disabled Reason',
+ 'Hosts',
+ 'Down Hosts',
+ 'Last Heartbeat',
+ 'Created At',
+ 'Updated At',
+ 'Replication Status',
+ 'Frozen',
+ 'Active Backend ID',
+ )
+ data = (
+ cluster.name,
+ cluster.binary,
+ cluster.state,
+ cluster.status,
+ cluster.disabled_reason,
+ cluster.num_hosts,
+ cluster.num_down_hosts,
+ cluster.last_heartbeat,
+ cluster.created_at,
+ cluster.updated_at,
+ cluster.replication_status,
+ cluster.frozen,
+ cluster.active_backend_id,
+ )
+
+ def setUp(self):
+ super().setUp()
+
+ self.cluster_mock.update.return_value = self.cluster
+
+ self.cmd = \
+ block_storage_cluster.SetBlockStorageCluster(self.app, None)
+
+ def test_cluster_set(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ '--enable',
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', 'cinder-volume'),
+ ('disabled', False),
+ ('disabled_reason', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, tuple(data))
+
+ self.cluster_mock.update.assert_called_once_with(
+ self.cluster.name,
+ 'cinder-volume',
+ disabled=False,
+ disabled_reason=None,
+ )
+
+ def test_cluster_set_disable_with_reason(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ '--binary', self.cluster.binary,
+ '--disable',
+ '--disable-reason', 'foo',
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', self.cluster.binary),
+ ('disabled', True),
+ ('disabled_reason', 'foo'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, tuple(data))
+
+ self.cluster_mock.update.assert_called_once_with(
+ self.cluster.name,
+ self.cluster.binary,
+ disabled=True,
+ disabled_reason='foo',
+ )
+
+ def test_cluster_set_only_with_disable_reason(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ '--disable-reason', 'foo',
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', 'cinder-volume'),
+ ('disabled', None),
+ ('disabled_reason', 'foo'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ "Cannot specify --disable-reason without --disable", str(exc))
+
+ def test_cluster_set_enable_with_disable_reason(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ '--enable',
+ '--disable-reason', 'foo',
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', 'cinder-volume'),
+ ('disabled', False),
+ ('disabled_reason', 'foo'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ "Cannot specify --disable-reason without --disable", str(exc))
+
+ def test_cluster_set_pre_v37(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.6')
+
+ arglist = [
+ '--enable',
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', 'cinder-volume'),
+ ('disabled', False),
+ ('disabled_reason', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-volume-api-version 3.7 or greater is required', str(exc))
+
+
+class TestBlockStorageClusterShow(TestBlockStorageCluster):
+
+ cluster = volume_fakes.FakeCluster.create_one_cluster()
+ columns = (
+ 'Name',
+ 'Binary',
+ 'State',
+ 'Status',
+ 'Disabled Reason',
+ 'Hosts',
+ 'Down Hosts',
+ 'Last Heartbeat',
+ 'Created At',
+ 'Updated At',
+ 'Replication Status',
+ 'Frozen',
+ 'Active Backend ID',
+ )
+ data = (
+ cluster.name,
+ cluster.binary,
+ cluster.state,
+ cluster.status,
+ cluster.disabled_reason,
+ cluster.num_hosts,
+ cluster.num_down_hosts,
+ cluster.last_heartbeat,
+ cluster.created_at,
+ cluster.updated_at,
+ cluster.replication_status,
+ cluster.frozen,
+ cluster.active_backend_id,
+ )
+
+ def setUp(self):
+ super().setUp()
+
+ self.cluster_mock.show.return_value = self.cluster
+
+ self.cmd = \
+ block_storage_cluster.ShowBlockStorageCluster(self.app, None)
+
+ def test_cluster_show(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.7')
+
+ arglist = [
+ '--binary', self.cluster.binary,
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', self.cluster.binary),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, tuple(data))
+
+ self.cluster_mock.show.assert_called_once_with(
+ self.cluster.name,
+ binary=self.cluster.binary,
+ )
+
+ def test_cluster_show_pre_v37(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.6')
+
+ arglist = [
+ '--binary', self.cluster.binary,
+ self.cluster.name,
+ ]
+ verifylist = [
+ ('cluster', self.cluster.name),
+ ('binary', self.cluster.binary),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-volume-api-version 3.7 or greater is required', str(exc))
diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py
new file mode 100644
index 0000000..b886726
--- /dev/null
+++ b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py
@@ -0,0 +1,144 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from cinderclient import api_versions
+from osc_lib import exceptions
+
+from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
+from openstackclient.volume.v3 import block_storage_resource_filter
+
+
+class TestBlockStorageResourceFilter(volume_fakes.TestVolume):
+
+ def setUp(self):
+ super().setUp()
+
+ # Get a shortcut to the ResourceFilterManager Mock
+ self.resource_filter_mock = \
+ self.app.client_manager.volume.resource_filters
+ self.resource_filter_mock.reset_mock()
+
+
+class TestBlockStorageResourceFilterList(TestBlockStorageResourceFilter):
+
+ # The resource filters to be listed
+ fake_resource_filters = \
+ volume_fakes.FakeResourceFilter.create_resource_filters()
+
+ def setUp(self):
+ super().setUp()
+
+ self.resource_filter_mock.list.return_value = \
+ self.fake_resource_filters
+
+ # Get the command object to test
+ self.cmd = block_storage_resource_filter\
+ .ListBlockStorageResourceFilter(self.app, None)
+
+ def test_resource_filter_list(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.33')
+
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = ('Resource', 'Filters')
+ expected_data = tuple(
+ (
+ resource_filter.resource,
+ resource_filter.filters,
+ ) for resource_filter in self.fake_resource_filters
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, tuple(data))
+
+ # checking if proper call was made to list clusters
+ self.resource_filter_mock.list.assert_called_with()
+
+ def test_resource_filter_list_pre_v333(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.32')
+
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-volume-api-version 3.33 or greater is required', str(exc))
+
+
+class TestBlockStorageResourceFilterShow(TestBlockStorageResourceFilter):
+
+ # The resource filters to be listed
+ fake_resource_filter = \
+ volume_fakes.FakeResourceFilter.create_one_resource_filter()
+
+ def setUp(self):
+ super().setUp()
+
+ self.resource_filter_mock.list.return_value = \
+ iter([self.fake_resource_filter])
+
+ # Get the command object to test
+ self.cmd = block_storage_resource_filter\
+ .ShowBlockStorageResourceFilter(self.app, None)
+
+ def test_resource_filter_show(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.33')
+
+ arglist = [
+ self.fake_resource_filter.resource,
+ ]
+ verifylist = [
+ ('resource', self.fake_resource_filter.resource),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ expected_columns = ('filters', 'resource')
+ expected_data = (
+ self.fake_resource_filter.filters,
+ self.fake_resource_filter.resource,
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(expected_columns, columns)
+ self.assertEqual(expected_data, data)
+
+ # checking if proper call was made to list clusters
+ self.resource_filter_mock.list.assert_called_with(resource='volume')
+
+ def test_resource_filter_show_pre_v333(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.32')
+
+ arglist = [
+ self.fake_resource_filter.resource,
+ ]
+ verifylist = [
+ ('resource', self.fake_resource_filter.resource),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ exc = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-volume-api-version 3.33 or greater is required', str(exc))
diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py
index 09f698e..44fac6c 100644
--- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py
+++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py
@@ -73,8 +73,9 @@ class TestVolumeAttachmentCreate(TestVolumeAttachment):
self.volumes_mock.get.return_value = self.volume
self.servers_mock.get.return_value = self.server
+ # VolumeAttachmentManager.create returns a dict
self.volume_attachments_mock.create.return_value = \
- self.volume_attachment
+ self.volume_attachment.to_dict()
self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None)
diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py
index 1a83a3c..790cb46 100644
--- a/openstackclient/volume/v1/volume_backup.py
+++ b/openstackclient/volume/v1/volume_backup.py
@@ -231,18 +231,23 @@ class RestoreVolumeBackup(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help=_('Volume to restore to (name or ID)')
+ nargs='?',
+ help=_('Volume to restore to (name or ID) (default to None)')
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- backup = utils.find_resource(volume_client.backups,
- parsed_args.backup)
- destination_volume = utils.find_resource(volume_client.volumes,
- parsed_args.volume)
- return volume_client.restores.restore(backup.id,
- destination_volume.id)
+ backup = utils.find_resource(
+ volume_client.backups, parsed_args.backup,
+ )
+ volume_id = None
+ if parsed_args.volume is not None:
+ volume_id = utils.find_resource(
+ volume_client.volumes,
+ parsed_args.volume,
+ ).id
+ return volume_client.restores.restore(backup.id, volume_id)
class ShowVolumeBackup(command.ShowOne):
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index 4f015d1..c584943 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -411,7 +411,7 @@ class UnsetVolumeType(command.Command):
"--encryption-type",
action="store_true",
help=_("Remove the encryption type for this volume type "
- "(admin oly)"),
+ "(admin only)"),
)
return parser
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index cab0b2f..6d14b36 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -361,7 +361,6 @@ class ListVolume(command.Lister):
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
- compute_client = self.app.client_manager.compute
identity_client = self.app.client_manager.identity
if parsed_args.long:
@@ -393,6 +392,7 @@ class ListVolume(command.Lister):
# Cache the server list
server_cache = {}
try:
+ compute_client = self.app.client_manager.compute
for s in compute_client.servers.list():
server_cache[s.id] = s
except Exception:
diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py
index 96b22a6..d96b28e 100644
--- a/openstackclient/volume/v2/volume_backup.py
+++ b/openstackclient/volume/v2/volume_backup.py
@@ -310,9 +310,17 @@ class ListVolumeBackup(command.Lister):
filter_volume_id = None
if parsed_args.volume:
- filter_volume_id = utils.find_resource(
- volume_client.volumes, parsed_args.volume,
- ).id
+ try:
+ filter_volume_id = utils.find_resource(
+ volume_client.volumes, parsed_args.volume,
+ ).id
+ except exceptions.CommandError:
+ # Volume with that ID does not exist, but search for backups
+ # for that volume nevertheless
+ LOG.debug("No volume with ID %s existing, continuing to "
+ "search for backups for that volume ID",
+ parsed_args.volume)
+ filter_volume_id = parsed_args.volume
marker_backup_id = None
if parsed_args.marker:
@@ -355,18 +363,50 @@ class RestoreVolumeBackup(command.ShowOne):
parser.add_argument(
"volume",
metavar="<volume>",
- help=_("Volume to restore to (name or ID)")
+ nargs="?",
+ help=_(
+ "Volume to restore to "
+ "(name or ID for existing volume, name only for new volume) "
+ "(default to None)"
+ )
+ )
+ parser.add_argument(
+ "--force",
+ action="store_true",
+ help=_(
+ "Restore the backup to an existing volume "
+ "(default to False)"
+ )
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
+
backup = utils.find_resource(volume_client.backups, parsed_args.backup)
- destination_volume = utils.find_resource(volume_client.volumes,
- parsed_args.volume)
- backup = volume_client.restores.restore(backup.id,
- destination_volume.id)
- return zip(*sorted(backup._info.items()))
+
+ volume_name = None
+ volume_id = None
+ try:
+ volume_id = utils.find_resource(
+ volume_client.volumes,
+ parsed_args.volume,
+ ).id
+ except Exception:
+ volume_name = parsed_args.volume
+ else:
+ # If we didn't fail, the volume must already exist. We only allow
+ # this to work if the user forced things
+ if not parsed_args.force:
+ msg = _(
+ "Volume '%s' already exists; if you want to restore the "
+ "backup to it you need to specify the '--force' option"
+ ) % parsed_args.volume
+ raise exceptions.CommandError(msg)
+
+ return volume_client.restores.restore(
+ backup.id, volume_id, volume_name,
+ )
class SetVolumeBackup(command.Command):
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
index 656f59d..53d8d27 100644
--- a/openstackclient/volume/v2/volume_snapshot.py
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -98,7 +98,7 @@ class CreateVolumeSnapshot(command.ShowOne):
"--remote-source",
metavar="<key=value>",
action=parseractions.KeyValueAction,
- help=_("The attribute(s) of the exsiting remote volume snapshot "
+ help=_("The attribute(s) of the existing remote volume snapshot "
"(admin required) (repeat option to specify multiple "
"attributes) e.g.: '--remote-source source-name=test_name "
"--remote-source source-id=test_id'"),
diff --git a/openstackclient/volume/v3/block_storage_cluster.py b/openstackclient/volume/v3/block_storage_cluster.py
new file mode 100644
index 0000000..34b25ef
--- /dev/null
+++ b/openstackclient/volume/v3/block_storage_cluster.py
@@ -0,0 +1,281 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from cinderclient import api_versions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+
+
+def _format_cluster(cluster, detailed=False):
+ columns = (
+ 'name',
+ 'binary',
+ 'state',
+ 'status',
+ )
+ column_headers = (
+ 'Name',
+ 'Binary',
+ 'State',
+ 'Status',
+ )
+
+ if detailed:
+ columns += (
+ 'disabled_reason',
+ 'num_hosts',
+ 'num_down_hosts',
+ 'last_heartbeat',
+ 'created_at',
+ 'updated_at',
+ # optional columns, depending on whether replication is enabled
+ 'replication_status',
+ 'frozen',
+ 'active_backend_id',
+ )
+ column_headers += (
+ 'Disabled Reason',
+ 'Hosts',
+ 'Down Hosts',
+ 'Last Heartbeat',
+ 'Created At',
+ 'Updated At',
+ # optional columns, depending on whether replication is enabled
+ 'Replication Status',
+ 'Frozen',
+ 'Active Backend ID',
+ )
+
+ return (
+ column_headers,
+ utils.get_item_properties(
+ cluster,
+ columns,
+ ),
+ )
+
+
+class ListBlockStorageCluster(command.Lister):
+ """List block storage clusters.
+
+ This command requires ``--os-volume-api-version`` 3.7 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ '--cluster', metavar='<name>', default=None,
+ help=_(
+ 'Filter by cluster name, without backend will list '
+ 'all clustered services from the same cluster.'
+ ),
+ )
+ parser.add_argument(
+ '--binary',
+ metavar='<binary>',
+ help=_('Cluster binary.'),
+ )
+ parser.add_argument(
+ '--up',
+ action='store_true',
+ dest='is_up',
+ default=None,
+ help=_('Filter by up status.'),
+ )
+ parser.add_argument(
+ '--down',
+ action='store_false',
+ dest='is_up',
+ help=_('Filter by down status.'),
+ )
+ parser.add_argument(
+ '--disabled',
+ action='store_true',
+ dest='is_disabled',
+ default=None,
+ help=_('Filter by disabled status.'),
+ )
+ parser.add_argument(
+ '--enabled',
+ action='store_false',
+ dest='is_disabled',
+ help=_('Filter by enabled status.'),
+ )
+ parser.add_argument(
+ '--num-hosts',
+ metavar='<hosts>',
+ type=int,
+ default=None,
+ help=_('Filter by number of hosts in the cluster.'),
+ )
+ parser.add_argument(
+ '--num-down-hosts',
+ metavar='<hosts>',
+ type=int,
+ default=None,
+ help=_('Filter by number of hosts that are down.'),
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_("List additional fields in output")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.7'):
+ msg = _(
+ "--os-volume-api-version 3.7 or greater is required to "
+ "support the 'block storage cluster list' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ columns = ('Name', 'Binary', 'State', 'Status')
+ if parsed_args.long:
+ columns += (
+ 'Num Hosts',
+ 'Num Down Hosts',
+ 'Last Heartbeat',
+ 'Disabled Reason',
+ 'Created At',
+ 'Updated At',
+ )
+
+ data = volume_client.clusters.list(
+ name=parsed_args.cluster,
+ binary=parsed_args.binary,
+ is_up=parsed_args.is_up,
+ disabled=parsed_args.is_disabled,
+ num_hosts=parsed_args.num_hosts,
+ num_down_hosts=parsed_args.num_down_hosts,
+ detailed=parsed_args.long,
+ )
+
+ return (
+ columns,
+ (utils.get_item_properties(s, columns) for s in data),
+ )
+
+
+class SetBlockStorageCluster(command.Command):
+ """Set block storage cluster properties.
+
+ This command requires ``--os-volume-api-version`` 3.7 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'cluster',
+ metavar='<cluster>',
+ help=_('Name of block storage cluster to update (name only)')
+ )
+ parser.add_argument(
+ '--binary',
+ metavar='<binary>',
+ default='cinder-volume',
+ help=_(
+ "Name of binary to filter by; defaults to 'cinder-volume' "
+ "(optional)"
+ )
+ )
+ enabled_group = parser.add_mutually_exclusive_group()
+ enabled_group.add_argument(
+ '--enable',
+ action='store_false',
+ dest='disabled',
+ default=None,
+ help=_('Enable cluster')
+ )
+ enabled_group.add_argument(
+ '--disable',
+ action='store_true',
+ dest='disabled',
+ help=_('Disable cluster')
+ )
+ parser.add_argument(
+ '--disable-reason',
+ metavar='<reason>',
+ dest='disabled_reason',
+ help=_(
+ 'Reason for disabling the cluster '
+ '(should be used with --disable option)'
+ )
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.7'):
+ msg = _(
+ "--os-volume-api-version 3.7 or greater is required to "
+ "support the 'block storage cluster set' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ if parsed_args.disabled_reason and not parsed_args.disabled:
+ msg = _("Cannot specify --disable-reason without --disable")
+ raise exceptions.CommandError(msg)
+
+ cluster = volume_client.clusters.update(
+ parsed_args.cluster,
+ parsed_args.binary,
+ disabled=parsed_args.disabled,
+ disabled_reason=parsed_args.disabled_reason,
+ )
+
+ return _format_cluster(cluster, detailed=True)
+
+
+class ShowBlockStorageCluster(command.ShowOne):
+ """Show detailed information for a block storage cluster.
+
+ This command requires ``--os-volume-api-version`` 3.7 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'cluster',
+ metavar='<cluster>',
+ help=_('Name of block storage cluster.'),
+ )
+ parser.add_argument(
+ '--binary',
+ metavar='<binary>',
+ help=_('Service binary.'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.7'):
+ msg = _(
+ "--os-volume-api-version 3.7 or greater is required to "
+ "support the 'block storage cluster show' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ cluster = volume_client.clusters.show(
+ parsed_args.cluster,
+ binary=parsed_args.binary,
+ )
+
+ return _format_cluster(cluster, detailed=True)
diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py
new file mode 100644
index 0000000..4bcacf9
--- /dev/null
+++ b/openstackclient/volume/v3/block_storage_resource_filter.py
@@ -0,0 +1,83 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Volume V3 Resource Filters implementations"""
+
+from cinderclient import api_versions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+
+
+class ListBlockStorageResourceFilter(command.Lister):
+ _description = _('List block storage resource filters')
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.33'):
+ msg = _(
+ "--os-volume-api-version 3.33 or greater is required to "
+ "support the 'block storage resource filter list' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ column_headers = (
+ 'Resource',
+ 'Filters',
+ )
+
+ data = volume_client.resource_filters.list()
+
+ return (
+ column_headers,
+ (utils.get_item_properties(s, column_headers) for s in data)
+ )
+
+
+class ShowBlockStorageResourceFilter(command.ShowOne):
+ _description = _('Show filters for a block storage resource type')
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'resource',
+ metavar='<resource>',
+ help=_('Resource to show filters for (name).')
+ )
+
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.33'):
+ msg = _(
+ "--os-volume-api-version 3.33 or greater is required to "
+ "support the 'block storage resource filter show' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ data = volume_client.resource_filters.list(
+ resource=parsed_args.resource
+ )
+ if not data:
+ msg = _(
+ "No resource filter with a name of {parsed_args.resource}' "
+ "exists."
+ )
+ raise exceptions.CommandError(msg)
+ resource_filter = next(data)
+
+ return zip(*sorted(resource_filter._info.items()))
diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py
index 39a9c37..c740183 100644
--- a/openstackclient/volume/v3/volume_attachment.py
+++ b/openstackclient/volume/v3/volume_attachment.py
@@ -51,18 +51,27 @@ def _format_attachment(attachment):
'Properties',
)
- # TODO(stephenfin): Improve output with the nested connection_info
- # field - cinderclient printed two things but that's equally ugly
- return (
- column_headers,
- utils.get_item_properties(
+ # VolumeAttachmentManager.create returns a dict while everything else
+ # returns a VolumeAttachment object
+ if isinstance(attachment, dict):
+ data = []
+ for column in columns:
+ if column == 'connection_info':
+ data.append(format_columns.DictColumn(attachment[column]))
+ continue
+ data.append(attachment[column])
+ else:
+ data = utils.get_item_properties(
attachment,
columns,
formatters={
'connection_info': format_columns.DictColumn,
},
- ),
- )
+ )
+
+ # TODO(stephenfin): Improve output with the nested connection_info
+ # field - cinderclient printed two things but that's equally ugly
+ return (column_headers, data)
class CreateVolumeAttachment(command.ShowOne):
diff --git a/python_openstackclient.egg-info/PKG-INFO b/python_openstackclient.egg-info/PKG-INFO
index 7868b65..0a41b07 100644
--- a/python_openstackclient.egg-info/PKG-INFO
+++ b/python_openstackclient.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: python-openstackclient
-Version: 5.8.0
+Version: 6.0.0
Summary: OpenStack Command-line Client
Home-page: https://docs.openstack.org/python-openstackclient/latest/
Author: OpenStack
@@ -154,7 +154,6 @@ Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
-Requires-Python: >=3.6
+Classifier: Programming Language :: Python :: 3.9
+Requires-Python: >=3.8
diff --git a/python_openstackclient.egg-info/SOURCES.txt b/python_openstackclient.egg-info/SOURCES.txt
index 43ad681..b4f120a 100644
--- a/python_openstackclient.egg-info/SOURCES.txt
+++ b/python_openstackclient.egg-info/SOURCES.txt
@@ -11,7 +11,6 @@ HACKING.rst
LICENSE
README.rst
bindep.txt
-lower-constraints.txt
requirements.txt
setup.cfg
setup.py
@@ -44,6 +43,8 @@ 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/block-storage-cluster.rst
+doc/source/cli/command-objects/block-storage-resource-filter.rst
doc/source/cli/command-objects/catalog.rst
doc/source/cli/command-objects/command.rst
doc/source/cli/command-objects/complete.rst
@@ -232,6 +233,7 @@ openstackclient/compute/v2/server_backup.py
openstackclient/compute/v2/server_event.py
openstackclient/compute/v2/server_group.py
openstackclient/compute/v2/server_image.py
+openstackclient/compute/v2/server_migration.py
openstackclient/compute/v2/server_volume.py
openstackclient/compute/v2/service.py
openstackclient/compute/v2/usage.py
@@ -479,6 +481,7 @@ openstackclient/tests/unit/compute/v2/test_server_backup.py
openstackclient/tests/unit/compute/v2/test_server_event.py
openstackclient/tests/unit/compute/v2/test_server_group.py
openstackclient/tests/unit/compute/v2/test_server_image.py
+openstackclient/tests/unit/compute/v2/test_server_migration.py
openstackclient/tests/unit/compute/v2/test_server_volume.py
openstackclient/tests/unit/compute/v2/test_service.py
openstackclient/tests/unit/compute/v2/test_usage.py
@@ -605,6 +608,8 @@ openstackclient/tests/unit/volume/v2/test_volume_snapshot.py
openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py
openstackclient/tests/unit/volume/v3/__init__.py
openstackclient/tests/unit/volume/v3/fakes.py
+openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py
+openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py
openstackclient/tests/unit/volume/v3/test_volume_attachment.py
openstackclient/tests/unit/volume/v3/test_volume_group.py
openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py
@@ -634,6 +639,8 @@ openstackclient/volume/v2/volume_snapshot.py
openstackclient/volume/v2/volume_transfer_request.py
openstackclient/volume/v2/volume_type.py
openstackclient/volume/v3/__init__.py
+openstackclient/volume/v3/block_storage_cluster.py
+openstackclient/volume/v3/block_storage_resource_filter.py
openstackclient/volume/v3/volume_attachment.py
openstackclient/volume/v3/volume_group.py
openstackclient/volume/v3/volume_group_snapshot.py
@@ -650,6 +657,7 @@ python_openstackclient.egg-info/top_level.txt
releasenotes/notes/.placeholder
releasenotes/notes/L3-conntrack-helper-bd0d9da041747e84.yaml
releasenotes/notes/add-auto-and-none-as-nic-parameter-ed23a6e7f99f250d.yaml
+releasenotes/notes/add-block-storage-cluster-commands-fae8f686582bbbcf.yaml
releasenotes/notes/add-community-option-to-image-list-ac0651eb2e5d632f.yaml
releasenotes/notes/add-description-field-in-port-forwarding-c536e077b243d517.yaml
releasenotes/notes/add-description-to-role-afe7b6ff668df261.yaml
@@ -681,6 +689,7 @@ releasenotes/notes/add-missing-server-list-opts-c41e97e86ff1e1ca.yaml
releasenotes/notes/add-missing-server-rebuild-opts-5c75e838d8f0487d.yaml
releasenotes/notes/add-missing-server-set-opts-e1b4300f5f42e863.yaml
releasenotes/notes/add-missing-volume-backup-opts-b9246aded87427ce.yaml
+releasenotes/notes/add-more-server-list-columns-4e3b87929dd330f7.yaml
releasenotes/notes/add-network-auto-allocated-topology-481580f48840bfc4.yaml
releasenotes/notes/add-network-flavor-profile-e7cc5b353c3ed9d9.yaml
releasenotes/notes/add-network-list-option-to-ports-9d101344ddeb3e64.yaml
@@ -901,6 +910,7 @@ releasenotes/notes/bug-1596818-d4cd93dd4d38d3d6.yaml
releasenotes/notes/bug-1596821-a07599eb4beb6342.yaml
releasenotes/notes/bug-1597184-f4fb687b3d4d99d9.yaml
releasenotes/notes/bug-1597188-a2ff62b809865365.yaml
+releasenotes/notes/bug-1597189-02a8d8a402725860.yaml
releasenotes/notes/bug-1597192-52801f7520287309.yaml
releasenotes/notes/bug-1597195-54ff1ecf381899f6.yaml
releasenotes/notes/bug-1597198-e36b55f3fd185a3a.yaml
@@ -1064,6 +1074,7 @@ releasenotes/notes/drop-py2-421c90fbdf18dbc2.yaml
releasenotes/notes/entrypoint-3.8-0597d159889042f7.yaml
releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml
releasenotes/notes/fix-flavor-props-formatting-d21e97745543caa7.yaml
+releasenotes/notes/fix-image-create-from-volume-c573e553161605c4.yaml
releasenotes/notes/fix-network-rbac-create-d1f4de77ad2dd421.yaml
releasenotes/notes/fix-openstak-image-save-sdk-port-eb160e8ffc92e514.yaml
releasenotes/notes/fix-story-2007890-0974f3e69f26801e.yaml
@@ -1075,6 +1086,7 @@ releasenotes/notes/floating-ip-set-unset-port-28e33875937b69cf.yaml
releasenotes/notes/floatingip_dns_integration-f26c7575694d098d.yaml
releasenotes/notes/force-flag-openstackclient-c172de2717e5cfac.yaml
releasenotes/notes/image-list-filter-multiple-properties-03c51d43131ee3b6.yaml
+releasenotes/notes/image-list-multiple-tags-a394799c7807f031.yaml
releasenotes/notes/image-set-to-update-image-membership-68221f226ca3b6e0.yaml
releasenotes/notes/image_set_visibility-babf4ff2f687d465.yaml
releasenotes/notes/implement-system-scope-4c3c47996f98deac.yaml
@@ -1120,6 +1132,8 @@ releasenotes/notes/pass_ssh_args-cf26a2ce26ccddaf.yaml
releasenotes/notes/port-device-profile-4a3bf800da21c778.yaml
releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml
releasenotes/notes/properties-with-image-property-field.yaml-c51bf37c3106d6ff.yaml
+releasenotes/notes/qos-min-pps-rule-bfe22cea1966c4a0.yaml
+releasenotes/notes/quota-network-force-920913981b45ba1a.yaml
releasenotes/notes/rbac-add-address-group-f9bb83238b5a7c1f.yaml
releasenotes/notes/rbac-add-address-scope-7f6409ab70d36306.yaml
releasenotes/notes/rbac-add-security-group-35370701a06ac906.yaml
@@ -1144,7 +1158,9 @@ releasenotes/notes/server-add-tag-63f9cd01dbd82d1b.yaml
releasenotes/notes/server-changes-3962541b6ebdbbd8.yaml
releasenotes/notes/server-description-ae9618fc09544cac.yaml
releasenotes/notes/server-group-create_rule_option-9f84e52f35e7c3ba.yaml
+releasenotes/notes/server-list-host-status-1f542a5bc4292a62.yaml
releasenotes/notes/server-list-selectable-fields.yaml-1d5fb4784fa6f232.yaml
+releasenotes/notes/server-migration-by-uuid-59f8272f63abee5d.yaml
releasenotes/notes/server-ops-all-projects-2ce2202cdf617184.yaml
releasenotes/notes/server-rebuild-property-e8c6439b04e27c80.yaml
releasenotes/notes/server-rebuild-with-keypair-name-83c1aa20db136d91.yaml
@@ -1183,6 +1199,7 @@ releasenotes/notes/unset-subnet-pool-333052dd85b95653.yaml
releasenotes/notes/usage-list-all-49ca7a62c50e71d3.yaml
releasenotes/notes/use_sdk_for_image-f49d2df38e7d9f81.yaml
releasenotes/notes/versions-show-12a2443624c83048.yaml
+releasenotes/notes/volume-attachment-create-output-fix-56515b8fcdd260b9.yaml
releasenotes/notes/volume-backend-c5faae0b31556a24.yaml
releasenotes/notes/volume-backup-record-9f5987c45e294dc6.yaml
releasenotes/notes/volume-migrate-command-52cf6edd62fe17a7.yaml
@@ -1206,6 +1223,7 @@ releasenotes/source/ussuri.rst
releasenotes/source/victoria.rst
releasenotes/source/wallaby.rst
releasenotes/source/xena.rst
+releasenotes/source/yoga.rst
releasenotes/source/_static/.placeholder
releasenotes/source/_templates/.placeholder
tools/fast8.sh \ No newline at end of file
diff --git a/python_openstackclient.egg-info/entry_points.txt b/python_openstackclient.egg-info/entry_points.txt
index 28d1638..7d9ca12 100644
--- a/python_openstackclient.egg-info/entry_points.txt
+++ b/python_openstackclient.egg-info/entry_points.txt
@@ -84,12 +84,12 @@ server_lock = openstackclient.compute.v2.server:LockServer
server_migrate = openstackclient.compute.v2.server:MigrateServer
server_migrate_confirm = openstackclient.compute.v2.server:MigrateConfirm
server_migrate_revert = openstackclient.compute.v2.server:MigrateRevert
-server_migration_abort = openstackclient.compute.v2.server:AbortMigration
+server_migration_abort = openstackclient.compute.v2.server_migration:AbortMigration
server_migration_confirm = openstackclient.compute.v2.server:ConfirmMigration
-server_migration_force_complete = openstackclient.compute.v2.server:ForceCompleteMigration
-server_migration_list = openstackclient.compute.v2.server:ListMigration
+server_migration_force_complete = openstackclient.compute.v2.server_migration:ForceCompleteMigration
+server_migration_list = openstackclient.compute.v2.server_migration:ListMigration
server_migration_revert = openstackclient.compute.v2.server:RevertMigration
-server_migration_show = openstackclient.compute.v2.server:ShowMigration
+server_migration_show = openstackclient.compute.v2.server_migration:ShowMigration
server_pause = openstackclient.compute.v2.server:PauseServer
server_reboot = openstackclient.compute.v2.server:RebootServer
server_rebuild = openstackclient.compute.v2.server:RebuildServer
@@ -572,6 +572,11 @@ volume_type_unset = openstackclient.volume.v2.volume_type:UnsetVolumeType
volume_unset = openstackclient.volume.v2.volume:UnsetVolume
[openstack.volume.v3]
+block_storage_cluster_list = openstackclient.volume.v3.block_storage_cluster:ListBlockStorageCluster
+block_storage_cluster_set = openstackclient.volume.v3.block_storage_cluster:SetBlockStorageCluster
+block_storage_cluster_show = openstackclient.volume.v3.block_storage_cluster:ShowBlockStorageCluster
+block_storage_resource_filter_list = openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter
+block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter
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
diff --git a/python_openstackclient.egg-info/pbr.json b/python_openstackclient.egg-info/pbr.json
index b97b200..22bd4c1 100644
--- a/python_openstackclient.egg-info/pbr.json
+++ b/python_openstackclient.egg-info/pbr.json
@@ -1 +1 @@
-{"git_version": "63452f36", "is_release": true} \ No newline at end of file
+{"git_version": "27b2496e", "is_release": true} \ No newline at end of file
diff --git a/releasenotes/notes/add-block-storage-cluster-commands-fae8f686582bbbcf.yaml b/releasenotes/notes/add-block-storage-cluster-commands-fae8f686582bbbcf.yaml
new file mode 100644
index 0000000..be50aeb
--- /dev/null
+++ b/releasenotes/notes/add-block-storage-cluster-commands-fae8f686582bbbcf.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add ``block storage cluster create``, ``block storage cluster delete``,
+ ``block storage cluster list`` and ``block storage cluster show`` commands
+ to create, delete, list, and show block storage service clusters,
+ respectively.
diff --git a/releasenotes/notes/add-more-server-list-columns-4e3b87929dd330f7.yaml b/releasenotes/notes/add-more-server-list-columns-4e3b87929dd330f7.yaml
new file mode 100644
index 0000000..a4bf004
--- /dev/null
+++ b/releasenotes/notes/add-more-server-list-columns-4e3b87929dd330f7.yaml
@@ -0,0 +1,16 @@
+---
+feature:
+ - |
+ The ``server list`` command now allows users to select the following
+ additional columns using the ``-c COLUMN`` option:
+
+ - Task State
+ - Power State
+ - Image ID
+ - Flavor ID
+ - Availability Zone
+ - Host
+ - Properties
+
+ These correspond to columns displayed by default when using the ``--long``
+ option. \ No newline at end of file
diff --git a/releasenotes/notes/bug-1597189-02a8d8a402725860.yaml b/releasenotes/notes/bug-1597189-02a8d8a402725860.yaml
new file mode 100644
index 0000000..69d1eb7
--- /dev/null
+++ b/releasenotes/notes/bug-1597189-02a8d8a402725860.yaml
@@ -0,0 +1,10 @@
+---
+features:
+ - |
+ The ``volume`` argument of the ``volume backup restore`` command is now
+ optional and can refer to the name of a new volume that should be created
+ rather than a name or ID of an existing volume (which would be
+ overwritten). If not provided, cinder will generate a new volume with a
+ unique name. To restore a backup to an existing volume, you must now
+ specify the ``--force`` option (volume v2, v3 only).
+ [Bug `1597189 <https://bugs.launchpad.net/bugs/1597189>`_]
diff --git a/releasenotes/notes/fix-image-create-from-volume-c573e553161605c4.yaml b/releasenotes/notes/fix-image-create-from-volume-c573e553161605c4.yaml
new file mode 100644
index 0000000..92fc741
--- /dev/null
+++ b/releasenotes/notes/fix-image-create-from-volume-c573e553161605c4.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+ - |
+ Fixed create image from volume command. If user wants to
+ pass ``visibility`` and ``protected`` fields, they need to
+ specify volume microversion 3.1 or greater by passing
+ ``os-volume-api-version 3.1`` with the command.
diff --git a/releasenotes/notes/image-list-multiple-tags-a394799c7807f031.yaml b/releasenotes/notes/image-list-multiple-tags-a394799c7807f031.yaml
new file mode 100644
index 0000000..82c966b
--- /dev/null
+++ b/releasenotes/notes/image-list-multiple-tags-a394799c7807f031.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ The ``image list`` now accepts multiple ``--tag`` options, allowing you to
+ filter images on more than one tag.
diff --git a/releasenotes/notes/qos-min-pps-rule-bfe22cea1966c4a0.yaml b/releasenotes/notes/qos-min-pps-rule-bfe22cea1966c4a0.yaml
new file mode 100644
index 0000000..71e9910
--- /dev/null
+++ b/releasenotes/notes/qos-min-pps-rule-bfe22cea1966c4a0.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add support for QoS minimum packet rate rule to following commands:
+ ``network qos rule create``, ``network qos rule delete``,
+ ``network qos rule list``, ``network qos rule show`` and
+ ``network qos rule set``
diff --git a/releasenotes/notes/quota-network-force-920913981b45ba1a.yaml b/releasenotes/notes/quota-network-force-920913981b45ba1a.yaml
new file mode 100644
index 0000000..06507df
--- /dev/null
+++ b/releasenotes/notes/quota-network-force-920913981b45ba1a.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - Add ``--force`` options to the ``openstack quota set`` command for network
+ service commands. Neutron quota engine now accepts "force" flag to set
+ a new resource quota limit, regardless of the current resource usage.
diff --git a/releasenotes/notes/server-list-host-status-1f542a5bc4292a62.yaml b/releasenotes/notes/server-list-host-status-1f542a5bc4292a62.yaml
new file mode 100644
index 0000000..5ae92bc
--- /dev/null
+++ b/releasenotes/notes/server-list-host-status-1f542a5bc4292a62.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ The ``server list`` command will now display ``Host Status`` when the
+ ``--long`` option is specified and the compute API microversion is 2.16 or
+ greater.
diff --git a/releasenotes/notes/server-migration-by-uuid-59f8272f63abee5d.yaml b/releasenotes/notes/server-migration-by-uuid-59f8272f63abee5d.yaml
new file mode 100644
index 0000000..5421a5f
--- /dev/null
+++ b/releasenotes/notes/server-migration-by-uuid-59f8272f63abee5d.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ The ``server migration abort``, ``server migration force complete`` and
+ ``server migration show`` commands will now accept a server migration UUID
+ in addition to an ID.
diff --git a/releasenotes/notes/volume-attachment-create-output-fix-56515b8fcdd260b9.yaml b/releasenotes/notes/volume-attachment-create-output-fix-56515b8fcdd260b9.yaml
new file mode 100644
index 0000000..b8bd06e
--- /dev/null
+++ b/releasenotes/notes/volume-attachment-create-output-fix-56515b8fcdd260b9.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ The ``volume attachment create`` command will now display information
+ for successfully created volume attachments. Previously an empty table was
+ returned.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 0ad18a2..829b09d 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@ OpenStackClient Release Notes
:maxdepth: 1
unreleased
+ yoga
xena
wallaby
victoria
diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst
new file mode 100644
index 0000000..7cd5e90
--- /dev/null
+++ b/releasenotes/source/yoga.rst
@@ -0,0 +1,6 @@
+=========================
+Yoga Series Release Notes
+=========================
+
+.. release-notes::
+ :branch: stable/yoga
diff --git a/requirements.txt b/requirements.txt
index cb414eb..118b7b9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,7 @@
+# Requirements lower bounds listed here are our best effort to keep them up to
+# date but we do not test them so no guarantee of having them all correct. If
+# you find any incorrect lower bounds, let us know or propose a fix.
+
# 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.
diff --git a/setup.cfg b/setup.cfg
index 8c0163f..7780fa2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,7 @@ description_file =
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
home_page = https://docs.openstack.org/python-openstackclient/latest/
-python_requires = >=3.6
+python_requires = >=3.8
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
@@ -15,9 +15,8 @@ classifier =
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.6
- Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
+ Programming Language :: Python :: 3.9
[files]
packages =
@@ -107,12 +106,8 @@ openstack.compute.v2 =
server_migrate = openstackclient.compute.v2.server:MigrateServer
server_migrate_confirm = openstackclient.compute.v2.server:MigrateConfirm
server_migrate_revert = openstackclient.compute.v2.server:MigrateRevert
- server_migration_abort = openstackclient.compute.v2.server:AbortMigration
server_migration_confirm = openstackclient.compute.v2.server:ConfirmMigration
- server_migration_force_complete = openstackclient.compute.v2.server:ForceCompleteMigration
- server_migration_list = openstackclient.compute.v2.server:ListMigration
server_migration_revert = openstackclient.compute.v2.server:RevertMigration
- server_migration_show = openstackclient.compute.v2.server:ShowMigration
server_pause = openstackclient.compute.v2.server:PauseServer
server_reboot = openstackclient.compute.v2.server:RebootServer
server_rebuild = openstackclient.compute.v2.server:RebuildServer
@@ -153,6 +148,11 @@ openstack.compute.v2 =
server_image_create = openstackclient.compute.v2.server_image:CreateServerImage
+ server_migration_abort = openstackclient.compute.v2.server_migration:AbortMigration
+ server_migration_force_complete = openstackclient.compute.v2.server_migration:ForceCompleteMigration
+ server_migration_list = openstackclient.compute.v2.server_migration:ListMigration
+ server_migration_show = openstackclient.compute.v2.server_migration:ShowMigration
+
server_volume_list = openstackclient.compute.v2.server_volume:ListServerVolume
server_volume_update = openstackclient.compute.v2.server_volume:UpdateServerVolume
@@ -742,6 +742,12 @@ openstack.volume.v3 =
volume_message_list = openstackclient.volume.v3.volume_message:ListMessages
volume_message_show = openstackclient.volume.v3.volume_message:ShowMessage
+ block_storage_cluster_list = openstackclient.volume.v3.block_storage_cluster:ListBlockStorageCluster
+ block_storage_cluster_set = openstackclient.volume.v3.block_storage_cluster:SetBlockStorageCluster
+ block_storage_cluster_show = openstackclient.volume.v3.block_storage_cluster:ShowBlockStorageCluster
+ block_storage_resource_filter_list = openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter
+ block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter
+
volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot
volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot
diff --git a/tox.ini b/tox.ini
index 6eefc26..f631380 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-minversion = 3.2.0
+minversion = 3.18.0
envlist = py38,pep8
skipsdist = True
# Automatic envs (pyXX) will only use the python version appropriate to that
@@ -18,7 +18,7 @@ deps =
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt
commands = stestr run {posargs}
-whitelist_externals = stestr
+allowlist_externals = stestr
[testenv:fast8]
# Use same environment directory as pep8 env to save space and install time
@@ -71,7 +71,7 @@ commands =
pythom -m pip install -q -e "git+file://{toxinidir}/../openstacksdk#egg=openstacksdk"
python -m pip freeze
stestr run {posargs}
-whitelist_externals = stestr
+allowlist_externals = stestr
[testenv:functional]
setenv = OS_TEST_PATH=./openstackclient/tests/functional
@@ -138,9 +138,3 @@ exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes
ignore = W504
import-order-style = pep8
application_import_names = openstackclient
-
-[testenv:lower-constraints]
-deps =
- -c{toxinidir}/lower-constraints.txt
- -r{toxinidir}/test-requirements.txt
- -r{toxinidir}/requirements.txt