diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6154c51ea0..b277bb059b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,40 +1,42 @@ --- -default_language_version: - # force all unspecified python hooks to run python3 - python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: mixed-line-ending args: ['--fix', 'lf'] exclude: '.*\.(svg)$' - - id: check-byte-order-marker + - id: fix-byte-order-marker - id: check-executables-have-shebangs - id: check-merge-conflict - id: debug-statements - id: check-yaml files: .*\.(yaml|yml)$ args: ['--unsafe'] - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.0 hooks: - - id: pyupgrade - args: ['--py38-plus'] - - repo: https://github.com/psf/black - rev: 24.4.0 - hooks: - - id: black - args: ['-S', '-l', '79'] - - repo: https://github.com/PyCQA/bandit - rev: 1.7.8 - hooks: - - id: bandit - args: ['-x', 'tests'] + - id: ruff-check + args: ['--fix', '--unsafe-fixes'] + - id: ruff-format - repo: https://opendev.org/openstack/hacking - rev: 6.1.0 + rev: 7.0.0 hooks: - id: hacking additional_dependencies: [] - exclude: '^(doc|releasenotes|tools)/.*$' + exclude: '^(doc|releasenotes)/.*$' + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy + additional_dependencies: + - types-requests + # keep this in-sync with '[tool.mypy] exclude' in 'pyproject.toml' + exclude: | + (?x)( + doc/.* + | examples/.* + | hacking/.* + | releasenotes/.* + ) diff --git a/.zuul.yaml b/.zuul.yaml index 4915f9f858..d63ee7c5f4 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -6,6 +6,11 @@ Run unit tests for OpenStackClient with master branch of important libs. Takes advantage of the base tox job's install-siblings feature. + irrelevant-files: &common-irrelevant-files + - ^.*\.rst$ + - ^doc/.*$ + - ^releasenotes/.*$ + - ^\.pre-commit-config\.yaml$ required-projects: - openstack/cliff - openstack/keystoneauth @@ -18,8 +23,8 @@ zuul_work_dir: src/opendev.org/openstack/python-openstackclient - job: - name: osc-tox-py38-tips - parent: openstack-tox-py38 + name: osc-tox-py310-tips + parent: openstack-tox-py310 description: | Run unit tests for OpenStackClient with master branch of important libs. @@ -38,8 +43,8 @@ zuul_work_dir: src/opendev.org/openstack/python-openstackclient - job: - name: osc-tox-py310-tips - parent: openstack-tox-py310 + name: osc-tox-py313-tips + parent: openstack-tox-py313 description: | Run unit tests for OpenStackClient with master branch of important libs. @@ -81,13 +86,6 @@ # NOTE(amotoki): Some neutron features are enabled by devstack plugin neutron: https://opendev.org/openstack/neutron devstack_services: - ceilometer-acentral: false - ceilometer-acompute: false - ceilometer-alarm-evaluator: false - ceilometer-alarm-notifier: false - ceilometer-anotification: false - ceilometer-api: false - ceilometer-collector: false s-account: true s-container: true s-object: true @@ -139,22 +137,6 @@ tox_envlist: functional tox_install_siblings: true -- secret: - name: osc-dockerhub - data: - username: osclientzuul - password: !encrypted/pkcs1-oaep - - LbIZjJiVstRVXMpoLQ3+/JcNB6lKVUWJXXo5+Outf+PKAaO7mNnv8XLiFMKnJ6ftopLyu - hWbX9rA+NddvplLQkf1xxkh7QBBU8PToLr58quI2SENUclt4tpjxbZfZu451kFSNJvNvR - E58cHHpfJZpyRnS2htXmN/Qy24gbV2w7CQxSZD2YhlcrerD8uQ8rWEnlY1wcJEaEGomtS - ZTGxsdK2TsZC2cd4b7TG7+xbl2i+hjADzwSQAgUzlLlwuG71667+IWk4SOZ7OycJTv9NN - ZTak8+CGfiMKdmsxZ1Z8uD7DC+RIklDjMWyly6zuhWzfhOmsmU0CesR50moodRUvbK79p - NZM8u0hBex5cl2EpUEwJL/FSPJXUhDMPoMoTZT/SAuXf25R9eZ9JGrKsIAlmVhpl8ifoE - 8TpPyvIHGS3YelTQjhqOX0wGb9T4ZauQCcI5Ajzy9NuCTyD9xxme9OX1zz7gMACRnVHvz - q7U7Ue90MnmGH6E2SgKjIZhyzy9Efwb7JUvH1Zb3hlrjCjEhwi9MV5FnABTEeXyYwE10s - 3o/KZg2zvdWkVG6x0dEkjpoQaNuaB7T2Na7Sm421n/z3LCzhiQGuTUjENnL6cMEtuA6Pp - BfI5+Qlg7HMwkBXNB73EPfWHzbCR3VNrzGYTy9FvhGud0/cXsuBXgps4WH63ic= - - job: name: osc-build-image parent: opendev-build-docker-image @@ -164,49 +146,21 @@ - python-builder-3.11-bookworm-container-image - python-base-3.11-bookworm-container-image provides: osc-container-image - vars: &osc_image_vars + vars: docker_images: - context: . - repository: osclient/python-openstackclient - -- job: - name: osc-upload-image - parent: opendev-upload-docker-image - description: Build Docker images and upload to Docker Hub. - allowed-projects: openstack/python-openstackclient - requires: - - python-builder-3.11-bookworm-container-image - - python-base-3.11-bookworm-container-image - provides: osc-container-image - secrets: - - name: docker_credentials - secret: osc-dockerhub - pass-to-parent: true - vars: *osc_image_vars - -- job: - name: osc-promote-image - parent: opendev-promote-docker-image - allowed-projects: openstack/python-openstackclient - description: Promote previously uploaded Docker images. - secrets: - - name: docker_credentials - secret: osc-dockerhub - pass-to-parent: true - nodeset: - nodes: [] - vars: *osc_image_vars + tags: [] - project-template: name: osc-tox-unit-tips check: jobs: - - osc-tox-py38-tips - osc-tox-py310-tips + - osc-tox-py313-tips gate: jobs: - - osc-tox-py38-tips - osc-tox-py310-tips + - osc-tox-py313-tips - project: templates: @@ -219,7 +173,10 @@ - release-notes-jobs-python3 check: jobs: - - osc-build-image + - openstackclient-check-plugins: + voting: true + - osc-build-image: + voting: false - osc-functional-devstack - osc-functional-devstack-tips: # The functional-tips job only tests the latest and shouldn't be run @@ -227,8 +184,4 @@ branches: ^master$ gate: jobs: - - osc-upload-image - osc-functional-devstack - promote: - jobs: - - osc-promote-image diff --git a/Dockerfile b/Dockerfile index 420fdb5ea2..6709be7514 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,12 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM docker.io/opendevorg/python-builder:3.11-bookworm as builder +FROM docker.io/opendevorg/python-builder:3.12-bookworm AS builder COPY . /tmp/src RUN assemble -FROM docker.io/opendevorg/python-base:3.11-bookworm +FROM docker.io/opendevorg/python-base:3.12-bookworm + +LABEL org.opencontainers.image.title="python-openstackclient" +LABEL org.opencontainers.image.description="Client for OpenStack services." +LABEL org.opencontainers.image.licenses="Apache License 2.0" +LABEL org.opencontainers.image.url="https://www.openstack.org/" +LABEL org.opencontainers.image.documentation="https://docs.openstack.org/python-openstackclient/latest/" +LABEL org.opencontainers.image.source="https://opendev.org/openstack/python-openstackclient" COPY --from=builder /output/ /output RUN /output/install-from-bindep diff --git a/README.rst b/README.rst index f526ec6adf..af3837a351 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,3 @@ -======================== -Team and repository tags -======================== - -.. image:: https://governance.openstack.org/tc/badges/python-openstackclient.svg - :target: https://governance.openstack.org/tc/reference/tags/index.html - -.. Change things from this point on - =============== OpenStackClient =============== @@ -15,96 +6,158 @@ OpenStackClient :target: https://pypi.org/project/python-openstackclient/ :alt: Latest Version -OpenStackClient (aka OSC) is a command-line client for OpenStack that brings +OpenStackClient (OSC) is a command-line client for OpenStack that brings the command set for Compute, Identity, Image, Network, Object Store and Block Storage APIs together in a single shell with a uniform command structure. +Support for additional service APIs is provided via plugins. The primary goal is to provide a unified shell command structure and a common language to describe operations in OpenStack. -* `PyPi`_ - package installation -* `Online Documentation`_ -* `Launchpad project`_ - bugs and feature requests -* `Blueprints`_ - feature specifications (historical only) -* `Source`_ -* `Developer`_ - getting started as a developer -* `Contributing`_ - contributing code -* `Testing`_ - testing code -* IRC: #openstack-sdks on OFTC (irc.oftc.net) -* License: Apache 2.0 - -.. _PyPi: https://pypi.org/project/python-openstackclient -.. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/ -.. _Blueprints: https://blueprints.launchpad.net/python-openstackclient -.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient -.. _Source: https://opendev.org/openstack/python-openstackclient -.. _Developer: https://docs.openstack.org/project-team-guide/project-setup/python.html -.. _Contributing: https://docs.openstack.org/infra/manual/developers.html -.. _Testing: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html#testing -.. _Release Notes: https://docs.openstack.org/releasenotes/python-openstackclient - Getting Started =============== -OpenStack Client can be installed from PyPI using pip:: +OpenStack Client can be installed from PyPI using pip: - pip install python-openstackclient +.. code-block:: shell -There are a few variants on getting help. A list of global options and supported -commands is shown with ``--help``:: + python3 -m pip install python-openstackclient - openstack --help +You can use ``--help`` or the ``help`` command to get a list of global options +and supported commands: -There is also a ``help`` command that can be used to get help text for a specific -command:: +.. code-block:: shell + openstack --help openstack help + +You can also get help for a specific command: + +.. code-block:: shell + + openstack server create --help openstack help server create -If you want to make changes to the OpenStackClient for testing and contribution, -make any changes and then run:: +You can add support for additional services by installing their clients. For +example, to add support for the DNS service (designate): - python setup.py develop +.. code-block:: shell -or:: + python3 -m pip install python3-designateclient - pip install -e . +A ``Dockerfile`` is provided for your convenience in the repository. You can +use this to build your own container images: -Configuration -============= +.. code-block:: shell -The CLI is configured via environment variables and command-line -options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. + git clone https://opendev.org/openstack/python-openstackclient + cd python-openstackclient + podman build . -t example.com/myuser/openstackclient -Authentication using username/password is most commonly used: +For more information the available options and commands, refer to the `Users +Guide`__. -- For a local user, your configuration will look like the one below:: +.. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html + +Configuration +============= + +OpenStack Client must be configured with authentication information in order to +communicate with a given OpenStack cloud. This configuration can be achieved +via a ``clouds.yaml`` file, a set of environment variables (often shared via an +``openrc`` file), a set of command-line options, or a combination of all three. +Your cloud provider or deployment tooling will typically provide either a +``clouds.yaml`` file or ``openrc`` file for you. If using a ``clouds.yaml`` +file, OpenStack Client expects to find it in one of the following locations: + +* If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment + variable +* ``.`` (the current directory) +* ``$HOME/.config/openstack`` +* ``/etc/openstack`` + +The options you should set will depend on the configuration of your cloud and +the authentication mechanism(s) supported. For example, consider a cloud that +supports username/password authentication. Configuration for this cloud using a +``clouds.yaml`` file would look like so: + +.. code-block:: yaml + + clouds: + my-cloud: + auth: + auth_url: '' + project_name: '' + project_domain_name: '' + username: '' + user_domain_name: '' + password: '' # (optional) + region_name: '' + +The corresponding environment variables would look very similar: + +.. code-block:: shell export OS_AUTH_URL= - export OS_IDENTITY_API_VERSION=3 + export OS_REGION_NAME= export OS_PROJECT_NAME= export OS_PROJECT_DOMAIN_NAME= export OS_USERNAME= export OS_USER_DOMAIN_NAME= export OS_PASSWORD= # (optional) - The corresponding command-line options look very similar:: +Likewise, the corresponding command-line options would look very similar: - --os-auth-url - --os-identity-api-version 3 +:: + + openstack + --os-auth-url + --os-region --os-project-name --os-project-domain-name --os-username --os-user-domain-name [--os-password ] -- For a federated user, your configuration will look the so:: +.. note:: + + If a password is not provided above (in plaintext), you will be + interactively prompted to provide one securely. + +Some clouds use federated authentication. If this is the case, your +configuration will be slightly more involved. For example, to configure +username/password authentication for a federated user using a ``clouds.yaml`` +file: + +.. code-block:: yaml + + clouds: + my-cloud: + auth: + auth_url: '' + project_name: '' + project_domain_name: '' + username: '' + user_domain_name: '' + password: '' + identity_provider: '' + client_id: '' + client_secret: '' + openid_scope: '' + protocol: '' + access_token_type: '' + discovery_endpoint: '' + auth_type: 'v3oidcpassword' + region_name: '' + +The corresponding environment variables would look very similar: + +.. code-block:: shell export OS_PROJECT_NAME= export OS_PROJECT_DOMAIN_NAME= export OS_AUTH_URL= export OS_IDENTITY_API_VERSION=3 - export OS_AUTH_PLUGIN=openid export OS_AUTH_TYPE=v3oidcpassword export OS_USERNAME= export OS_PASSWORD= @@ -116,7 +169,9 @@ Authentication using username/password is most commonly used: export OS_ACCESS_TOKEN_TYPE= export OS_DISCOVERY_ENDPOINT= - The corresponding command-line options look very similar:: +Likewise, the corresponding command-line options would look very similar: + +.. code-block:: shell --os-project-name --os-project-domain-name @@ -134,5 +189,41 @@ Authentication using username/password is most commonly used: --os-access-token-type --os-discovery-endpoint -If a password is not provided above (in plaintext), you will be interactively -prompted to provide one securely. +For more information on configuring authentication, including an overview of +the many authentication mechanisms supported, refer to the `Authentication +guide`__. For more information on configuration in general, refer to the +`Configuration guide`__. + +.. __: https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. +.. __: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html + +Contributing +============ + +You can clone the repository from opendev.org:: + + git clone https://opendev.org/openstack/python-openstackclient + cd python-openstackclient + +OpenStack Client uses the same contributor process as other OpenStack projects. +For information on this process, including help on setting up you Gerrit +account and an overview of the CI process, refer to the `OpenStack Contributors +Guide`__. + +For more information on contributing to OpenStack Client itself, including +guidance on how to design new commands and how to report bugs, refer to the +`Contributors Guide`__. + +.. __: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html +.. __: https://docs.opendev.org/opendev/infra-manual/latest/developers.html + +Links +----- + +* `Issue Tracker `_ +* `Code Review `_ +* `Documentation `_ +* `PyPi `_ +* `Mailing list `_ +* `Release Notes `_ +* `IRC (#openstack-sdks on OFTC (irc.oftc.net)) `_ diff --git a/bindep.txt b/bindep.txt index 4c90a026fe..8402431aed 100644 --- a/bindep.txt +++ b/bindep.txt @@ -8,3 +8,4 @@ libffi-dev [compile test platform:dpkg] libssl-dev [compile test platform:dpkg] python3-dev [compile test platform:dpkg] python3-devel [compile test platform:rpm] +libpcre2-dev [test platform:dpkg] diff --git a/doc/requirements.txt b/doc/requirements.txt index 79e3ded4f6..05a9bfa87c 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -4,7 +4,7 @@ sphinx>=2.0.0,!=2.1.0 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD # redirect tests in docs -whereto>=0.4.0 # Apache-2.0 +whereto>=0.5.0 # Apache-2.0 # Install these to generate sphinx autodocs aodhclient>=0.9.0 # Apache-2.0 diff --git a/doc/source/cli/_hidden/image.rst b/doc/source/cli/_hidden/image.rst index 85ffde6f39..06919e7ab9 100644 --- a/doc/source/cli/_hidden/image.rst +++ b/doc/source/cli/_hidden/image.rst @@ -3,7 +3,7 @@ image ===== .. NOTE(efried): This page is hidden from the main TOC; it's here so links in - the wild redirect somewhere sane, because previously identity v2 and v3 were + the wild redirect somewhere sane, because previously image v2 and v3 were combined in a single page. .. toctree:: diff --git a/doc/source/cli/authentication.rst b/doc/source/cli/authentication.rst index 8ed318f526..8c09fc3648 100644 --- a/doc/source/cli/authentication.rst +++ b/doc/source/cli/authentication.rst @@ -295,6 +295,15 @@ or, using environment variables: $ TOKEN=$(openstack token issue -f value -c id) +.. note:: + + The above examples assume you require a project-scoped token. You can omit + the project-related configuration if your user has a default project ID set. + Conversely, if requesting domain-scoped or system-scoped, you should update + these examples accordingly. If the user does not have a default project + configured and no scoping information is provided, the resulting token will + be unscoped. + ``v3totp`` ~~~~~~~~~~ diff --git a/doc/source/cli/command-objects/access-rules.rst b/doc/source/cli/command-objects/access-rules.rst index bc8458283f..6e811fc7df 100644 --- a/doc/source/cli/command-objects/access-rules.rst +++ b/doc/source/cli/command-objects/access-rules.rst @@ -9,53 +9,11 @@ rule comprises of a service type, a request path, and a request method. Access rules may only be created as attributes of application credentials, but they may be viewed and deleted independently. +.. autoprogram-cliff:: openstack.identity.v3 + :command: access rule delete -access rule delete ------------------- +.. autoprogram-cliff:: openstack.identity.v3 + :command: access rule list -Delete access rule(s) - -.. program:: access rule delete -.. code:: bash - - openstack access rule delete [ ...] - -.. describe:: - - Access rule(s) to delete (ID) - -access rule list ----------------- - -List access rules - -.. program:: access rule list -.. code:: bash - - openstack access rule list - [--user ] - [--user-domain ] - -.. option:: --user - - User whose access rules to list (name or ID). If not provided, looks up the - current user's access rules. - -.. option:: --user-domain - - Domain the user belongs to (name or ID). This can be - used in case collisions between user names exist. - -access rule show ---------------------------- - -Display access rule details - -.. program:: access rule show -.. code:: bash - - openstack access rule show - -.. describe:: - - Access rule to display (ID) +.. autoprogram-cliff:: openstack.identity.v3 + :command: access rule show diff --git a/doc/source/cli/command-objects/consistency-group-snapshot.rst b/doc/source/cli/command-objects/consistency-group-snapshot.rst index 29d5065663..51241685fd 100644 --- a/doc/source/cli/command-objects/consistency-group-snapshot.rst +++ b/doc/source/cli/command-objects/consistency-group-snapshot.rst @@ -2,95 +2,16 @@ consistency group snapshot ========================== -Block Storage v2 +Block Storage v2, v3 -consistency group snapshot create ---------------------------------- +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot create -Create new consistency group snapshot. +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot delete -.. program:: consistency group snapshot create -.. code:: bash +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot list - openstack consistency group snapshot create - [--consistency-group ] - [--description ] - [] - -.. option:: --consistency-group - - Consistency group to snapshot (name or ID) - (default to be the same as ) - -.. option:: --description - - Description of this consistency group snapshot - -.. _consistency_group_snapshot_create-snapshot-name: -.. describe:: - - Name of new consistency group snapshot (default to None) - -consistency group snapshot delete ---------------------------------- - -Delete consistency group snapshot(s) - -.. program:: consistency group snapshot delete -.. code:: bash - - openstack consistency group snapshot delete - [ ...] - -.. _consistency_group_snapshot_delete-consistency-group-snapshot: -.. describe:: - - Consistency group snapshot(s) to delete (name or ID) - -consistency group snapshot list -------------------------------- - -List consistency group snapshots. - -.. program:: consistency group snapshot list -.. code:: bash - - openstack consistency group snapshot list - [--all-projects] - [--long] - [--status ] - [--consistency-group ] - -.. option:: --all-projects - - Show detail for all projects. Admin only. - (defaults to False) - -.. option:: --long - - List additional fields in output - -.. option:: --status - - Filters results by a status - ("available", "error", "creating", "deleting" or "error_deleting") - -.. option:: --consistency-group - - Filters results by a consistency group (name or ID) - -consistency group snapshot show -------------------------------- - -Display consistency group snapshot details. - -.. program:: consistency group snapshot show -.. code:: bash - - openstack consistency group snapshot show - - -.. _consistency_group_snapshot_show-consistency-group-snapshot: -.. describe:: - - Consistency group snapshot to display (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot show diff --git a/doc/source/cli/command-objects/consistency-group.rst b/doc/source/cli/command-objects/consistency-group.rst index 57082c6df8..9ff207ff79 100644 --- a/doc/source/cli/command-objects/consistency-group.rst +++ b/doc/source/cli/command-objects/consistency-group.rst @@ -2,172 +2,25 @@ consistency group ================= -Block Storage v2 +Block Storage v2, v3 -consistency group add volume ----------------------------- +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group add volume -Add volume(s) to consistency group. +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group create -.. program:: consistency group add volume -.. code:: bash +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group delete - openstack consistency group add volume - - [ ...] +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group list -.. _consistency_group_add_volume: -.. describe:: +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group remove volume - Consistency group to contain (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group set -.. describe:: - - Volume(s) to add to (name or ID) - (repeat option to add multiple volumes) - -consistency group create ------------------------- - -Create new consistency group. - -.. program:: consistency group create -.. code:: bash - - openstack consistency group create - --volume-type | --consistency-group-source | --consistency-group-snapshot - [--description ] - [--availability-zone ] - [] - -.. option:: --volume-type - - Volume type of this consistency group (name or ID) - -.. option:: --consistency-group-source - - Existing consistency group (name or ID) - -.. option:: --consistency-group-snapshot - - Existing consistency group snapshot (name or ID) - -.. option:: --description - - Description of this consistency group - -.. option:: --availability-zone - - Availability zone for this consistency group - (not available if creating consistency group from source) - -.. _consistency_group_create-name: -.. describe:: - - Name of new consistency group (default to None) - -consistency group delete ------------------------- - -Delete consistency group(s). - -.. program:: consistency group delete -.. code:: bash - - openstack consistency group delete - [--force] - [ ...] - -.. option:: --force - - Allow delete in state other than error or available - -.. _consistency_group_delete-consistency-group: -.. describe:: - - Consistency group(s) to delete (name or ID) - -consistency group list ----------------------- - -List consistency groups. - -.. program:: consistency group list -.. code:: bash - - openstack consistency group list - [--all-projects] - [--long] - -.. option:: --all-projects - - Show detail for all projects. Admin only. - (defaults to False) - -.. option:: --long - - List additional fields in output - -consistency group remove volume -------------------------------- - -Remove volume(s) from consistency group. - -.. program:: consistency group remove volume -.. code:: bash - - openstack consistency group remove volume - - [ ...] - -.. _consistency_group_remove_volume: -.. describe:: - - Consistency group containing (name or ID) - -.. describe:: - - Volume(s) to remove from (name or ID) - (repeat option to remove multiple volumes) - -consistency group set ---------------------- - -Set consistency group properties. - -.. program:: consistency group set -.. code:: bash - - openstack consistency group set - [--name ] - [--description ] - - -.. option:: --name - - New consistency group name - -.. option:: --description - - New consistency group description - -.. _consistency_group_set-consistency-group: -.. describe:: - - Consistency group to modify (name or ID) - -consistency group show ----------------------- - -Display consistency group details. - -.. program:: consistency group show -.. code:: bash - - openstack consistency group show - - -.. _consistency_group_show-consistency-group: -.. describe:: - - Consistency group to display (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group show diff --git a/doc/source/cli/command-objects/console-connection.rst b/doc/source/cli/command-objects/console-connection.rst new file mode 100644 index 0000000000..c3358050fb --- /dev/null +++ b/doc/source/cli/command-objects/console-connection.rst @@ -0,0 +1,10 @@ +================== +console connection +================== + +Server console connection information + +Compute v2 + +.. autoprogram-cliff:: openstack.compute.v2 + :command: console connection show diff --git a/doc/source/cli/command-objects/limits.rst b/doc/source/cli/command-objects/limits.rst index 3a0f99b376..11d53802c6 100644 --- a/doc/source/cli/command-objects/limits.rst +++ b/doc/source/cli/command-objects/limits.rst @@ -4,7 +4,7 @@ limits The Compute and Block Storage APIs have resource usage limits. -Compute v2, Block Storage v1 +Block Storage v2, v3; Compute v2 .. autoprogram-cliff:: openstack.common diff --git a/doc/source/cli/command-objects/quota.rst b/doc/source/cli/command-objects/quota.rst index cab1265240..59a8a9bb4e 100644 --- a/doc/source/cli/command-objects/quota.rst +++ b/doc/source/cli/command-objects/quota.rst @@ -5,7 +5,7 @@ quota Resource quotas appear in multiple APIs, OpenStackClient presents them as a single object with multiple properties. -Block Storage v1, v2, Compute v2, Network v2 +Block Storage v1, v3; Compute v2; Network v2 .. autoprogram-cliff:: openstack.common :command: quota * diff --git a/doc/source/cli/command-objects/role-assignment.rst b/doc/source/cli/command-objects/role-assignment.rst index b29f32c690..aa618d4dd3 100644 --- a/doc/source/cli/command-objects/role-assignment.rst +++ b/doc/source/cli/command-objects/role-assignment.rst @@ -4,103 +4,5 @@ role assignment Identity v2, v3 -role assignment list --------------------- - -List role assignments - -.. program:: role assignment list -.. code:: bash - - openstack role assignment list - [--role ] - [--role-domain ] - [--user ] - [--user-domain ] - [--group ] - [--group-domain ] - [--domain ] - [--project ] - [--project-domain ] - [--effective] - [--inherited] - [--names] - -.. option:: --role - - Role to filter (name or ID) - - .. versionadded:: 3 - -.. option:: --role-domain - - Domain the role belongs to (name or ID). - This can be used in case collisions between role names exist. - - .. versionadded:: 3 - -.. option:: --user - - User to filter (name or ID) - -.. option:: --user-domain - - Domain the user belongs to (name or ID). - This can be used in case collisions between user names exist. - - .. versionadded:: 3 - -.. option:: --group - - Group to filter (name or ID) - - .. versionadded:: 3 - -.. option:: --group-domain - - Domain the group belongs to (name or ID). - This can be used in case collisions between group names exist. - - .. versionadded:: 3 - -.. option:: --domain - - Domain to filter (name or ID) - - .. versionadded:: 3 - -.. option:: --project - - Project to filter (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - - .. versionadded:: 3 - -.. option:: --effective - - Returns only effective role assignments (defaults to False) - - .. versionadded:: 3 - -.. option:: --inherited - - Specifies if the role grant is inheritable to the sub projects - - .. versionadded:: 3 - -.. option:: --names - - Returns role assignments with names instead of IDs - -.. option:: --auth-user - - Returns role assignments for the authenticated user. - -.. option:: --auth-project - - Returns role assignments for the project to which the authenticated user - is scoped. +.. autoprogram-cliff:: openstack.identity.v3 + :command: role assignment list diff --git a/doc/source/cli/command-objects/volume-backup.rst b/doc/source/cli/command-objects/volume-backup.rst index 63e5f3655b..7b036ca0ce 100644 --- a/doc/source/cli/command-objects/volume-backup.rst +++ b/doc/source/cli/command-objects/volume-backup.rst @@ -2,7 +2,7 @@ volume backup ============= -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume backup * diff --git a/doc/source/cli/command-objects/volume-qos.rst b/doc/source/cli/command-objects/volume-qos.rst index 3475b93860..82c4d540c7 100644 --- a/doc/source/cli/command-objects/volume-qos.rst +++ b/doc/source/cli/command-objects/volume-qos.rst @@ -2,7 +2,7 @@ volume qos ========== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume qos * diff --git a/doc/source/cli/command-objects/volume-service.rst b/doc/source/cli/command-objects/volume-service.rst index 3283b29e29..43d455ff5a 100644 --- a/doc/source/cli/command-objects/volume-service.rst +++ b/doc/source/cli/command-objects/volume-service.rst @@ -2,7 +2,7 @@ volume service ============== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume service * diff --git a/doc/source/cli/command-objects/volume-snapshot.rst b/doc/source/cli/command-objects/volume-snapshot.rst index be3ad303b9..e63e436dff 100644 --- a/doc/source/cli/command-objects/volume-snapshot.rst +++ b/doc/source/cli/command-objects/volume-snapshot.rst @@ -2,7 +2,7 @@ volume snapshot =============== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume snapshot * diff --git a/doc/source/cli/command-objects/volume-transfer-request.rst b/doc/source/cli/command-objects/volume-transfer-request.rst index 61e38c1cf7..97dac02a0a 100644 --- a/doc/source/cli/command-objects/volume-transfer-request.rst +++ b/doc/source/cli/command-objects/volume-transfer-request.rst @@ -2,7 +2,7 @@ volume transfer request ======================= -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume transfer request * diff --git a/doc/source/cli/command-objects/volume-type.rst b/doc/source/cli/command-objects/volume-type.rst index 1a74a8a66c..003ee67306 100644 --- a/doc/source/cli/command-objects/volume-type.rst +++ b/doc/source/cli/command-objects/volume-type.rst @@ -2,7 +2,7 @@ volume type =========== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume type * diff --git a/doc/source/cli/command-objects/volume.rst b/doc/source/cli/command-objects/volume.rst index 9b49177268..337bb9fa2b 100644 --- a/doc/source/cli/command-objects/volume.rst +++ b/doc/source/cli/command-objects/volume.rst @@ -2,397 +2,33 @@ volume ====== -Block Storage v1, v2 +Block Storage v2, v3 -volume create -------------- +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume create -Create new volume +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume delete -.. program:: volume create -.. code:: bash +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume list - openstack volume create - [--size ] - [--type ] - [--image | --snapshot | --source ] - [--description ] - [--availability-zone ] - [--consistency-group ] - [--property [...] ] - [--hint [...] ] - [--bootable | --non-bootable] - [--read-only | --read-write] - +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume migrate -.. option:: --size +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume set - Volume size in GB - (Required unless --snapshot or --source is specified) +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume show -.. option:: --type - - Set the type of volume - - Select ```` from the available types as shown - by ``volume type list``. - -.. option:: --image - - Use ```` as source of volume (name or ID) - - This is commonly used to create a boot volume for a server. - -.. option:: --snapshot - - Use ```` as source of volume (name or ID) - -.. option:: --source - - Volume to clone (name or ID) - -.. option:: --description - - Volume description - -.. option:: --availability-zone - - Create volume in ```` - -.. option:: --consistency-group - - Consistency group where the new volume belongs to - -.. option:: --property - - Set a property on this volume (repeat option to set multiple properties) - -.. option:: --hint - - Arbitrary scheduler hint key-value pairs to help boot an instance - (repeat option to set multiple hints) - -.. option:: --bootable - - Mark volume as bootable - -.. option:: --non-bootable - - Mark volume as non-bootable (default) - -.. option:: --read-only - - Set volume to read-only access mode - -.. option:: --read-write - - Set volume to read-write access mode (default) - -.. _volume_create-name: -.. describe:: - - Volume name - -volume delete -------------- - -Delete volume(s) - -.. program:: volume delete -.. code:: bash - - openstack volume delete - [--force | --purge] - [ ...] - -.. option:: --force - - Attempt forced removal of volume(s), regardless of state (defaults to False) - -.. option:: --purge - - Remove any snapshots along with volume(s) (defaults to False) - - *Volume version 2 only* - -.. _volume_delete-volume: -.. describe:: - - Volume(s) to delete (name or ID) - -volume list ------------ - -List volumes - -.. program:: volume list -.. code:: bash - - openstack volume list - [--project [--project-domain ]] - [--user [--user-domain ]] - [--name ] - [--status ] - [--all-projects] - [--long] - [--limit ] - [--marker ] - -.. option:: --project - - Filter results by ```` (name or ID) (admin only) - - *Volume version 2 only* - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - - This can be used in case collisions between project names exist. - - *Volume version 2 only* - -.. option:: --user - - Filter results by ```` (name or ID) (admin only) - - *Volume version 2 only* - -.. option:: --user-domain - - Domain the user belongs to (name or ID). - - This can be used in case collisions between user names exist. - - *Volume version 2 only* - -.. option:: --name - - Filter results by volume name - -.. option:: --status - - Filter results by status - -.. option:: --all-projects - - Include all projects (admin only) - -.. option:: --long - - List additional fields in output - -.. option:: --limit - - Maximum number of volumes to display - -.. option:: --marker - - The last volume ID of the previous page - - *Volume version 2 only* - -volume migrate --------------- - -Migrate volume to a new host - -.. program:: volume migrate -.. code:: bash - - openstack volume migrate - --host - [--force-host-copy] - [--lock-volume] - - -.. option:: --host - - Destination host (takes the form: host@backend-name#pool) (required) - -.. option:: --force-host-copy - - Enable generic host-based force-migration, - which bypasses driver optimizations - -.. option:: --lock-volume - - If specified, the volume state will be locked and will not allow - a migration to be aborted (possibly by another operation) - - *Volume version 2 only* - -.. _volume_migrate-volume: -.. describe:: - - Volume to migrate (name or ID) - -volume set ----------- - -Set volume properties - -.. program:: volume set -.. code:: bash - - openstack volume set - [--name ] - [--size ] - [--description ] - [--no-property] - [--property [...] ] - [--image-property [...] ] - [--state ] - [--attached | --detached ] - [--type ] - [--retype-policy ] - [--bootable | --non-bootable] - [--read-only | --read-write] - - -.. option:: --name - - New volume name - -.. option:: --size - - Extend volume size in GB - -.. option:: --description - - New volume description - -.. option:: --no-property - - Remove all properties from :ref:`\ ` - (specify both :option:`--no-property` and :option:`--property` to - remove the current properties before setting new properties.) - -.. option:: --property - - Set a property on this volume (repeat option to set multiple properties) - -.. option:: --type - - New volume type (name or ID) - - *Volume version 2 only* - -.. option:: --retype-policy - - Migration policy while re-typing volume - ("never" or "on-demand", default is "never" ) - (available only when :option:`--type` option is specified) - - *Volume version 2 only* - -.. option:: --bootable - - Mark volume as bootable - -.. option:: --non-bootable - - Mark volume as non-bootable - -.. option:: --read-only - - Set volume to read-only access mode - -.. option:: --read-write - - Set volume to read-write access mode - -.. option:: --image-property - - Set an image property on this volume - (repeat option to set multiple image properties) - - Image properties are copied along with the image when creating a volume - using ``--image``. Note that these properties are immutable on the image - itself, this option updates the copy attached to this volume. - - *Volume version 2 only* - -.. option:: --state - - New volume state - ("available", "error", "creating", "deleting", "in-use", - "attaching", "detaching", "error_deleting" or "maintenance") (admin only) - (This option simply changes the state of the volume in the database with - no regard to actual status, exercise caution when using) - - *Volume version 2 only* - -.. option:: --attached - - Set volume attachment status to "attached" (admin only) - (This option simply changes the state of the volume in the database with - no regard to actual status, exercise caution when using) - - *Volume version 2 only* - -.. option:: --deattach - - Set volume attachment status to "detached" (admin only) - (This option simply changes the state of the volume in the database with - no regard to actual status, exercise caution when using) - - *Volume version 2 only* - -.. _volume_set-volume: -.. describe:: - - Volume to modify (name or ID) - -volume show ------------ - -Show volume details - -.. program:: volume show -.. code:: bash - - openstack volume show - - -.. _volume_show-volume: -.. describe:: - - Volume to display (name or ID) - -volume unset ------------- - -Unset volume properties - -.. program:: volume unset -.. code:: bash - - openstack volume unset - [--property ] - [--image-property ] - - -.. option:: --property - - Remove a property from volume (repeat option to remove multiple properties) - -.. option:: --image-property - - Remove an image property from volume - (repeat option to remove multiple image properties) - - *Volume version 2 only* - -.. _volume_unset-volume: -.. describe:: - - Volume to modify (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume unset Block Storage v3 - .. autoprogram-cliff:: openstack.volume.v3 - :command: volume summary +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume summary - .. autoprogram-cliff:: openstack.volume.v3 - :command: volume revert +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume revert diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index c248904fe1..f978962274 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -24,10 +24,10 @@ location-delete,,Remove locations (and related metadata) from an image. location-update,,Update metadata of an image's location. md-namespace-create,image metadef namespace create,Create a new metadata definitions namespace. md-namespace-delete,image metadef namespace delete,Delete specified metadata definitions namespace with its contents. -md-namespace-import,,Import a metadata definitions namespace from file or standard input. +md-namespace-import,WONTFIX,Import a metadata definitions namespace from file or standard input. md-namespace-list,image metadef namespace list,List metadata definitions namespaces. -md-namespace-objects-delete,,Delete all metadata definitions objects inside a specific namespace. -md-namespace-properties-delete,,Delete all metadata definitions property inside a specific namespace. +md-namespace-objects-delete,image metadef object delete,Delete all metadata definitions objects inside a specific namespace. +md-namespace-properties-delete,image metadef property delete,Delete all metadata definitions property inside a specific namespace. md-namespace-resource-type-list,image metadef resource type association list,List resource types associated to specific namespace. md-namespace-show,image metadef namespace show,Describe a specific metadata definitions namespace. md-namespace-tags-delete,,Delete all metadata definitions tags inside a specific namespace. diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index ac61cc0e01..2622ee58b3 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -25,10 +25,3 @@ Plugin Commands watcher zaqar zun - -.. TODO(efried): Make pages for the following once they're fixed. - -.. cue -.. # cueclient is not in global-requirements -.. # list-plugins:: openstack.mb.v1 -.. # :detailed: diff --git a/doc/source/contributor/command-errors.rst b/doc/source/contributor/command-errors.rst index 5204e06b4b..f47dfec7c3 100644 --- a/doc/source/contributor/command-errors.rst +++ b/doc/source/contributor/command-errors.rst @@ -41,7 +41,7 @@ opened. ## ... def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute public_key = parsed_args.public_key if public_key: diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst index 2aa9498f1b..445aac92d1 100644 --- a/doc/source/contributor/index.rst +++ b/doc/source/contributor/index.rst @@ -11,7 +11,6 @@ command-wrappers command-errors command-logs - specs/commands plugins humaninterfaceguide api/modules diff --git a/doc/source/contributor/specs/command-objects/example.rst b/doc/source/contributor/specs/command-objects/example.rst deleted file mode 100644 index fa559433e2..0000000000 --- a/doc/source/contributor/specs/command-objects/example.rst +++ /dev/null @@ -1,86 +0,0 @@ -======= -example -======= - -This is a specification for the ``example`` command object. It is not intended -to be a complete template for new commands since other actions, options -and/or arguments may be used. You can include general specification information -before the commands below. This information could include links to related material -or descriptions of similar commands. - -[example API name] [example API version] - -example create --------------- - -Create new example - -.. program:: example create -.. code:: bash - - openstack example create - - -.. describe:: - - New example name - -example delete --------------- - -Delete example(s) - -.. program:: example delete -.. code:: bash - - openstack example delete - [ ...] - -.. describe:: - - Example(s) to delete (name or ID) - -example list ------------- - -List examples - -.. program:: example list -.. code:: bash - - openstack example list - -example set ------------ - -Set example properties - -.. program:: example set -.. code:: bash - - openstack example set - [--name ] - - -.. option:: --name - - New example name - -.. describe:: - - Example to modify (name or ID) - -example show ------------- - -Display example details - -.. program:: example show -.. code:: bash - - openstack example show - - -.. describe:: - - Example to display (name or ID) diff --git a/doc/source/contributor/specs/commands.rst b/doc/source/contributor/specs/commands.rst deleted file mode 100644 index f9d757e785..0000000000 --- a/doc/source/contributor/specs/commands.rst +++ /dev/null @@ -1,44 +0,0 @@ -============= -Command Specs -============= - -Specifications for new commands, objects and actions are listed below. -These specifications have not been implemented. See -:ref:`command-list` for implemented commands and -:ref:`command-structure` for implemented objects and actions. - -It is optional to propose a specifications patch for new commands, -objects and actions here before submitting the implementation. Once your -specifications patch merges then you may proceed with the implementation. -Your implementation patches should move applicable portions of the -specifications patch to the official :ref:`command-list` -and :ref:`command-structure` documentation. - -Objects Specs -------------- - -Add specifications for new objects based on the ``example`` object. - -Actions Specs -------------- - -Add specifications for new actions based on the ``example`` action. - -.. toctree:: - :maxdepth: 1 - - network-topology - -Commands Specs --------------- - -Add specifications for new commands based on the commands for the -``example`` object. The ``example`` commands are not intended to -be a complete template for new commands since other actions, options -and/or arguments may be used. - -.. toctree:: - :glob: - :maxdepth: 2 - - command-objects/* diff --git a/doc/source/contributor/specs/network-topology.rst b/doc/source/contributor/specs/network-topology.rst deleted file mode 100644 index 6789ee975f..0000000000 --- a/doc/source/contributor/specs/network-topology.rst +++ /dev/null @@ -1,44 +0,0 @@ -================ -network topology -================ - -A **network topology** shows a topological graph about -devices which connect to the specific network. Also, it -will return availability information for each individual -device within the network as well. One other thing to note -is that it is the intention for OSC to collect data from -existing REST APIs - -Network v2 - -network topology list ---------------------- - -List network topologies - -.. program:: network topology list -.. code:: bash - - openstack network topology list - [--project ] - -.. option:: --project - - List network topologies for given project - (name or ID) - -network topology show ---------------------- - -Show network topology details - -.. program:: network topology show -.. code:: bash - - openstack network topology show - - -.. _network_topology_show-network: -.. describe:: - - Show network topology for a specific network (name or ID) diff --git a/examples/common.py b/examples/common.py index d97bfb55cb..650139ec27 100755 --- a/examples/common.py +++ b/examples/common.py @@ -241,9 +241,9 @@ def run(opts): # Do some basic testing here sys.stdout.write("Default run command\n") - sys.stdout.write("Verbose level: %s\n" % opts.verbose_level) - sys.stdout.write("Debug: %s\n" % opts.debug) - sys.stdout.write("dump_stack_trace: %s\n" % dump_stack_trace) + sys.stdout.write(f"Verbose level: {opts.verbose_level}\n") + sys.stdout.write(f"Debug: {opts.debug}\n") + sys.stdout.write(f"dump_stack_trace: {dump_stack_trace}\n") def setup(): diff --git a/examples/object_api.py b/examples/object_api.py index 577fc052fa..5917d35fa6 100755 --- a/examples/object_api.py +++ b/examples/object_api.py @@ -94,14 +94,14 @@ def run(opts): c_list = obj_api.container_list() print("Name\tCount\tBytes") for c in c_list: - print("%s\t%d\t%d" % (c['name'], c['count'], c['bytes'])) + print(f"{c['name']}\t{c['count']}\t{c['bytes']}") if len(c_list) > 0: # See what is in the first container o_list = obj_api.object_list(c_list[0]['name']) print("\nObject") for o in o_list: - print("%s" % o) + print(f"{o}") if __name__ == "__main__": diff --git a/examples/osc-lib.py b/examples/osc-lib.py index cc04bc70fe..fb8ea2dc3a 100755 --- a/examples/osc-lib.py +++ b/examples/osc-lib.py @@ -87,20 +87,20 @@ def run(opts): c_list = client_manager.object_store.container_list() print("Name\tCount\tBytes") for c in c_list: - print("%s\t%d\t%d" % (c['name'], c['count'], c['bytes'])) + print(f"{c['name']}\t{c['count']}\t{c['bytes']}") if len(c_list) > 0: # See what is in the first container o_list = client_manager.object_store.object_list(c_list[0]['name']) print("\nObject") for o in o_list: - print("%s" % o) + print(f"{o}") # Look at the compute flavors flavor_list = client_manager.compute.flavors.list() print("\nFlavors:") for f in flavor_list: - print("%s" % f) + print(f"{f}") if __name__ == "__main__": diff --git a/hacking/checks.py b/hacking/checks.py new file mode 100644 index 0000000000..0eb485e7e2 --- /dev/null +++ b/hacking/checks.py @@ -0,0 +1,179 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import ast +import os +import re + +from hacking import core + +""" +Guidelines for writing new hacking checks + + - Use only for python-openstackclient specific tests. OpenStack general tests + should be submitted to the common 'hacking' module. + - Pick numbers in the range O4xx. Find the current test with the highest + allocated number and then pick the next value. +""" + + +@core.flake8ext +def assert_no_oslo(logical_line): + """Check for use of oslo libraries. + + O400 + """ + if re.match(r'(from|import) oslo_.*', logical_line): + yield (0, "0400: oslo libraries should not be used in SDK projects") + + +@core.flake8ext +def assert_no_duplicated_setup(logical_line, filename): + """Check for use of various unnecessary test duplications. + + O401 + """ + if os.path.join('openstackclient', 'tests', 'unit') not in filename: + return + + if re.match(r'self.app = .*\(self.app, self.namespace\)', logical_line): + yield ( + 0, + 'O401: It is not necessary to create dummy Namespace objects', + ) + + if os.path.basename(filename) != 'fakes.py': + if re.match( + r'self.[a-z_]+_client = self.app.client_manager.*', logical_line + ): + yield ( + 0, + "O401: Aliases for mocks of the service client are already " + "provided by the respective service's FakeClientMixin class", + ) + + if match := re.match( + r'self.app.client_manager.([a-z_]+) = mock.Mock', logical_line + ): + service = match.group(1) + if service == 'auth_ref': + return + yield ( + 0, + f"O401: client_manager.{service} mocks are already provided " + f"by the {service} service's FakeClientMixin class", + ) + + +@core.flake8ext +def assert_use_of_client_aliases(logical_line): + """Ensure we use $service_client instead of $sdk_connection.service. + + O402 + """ + # we should expand the list of services as we drop legacy clients + if match := re.match( + r'self\.app\.client_manager\.sdk_connnection\.(compute|network|image)', + logical_line, + ): + service = match.group(1) + yield (0, f"0402: prefer {service}_client to sdk_connection.{service}") + + if match := re.match( + r'(self\.app\.client_manager\.(compute|network|image)+\.[a-z_]+) = mock.Mock', # noqa: E501 + logical_line, + ): + yield ( + 0, + f"O402: {match.group(1)} is already a mock: there's no need to " + f"assign a new mock.Mock instance.", + ) + + if match := re.match( + r'(self\.(compute|network|image)_client\.[a-z_]+) = mock.Mock', + logical_line, + ): + yield ( + 0, + f"O402: {match.group(1)} is already a mock: there's no need to " + f"assign a new mock.Mock instance.", + ) + + +class SDKProxyFindChecker(ast.NodeVisitor): + """NodeVisitor to find ``*_client.find_*`` statements.""" + + def __init__(self): + self.error = False + + def visit_Call(self, node): + # No need to keep visiting the AST if we already found something. + if self.error: + return + + self.generic_visit(node) + + if not ( + isinstance(node.func, ast.Attribute) + and node.func.attr.startswith('find_') # and + # isinstance(node.func.value, ast.Attribute) and + # node.func.value.attr.endswith('_client') + ): + # print(f'skipping: got {node.func}') + return + + if not ( + ( + # handle calls like 'identity_client.find_project' + isinstance(node.func.value, ast.Name) + and node.func.value.id.endswith('client') + ) + or ( + # handle calls like 'self.app.client_manager.image.find_image' + isinstance(node.func.value, ast.Attribute) + and node.func.value.attr + in ('identity', 'network', 'image', 'compute') + ) + ): + return + + if not any(kw.arg == 'ignore_missing' for kw in node.keywords): + self.error = True + + +@core.flake8ext +def assert_find_ignore_missing_kwargs(logical_line, filename): + """Ensure ignore_missing is always used for ``find_*`` SDK proxy calls. + + Okay: self.compute_client.find_server(foo, ignore_missing=True) + Okay: self.image_client.find_server(foo, ignore_missing=False) + Okay: self.volume_client.volumes.find(name='foo') + O403: self.network_client.find_network(parsed_args.network) + O403: self.compute_client.find_flavor(flavor_id, get_extra_specs=True) + """ + if 'tests' in filename: + return + + checker = SDKProxyFindChecker() + try: + parsed_logical_line = ast.parse(logical_line) + except SyntaxError: + # let flake8 catch this itself + # https://github.com/PyCQA/flake8/issues/1948 + return + checker.visit(parsed_logical_line) + if checker.error: + yield ( + 0, + 'O403: Calls to find_* proxy methods must explicitly set ' + 'ignore_missing', + ) diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py index c8def49e37..5f78b0ee54 100644 --- a/openstackclient/api/api.py +++ b/openstackclient/api/api.py @@ -12,6 +12,7 @@ # """Base API Library""" + from keystoneauth1 import exceptions as ks_exceptions from keystoneauth1 import session as ks_session from osc_lib import exceptions @@ -306,7 +307,7 @@ def find( except ks_exceptions.NotFound: kwargs = {attr: value} try: - ret = self.find_one("/%s/detail" % (path), **kwargs) + ret = self.find_one(f"/{path}/detail", **kwargs) except ks_exceptions.NotFound: msg = _("%s not found") % value raise exceptions.NotFound(msg) diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index 95521eae9a..41e1b685b0 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -64,7 +64,7 @@ def list_security_groups(compute_client, all_projects=None): def find_security_group(compute_client, name_or_id): - """Find the name for a given security group name or ID + """Find the security group for a given name or ID https://docs.openstack.org/api-ref/compute/#show-security-group-details @@ -240,7 +240,7 @@ def list_networks(compute_client): def find_network(compute_client, name_or_id): - """Find the ID for a given network name or ID + """Find the network for a given name or ID https://docs.openstack.org/api-ref/compute/#show-network-details diff --git a/openstackclient/api/image_v2.py b/openstackclient/api/image_v2.py index d016318957..9b0e9b1f8e 100644 --- a/openstackclient/api/image_v2.py +++ b/openstackclient/api/image_v2.py @@ -33,7 +33,7 @@ def image_list( private=False, community=False, shared=False, - **filter + **filter, ): """Get available images diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index bc49ac850d..933b01b836 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -89,7 +89,7 @@ def container_list( marker=None, end_marker=None, prefix=None, - **params + **params, ): """Get containers in an account @@ -116,7 +116,7 @@ def container_list( marker=marker, end_marker=end_marker, prefix=prefix, - **params + **params, ) while listing: marker = listing[-1]['name'] @@ -125,7 +125,7 @@ def container_list( marker=marker, end_marker=end_marker, prefix=prefix, - **params + **params, ) if listing: data.extend(listing) @@ -256,9 +256,9 @@ def object_create( # object's name in the container. object_name_str = name if name else object - full_url = "{}/{}".format( - urllib.parse.quote(container), - urllib.parse.quote(object_name_str), + full_url = ( + f"{urllib.parse.quote(container)}/" + f"{urllib.parse.quote(object_name_str)}" ) with open(object, 'rb') as f: response = self.create( @@ -293,8 +293,7 @@ def object_delete( return self.delete( - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)) + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}" ) def object_list( @@ -306,7 +305,7 @@ def object_list( end_marker=None, delimiter=None, prefix=None, - **params + **params, ): """List objects in a container @@ -341,7 +340,7 @@ def object_list( end_marker=end_marker, prefix=prefix, delimiter=delimiter, - **params + **params, ) while listing: if delimiter: @@ -355,7 +354,7 @@ def object_list( end_marker=end_marker, prefix=prefix, delimiter=delimiter, - **params + **params, ) if listing: data.extend(listing) @@ -395,8 +394,7 @@ def object_save( response = self._request( 'GET', - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", stream=True, ) if response.status_code == 200: @@ -431,8 +429,7 @@ def object_set( headers = self._set_properties(properties, 'X-Object-Meta-%s') if headers: self.create( - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", headers=headers, ) @@ -455,8 +452,7 @@ def object_unset( headers = self._unset_properties(properties, 'X-Remove-Object-Meta-%s') if headers: self.create( - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", headers=headers, ) @@ -480,8 +476,7 @@ def object_show( response = self._request( 'HEAD', - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", ) data = { diff --git a/openstackclient/api/volume_v2.py b/openstackclient/api/volume_v2.py new file mode 100644 index 0000000000..9575379c69 --- /dev/null +++ b/openstackclient/api/volume_v2.py @@ -0,0 +1,60 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Volume v2 API Library + +A collection of wrappers for deprecated Block Storage v2 APIs that are not +intentionally supported by SDK. +""" + +import http + +from openstack import exceptions as sdk_exceptions +from osc_lib import exceptions + + +# consistency groups + + +def find_consistency_group(compute_client, name_or_id): + """Find the consistency group for a given name or ID + + https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details + + :param volume_client: A volume client + :param name_or_id: The name or ID of the consistency group to look up + :returns: A consistency group object + :raises exception.NotFound: If a matching consistency group could not be + found or more than one match was found + """ + response = compute_client.get(f'/consistencygroups/{name_or_id}') + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['consistencygroup'] + + response = compute_client.get('/consistencygroups') + sdk_exceptions.raise_from_response(response) + found = None + consistency_groups = response.json()['consistencygroups'] + for consistency_group in consistency_groups: + if consistency_group['name'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = consistency_group + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/api/volume_v3.py b/openstackclient/api/volume_v3.py new file mode 100644 index 0000000000..1a3f25fa01 --- /dev/null +++ b/openstackclient/api/volume_v3.py @@ -0,0 +1,60 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Volume v3 API Library + +A collection of wrappers for deprecated Block Storage v3 APIs that are not +intentionally supported by SDK. +""" + +import http + +from openstack import exceptions as sdk_exceptions +from osc_lib import exceptions + + +# consistency groups + + +def find_consistency_group(compute_client, name_or_id): + """Find the consistency group for a given name or ID + + https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details + + :param volume_client: A volume client + :param name_or_id: The name or ID of the consistency group to look up + :returns: A consistency group object + :raises exception.NotFound: If a matching consistency group could not be + found or more than one match was found + """ + response = compute_client.get(f'/consistencygroups/{name_or_id}') + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['consistencygroup'] + + response = compute_client.get('/consistencygroups') + sdk_exceptions.raise_from_response(response) + found = None + consistency_groups = response.json()['consistencygroups'] + for consistency_group in consistency_groups: + if consistency_group['name'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = consistency_group + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/command.py b/openstackclient/command.py new file mode 100644 index 0000000000..124d755aa9 --- /dev/null +++ b/openstackclient/command.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cliff import lister +from cliff import show +from osc_lib.command import command + +from openstackclient import shell + + +class Command(command.Command): + app: shell.OpenStackShell + + +class Lister(Command, lister.Lister): ... + + +class ShowOne(Command, show.ShowOne): ... diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index acc661a8d8..6f5e4fd455 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -17,9 +17,9 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -117,7 +117,7 @@ def get_parser(self, prog_name): return parser def _get_compute_availability_zones(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute try: data = list(compute_client.availability_zones(details=True)) except sdk_exceptions.ForbiddenException: # policy doesn't allow @@ -172,17 +172,14 @@ def _get_network_availability_zones(self, parsed_args): return result def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Zone Name', 'Zone Status') if parsed_args.long: - columns = ( - 'Zone Name', - 'Zone Status', + columns += ( 'Zone Resource', 'Host Name', 'Service Name', 'Service Status', ) - else: - columns = ('Zone Name', 'Zone Status') # Show everything by default. show_all = ( diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 8caf94a5d9..51911aaf99 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -15,18 +15,29 @@ """Manage access to the clients, including authenticating when needed.""" +import argparse +from collections.abc import Callable import importlib import logging import sys +import typing as ty +from osc_lib.cli import client_config from osc_lib import clientmanager from osc_lib import shell import stevedore +if ty.TYPE_CHECKING: + from keystoneauth1 import access as ksa_access + from openstack.compute.v2 import _proxy as compute_proxy + from openstack.image.v2 import _proxy as image_proxy + from openstack.network.v2 import _proxy as network_proxy + + from openstackclient.api import object_store_v1 LOG = logging.getLogger(__name__) -PLUGIN_MODULES = [] +PLUGIN_MODULES: list[ty.Any] = [] USER_AGENT = 'python-openstackclient' @@ -39,11 +50,23 @@ class ClientManager(clientmanager.ClientManager): in osc-lib so we need to maintain a transition period. """ - # A simple incrementing version for the plugin to know what is available - PLUGIN_INTERFACE_VERSION = "2" - - # Let the commands set this - _auth_required = False + if ty.TYPE_CHECKING: + # we know this will be set by us and will not be nullable + auth_ref: ksa_access.AccessInfo + + # this is a hack to keep mypy happy: the actual attributes are set in + # get_plugin_modules below + # TODO(stephenfin): Change the types of identity and volume once we've + # migrated everything to SDK. Hopefully by then we'll have figured out + # how to statically distinguish between the v2 and v3 versions of both + # services... + # TODO(stephenfin): We also need to migrate object storage... + compute: compute_proxy.Proxy + identity: ty.Any + image: image_proxy.Proxy + network: network_proxy.Proxy + object_store: object_store_v1.APIv1 + volume: ty.Any def __init__( self, @@ -80,6 +103,12 @@ def setup_auth(self): self._auth_required and self._cli_options._openstack_config is not None ): + if not isinstance( + self._cli_options._openstack_config, client_config.OSC_Config + ): + # programmer error + raise TypeError('unexpected type for _openstack_config') + self._cli_options._openstack_config._pw_callback = ( shell.prompt_for_password ) @@ -101,11 +130,18 @@ def _fallback_load_auth_plugin(self, e): # expect, delete fake token and endpoint, then try to # load auth plugin again with user specified options. # We know it looks ugly, but it's necessary. - if self._cli_options.config['auth']['token'] == 'x': + if self._cli_options.config['auth']['token'] == 'x': # noqa: S105 # restore original auth_type self._cli_options.config['auth_type'] = self._original_auth_type del self._cli_options.config['auth']['token'] del self._cli_options.config['auth']['endpoint'] + + if not isinstance( + self._cli_options._openstack_config, client_config.OSC_Config + ): + # programmer error + raise TypeError('unexpected type for _openstack_config') + self._cli_options._auth = ( self._cli_options._openstack_config.load_auth_plugin( self._cli_options.config, @@ -116,7 +152,6 @@ def _fallback_load_auth_plugin(self, e): def is_network_endpoint_enabled(self): """Check if the network endpoint is enabled""" - # NOTE(dtroyer): is_service_available() can also return None if # there is no Service Catalog, callers here are # not expecting that so fold None into True to @@ -125,71 +160,72 @@ def is_network_endpoint_enabled(self): def is_compute_endpoint_enabled(self): """Check if Compute endpoint is enabled""" - return self.is_service_available('compute') is not False - def is_volume_endpoint_enabled(self, volume_client): + # TODO(stephenfin): Drop volume_client argument in OSC 8.0 or later. + def is_volume_endpoint_enabled(self, volume_client=None): """Check if volume endpoint is enabled""" - # NOTE(jcross): Cinder did some interesting things with their service - # name so we need to figure out which version to look - # for when calling is_service_available() - endpoint_data = volume_client.get_endpoint_data() - # Not sure how endpoint data stores the api version for v2 API, - # for v3 it is a tuple (3, 0) - if endpoint_data.api_version and isinstance( - endpoint_data.api_version, tuple - ): - volume_version = endpoint_data.api_version[0] - else: - # Setting volume_version as 2 here if it doesn't satisfy the - # conditions for version 3 - volume_version = 2 - if ( - self.is_service_available("volumev%s" % volume_version) - is not False - ): - return True - elif self.is_service_available('volume') is not False: - return True - else: - return False + # We check against the service type and all aliases defined by the + # Service Types Authority + # https://service-types.openstack.org/service-types.json + return ( + self.is_service_available('block-storage') is not False + or self.is_service_available('volume') is not False + or self.is_service_available('volumev3') is not False + or self.is_service_available('volumev2') is not False + or self.is_service_available('block-store') is not False + ) # Plugin Support +ArgumentParserT = ty.TypeVar('ArgumentParserT', bound=argparse.ArgumentParser) + + +@ty.runtime_checkable # Optional: allows usage with isinstance() +class PluginModule(ty.Protocol): + DEFAULT_API_VERSION: str + API_VERSION_OPTION: str + API_NAME: str + API_VERSIONS: tuple[str] + + make_client: Callable[..., ty.Any] + build_option_parser: Callable[[ArgumentParserT], ArgumentParserT] + check_api_version: Callable[[str], bool] + + +def _on_load_failure_callback( + manager: stevedore.ExtensionManager, + ep: importlib.metadata.EntryPoint, + err: BaseException, +) -> None: + sys.stderr.write( + f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n" + ) + def get_plugin_modules(group): """Find plugin entry points""" mod_list = [] - mgr = stevedore.ExtensionManager(group) + mgr: stevedore.ExtensionManager[PluginModule] + mgr = stevedore.ExtensionManager( + group, on_load_failure_callback=_on_load_failure_callback + ) for ep in mgr: LOG.debug('Found plugin %s', ep.name) - # Different versions of stevedore use different - # implementations of EntryPoint from other libraries, which - # are not API-compatible. - try: - module_name = ep.entry_point.module_name - except AttributeError: - try: - module_name = ep.entry_point.module - except AttributeError: - module_name = ep.entry_point.value + module_name = ep.entry_point.module try: module = importlib.import_module(module_name) except Exception as err: sys.stderr.write( - "WARNING: Failed to import plugin {}: {}.\n".format( - ep.name, err - ) + f"WARNING: Failed to import plugin " + f"{ep.module_name}:{ep.name}: {err}.\n" ) continue mod_list.append(module) - init_func = getattr(module, 'Initialize', None) - if init_func: - init_func('x') # Add the plugin to the ClientManager setattr( diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py index 418f7fdd0f..4637ad22b8 100644 --- a/openstackclient/common/configuration.py +++ b/openstackclient/common/configuration.py @@ -14,8 +14,8 @@ """Configuration action implementations""" from keystoneauth1.loading import base -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ REDACTED = "" diff --git a/openstackclient/common/envvars.py b/openstackclient/common/envvars.py new file mode 100644 index 0000000000..5f702ff3d4 --- /dev/null +++ b/openstackclient/common/envvars.py @@ -0,0 +1,57 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os + +from openstackclient.i18n import _ + + +def bool_from_str(value, strict=False): + true_strings = ('1', 't', 'true', 'on', 'y', 'yes') + false_strings = ('0', 'f', 'false', 'off', 'n', 'no') + + if isinstance(value, bool): + return value + + lowered = value.strip().lower() + if lowered in true_strings: + return True + elif lowered in false_strings or not strict: + return False + + msg = _( + "Unrecognized value '%(value)s'; acceptable values are: %(valid)s" + ) % { + 'value': value, + 'valid': ', '.join( + f"'{s}'" for s in sorted(true_strings + false_strings) + ), + } + raise ValueError(msg) + + +def boolenv(*vars, default=False): + """Search for the first defined of possibly many bool-like env vars. + + Returns the first environment variable defined in vars, or returns the + default. + + :param vars: Arbitrary strings to search for. Case sensitive. + :param default: The default to return if no value found. + :returns: A boolean corresponding to the value found, else the default if + no value found. + """ + for v in vars: + value = os.environ.get(v, None) + if value: + return bool_from_str(value) + return default diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index ad7b1a9341..3f9b257bf3 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -17,9 +17,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -73,17 +73,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Name', 'Alias', 'Description') if parsed_args.long: - columns = ( - 'Name', - 'Alias', - 'Description', - 'Namespace', - 'Updated At', - 'Links', - ) - else: - columns = ('Name', 'Alias', 'Description') + columns += ('Namespace', 'Updated At', 'Links') data = [] @@ -106,7 +98,7 @@ def take_action(self, parsed_args): LOG.warning(message) if parsed_args.compute or show_all: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute try: data += compute_client.extensions() except Exception: diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index 043bf5321b..6512e0fcd1 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -17,9 +17,9 @@ import itertools -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -101,9 +101,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute - volume_client = self.app.client_manager.sdk_connection.volume - project_id = None if parsed_args.project is not None: identity_client = self.app.client_manager.identity @@ -125,13 +122,15 @@ def take_action(self, parsed_args): volume_limits = None if self.app.client_manager.is_compute_endpoint_enabled(): + compute_client = self.app.client_manager.compute compute_limits = compute_client.get_limits( reserved=parsed_args.is_reserved, tenant_id=project_id ) - if self.app.client_manager.is_volume_endpoint_enabled(volume_client): + if self.app.client_manager.is_volume_endpoint_enabled(): + volume_client = self.app.client_manager.sdk_connection.volume volume_limits = volume_client.get_limits( - project_id=project_id, + project=project_id, ) if parsed_args.is_absolute: diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index e9102561e6..6ca5dc2315 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -17,9 +17,9 @@ import sys -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -48,7 +48,9 @@ def take_action(self, parsed_args): columns = ('Command Group', 'Commands') if parsed_args.group: - groups = (group for group in groups if parsed_args.group in group) + groups = sorted( + group for group in groups if parsed_args.group in group + ) commands = [] for group in groups: @@ -111,8 +113,8 @@ def take_action(self, parsed_args): data[k] = mods[k].version.__version__ else: data[k] = mods[k].__version__ - except Exception: + except Exception: # noqa: S110 # Catch all exceptions, just skip it - pass # nosec: B110 + pass return zip(*sorted(data.items())) diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 2ac156cba5..444f23ec2c 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -17,10 +17,11 @@ import logging import os import queue +import typing as ty from cliff.formatters import table -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -35,7 +36,7 @@ def ask_user_yesno(msg): :return bool: User choice """ while True: - answer = getpass._raw_input('{} [{}]: '.format(msg, 'y/n')) + answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n')) if answer in ('y', 'Y', 'yes'): return True elif answer in ('n', 'N', 'no'): @@ -89,18 +90,20 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - sdk = self.app.client_manager.sdk_connection + connection = self.app.client_manager.sdk_connection if parsed_args.auth_project: - project_connect = sdk + # is we've got a project already configured, use the connection + # as-is + pass elif parsed_args.project: - project = sdk.identity.find_project( + project = connection.identity.find_project( name_or_id=parsed_args.project, ignore_missing=False ) - project_connect = sdk.connect_as_project(project) + connection = connection.connect_as_project(project) - if project_connect: - status_queue = queue.Queue() + if connection: + status_queue: queue.Queue[ty.Any] = queue.Queue() parsed_args.max_width = int( os.environ.get('CLIFF_MAX_TERM_WIDTH', 0) ) @@ -119,7 +122,7 @@ def take_action(self, parsed_args): if parsed_args.updated_before: filters['updated_at'] = parsed_args.updated_before - project_connect.project_cleanup( + connection.project_cleanup( dry_run=True, status_queue=status_queue, filters=filters, @@ -149,6 +152,9 @@ def take_action(self, parsed_args): self.log.warning(_('Deleting resources')) - project_connect.project_cleanup( - dry_run=False, status_queue=status_queue, filters=filters + connection.project_cleanup( + dry_run=False, + status_queue=status_queue, + filters=filters, + skip_resources=parsed_args.skip_resource, ) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index c408e97bac..6d0025a754 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -18,12 +18,13 @@ import itertools import logging import sys +import typing as ty from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -132,7 +133,7 @@ def get_compute_quotas( default=False, ): try: - client = app.client_manager.sdk_connection.compute + client = app.client_manager.compute if default: quota = client.get_quota_set_defaults(project_id) else: @@ -176,25 +177,37 @@ def get_network_quotas( default=False, ): def _network_quota_to_dict(network_quota, detail=False): - if type(network_quota) is not dict: - dict_quota = network_quota.to_dict() - else: - dict_quota = network_quota - - result = {} - + dict_quota = network_quota.to_dict(computed=False) + + if not detail: + return dict_quota + + # Neutron returns quota details in dict which is in format like: + # {'resource_name': {'in_use': X, 'limit': Y, 'reserved': Z}, + # 'resource_name_2': {'in_use': X2, 'limit': Y2, 'reserved': Z2}} + # + # but Nova and Cinder returns quota in different format, like: + # {'resource_name': X, + # 'resource_name_2': X2, + # 'usage': { + # 'resource_name': Y, + # 'resource_name_2': Y2 + # }, + # 'reserved': { + # 'resource_name': Z, + # 'resource_name_2': Z2 + # }} + # + # so we need to make conversion to have data in same format from + # all of the services + result: dict[str, ty.Any] = {"usage": {}, "reservation": {}} for key, values in dict_quota.items(): if values is None: continue - - # NOTE(slaweq): Neutron returns values with key "used" but Nova for - # example returns same data with key "in_use" instead. Because of - # that we need to convert Neutron key to the same as is returned - # from Nova to make result more consistent - if isinstance(values, dict) and 'used' in values: - values['in_use'] = values.pop("used") - - result[key] = values + if isinstance(values, dict): + result[key] = values['limit'] + result["reservation"][key] = values['reserved'] + result["usage"][key] = values['used'] return result @@ -243,15 +256,20 @@ def get_parser(self, prog_name): return parser def _list_quota_compute(self, parsed_args, project_ids): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = [] for project_id in project_ids: try: project_data = compute_client.get_quota_set(project_id) + # NOTE(stephenfin): Unfortunately, Nova raises a HTTP 400 (Bad + # Request) if the project ID is invalid, even though the project + # ID is actually the resource's identifier which would normally + # lead us to expect a HTTP 404 (Not Found). except ( - sdk_exceptions.NotFoundException, + sdk_exceptions.BadRequestException, sdk_exceptions.ForbiddenException, + sdk_exceptions.NotFoundException, ) as exc: # Project not found, move on to next one LOG.warning(f"Project {project_id} not found: {exc}") @@ -273,7 +291,7 @@ def _list_quota_compute(self, parsed_args, project_ids): if default_result != project_result: result += project_result - columns = ( + columns: tuple[str, ...] = ( 'id', 'cores', 'injected_files', @@ -286,7 +304,7 @@ def _list_quota_compute(self, parsed_args, project_ids): 'server_groups', 'server_group_members', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Project ID', 'Cores', 'Injected Files', @@ -312,8 +330,8 @@ def _list_quota_volume(self, parsed_args, project_ids): try: project_data = volume_client.get_quota_set(project_id) except ( - sdk_exceptions.NotFoundException, sdk_exceptions.ForbiddenException, + sdk_exceptions.NotFoundException, ) as exc: # Project not found, move on to next one LOG.warning(f"Project {project_id} not found: {exc}") @@ -335,7 +353,7 @@ def _list_quota_volume(self, parsed_args, project_ids): if default_result != project_result: result += project_result - columns = ( + columns: tuple[str, ...] = ( 'id', 'backups', 'backup_gigabytes', @@ -344,7 +362,7 @@ def _list_quota_volume(self, parsed_args, project_ids): 'snapshots', 'volumes', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Project ID', 'Backups', 'Backup Gigabytes', @@ -390,7 +408,7 @@ def _list_quota_network(self, parsed_args, project_ids): if default_result != project_result: result += project_result - columns = ( + columns: tuple[str, ...] = ( 'id', 'floating_ips', 'networks', @@ -402,7 +420,7 @@ def _list_quota_network(self, parsed_args, project_ids): 'subnets', 'subnet_pools', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Project ID', 'Floating IPs', 'Networks', @@ -507,8 +525,8 @@ def get_parser(self, prog_name): ) for k, v, h in self._build_options_list(): parser.add_argument( - '--%s' % v, - metavar='<%s>' % v, + f'--{v}', + metavar=f'<{v}>', dest=k, type=int, help=h, @@ -565,33 +583,42 @@ def take_action(self, parsed_args): msg = _('--force cannot be used with --class or --default') raise exceptions.CommandError(msg) - compute_client = self.app.client_manager.sdk_connection.compute - volume_client = self.app.client_manager.sdk_connection.volume - compute_kwargs = {} - for k, v in COMPUTE_QUOTAS.items(): - value = getattr(parsed_args, k, None) - if value is not None: - compute_kwargs[k] = value + volume_kwargs = {} + network_kwargs = {} - if compute_kwargs and parsed_args.force is True: - compute_kwargs['force'] = parsed_args.force + if self.app.client_manager.is_compute_endpoint_enabled(): + compute_client = self.app.client_manager.compute - volume_kwargs = {} - for k, v in VOLUME_QUOTAS.items(): - value = getattr(parsed_args, k, None) - if value is not None: - if parsed_args.volume_type and k in IMPACT_VOLUME_TYPE_QUOTAS: - k = k + '_%s' % parsed_args.volume_type - volume_kwargs[k] = value + for k, v in COMPUTE_QUOTAS.items(): + value = getattr(parsed_args, k, None) + if value is not None: + compute_kwargs[k] = value + + if compute_kwargs and parsed_args.force is True: + compute_kwargs['force'] = parsed_args.force + + if self.app.client_manager.is_volume_endpoint_enabled(): + volume_client = self.app.client_manager.sdk_connection.volume + + for k, v in VOLUME_QUOTAS.items(): + value = getattr(parsed_args, k, None) + if value is not None: + if ( + parsed_args.volume_type + and k in IMPACT_VOLUME_TYPE_QUOTAS + ): + k = k + f'_{parsed_args.volume_type}' + volume_kwargs[k] = value - network_kwargs = {} if self.app.client_manager.is_network_endpoint_enabled(): + network_client = self.app.client_manager.network + for k, v in NETWORK_QUOTAS.items(): value = getattr(parsed_args, k, None) if value is not None: network_kwargs[k] = value - else: + elif self.app.client_manager.is_compute_endpoint_enabled(): for k, v in NOVA_NETWORK_QUOTAS.items(): value = getattr(parsed_args, k, None) if value is not None: @@ -632,11 +659,7 @@ def take_action(self, parsed_args): compute_client.update_quota_set(project, **compute_kwargs) if volume_kwargs: volume_client.update_quota_set(project, **volume_kwargs) - if ( - network_kwargs - and self.app.client_manager.is_network_endpoint_enabled() - ): - network_client = self.app.client_manager.network + if network_kwargs: network_client.update_quota(project, **network_kwargs) @@ -723,21 +746,32 @@ def take_action(self, parsed_args): # values if the project or class does not exist. This is expected # behavior. However, we have already checked for the presence of the # project above so it shouldn't be an issue. - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_quota_info = get_compute_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'volume'}: + + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_quota_info = get_volume_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'network'}: + + if parsed_args.service == 'network' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_network_endpoint_enabled() + ): network_quota_info = get_network_quotas( self.app, project, @@ -746,24 +780,42 @@ def take_action(self, parsed_args): ) info = {} + if parsed_args.usage: + info["reservation"] = compute_quota_info.pop("reservation", {}) + info["reservation"].update( + volume_quota_info.pop("reservation", {}) + ) + info["reservation"].update( + network_quota_info.pop("reservation", {}) + ) + + info["usage"] = compute_quota_info.pop("usage", {}) + info["usage"].update(volume_quota_info.pop("usage", {})) + info["usage"].update(network_quota_info.pop("usage", {})) + info.update(compute_quota_info) info.update(volume_quota_info) info.update(network_quota_info) - # Map the internal quota names to the external ones - # COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips, - # secgroup-rules and secgroups as dict value, so when - # neutron is enabled, quotas of these three resources - # in nova will be replaced by neutron's. - for k, v in itertools.chain( - COMPUTE_QUOTAS.items(), - NOVA_NETWORK_QUOTAS.items(), - VOLUME_QUOTAS.items(), - NETWORK_QUOTAS.items(), - ): - if not k == v and info.get(k) is not None: - info[v] = info[k] - info.pop(k) + def _normalize_names(section: dict) -> None: + # Map the internal quota names to the external ones + # COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips, + # secgroup-rules and secgroups as dict value, so when + # neutron is enabled, quotas of these three resources + # in nova will be replaced by neutron's. + for k, v in itertools.chain( + COMPUTE_QUOTAS.items(), + NOVA_NETWORK_QUOTAS.items(), + VOLUME_QUOTAS.items(), + NETWORK_QUOTAS.items(), + ): + if not k == v and section.get(k) is not None: + section[v] = section.pop(k) + + _normalize_names(info) + if parsed_args.usage: + _normalize_names(info["reservation"]) + _normalize_names(info["usage"]) # Remove the 'id' field since it's not very useful if 'id' in info: @@ -788,11 +840,11 @@ def take_action(self, parsed_args): if k not in ('usage', 'reservation') ] - columns = ( + columns: tuple[str, ...] = ( 'resource', 'limit', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Resource', 'Limit', ) @@ -870,12 +922,18 @@ def take_action(self, parsed_args): ) # compute quotas - if parsed_args.service in {'all', 'compute'}: - compute_client = self.app.client_manager.sdk_connection.compute + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): + compute_client = self.app.client_manager.compute compute_client.revert_quota_set(project.id) # volume quotas - if parsed_args.service in {'all', 'volume'}: + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_client = self.app.client_manager.sdk_connection.volume volume_client.revert_quota_set(project.id) diff --git a/openstackclient/common/versions.py b/openstackclient/common/versions.py index 662233875f..dfd84e059d 100644 --- a/openstackclient/common/versions.py +++ b/openstackclient/common/versions.py @@ -14,8 +14,7 @@ """Versions Action Implementation""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index 23979ec7f3..73ce2f87d5 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# import logging @@ -21,13 +20,11 @@ LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '2.1' API_VERSION_OPTION = 'os_compute_api_version' API_NAME = 'compute' -API_VERSIONS = { - '2': 'openstack.connection.Connection', - '2.1': 'openstack.connection.Connection', -} +API_VERSIONS = ('2', '2.1') def make_client(instance): @@ -49,3 +46,8 @@ def build_option_parser(parser): % DEFAULT_API_VERSION, ) return parser + + +def check_api_version(check_version): + # SDK supports auto-negotiation for us: always return True + return True diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 8f897ca8af..71b68d4c61 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -56,7 +56,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute # doing this since openstacksdk has decided not to support this # deprecated command @@ -95,7 +95,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for id in parsed_args.id: try: @@ -114,7 +114,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.id) - msg = _("%(result)s of %(total)s agents failed " "to delete.") % { + msg = _("%(result)s of %(total)s agents failed to delete.") % { 'result': result, 'total': total, } @@ -139,7 +139,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute columns = ( "Agent ID", "Hypervisor", @@ -194,7 +194,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute response = compute_client.get('/os-agents', microversion='2.1') sdk_exceptions.raise_from_response(response) diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 967eec02cd..f8c3d9677e 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -17,21 +17,23 @@ """Compute v2 Aggregate action implementations""" import logging +import typing as ty +from cliff import columns 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 from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -_aggregate_formatters = { +_aggregate_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = { 'Hosts': format_columns.ListColumn, 'Metadata': format_columns.DictColumn, 'hosts': format_columns.ListColumn, @@ -67,7 +69,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False @@ -110,7 +112,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute attrs = {'name': parsed_args.name} @@ -146,7 +148,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for a in parsed_args.aggregate: try: @@ -168,9 +170,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.aggregate) - msg = _( - "%(result)s of %(total)s aggregates failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s aggregates failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -188,44 +191,50 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregates = list(compute_client.aggregates()) + if sdk_utils.supports_microversion(compute_client, '2.41'): + column_headers: tuple[str, ...] = ("ID", "UUID") + columns: tuple[str, ...] = ("id", "uuid") + else: + column_headers = ("ID",) + columns = ("id",) + + column_headers += ( + "Name", + "Availability Zone", + ) + columns += ( + "name", + "availability_zone", + ) + if parsed_args.long: # Remove availability_zone from metadata because Nova doesn't for aggregate in aggregates: if 'availability_zone' in aggregate.metadata: aggregate.metadata.pop('availability_zone') - # This is the easiest way to change column headers - column_headers = ( - "ID", - "Name", - "Availability Zone", + + column_headers += ( "Properties", "Hosts", ) - columns = ( - "ID", - "Name", - "Availability Zone", - "Metadata", - "Hosts", - ) - else: - column_headers = columns = ( - "ID", - "Name", - "Availability Zone", + columns += ( + "metadata", + "hosts", ) - data = ( - utils.get_item_properties( - s, columns, formatters=_aggregate_formatters - ) - for s in aggregates + return ( + column_headers, + ( + utils.get_item_properties( + s, columns, formatters=_aggregate_formatters + ) + for s in aggregates + ), ) - return (column_headers, data) class RemoveAggregateHost(command.ShowOne): @@ -244,7 +253,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False @@ -301,7 +310,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) @@ -314,7 +323,7 @@ def take_action(self, parsed_args): if kwargs: compute_client.update_aggregate(aggregate.id, **kwargs) - properties = {} + properties: dict[str, ty.Any] = {} if parsed_args.no_property: # NOTE(RuiChen): "availability_zone" can not be unset from # properties. It is already excluded from show and create output. @@ -346,7 +355,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) @@ -386,7 +395,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) @@ -421,7 +430,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.81'): msg = _( @@ -430,15 +439,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) + image_client = self.app.client_manager.sdk_connection.image + aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) images = [] for img in parsed_args.image: - image = self.app.client_manager.sdk_connection.image.find_image( - img, ignore_missing=False - ) + image = image_client.find_image(img, ignore_missing=False) images.append(image.id) compute_client.aggregate_precache_images(aggregate.id, images) diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index 3059cb1000..cbcce4b09f 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -16,19 +16,18 @@ """Compute v2 Console action implementations""" from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ def _get_console_columns(item): # To maintain backwards compatibility we need to rename sdk props to # whatever OSC was using before - column_map = {} hidden_columns = ['id', 'links', 'location', 'name'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -56,7 +55,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( name_or_id=parsed_args.server, ignore_missing=False @@ -65,13 +64,15 @@ def take_action(self, parsed_args): output = compute_client.get_server_console_output( server.id, length=parsed_args.lines ) - data = None + data: str | None = None if output: data = output.get('output', None) if data and data[-1] != '\n': data += '\n' - self.app.stdout.write(data) + + if data: + self.app.stdout.write(data) class ShowConsoleURL(command.ShowOne): @@ -107,6 +108,13 @@ def get_parser(self, prog_name): const='spice-html5', help=_("Show SPICE console URL"), ) + type_group.add_argument( + '--spice-direct', + dest='url_type', + action='store_const', + const='spice-direct', + help=_("Show SPICE direct protocol native console URL"), + ) type_group.add_argument( '--rdp', dest='url_type', @@ -131,7 +139,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) diff --git a/openstackclient/compute/v2/console_connection.py b/openstackclient/compute/v2/console_connection.py new file mode 100644 index 0000000000..97eb1a80e1 --- /dev/null +++ b/openstackclient/compute/v2/console_connection.py @@ -0,0 +1,48 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Compute v2 Console auth token implementations.""" + +from osc_lib import utils + +from openstackclient import command +from openstackclient.i18n import _ + + +def _get_console_connection_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['id', 'location', 'name'] + return utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class ShowConsoleConnectionInformation(command.ShowOne): + _description = _("Show server's remote console connection information") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'token', + metavar='', + help=_("Nova console token to lookup"), + ) + return parser + + def take_action(self, parsed_args): + compute_client = self.app.client_manager.compute + data = compute_client.validate_console_auth_token(parsed_args.token) + display_columns, columns = _get_console_connection_columns(data) + data = utils.get_dict_properties(data, columns) + + return (display_columns, data) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index e4df628856..de3a710298 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -21,10 +21,10 @@ 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 from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -149,19 +149,32 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity if parsed_args.project and parsed_args.public: msg = _("--project is only allowed with --private") raise exceptions.CommandError(msg) + flavor_id = parsed_args.id + if parsed_args.id == 'auto': + # novaclient aliased 'auto' to mean "generate a UUID for me": we + # do the same to avoid breaking existing users + flavor_id = None + + msg = _( + "Passing '--id auto' is deprecated. Nova will automatically " + "assign a UUID-like ID if no ID is provided. Omit the '--id' " + "parameter instead." + ) + self.log.warning(msg) + args = { 'name': parsed_args.name, 'ram': parsed_args.ram, 'vcpus': parsed_args.vcpus, 'disk': parsed_args.disk, - 'id': parsed_args.id, + 'id': flavor_id, 'ephemeral': parsed_args.ephemeral, 'swap': parsed_args.swap, 'rxtx_factor': parsed_args.rxtx_factor, @@ -190,8 +203,7 @@ def take_action(self, parsed_args): compute_client.flavor_add_tenant_access(flavor.id, project_id) except Exception as e: msg = _( - "Failed to add project %(project)s access to " - "flavor: %(e)s" + "Failed to add project %(project)s access to flavor: %(e)s" ) LOG.error(msg, {'project': parsed_args.project, 'e': e}) if parsed_args.properties: @@ -224,7 +236,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for f in parsed_args.flavor: try: @@ -242,7 +254,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.flavor) - msg = _("%(result)s of %(total)s flavors failed " "to delete.") % { + msg = _("%(result)s of %(total)s flavors failed to delete.") % { 'result': result, 'total': total, } @@ -297,7 +309,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute # is_public is ternary - None means give all flavors, # True is public only and False is private only # By default Nova assumes True and gives admins public flavors @@ -330,7 +342,7 @@ def take_action(self, parsed_args): if parsed_args.long and not f.extra_specs: compute_client.fetch_flavor_extra_specs(f) - columns = ( + columns: tuple[str, ...] = ( "id", "name", "ram", @@ -346,7 +358,7 @@ def take_action(self, parsed_args): "extra_specs", ) - column_headers = ( + column_headers: tuple[str, ...] = ( "ID", "Name", "RAM", @@ -404,9 +416,7 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar='', - help=_( - 'Set flavor access to project (name or ID) ' '(admin only)' - ), + help=_('Set flavor access to project (name or ID) (admin only)'), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( @@ -421,7 +431,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity try: @@ -483,7 +493,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -500,7 +510,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute flavor = compute_client.find_flavor( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) @@ -560,8 +570,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Remove flavor access from project (name or ID) ' - '(admin only)' + 'Remove flavor access from project (name or ID) (admin only)' ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -569,7 +578,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity try: @@ -577,7 +586,7 @@ def take_action(self, parsed_args): parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) except sdk_exceptions.ResourceNotFound as e: - raise exceptions.CommandError(_(e.message)) + raise exceptions.CommandError(e.message) result = 0 if parsed_args.properties: @@ -612,5 +621,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index 27e8e2705a..58023676d4 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -16,9 +16,9 @@ """Host action implementations""" from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -35,7 +35,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute self.log.warning( "API has been deprecated; " @@ -83,7 +83,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute self.log.warning( "API has been deprecated; " @@ -121,7 +121,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute self.log.warning( "API has been deprecated; " diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index fffeb35216..9e1b265b19 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -21,10 +21,10 @@ from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ @@ -90,7 +90,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute list_opts = {} @@ -119,14 +119,20 @@ def take_action(self, parsed_args): if parsed_args.matching: list_opts['hypervisor_hostname_pattern'] = parsed_args.matching - column_headers = ( + column_headers: tuple[str, ...] = ( "ID", "Hypervisor Hostname", "Hypervisor Type", "Host IP", "State", ) - columns = ('id', 'name', 'hypervisor_type', 'host_ip', 'state') + columns: tuple[str, ...] = ( + 'id', + 'name', + 'hypervisor_type', + 'host_ip', + 'state', + ) if parsed_args.long: if not sdk_utils.supports_microversion(compute_client, '2.88'): @@ -164,10 +170,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute - hypervisor = compute_client.find_hypervisor( - parsed_args.hypervisor, ignore_missing=False - ).copy() + compute_client = self.app.client_manager.compute + + hypervisor_id = compute_client.find_hypervisor( + parsed_args.hypervisor, ignore_missing=False, details=False + ).id + hypervisor = compute_client.get_hypervisor(hypervisor_id).copy() # Some of the properties in the hypervisor object need to be processed # before they get reported to the user. We spend this section diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py index 63ca71fdb5..d151b41614 100644 --- a/openstackclient/compute/v2/hypervisor_stats.py +++ b/openstackclient/compute/v2/hypervisor_stats.py @@ -13,9 +13,9 @@ """Hypervisor Stats action implementations""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -42,7 +42,7 @@ class ShowHypervisorStats(command.ShowOne): def take_action(self, parsed_args): # The command is deprecated since it is being dropped in Nova. self.log.warning(_("This command is deprecated.")) - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute # We do API request directly cause this deprecated method is not and # will not be supported by OpenStackSDK. response = compute_client.get( diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 8e43899539..b7744698b4 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -22,10 +22,10 @@ from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives import serialization from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -61,14 +61,13 @@ def _generate_keypair(): def _get_keypair_columns(item, hide_pub_key=False, hide_priv_key=False): # To maintain backwards compatibility we need to rename sdk props to # whatever OSC was using before - column_map = {} hidden_columns = ['links', 'location'] if hide_pub_key: hidden_columns.append('public_key') if hide_priv_key: hidden_columns.append('private_key') return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -121,13 +120,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {'name': parsed_args.name} if parsed_args.public_key: - generated_keypair = None try: with open(os.path.expanduser(parsed_args.public_key)) as p: public_key = p.read() @@ -230,7 +228,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {} @@ -258,13 +256,13 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _("Failed to delete key with name " "'%(name)s': %(e)s"), + _("Failed to delete key with name '%(name)s': %(e)s"), {'name': n, 'e': e}, ) if result > 0: total = len(parsed_args.name) - msg = _("%(result)s of %(total)s keys failed " "to delete.") % { + msg = _("%(result)s of %(total)s keys failed to delete.") % { 'result': result, 'total': total, } @@ -300,8 +298,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity + identity_sdk_client = self.app.client_manager.sdk_connection.identity kwargs = {} @@ -347,11 +346,17 @@ def take_action(self, parsed_args): parsed_args.project, parsed_args.project_domain, ).id - users = identity_client.users.list(tenant_id=project) + assignments = identity_sdk_client.role_assignments( + scope_project_id=project + ) + user_ids = set() + for assignment in assignments: + if assignment.user: + user_ids.add(assignment.user['id']) data = [] - for user in users: - kwargs['user_id'] = user.id + for user_id in user_ids: + kwargs['user_id'] = user_id data.extend(compute_client.keypairs(**kwargs)) elif parsed_args.user: if not sdk_utils.supports_microversion(compute_client, '2.10'): @@ -372,7 +377,7 @@ def take_action(self, parsed_args): else: data = compute_client.keypairs(**kwargs) - columns = ("Name", "Fingerprint") + columns: tuple[str, ...] = ("Name", "Fingerprint") if sdk_utils.supports_microversion(compute_client, '2.2'): columns += ("Type",) @@ -411,7 +416,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {} diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 78ab16d72b..1eee828d9b 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -29,11 +29,12 @@ 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 from osc_lib import exceptions from osc_lib import utils from openstackclient.api import compute_v2 +from openstackclient import command +from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -44,7 +45,7 @@ IMAGE_STRING_FOR_BFV = 'N/A (booted from volume)' -class PowerStateColumn(cliff_columns.FormattableColumn): +class PowerStateColumn(cliff_columns.FormattableColumn[int]): """Generate a formatted string of a server's power state.""" power_states = [ @@ -65,7 +66,7 @@ def human_readable(self): return 'N/A' -class AddressesColumn(cliff_columns.FormattableColumn): +class AddressesColumn(cliff_columns.FormattableColumn[ty.Any]): """Generate a formatted string of a server's addresses.""" def human_readable(self): @@ -86,7 +87,7 @@ def machine_readable(self): } -class HostColumn(cliff_columns.FormattableColumn): +class HostColumn(cliff_columns.FormattableColumn[str | None]): """Generate a formatted string of a hostname.""" def human_readable(self): @@ -183,8 +184,17 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'updated_at': 'updated', 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', - 'pinned_availability_zone': 'pinned_availability_zone', } + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if sdk_utils.supports_microversion(compute_client, '2.96'): + column_map['pinned_availability_zone'] = 'pinned_availability_zone' + + # NOTE(ratailor): microversion 2.100 introduces + # scheduler_hints support + if sdk_utils.supports_microversion(compute_client, '2.100'): + column_map['scheduler_hints'] = 'scheduler_hints' + # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates ignored_columns = { @@ -204,7 +214,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'min_count', 'networks', 'personality', - 'scheduler_hints', # aliases 'volumes', # unnecessary @@ -235,6 +244,16 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info = data + # NOTE(dviroel): microversion 2.100 is now retrieving scheduler_hints + # content from request_spec on detailed responses + if not sdk_utils.supports_microversion(compute_client, '2.100'): + info.pop('scheduler_hints', None) + + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if not sdk_utils.supports_microversion(compute_client, '2.96'): + info.pop('pinned_availability_zone', None) + # Convert the image blob to a name image_info = info.get('image', {}) if image_info and any(image_info.values()): @@ -321,49 +340,13 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info['OS-EXT-STS:power_state'] ) - return info - - -def bool_from_str(value, strict=False): - true_strings = ('1', 't', 'true', 'on', 'y', 'yes') - false_strings = ('0', 'f', 'false', 'off', 'n', 'no') - - if isinstance(value, bool): - return value - - lowered = value.strip().lower() - if lowered in true_strings: - return True - elif lowered in false_strings or not strict: - return False - - msg = _( - "Unrecognized value '%(value)s'; acceptable values are: %(valid)s" - ) % { - 'value': value, - 'valid': ', '.join( - f"'{s}'" for s in sorted(true_strings + false_strings) - ), - } - raise ValueError(msg) - - -def boolenv(*vars, default=False): - """Search for the first defined of possibly many bool-like env vars. - - Returns the first environment variable defined in vars, or returns the - default. + if sdk_utils.supports_microversion(compute_client, '2.100'): + if 'scheduler_hints' in info: + info['scheduler_hints'] = format_columns.DictListColumn( + info.pop('scheduler_hints', {}), + ) - :param vars: Arbitrary strings to search for. Case sensitive. - :param default: The default to return if no value found. - :returns: A boolean corresponding to the value found, else the default if - no value found. - """ - for v in vars: - value = os.environ.get(v, None) - if value: - return bool_from_str(value) - return default + return info class AddFixedIP(command.ShowOne): @@ -399,7 +382,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -430,7 +413,7 @@ def take_action(self, parsed_args): interface = compute_client.create_server_interface(server.id, **kwargs) - columns = ( + columns: tuple[str, ...] = ( 'port_id', 'server_id', 'net_id', @@ -438,7 +421,7 @@ def take_action(self, parsed_args): 'port_state', 'fixed_ips', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Port ID', 'Server ID', 'Network ID', @@ -493,7 +476,7 @@ def update_parser_common(self, parser): return parser def take_action_network(self, client, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute attrs = {} obj = client.find_ip( @@ -586,7 +569,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -640,7 +623,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -670,7 +653,7 @@ def take_action(self, parsed_args): class AddServerSecurityGroup(command.Command): - _description = _("Add security group to server") + _description = _("Add security group(s) to server") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -680,28 +663,62 @@ def get_parser(self, prog_name): help=_('Server (name or ID)'), ) parser.add_argument( - 'group', - metavar='', - help=_('Security group to add (name or ID)'), + 'security_groups', + metavar='', + nargs='+', + help=_( + 'Security group(s) to add to the server (name or ID) ' + '(repeat option to add multiple groups)' + ), ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) if self.app.client_manager.is_network_endpoint_enabled(): # the server handles both names and IDs for neutron SGs, so just - # pass things through - security_group = parsed_args.group + # pass things through if using neutron + security_groups = parsed_args.security_groups else: - # however, if using nova-network then it needs a name, not an ID - security_group = compute_v2.find_security_group( - compute_client, parsed_args.group - )['name'] - compute_client.add_security_group_to_server(server, security_group) + # however, if using nova-network then it needs names, not IDs + security_groups = [] + for security_group in parsed_args.security_groups: + security_groups.append( + compute_v2.find_security_group( + compute_client, security_group + )['name'] + ) + + errors = 0 + for security_group in security_groups: + try: + compute_client.add_security_group_to_server( + server, + {'name': security_group}, + ) + except sdk_exceptions.HttpException as e: + errors += 1 + LOG.error( + _( + "Failed to add security group with name or ID " + "'%(security_group)s' to server '%(server)s': %(e)s" + ), + { + 'security_group': security_group, + 'server': server.id, + 'e': e, + }, + ) + + if errors > 0: + msg = _( + "%(errors)d of %(total)d security groups were not added." + ) % {'errors': errors, 'total': len(security_groups)} + raise exceptions.CommandError(msg) class AddServerVolume(command.ShowOne): @@ -759,7 +776,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.sdk_connection.volume server = compute_client.find_server( @@ -808,8 +825,13 @@ def take_action(self, parsed_args): **kwargs, ) - columns = ('id', 'server id', 'volume id', 'device') - column_headers = ('ID', 'Server ID', 'Volume ID', 'Device') + columns: tuple[str, ...] = ('id', 'server id', 'volume id', 'device') + column_headers: tuple[str, ...] = ( + 'ID', + 'Server ID', + 'Volume ID', + 'Device', + ) if sdk_utils.supports_microversion(compute_client, '2.49'): columns += ('tag',) column_headers += ('Tag',) @@ -993,7 +1015,6 @@ def __call__(self, parser, namespace, values, option_string=None): class BDMAction(parseractions.MultiKeyValueAction): def __init__(self, option_strings, dest, **kwargs): - required_keys = [] optional_keys = [ 'uuid', 'source_type', @@ -1011,7 +1032,7 @@ def __init__(self, option_strings, dest, **kwargs): super().__init__( option_strings, dest, - required_keys=required_keys, + required_keys=[], optional_keys=optional_keys, **kwargs, ) @@ -1323,13 +1344,26 @@ def get_parser(self, prog_name): 'This option requires cloud support.' ), ) - parser.add_argument( + secgroups = parser.add_mutually_exclusive_group() + secgroups.add_argument( + '--no-security-group', + dest='security_groups', + action='store_const', + const=[], + help=_( + 'Do not associate a security group with ports attached to ' + 'this server. This does not affect the security groups ' + 'associated with pre-existing ports.' + ), + ) + secgroups.add_argument( '--security-group', metavar='', action='append', - default=[], + dest='security_groups', help=_( - 'Security group to assign to this server (name or ID) ' + 'Security group to associate with ports attached to this ' + 'server (name or ID) ' '(repeat option to set multiple groups)' ), ) @@ -1355,7 +1389,7 @@ def get_parser(self, prog_name): default=[], help=_( 'File(s) to inject into image before boot ' - '(repeat option to set multiple files)' + '(repeat option to set multiple files) ' '(supported by --os-compute-api-version 2.57 or below)' ), ) @@ -1504,10 +1538,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.volume image_client = self.app.client_manager.image @@ -1520,17 +1554,6 @@ def _show_progress(progress): if not image and parsed_args.image_properties: - def emit_duplicated_warning(img): - img_uuid_list = [str(image.id) for image in img] - LOG.warning( - 'Multiple matching images: %(img_uuid_list)s\n' - 'Using image: %(chosen_one)s', - { - 'img_uuid_list': img_uuid_list, - 'chosen_one': img_uuid_list[0], - }, - ) - def _match_image(image_api, wanted_properties): image_list = image_api.images() images_matched = [] @@ -1568,13 +1591,20 @@ def _match_image(image_api, wanted_properties): images = _match_image(image_client, parsed_args.image_properties) if len(images) > 1: - emit_duplicated_warning(images, parsed_args.image_properties) + img_uuid_list = [str(image.id) for image in images] + LOG.warning( + 'Multiple matching images: %(img_uuid_list)s\n' + 'Using image: %(chosen_one)s', + { + 'img_uuid_list': img_uuid_list, + 'chosen_one': img_uuid_list[0], + }, + ) if images: image = images[0] else: msg = _( - 'No images match the property expected by ' - '--image-property' + 'No images match the property expected by --image-property' ) raise exceptions.CommandError(msg) @@ -1829,7 +1859,7 @@ def _match_image(image_api, wanted_properties): if 'delete_on_termination' in mapping: try: - value = bool_from_str( + value = envvars.bool_from_str( mapping['delete_on_termination'], strict=True, ) @@ -1858,7 +1888,7 @@ def _match_image(image_api, wanted_properties): # Default to empty list if nothing was specified and let nova # decide the default behavior. - networks: ty.Union[str, ty.List[ty.Dict[str, str]], None] = [] + networks: str | list[dict[str, str]] | None = [] if 'auto' in parsed_args.nics or 'none' in parsed_args.nics: if len(parsed_args.nics) > 1: @@ -1923,7 +1953,7 @@ def _match_image(image_api, wanted_properties): # convert from the novaclient-derived "NIC" view to the actual # "network" view - network = {} + network: dict[str, str] = {} if nic['net-id']: network['uuid'] = nic['net-id'] @@ -1932,14 +1962,14 @@ def _match_image(image_api, wanted_properties): network['port'] = nic['port-id'] if nic['v4-fixed-ip']: - network['fixed'] = nic['v4-fixed-ip'] + network['fixed_ip'] = nic['v4-fixed-ip'] elif nic['v6-fixed-ip']: - network['fixed'] = nic['v6-fixed-ip'] + network['fixed_ip'] = nic['v6-fixed-ip'] if nic.get('tag'): # tags are optional network['tag'] = nic['tag'] - networks.append(network) + networks.append(network) # type: ignore[union-attr] if not parsed_args.nics and sdk_utils.supports_microversion( compute_client, '2.37' @@ -1948,22 +1978,25 @@ def _match_image(image_api, wanted_properties): # 'auto' to maintain legacy behavior if a nic wasn't specified. networks = 'auto' - # Check security group exist and convert ID to name - security_groups = [] - if self.app.client_manager.is_network_endpoint_enabled(): - network_client = self.app.client_manager.network - for each_sg in parsed_args.security_group: - sg = network_client.find_security_group( - each_sg, ignore_missing=False - ) - # Use security group ID to avoid multiple security group have - # same name in neutron networking backend - security_groups.append({'name': sg.id}) - else: - # Handle nova-network case - for each_sg in parsed_args.security_group: - sg = compute_v2.find_security_group(compute_client, each_sg) - security_groups.append({'name': sg['name']}) + # Check security group(s) exist and convert ID to name + security_groups = None + if parsed_args.security_groups is not None: + security_groups = [] + if self.app.client_manager.is_network_endpoint_enabled(): + network_client = self.app.client_manager.network + for security_group in parsed_args.security_groups: + sg = network_client.find_security_group( + security_group, ignore_missing=False + ) + # Use security group ID to avoid multiple security group + # have same name in neutron networking backend + security_groups.append({'name': sg.id}) + else: # nova-network + for security_group in parsed_args.security_groups: + sg = compute_v2.find_security_group( + compute_client, security_group + ) + security_groups.append({'name': sg['name']}) hints = {} for key, values in parsed_args.hints.items(): @@ -2026,7 +2059,7 @@ def _match_image(image_api, wanted_properties): if files: kwargs['personality'] = files - if security_groups: + if security_groups is not None: kwargs['security_groups'] = security_groups if block_device_mapping_v2: @@ -2111,13 +2144,11 @@ def _match_image(image_api, wanted_properties): f.close() if parsed_args.wait: - if utils.wait_for_status( + if not utils.wait_for_status( compute_client.get_server, server.id, callback=_show_progress, ): - self.app.stdout.write('\n') - else: msg = _('Error creating server: %s') % parsed_args.server_name raise exceptions.CommandError(msg) @@ -2148,9 +2179,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for name_or_id in parsed_args.server: - server = compute_client.find_server(name_or_id) + server = compute_client.find_server( + name_or_id, ignore_missing=False + ) server.trigger_crash_dump(compute_client) @@ -2173,7 +2206,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Delete server(s) in another project by name (admin only)' '(can be specified using the ALL_PROJECTS envvar)' @@ -2189,28 +2222,53 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute + + deleted_servers = [] for server in parsed_args.server: - server_obj = compute_client.find_server( - server, - ignore_missing=False, - all_projects=parsed_args.all_projects, - ) + try: + server_obj = compute_client.find_server( + server, + ignore_missing=False, + all_projects=parsed_args.all_projects, + ) - compute_client.delete_server(server_obj, force=parsed_args.force) + compute_client.delete_server( + server_obj, force=parsed_args.force + ) + deleted_servers.append(server_obj) + except Exception as e: + LOG.error( + _( + "Failed to delete server with " + "name or ID '%(server)s': %(e)s" + ), + {'server': server, 'e': e}, + ) - if parsed_args.wait: + if parsed_args.wait: + for server_obj in deleted_servers: try: compute_client.wait_for_delete( server_obj, callback=_show_progress ) except sdk_exceptions.ResourceTimeout: msg = _('Error deleting server: %s') % server_obj.id + deleted_servers.remove(server_obj) raise exceptions.CommandError(msg) + fails = len(parsed_args.server) - len(deleted_servers) + if fails > 0: + total = len(parsed_args.server) + msg = _("%(fails)s of %(total)s servers failed to delete.") % { + 'fails': fails, + 'total': total, + } + raise exceptions.CommandError(msg) + class PercentAction(argparse.Action): def __init__( @@ -2339,7 +2397,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Include all projects (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' @@ -2377,8 +2435,7 @@ def get_parser(self, prog_name): parser.add_argument( '--key-name', help=_( - 'Search by keypair name ' - '(admin only before microversion 2.83)' + 'Search by keypair name (admin only before microversion 2.83)' ), ) config_drive_group = parser.add_mutually_exclusive_group() @@ -2601,7 +2658,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity image_client = self.app.client_manager.image @@ -2648,7 +2705,7 @@ def take_action(self, parsed_args): 'status': parsed_args.status, 'flavor': flavor_id, 'image': image_id, - 'host': parsed_args.host, + 'compute_host': parsed_args.host, 'project_id': project_id, 'all_projects': parsed_args.all_projects, 'user_id': user_id, @@ -2757,12 +2814,12 @@ def take_action(self, parsed_args): msg % search_opts['changes-since'] ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'status', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', @@ -2823,16 +2880,25 @@ def take_action(self, parsed_args): if parsed_args.long: columns += ( 'availability_zone', - 'pinned_availability_zone', 'hypervisor_hostname', 'metadata', ) column_headers += ( 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', ) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) + + if sdk_utils.supports_microversion(compute_client, '2.100'): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) + + if parsed_args.all_projects: + columns += ('project_id',) + column_headers += ('Project ID',) # support for additional columns if parsed_args.columns: @@ -2866,16 +2932,26 @@ def take_action(self, parsed_args): column_headers += ('Availability Zone',) if c in ( 'pinned_availability_zone', - "Pinned Availability Zone", + 'Pinned Availability Zone', ): - columns += ('Pinned Availability Zone',) - column_headers += ('Pinned Availability Zone',) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) if c in ('Host', "host"): columns += ('hypervisor_hostname',) column_headers += ('Host',) if c in ('Properties', "properties"): columns += ('Metadata',) column_headers += ('Properties',) + if c in ( + 'scheduler_hints', + "Scheduler Hints", + ): + if sdk_utils.supports_microversion( + compute_client, '2.100' + ): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) # remove duplicates column_headers = tuple(dict.fromkeys(column_headers)) @@ -2915,10 +2991,10 @@ def take_action(self, parsed_args): for image_id in image_ids: try: images[image_id] = image_client.get_image(image_id) - except Exception: + except Exception: # noqa: S110 # retrieving image names is not crucial, so we swallow # any exceptions - pass # nosec: B110 + pass else: try: # some deployments can have *loads* of images so we only @@ -2936,10 +3012,10 @@ def take_action(self, parsed_args): ) for i in images_list: images[i.id] = i - except Exception: + except Exception: # noqa: S110 # retrieving image names is not crucial, so we swallow any # exceptions - pass # nosec: B110 + pass # create a dict that maps flavor_id to flavor object, which is used # to display the "Flavor Name" column. Note that 'flavor.id' is not @@ -2955,19 +3031,19 @@ def take_action(self, parsed_args): flavors[f_id] = compute_client.find_flavor( f_id, ignore_missing=False ) - except Exception: + except Exception: # noqa: S110 # retrieving flavor names is not crucial, so we swallow # any exceptions - pass # nosec: B110 + pass else: try: flavors_list = compute_client.flavors(is_public=None) for i in flavors_list: flavors[i.id] = i - except Exception: + except Exception: # noqa: S110 # retrieving flavor names is not crucial, so we swallow any # exceptions - pass # nosec: B110 + pass # Populate image_name, image_id, flavor_name and flavor_id attributes # of server objects so that we can display those columns. @@ -2978,7 +3054,7 @@ def take_action(self, parsed_args): # infrastructure failure situations. # For those servers with partial constructs we just skip the # processing of the image and flavor information. - if not hasattr(s, 'image') or not hasattr(s, 'flavor'): + if getattr(s, 'status') == 'UNKNOWN': continue if 'id' in s.image and s.image.id is not None: @@ -3042,6 +3118,7 @@ def take_action(self, parsed_args): 'metadata': format_columns.DictColumn, 'security_groups_name': format_columns.ListColumn, 'hypervisor_hostname': HostColumn, + 'scheduler_hints': format_columns.DictListColumn, }, ) for s in data @@ -3077,7 +3154,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} if parsed_args.reason: @@ -3175,7 +3252,7 @@ def get_parser(self, prog_name): action='store_true', default=None, help=_( - 'Allow disk over-commit on the destination host' + 'Allow disk over-commit on the destination host ' '(supported with --os-compute-api-version 2.24 or below)' ), ) @@ -3184,7 +3261,7 @@ def get_parser(self, prog_name): dest='disk_overcommit', action='store_false', help=_( - 'Do not over-commit disk on the destination host (default)' + 'Do not over-commit disk on the destination host (default) ' '(supported with --os-compute-api-version 2.24 or below)' ), ) @@ -3198,10 +3275,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -3304,7 +3381,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -3350,10 +3427,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server_id = compute_client.find_server( parsed_args.server, ignore_missing=False, @@ -3387,7 +3464,7 @@ def get_parser(self, prog_name): '--image', metavar='', help=_( - 'Recreate server from the specified image (name or ID).' + 'Recreate server from the specified image (name or ID). ' 'Defaults to the currently used one.' ), ) @@ -3555,10 +3632,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image server = compute_client.find_server( @@ -3587,6 +3664,9 @@ def _show_progress(progress): if parsed_args.name is not None: kwargs['name'] = parsed_args.name + if parsed_args.password is not None: + kwargs['admin_password'] = parsed_args.password + if parsed_args.preserve_ephemeral is not None: kwargs['preserve_ephemeral'] = parsed_args.preserve_ephemeral @@ -3725,9 +3805,7 @@ def _show_progress(progress): msg = _("The server status is not ACTIVE, SHUTOFF or ERROR.") raise exceptions.CommandError(msg) - server = compute_client.rebuild_server( - server, image, admin_password=parsed_args.password, **kwargs - ) + server = compute_client.rebuild_server(server, image, **kwargs) if parsed_args.wait: if utils.wait_for_status( @@ -3815,10 +3893,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image if parsed_args.host: @@ -3839,7 +3917,7 @@ def _show_progress(progress): kwargs = { 'host': parsed_args.host, - 'password': parsed_args.password, + 'admin_pass': parsed_args.password, } if not sdk_utils.supports_microversion(compute_client, '2.14'): @@ -3851,9 +3929,15 @@ def _show_progress(progress): compute_client.evacuate_server(server, **kwargs) if parsed_args.wait: + orig_status = server.status + success = ['ACTIVE'] + if orig_status == 'SHUTOFF': + success.append('SHUTOFF') + if utils.wait_for_status( compute_client.get_server, server.id, + success_status=success, callback=_show_progress, ): self.app.stdout.write(_('Complete\n')) @@ -3883,7 +3967,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -3912,14 +3996,12 @@ def update_parser_common(self, parser): return parser def take_action_network(self, client, parsed_args): - attrs = {} obj = client.find_ip( parsed_args.ip_address, ignore_missing=False, ) - attrs['port_id'] = None - client.update_ip(obj, **attrs) + client.update_ip(obj, port_id=None) def take_action_compute(self, client, parsed_args): server = client.find_server(parsed_args.server, ignore_missing=False) @@ -3944,7 +4026,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -3983,7 +4065,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -4016,14 +4098,18 @@ def get_parser(self, prog_name): help=_('Server (name or ID)'), ) parser.add_argument( - 'group', - metavar='', - help=_('Security group to remove (name or ID)'), + 'security_groups', + metavar='', + nargs='+', + help=_( + 'Security group(s) to remove from server (name or ID) ' + '(repeat option to remove multiple groups)' + ), ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -4031,15 +4117,43 @@ def take_action(self, parsed_args): if self.app.client_manager.is_network_endpoint_enabled(): # the server handles both names and IDs for neutron SGs, so just # pass things through - security_group = parsed_args.group + security_groups = parsed_args.security_groups else: - # however, if using nova-network then it needs a name, not an ID - security_group = compute_v2.find_security_group( - compute_client, parsed_args.group - )['name'] - compute_client.remove_security_group_from_server( - server, security_group - ) + # however, if using nova-network then it needs names, not IDs + security_groups = [] + for security_group in parsed_args.security_groups: + security_groups.append( + compute_v2.find_security_group( + compute_client, security_group + )['name'] + ) + + errors = 0 + for security_group in security_groups: + try: + compute_client.remove_security_group_from_server( + server, + {'name': security_group}, + ) + except sdk_exceptions.HttpException as e: + errors += 1 + LOG.error( + _( + "Failed to remove security group with name or ID " + "'%(security_group)s' from server '%(server)s': %(e)s" + ), + { + 'security_group': security_group, + 'server': server.id, + 'e': e, + }, + ) + + if errors > 0: + msg = _( + "%(errors)d of %(total)d security groups were not removed." + ) % {'errors': errors, 'total': len(security_groups)} + raise exceptions.CommandError(msg) class RemoveServerVolume(command.Command): @@ -4065,7 +4179,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.sdk_connection.volume server = compute_client.find_server( @@ -4118,7 +4232,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image image_ref = None @@ -4172,7 +4286,7 @@ def get_parser(self, prog_name): '--revert', action="store_true", help=_( - '**Deprecated** Restore server state before resize' + '**Deprecated** Restore server state before resize. ' "Replaced by the 'openstack server resize revert' and " "'openstack server migration revert' commands" ), @@ -4187,10 +4301,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4252,7 +4366,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4300,7 +4414,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4344,7 +4458,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4367,7 +4481,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4395,8 +4509,7 @@ def get_parser(self, prog_name): password_group.add_argument( '--password', help=_( - 'Set the server password. ' - 'This option requires cloud support.' + 'Set the server password. This option requires cloud support.' ), ) password_group.add_argument( @@ -4424,15 +4537,27 @@ def get_parser(self, prog_name): '(repeat option to set multiple properties)' ), ) + parser.add_argument( + '--auto-approve', + action='store_true', + help=_( + "Allow server state override without asking for confirmation" + ), + ) parser.add_argument( '--state', metavar='', choices=['active', 'error'], help=_( - 'New server state ' - '**WARNING** This can result in instances that are no longer ' - 'usable and should be used with caution ' - '(admin only)' + 'New server state.' + '**WARNING** Resetting the state is intended to work around ' + 'servers stuck in an intermediate state, such as deleting. ' + 'If the server is in an error state then this is almost ' + 'never the correct command to run and you should prefer hard ' + 'reboot where possible. In particular, if the server is in ' + 'an error state due to a move operation, setting the state ' + 'can result in instances that are no longer usable. Proceed ' + 'with caution. (admin only)' ), ) parser.add_argument( @@ -4467,8 +4592,22 @@ def get_parser(self, prog_name): ) return parser + @staticmethod + def ask_user_yesno(msg): + """Ask user Y/N question + + :param str msg: question text + :return bool: User choice + """ + while True: + answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n')) + if answer in ('y', 'Y', 'yes'): + return True + elif answer in ('n', 'N', 'no'): + return False + def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4517,6 +4656,17 @@ def take_action(self, parsed_args): ) if parsed_args.state: + if not parsed_args.auto_approve: + if not self.ask_user_yesno( + _( + "Resetting the server state can make it much harder " + "to recover a server from an error state. If the " + "server is in error status due to a failed move " + "operation then this is likely not the correct " + "approach to fix the problem. Do you wish to continue?" + ) + ): + return compute_client.reset_server_state(server, state=parsed_args.state) if parsed_args.root_password: @@ -4583,10 +4733,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server_ids = [] for server in parsed_args.servers: @@ -4681,7 +4831,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image server = compute_client.find_server( @@ -4811,7 +4961,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -4871,7 +5021,7 @@ def take_action(self, parsed_args): LOG.debug(f"ssh command: {cmd}") # we intentionally pass through user-provided arguments and run this in # the user's shell - os.system(cmd) # nosec: B605 + os.system(cmd) # noqa: S605 class StartServer(command.Command): @@ -4888,7 +5038,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Start server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' @@ -4897,7 +5047,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4923,16 +5073,16 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( - 'Stop server(s) in another project by name (admin only)' + 'Stop server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' ), ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4957,7 +5107,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4980,7 +5130,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -5003,7 +5153,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -5025,7 +5175,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -5092,7 +5242,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -5180,10 +5330,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} if parsed_args.availability_zone: diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index 59bd5540e8..bb06f761c9 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -17,10 +17,10 @@ import importlib -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -68,10 +68,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stderr.write('\rProgress: %s' % progress) + self.app.stderr.write(f'\rProgress: {progress}') self.app.stderr.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 1fc4672103..0e1e2b4632 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -22,10 +22,10 @@ import iso8601 from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ @@ -65,17 +65,11 @@ class ServerActionEventColumn(columns.FormattableColumn): """ def _format_event(self, event): - column_map = {} hidden_columns = ['id', 'name', 'location'] _, columns = utils.get_osc_show_columns_for_sdk_resource( - event, - column_map, - hidden_columns, - ) - data = utils.get_item_properties( - event, - columns, + event, {}, hidden_columns ) + data = utils.get_item_properties(event, columns) return dict(zip(columns, data)) def human_readable(self): @@ -88,17 +82,14 @@ def machine_readable(self): def _get_server_event_columns(item, client): - column_map = {} - hidden_columns = ['name', 'server_id', 'links', 'location'] + hidden_columns = ['name', 'server_id', 'links', 'location', 'finish_time'] if not sdk_utils.supports_microversion(client, '2.58'): # updated_at was introduced in 2.58 hidden_columns.append('updated_at') return utils.get_osc_show_columns_for_sdk_resource( - item, - column_map, - hidden_columns, + item, {}, hidden_columns ) @@ -148,7 +139,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} @@ -220,13 +211,13 @@ def take_action(self, parsed_args): data = compute_client.server_actions(server_id, **kwargs) - columns = ( + columns: tuple[str, ...] = ( 'request_id', 'server_id', 'action', 'start_time', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Request ID', 'Server ID', 'Action', @@ -275,7 +266,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute try: server_id = compute_client.find_server( diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index c4fe666e1b..b74c25626b 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -16,21 +16,23 @@ """Compute v2 Server Group action implementations""" import logging +import typing as ty +from cliff import columns 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 from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -_formatters = { +_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = { 'member_ids': format_columns.ListColumn, 'policies': format_columns.ListColumn, 'rules': format_columns.DictColumn, @@ -93,7 +95,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if parsed_args.policy in ('soft-affinity', 'soft-anti-affinity'): if not sdk_utils.supports_microversion(compute_client, '2.15'): @@ -153,7 +155,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for group in parsed_args.server_group: try: @@ -197,7 +199,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} @@ -216,12 +218,12 @@ def take_action(self, parsed_args): if sdk_utils.supports_microversion(compute_client, '2.64'): policy_key = 'Policy' - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', policy_key.lower(), ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', policy_key, @@ -264,7 +266,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute group = compute_client.find_server_group( parsed_args.server_group, ignore_missing=False ) diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index 12b45b2854..26dbb4ccc3 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -19,10 +19,10 @@ import logging from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -69,10 +69,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image server = compute_client.find_server( diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 18299156a8..f2ea68343f 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -15,10 +15,10 @@ import uuid from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -154,7 +154,7 @@ def print_migrations(self, parsed_args, compute_client, migrations): ) def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity search_opts = {} @@ -253,8 +253,17 @@ def _get_migration_by_uuid(compute_client, server_id, migration_uuid): if migration.uuid == migration_uuid: return migration else: - msg = _('In-progress live migration %s is not found for server %s.') - raise exceptions.CommandError(msg % (migration_uuid, server_id)) + msg = _( + 'In-progress live migration %(migration)s is not found for ' + 'server %(server)s' + ) + raise exceptions.CommandError( + msg + % { + 'migration': migration_uuid, + 'server': server_id, + } + ) class ShowMigration(command.ShowOne): @@ -280,7 +289,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.24'): msg = _( @@ -324,7 +333,7 @@ def take_action(self, parsed_args): ignore_missing=False, ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Server UUID', 'Status', @@ -343,7 +352,7 @@ def take_action(self, parsed_args): 'Updated At', ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'server_id', 'status', @@ -395,7 +404,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.24'): msg = _( @@ -460,7 +469,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.22'): msg = _( diff --git a/openstackclient/compute/v2/server_volume.py b/openstackclient/compute/v2/server_volume.py index b4322c0b1e..d92d137b73 100644 --- a/openstackclient/compute/v2/server_volume.py +++ b/openstackclient/compute/v2/server_volume.py @@ -15,10 +15,10 @@ """Compute v2 Server action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -34,7 +34,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, @@ -42,8 +42,8 @@ def take_action(self, parsed_args): ) volumes = compute_client.volume_attachments(server) - columns = () - column_headers = () + columns: tuple[str, ...] = () + column_headers: tuple[str, ...] = () if not sdk_utils.supports_microversion(compute_client, '2.89'): columns += ('id',) @@ -114,7 +114,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.sdk_connection.volume if parsed_args.delete_on_termination is not None: diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index b96386e033..b911835f80 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -18,10 +18,10 @@ import logging from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -52,7 +52,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for s in parsed_args.service: try: @@ -70,7 +70,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.service) msg = _( - "%(result)s of %(total)s compute services failed " "to delete." + "%(result)s of %(total)s compute services failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -108,8 +108,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute - columns = ( + compute_client = self.app.client_manager.compute + columns: tuple[str, ...] = ( "id", "binary", "host", @@ -118,7 +118,7 @@ def take_action(self, parsed_args): "state", "updated_at", ) - column_headers = ( + column_headers: tuple[str, ...] = ( "ID", "Binary", "Host", @@ -153,8 +153,7 @@ def get_parser(self, prog_name): "service", metavar="", help=_( - "Name of service (Binary name), for example " - "``nova-compute``" + "Name of service (Binary name), for example ``nova-compute``" ), ) enabled_group = parser.add_mutually_exclusive_group() @@ -222,7 +221,7 @@ def _find_service_by_host_and_binary(compute_client, host, binary): return services[0] def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if ( parsed_args.enable or not parsed_args.disable @@ -281,9 +280,7 @@ def take_action(self, parsed_args): force_down = False if force_down is not None: if not sdk_utils.supports_microversion(compute_client, '2.11'): - msg = _( - '--os-compute-api-version 2.11 or later is ' 'required' - ) + msg = _('--os-compute-api-version 2.11 or later is required') raise exceptions.CommandError(msg) try: compute_client.update_service_forced_down( @@ -299,7 +296,6 @@ def take_action(self, parsed_args): if result > 0: msg = _( - "Compute service %(service)s of host %(host)s failed to " - "set." + "Compute service %(service)s of host %(host)s failed to set." ) % {"service": parsed_args.service, "host": parsed_args.host} raise exceptions.CommandError(msg) diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 84118b9d97..3015a9e709 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -15,19 +15,21 @@ """Usage action implementations""" +from collections.abc import Collection import datetime import functools +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ # TODO(stephenfin): This exists in a couple of places and should be moved to a # common module -class ProjectColumn(cliff_columns.FormattableColumn): +class ProjectColumn(cliff_columns.FormattableColumn[str]): """Formattable column for project column. Unlike the parent FormattableColumn class, the initializer of the class @@ -53,14 +55,14 @@ def human_readable(self): return project -class CountColumn(cliff_columns.FormattableColumn): +class CountColumn(cliff_columns.FormattableColumn[Collection[ty.Any]]): def human_readable(self): return len(self._value) if self._value is not None else None -class FloatColumn(cliff_columns.FormattableColumn): +class FloatColumn(cliff_columns.FormattableColumn[float]): def human_readable(self): - return float("%.2f" % self._value) + return float(f"{self._value:.2f}") def _formatters(project_cache): @@ -115,8 +117,7 @@ def get_parser(self, prog_name): metavar="", default=None, help=_( - "Usage range start date, ex 2012-01-20" - " (default: 4 weeks ago)" + "Usage range start date, ex 2012-01-20 (default: 4 weeks ago)" ), ) parser.add_argument( @@ -136,7 +137,7 @@ def _format_project(project): else: return project - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute columns = ( "project_id", "server_usages", @@ -153,7 +154,7 @@ def _format_project(project): ) date_cli_format = "%Y-%m-%d" - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) if parsed_args.start: start = datetime.datetime.strptime( @@ -180,9 +181,9 @@ def _format_project(project): try: for p in self.app.client_manager.identity.projects.list(): project_cache[p.id] = p - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass if parsed_args.formatter == 'table' and len(usage_list) > 0: self.app.stdout.write( @@ -222,8 +223,7 @@ def get_parser(self, prog_name): metavar="", default=None, help=_( - "Usage range start date, ex 2012-01-20" - " (default: 4 weeks ago)" + "Usage range start date, ex 2012-01-20 (default: 4 weeks ago)" ), ) parser.add_argument( @@ -236,9 +236,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute date_cli_format = "%Y-%m-%d" - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) if parsed_args.start: start = datetime.datetime.strptime( diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index 5ed81e580f..707112cd05 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# import logging @@ -20,9 +19,9 @@ from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '3' API_VERSION_OPTION = 'os_identity_api_version' API_NAME = 'identity' @@ -64,8 +63,7 @@ def build_option_parser(parser): metavar='', default=utils.env('OS_IDENTITY_API_VERSION'), help=_( - 'Identity API version, default=%s ' - '(Env: OS_IDENTITY_API_VERSION)' + 'Identity API version, default=%s (Env: OS_IDENTITY_API_VERSION)' ) % DEFAULT_API_VERSION, ) diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 4e5f083de2..0684764706 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -34,7 +34,7 @@ def find_service_in_list(service_list, service_id): if service.id == service_id: return service raise exceptions.CommandError( - "No service with a type, name or ID of '%s' exists." % service_id + f"No service with a type, name or ID of '{service_id}' exists." ) @@ -88,23 +88,23 @@ def find_service_sdk(identity_client, name_type_or_id): raise exceptions.CommandError(e.message) # search for service type - services = identity_client.services() - result = None - for service in services: - if name_type_or_id == service.type: - if result: - msg = _( - "Multiple service matches found for '%s', " - "use an ID or name to be more specific." - ) - raise exceptions.CommandError(msg % name_type_or_id) - result = service - - if result is None: - msg = _("No service with a type, name or ID of '%s' exists.") - raise exceptions.CommandError(msg % name_type_or_id) + services = identity_client.services(type=name_type_or_id) + try: + service = next(services) + except StopIteration: + msg = _( + "No service with a type, name or ID of '%(query)s' exists." + ) % {"query": name_type_or_id} + raise exceptions.CommandError(msg) + + if next(services, None): + msg = _( + "Multiple service matches found for '%(query)s', " + "use an ID to be more specific." + ) % {"query": name_type_or_id} + raise exceptions.CommandError(msg) - return result + return service def get_resource(manager, name_type_or_id): @@ -184,59 +184,113 @@ def _get_token_resource(client, resource, parsed_name, parsed_domain=None): return parsed_name -def _get_domain_id_if_requested(identity_client, domain_name_or_id): - if not domain_name_or_id: - return None - domain = find_domain(identity_client, domain_name_or_id) - return domain.id - - def find_domain(identity_client, name_or_id): return _find_identity_resource( identity_client.domains, name_or_id, domains.Domain ) +def find_domain_id_sdk( + identity_client, name_or_id, *, validate_actor_existence=True +): + return _find_sdk_id( + identity_client.find_domain, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + ) + + def find_group(identity_client, name_or_id, domain_name_or_id=None): - domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) - if not domain_id: + if domain_name_or_id is None: return _find_identity_resource( identity_client.groups, name_or_id, groups.Group ) - else: - return _find_identity_resource( - identity_client.groups, - name_or_id, - groups.Group, - domain_id=domain_id, + + domain_id = find_domain(identity_client, domain_name_or_id).id + return _find_identity_resource( + identity_client.groups, + name_or_id, + groups.Group, + domain_id=domain_id, + ) + + +def find_group_id_sdk( + identity_client, + name_or_id, + domain_name_or_id=None, + *, + validate_actor_existence=True, +): + if domain_name_or_id is None: + return _find_sdk_id( + identity_client.find_group, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, ) + domain_id = find_domain_id_sdk( + identity_client, + name_or_id=domain_name_or_id, + validate_actor_existence=validate_actor_existence, + ) + return _find_sdk_id( + identity_client.find_group, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) + def find_project(identity_client, name_or_id, domain_name_or_id=None): - domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) - if not domain_id: + if domain_name_or_id is None: return _find_identity_resource( identity_client.projects, name_or_id, projects.Project ) - else: - return _find_identity_resource( - identity_client.projects, - name_or_id, - projects.Project, - domain_id=domain_id, - ) + domain_id = find_domain(identity_client, domain_name_or_id).id + return _find_identity_resource( + identity_client.projects, + name_or_id, + projects.Project, + domain_id=domain_id, + ) def find_user(identity_client, name_or_id, domain_name_or_id=None): - domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) - if not domain_id: + if domain_name_or_id is None: return _find_identity_resource( identity_client.users, name_or_id, users.User ) - else: - return _find_identity_resource( - identity_client.users, name_or_id, users.User, domain_id=domain_id + domain_id = find_domain(identity_client, domain_name_or_id).id + return _find_identity_resource( + identity_client.users, name_or_id, users.User, domain_id=domain_id + ) + + +def find_user_id_sdk( + identity_client, + name_or_id, + domain_name_or_id=None, + *, + validate_actor_existence=True, +): + if domain_name_or_id is None: + return _find_sdk_id( + identity_client.find_user, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, ) + domain_id = find_domain_id_sdk( + identity_client, + name_or_id=domain_name_or_id, + validate_actor_existence=validate_actor_existence, + ) + return _find_sdk_id( + identity_client.find_user, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) def _find_identity_resource( @@ -279,13 +333,20 @@ def _find_identity_resource( return resource_type(None, {'id': name_or_id, 'name': name_or_id}) -def get_immutable_options(parsed_args): - options = {} - if parsed_args.immutable: - options['immutable'] = True - if parsed_args.no_immutable: - options['immutable'] = False - return options +def _find_sdk_id( + find_command, name_or_id, *, validate_actor_existence=True, **kwargs +): + try: + resource = find_command( + name_or_id=name_or_id, ignore_missing=False, **kwargs + ) + except sdk_exceptions.ForbiddenException: + return name_or_id + except sdk_exceptions.ResourceNotFound as exc: + if not validate_actor_existence: + return name_or_id + raise exceptions.CommandError from exc + return resource.id def add_user_domain_option_to_parser(parser): @@ -344,23 +405,27 @@ def add_inherited_option_to_parser(parser): action='store_true', default=False, help=_( - 'Specifies if the role grant is inheritable to the sub ' 'projects' + 'Specifies if the role grant is inheritable to the sub projects' ), ) def add_resource_option_to_parser(parser): - enable_group = parser.add_mutually_exclusive_group() - enable_group.add_argument( + immutable_group = parser.add_mutually_exclusive_group() + immutable_group.add_argument( '--immutable', action='store_true', + dest='immutable', + default=None, help=_( 'Make resource immutable. An immutable project may not ' 'be deleted or modified except to remove the immutable flag' ), ) - enable_group.add_argument( + immutable_group.add_argument( '--no-immutable', - action='store_true', + action='store_false', + dest='immutable', + default=None, help=_('Make resource mutable (default)'), ) diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index ccac5d04ba..437cad2fc2 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -14,19 +14,20 @@ """Identity v2 Service Catalog action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class EndpointsColumn(cliff_columns.FormattableColumn): +class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): if not self._value: return "" diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 6970f46dc9..360a090246 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -18,10 +18,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -128,9 +128,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.access_keys) - msg = _( - "%(result)s of %(total)s EC2 keys failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s EC2 keys failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index ca11164b61..38f7f4e56c 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -111,9 +111,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.endpoints) - msg = _( - "%(result)s of %(total)s endpoints failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s endpoints failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -132,18 +133,19 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + + columns: tuple[str, ...] = ( + 'ID', + 'Region', + 'Service Name', + 'Service Type', + ) if parsed_args.long: - columns = ( - 'ID', - 'Region', - 'Service Name', - 'Service Type', + columns += ( 'PublicURL', 'AdminURL', 'InternalURL', ) - else: - columns = ('ID', 'Region', 'Service Name', 'Service Type') data = identity_client.endpoints.list() for ep in data: diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index 1607555236..bf19d7d09c 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -20,10 +20,10 @@ from keystoneauth1 import exceptions as ks_exc from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -59,6 +59,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Add a property to ' @@ -79,15 +80,15 @@ def take_action(self, parsed_args): if parsed_args.disable: enabled = False kwargs = {} - if parsed_args.property: - kwargs = parsed_args.property.copy() + if parsed_args.properties: + kwargs.update(parsed_args.properties) try: project = identity_client.tenants.create( parsed_args.name, description=parsed_args.description, enabled=enabled, - **kwargs + **kwargs, ) except ks_exc.Conflict: if parsed_args.or_show: @@ -105,7 +106,14 @@ def take_action(self, parsed_args): class DeleteProject(command.Command): - _description = _("Delete project(s)") + _description = _( + "Delete project(s). This command will remove specified " + "existing project(s) if an active user is authorized to do " + "this. If there are resources managed by other services " + "(for example, Nova, Neutron, Cinder) associated with " + "specified project(s), delete operation will proceed " + "regardless." + ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -140,9 +148,10 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.projects) - msg = _( - "%(errors)s of %(total)s projects failed " "to delete." - ) % {'errors': errors, 'total': total} + msg = _("%(errors)s of %(total)s projects failed to delete.") % { + 'errors': errors, + 'total': total, + } raise exceptions.CommandError(msg) @@ -169,10 +178,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ('ID', 'Name', 'Description', 'Enabled') - else: - columns = ('ID', 'Name') + columns += ('Description', 'Enabled') data = self.app.client_manager.identity.tenants.list() if parsed_args.sort: data = utils.sort_items(data, parsed_args.sort) @@ -223,6 +231,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Set a project property ' @@ -248,8 +257,8 @@ def take_action(self, parsed_args): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False - if parsed_args.property: - kwargs.update(parsed_args.property) + if parsed_args.properties: + kwargs.update(parsed_args.properties) if 'id' in kwargs: del kwargs['id'] if 'name' in kwargs: @@ -331,6 +340,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action='append', default=[], help=_( @@ -347,7 +357,7 @@ def take_action(self, parsed_args): parsed_args.project, ) kwargs = project._info - for key in parsed_args.property: + for key in parsed_args.properties: if key in kwargs: kwargs[key] = None identity_client.tenants.update(project.id, **kwargs) diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 971c61a7f7..e54c07af08 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -18,10 +18,10 @@ import logging from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -143,7 +143,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.roles) - msg = _("%(errors)s of %(total)s roles failed " "to delete.") % { + msg = _("%(errors)s of %(total)s roles failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index 6610bab941..0aa800ef84 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -11,12 +11,12 @@ # under the License. # -"""Identity v2 Assignment action implementations """ +"""Identity v2 Assignment action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ # noqa @@ -68,7 +68,7 @@ def take_action(self, parsed_args): parsed_args.user, ) elif parsed_args.authuser: - if auth_ref: + if auth_ref and auth_ref.user_id: user = utils.find_resource( identity_client.users, auth_ref.user_id ) @@ -80,7 +80,7 @@ def take_action(self, parsed_args): parsed_args.project, ) elif parsed_args.authproject: - if auth_ref: + if auth_ref and auth_ref.project_id: project = utils.find_resource( identity_client.projects, auth_ref.project_id ) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 362dc45150..5e8dca7354 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -100,9 +100,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.services) - msg = _( - "%(result)s of %(total)s services failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s services failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -120,10 +121,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('ID', 'Name', 'Type') if parsed_args.long: - columns = ('ID', 'Name', 'Type', 'Description') - else: - columns = ('ID', 'Name', 'Type') + columns += ('Description',) data = self.app.client_manager.identity.services.list() return ( columns, @@ -164,7 +164,7 @@ def take_action(self, parsed_args): return zip(*sorted(info.items())) msg = _( - "No service catalog with a type, name or ID of '%s' " "exists." + "No service catalog with a type, name or ID of '%s' exists." ) % (parsed_args.service) raise exceptions.CommandError(msg) else: diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py index fe9436d8b5..ebb2269a92 100644 --- a/openstackclient/identity/v2_0/token.py +++ b/openstackclient/identity/v2_0/token.py @@ -15,9 +15,9 @@ """Identity v2 Token action implementations""" -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 745a48d117..244b9e9da9 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -20,17 +20,17 @@ from cliff import columns as cliff_columns from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class ProjectColumn(cliff_columns.FormattableColumn): +class ProjectColumn(cliff_columns.FormattableColumn[str]): """Formattable column for project column. Unlike the parent FormattableColumn class, the initializer of the @@ -194,7 +194,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.users) - msg = _("%(errors)s of %(total)s users failed " "to delete.") % { + msg = _("%(errors)s of %(total)s users failed to delete.") % { 'errors': errors, 'total': total, } @@ -230,41 +230,26 @@ def take_action(self, parsed_args): ) project = project.id + columns: tuple[str, ...] = ('id', 'name') + column_headers: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ( - 'ID', - 'Name', - 'tenantId', - 'Email', - 'Enabled', - ) - column_headers = ( - 'ID', - 'Name', - 'Project', - 'Email', - 'Enabled', - ) + columns += ('tenantId', 'email', 'enabled') + column_headers += ('Project', 'Email', 'Enabled') # Cache the project list project_cache = {} try: for p in identity_client.tenants.list(): project_cache[p.id] = p - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass formatters['tenantId'] = functools.partial( ProjectColumn, project_cache=project_cache ) - else: - columns = column_headers = ('ID', 'Name') data = identity_client.users.list(tenant_id=project) if parsed_args.project: - d = {} - for s in data: - d[s.id] = s - data = d.values() + data = {s.id: s for s in data}.values() if parsed_args.long: # FIXME(dtroyer): Sometimes user objects have 'tenant_id' instead diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index 655ad14b1b..1859ef6fa5 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -42,29 +42,30 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + conn = self.app.client_manager.sdk_connection + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) errors = 0 for ac in parsed_args.access_rule: try: - access_rule = common.get_resource_by_id( - identity_client.access_rules, ac - ) - identity_client.access_rules.delete(access_rule.id) + access_rule = identity_client.get_access_rule(user_id, ac) + identity_client.delete_access_rule(user_id, access_rule.id) except Exception as e: errors += 1 LOG.error( - _( - "Failed to delete access rule with " - "ID '%(ac)s': %(e)s" - ), + _("Failed to delete access rule with ID '%(ac)s': %(e)s"), {'ac': ac, 'e': e}, ) if errors > 0: total = len(parsed_args.access_rule) msg = _( - "%(errors)s of %(total)s access rules failed " "to delete." + "%(errors)s of %(total)s access rules failed to delete." ) % {'errors': errors, 'total': total} raise exceptions.CommandError(msg) @@ -83,16 +84,21 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity if parsed_args.user: user_id = common.find_user( identity_client, parsed_args.user, parsed_args.user_domain ).id else: - user_id = None + conn = self.app.client_manager.sdk_connection + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) columns = ('ID', 'Service', 'Method', 'Path') - data = identity_client.access_rules.list(user=user_id) + data = identity_client.access_rules(user=user_id) return ( columns, ( @@ -119,11 +125,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - access_rule = common.get_resource_by_id( - identity_client.access_rules, parsed_args.access_rule + identity_client = self.app.client_manager.sdk_connection.identity + conn = self.app.client_manager.sdk_connection + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) + + access_rule = identity_client.get_access_rule( + user_id, parsed_args.access_rule ) - access_rule._info.pop('links', None) - - return zip(*sorted(access_rule._info.items())) + columns = ('ID', 'Method', 'Path', 'Service') + return ( + columns, + ( + utils.get_item_properties( + access_rule, + columns, + formatters={}, + ) + ), + ) diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 035be2d212..3b38f17b5a 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -18,19 +18,95 @@ import datetime import json import logging +import typing as ty import uuid -from osc_lib.command import command +from cliff import columns as cliff_columns from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common - LOG = logging.getLogger(__name__) +class RolesColumn(cliff_columns.FormattableColumn[ty.Any]): + """Generate a formatted string of role names.""" + + def human_readable(self): + return utils.format_list(list(r['name'] for r in self._value)) + + +def _format_application_credential( + application_credential, *, include_secret=False +): + column_headers: tuple[str, ...] = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + columns: tuple[str, ...] = ( + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', + ) + if include_secret: + column_headers += ('Secret',) + columns += ('secret',) + + return ( + column_headers, + utils.get_item_properties( + application_credential, columns, formatters={'roles': RolesColumn} + ), + ) + + +def _format_application_credentials(application_credentials): + column_headers = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + columns = ( + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', + ) + + return ( + column_headers, + ( + utils.get_item_properties( + x, columns, formatters={'roles': RolesColumn} + ) + for x in application_credentials + ), + ) + + # TODO(stephenfin): Move this to osc_lib since it's useful elsewhere def is_uuid_like(value) -> bool: """Returns validation of a value as a UUID. @@ -38,9 +114,6 @@ def is_uuid_like(value) -> bool: :param val: Value to verify :type val: string :returns: bool - - .. versionchanged:: 1.1.1 - Support non-lowercase UUIDs. """ try: formatted_value = ( @@ -76,6 +149,7 @@ def get_parser(self, prog_name): parser.add_argument( '--role', metavar='', + dest='roles', action='append', default=[], help=_( @@ -132,10 +206,15 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) role_ids = [] - for role in parsed_args.role: + for role in parsed_args.roles: if is_uuid_like(role): role_ids.append({'id': role}) else: @@ -179,31 +258,8 @@ def take_action(self, parsed_args): access_rules=access_rules, ) - # Format roles into something sensible - if application_credential['roles']: - roles = application_credential['roles'] - msg = ' '.join(r['name'] for r in roles) - application_credential['roles'] = msg - - columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - 'Secret', - ) - return ( - columns, - ( - utils.get_dict_properties( - application_credential, - columns, - ) - ), + return _format_application_credential( + application_credential, include_secret=True ) @@ -223,13 +279,18 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) errors = 0 for ac in parsed_args.application_credential: try: app_cred = identity_client.find_application_credential( - user_id, ac + user_id, ac, ignore_missing=False ) identity_client.delete_application_credential( user_id, app_cred.id @@ -252,6 +313,8 @@ def take_action(self, parsed_args): ) % {'errors': errors, 'total': total} raise exceptions.CommandError(msg) + return None + class ListApplicationCredential(command.Lister): _description = _("List application credentials") @@ -269,46 +332,23 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity if parsed_args.user: - user_id = common.find_user( + user_id = common.find_user_id_sdk( identity_client, parsed_args.user, parsed_args.user_domain - ).id + ) else: conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) - - data = identity_client.application_credentials(user=user_id) - - data_formatted = [] - for ac in data: - # Format roles into something sensible - roles = ac['roles'] - msg = ' '.join(r['name'] for r in roles) - ac['roles'] = msg - - data_formatted.append(ac) - - columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - ) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - formatters={}, - ) - for s in data_formatted - ), + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) + + application_credentials = identity_client.application_credentials( + user=user_id ) + return _format_application_credentials(application_credentials) + class ShowApplicationCredential(command.ShowOne): _description = _("Display application credential details") @@ -325,33 +365,14 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) - - app_cred = identity_client.find_application_credential( - user_id, parsed_args.application_credential + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) + + application_credential = identity_client.find_application_credential( + user_id, parsed_args.application_credential, ignore_missing=False ) - # Format roles into something sensible - roles = app_cred['roles'] - msg = ' '.join(r['name'] for r in roles) - app_cred['roles'] = msg - - columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - ) - return ( - columns, - ( - utils.get_dict_properties( - app_cred, - columns, - ) - ), - ) + return _format_application_credential(application_credential) diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index a161461ce3..7d37e6cbd2 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -9,24 +9,24 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Identity v3 Service Catalog action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class EndpointsColumn(cliff_columns.FormattableColumn): +class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): if not self._value: return "" diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index 924b5726d2..c58441ca83 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -82,9 +82,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.consumer) - msg = _( - "%(result)s of %(total)s consumers failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s consumers failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 1b0d525176..02eef649c7 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -28,6 +28,23 @@ LOG = logging.getLogger(__name__) +def _format_credential(credential): + columns = ( + 'blob', + 'id', + 'project_id', + 'type', + 'user_id', + ) + return ( + columns, + utils.get_item_properties( + credential, + columns, + ), + ) + + class CreateCredential(command.ShowOne): _description = _("Create new credential") @@ -53,32 +70,30 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Project which limits the scope of ' - 'the credential (name or ID)' + 'Project which limits the scope of the credential (name or ID)' ), ) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - user_id = utils.find_resource( - identity_client.users, parsed_args.user + identity_client = self.app.client_manager.sdk_connection.identity + user_id = identity_client.find_user( + parsed_args.user, ignore_missing=False ).id if parsed_args.project: - project = utils.find_resource( - identity_client.projects, parsed_args.project + project = identity_client.find_project( + parsed_args.project, ignore_missing=False ).id else: project = None - credential = identity_client.credentials.create( - user=user_id, + credential = identity_client.create_credential( + user_id=user_id, type=parsed_args.type, blob=parsed_args.data, - project=project, + project_id=project, ) - credential._info.pop('links') - return zip(*sorted(credential._info.items())) + return _format_credential(credential) class DeleteCredential(command.Command): @@ -95,11 +110,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.credential: try: - identity_client.credentials.delete(i) + identity_client.delete_credential(i) except Exception as e: result += 1 LOG.error( @@ -112,9 +127,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.credential) - msg = _( - "%(result)s of %(total)s credential failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s credential failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -137,14 +153,17 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.user: - user_id = common.find_user( - identity_client, - parsed_args.user, - parsed_args.user_domain, + domain_id = None + if parsed_args.user_domain: + domain_id = identity_client.find_domain( + parsed_args.user_domain, ignore_missing=False + ) + user_id = identity_client.find_user( + parsed_args.user, domain_id=domain_id, ignore_missing=False ).id kwargs["user_id"] = user_id @@ -153,7 +172,8 @@ def take_action(self, parsed_args): columns = ('ID', 'Type', 'User ID', 'Blob', 'Project ID') column_headers = ('ID', 'Type', 'User ID', 'Data', 'Project ID') - data = self.app.client_manager.identity.credentials.list(**kwargs) + data = identity_client.credentials(**kwargs) + return ( column_headers, ( @@ -199,27 +219,26 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Project which limits the scope of ' - 'the credential (name or ID)' + 'Project which limits the scope of the credential (name or ID)' ), ) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - user_id = utils.find_resource( - identity_client.users, parsed_args.user + user_id = identity_client.find_user( + parsed_args.user, ignore_missing=False ).id if parsed_args.project: - project = utils.find_resource( - identity_client.projects, parsed_args.project + project = identity_client.find_project( + parsed_args.project, ignore_missing=False ).id else: project = None - identity_client.credentials.update( + identity_client.update_credential( parsed_args.credential, user=user_id, type=parsed_args.type, @@ -241,10 +260,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - credential = utils.find_resource( - identity_client.credentials, parsed_args.credential - ) + identity_client = self.app.client_manager.sdk_connection.identity + credential = identity_client.get_credential(parsed_args.credential) - credential._info.pop('links') - return zip(*sorted(credential._info.items())) + return _format_credential(credential) diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index aaa2708302..28481c00d6 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -17,11 +17,11 @@ import logging -from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command +from openstack import exceptions as sdk_exceptions from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -29,6 +29,31 @@ LOG = logging.getLogger(__name__) +def _format_domain(domain): + columns = ( + 'id', + 'name', + 'is_enabled', + 'description', + 'options', + ) + column_headers = ( + 'id', + 'name', + 'enabled', + 'description', + 'options', + ) + + return ( + column_headers, + utils.get_item_properties( + domain, + columns, + ), + ) + + class CreateDomain(command.ShowOne): _description = _("Create new domain") @@ -47,12 +72,15 @@ def get_parser(self, prog_name): enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', + dest='is_enabled', action='store_true', + default=True, help=_('Enable domain (default)'), ) enable_group.add_argument( '--disable', - action='store_true', + dest='is_enabled', + action='store_false', help=_('Disable domain'), ) parser.add_argument( @@ -64,32 +92,29 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - enabled = True - if parsed_args.disable: - enabled = False - - options = common.get_immutable_options(parsed_args) + options = {} + if parsed_args.immutable is not None: + options['immutable'] = parsed_args.immutable try: - domain = identity_client.domains.create( + domain = identity_client.create_domain( name=parsed_args.name, description=parsed_args.description, options=options, - enabled=enabled, + is_enabled=parsed_args.is_enabled, ) - except ks_exc.Conflict: + except sdk_exceptions.ConflictException: if parsed_args.or_show: - domain = utils.find_resource( - identity_client.domains, parsed_args.name + domain = identity_client.find_domain( + parsed_args.name, ignore_missing=False ) LOG.info(_('Returning existing domain %s'), domain.name) else: raise - domain._info.pop('links') - return zip(*sorted(domain._info.items())) + return _format_domain(domain) class DeleteDomain(command.Command): @@ -106,12 +131,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.domain: try: - domain = utils.find_resource(identity_client.domains, i) - identity_client.domains.delete(domain.id) + domain = identity_client.find_domain(i, ignore_missing=False) + identity_client.delete_domain(domain.id) except Exception as e: result += 1 LOG.error( @@ -124,7 +149,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.domain) - msg = _("%(result)s of %(total)s domains failed " "to delete.") % { + msg = _("%(result)s of %(total)s domains failed to delete.") % { 'result': result, 'total': total, } @@ -143,7 +168,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--enabled', - dest='enabled', + dest='is_enabled', action='store_true', help=_('The domains that are enabled will be returned'), ) @@ -153,13 +178,17 @@ def take_action(self, parsed_args): kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name - if parsed_args.enabled: - kwargs['enabled'] = True + if parsed_args.is_enabled: + kwargs['is_enabled'] = True + + columns = ('id', 'name', 'is_enabled', 'description') + column_headers = ('ID', 'Name', 'Enabled', 'Description') + data = self.app.client_manager.sdk_connection.identity.domains( + **kwargs + ) - columns = ('ID', 'Name', 'Enabled', 'Description') - data = self.app.client_manager.identity.domains.list(**kwargs) return ( - columns, + column_headers, ( utils.get_item_properties( s, @@ -194,38 +223,37 @@ def get_parser(self, prog_name): enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', + dest='is_enabled', action='store_true', + default=None, help=_('Enable domain'), ) enable_group.add_argument( '--disable', - action='store_true', + dest='is_enabled', + action='store_false', + default=None, help=_('Disable domain'), ) common.add_resource_option_to_parser(parser) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - domain = utils.find_resource( - identity_client.domains, parsed_args.domain + identity_client = self.app.client_manager.sdk_connection.identity + domain = identity_client.find_domain( + parsed_args.domain, ignore_missing=False ) kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled + if parsed_args.immutable is not None: + kwargs['options'] = {'immutable': parsed_args.immutable} - if parsed_args.enable: - kwargs['enabled'] = True - if parsed_args.disable: - kwargs['enabled'] = False - - options = common.get_immutable_options(parsed_args) - if options: - kwargs['options'] = options - - identity_client.domains.update(domain.id, **kwargs) + identity_client.update_domain(domain.id, **kwargs) class ShowDomain(command.ShowOne): @@ -241,13 +269,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - domain_str = common._get_token_resource( - identity_client, 'domain', parsed_args.domain + identity_client = self.app.client_manager.sdk_connection.identity + domain = identity_client.find_domain( + parsed_args.domain, ignore_missing=False ) - domain = utils.find_resource(identity_client.domains, domain_str) - - domain._info.pop('links') - return zip(*sorted(domain._info.items())) + return _format_domain(domain) diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index a9fbe1ae4e..dbbf7a2474 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -14,10 +14,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -156,9 +156,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.access_key) - msg = _( - "%(result)s of %(total)s EC2 keys failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s EC2 keys failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 6e77bdf871..9083fdc708 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -28,11 +28,31 @@ LOG = logging.getLogger(__name__) -def get_service_name(service): - if hasattr(service, 'name'): - return service.name - else: - return '' +def _format_endpoint(endpoint, service): + columns = ( + 'is_enabled', + 'id', + 'interface', + 'region_id', + 'region_id', + 'service_id', + 'url', + ) + column_headers = ( + 'enabled', + 'id', + 'interface', + 'region', + 'region_id', + 'service_id', + 'url', + 'service_name', + 'service_type', + ) + + data = utils.get_item_properties(endpoint, columns) + data += (getattr(service, 'name', ''), service.type) + return column_headers, data class AddProjectToEndpoint(command.Command): @@ -44,15 +64,13 @@ def get_parser(self, prog_name): 'endpoint', metavar='', help=_( - 'Endpoint to associate with ' 'specified project (name or ID)' + 'Endpoint to associate with specified project (name or ID)' ), ) parser.add_argument( 'project', metavar='', - help=_( - 'Project to associate with ' 'specified endpoint name or ID)' - ), + help=_('Project to associate with specified endpoint name or ID)'), ) common.add_project_domain_option_to_parser(parser) return parser @@ -114,23 +132,23 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - service = common.find_service(identity_client, parsed_args.service) + identity_client = self.app.client_manager.sdk_connection.identity + service = common.find_service_sdk(identity_client, parsed_args.service) - endpoint = identity_client.endpoints.create( - service=service.id, - url=parsed_args.url, - interface=parsed_args.interface, - region=parsed_args.region, - enabled=parsed_args.enabled, - ) + kwargs = {} + + kwargs['service_id'] = service.id + kwargs['url'] = parsed_args.url + kwargs['interface'] = parsed_args.interface + kwargs['is_enabled'] = parsed_args.enabled + + if parsed_args.region: + region = identity_client.get_region(parsed_args.region) + kwargs['region_id'] = region.id - info = {} - endpoint._info.pop('links') - info.update(endpoint._info) - info['service_name'] = get_service_name(service) - info['service_type'] = service.type - return zip(*sorted(info.items())) + endpoint = identity_client.create_endpoint(**kwargs) + + return _format_endpoint(endpoint, service=service) class DeleteEndpoint(command.Command): @@ -147,14 +165,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.endpoint: try: - endpoint_id = utils.find_resource( - identity_client.endpoints, i + endpoint_id = identity_client.find_endpoint( + i, ignore_missing=False ).id - identity_client.endpoints.delete(endpoint_id) + identity_client.delete_endpoint(endpoint_id) except Exception as e: result += 1 LOG.error( @@ -167,9 +185,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.endpoint) - msg = _( - "%(result)s of %(total)s endpoints failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s endpoints failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -209,28 +228,37 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity endpoint = None if parsed_args.endpoint: - endpoint = utils.find_resource( - identity_client.endpoints, parsed_args.endpoint + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) + + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, ) - project = None + + project_id = None if parsed_args.project: - project = common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, + project_id = common._find_sdk_id( + identity_client.find_project, + name_or_id=common._get_token_resource( + identity_client, 'project', parsed_args.project + ), + domain_id=project_domain_id, ) if endpoint: - columns = ('ID', 'Name') - data = identity_client.endpoint_filter.list_projects_for_endpoint( - endpoint=endpoint.id - ) + column_headers: tuple[str, ...] = ('ID', 'Name') + columns: tuple[str, ...] = ('id', 'name') + data = identity_client.endpoint_projects(endpoint=endpoint.id) else: - columns = ( + column_headers = ( 'ID', 'Region', 'Service Name', @@ -239,37 +267,43 @@ def take_action(self, parsed_args): 'Interface', 'URL', ) + columns = ( + 'id', + 'region_id', + 'service_name', + 'service_type', + 'is_enabled', + 'interface', + 'url', + ) kwargs = {} if parsed_args.service: - service = common.find_service( + service = common.find_service_sdk( identity_client, parsed_args.service ) - kwargs['service'] = service.id + kwargs['service_id'] = service.id if parsed_args.interface: kwargs['interface'] = parsed_args.interface if parsed_args.region: - kwargs['region'] = parsed_args.region + region = identity_client.get_region(parsed_args.region) + kwargs['region_id'] = region.id - if project: - data = ( - identity_client.endpoint_filter.list_endpoints_for_project( - project=project.id - ) + if project_id: + data = list( + identity_client.project_endpoints(project=project_id) ) else: - data = identity_client.endpoints.list(**kwargs) - - service_list = identity_client.services.list() + data = list(identity_client.endpoints(**kwargs)) for ep in data: - service = common.find_service_in_list( - service_list, ep.service_id + service = identity_client.find_service( + ep.service_id, ignore_missing=False ) - ep.service_name = get_service_name(service) + ep.service_name = getattr(service, 'name', '') ep.service_type = service.type return ( - columns, + column_headers, ( utils.get_item_properties( s, @@ -290,14 +324,14 @@ def get_parser(self, prog_name): 'endpoint', metavar='', help=_( - 'Endpoint to dissociate from ' 'specified project (name or ID)' + 'Endpoint to dissociate from specified project (name or ID)' ), ) parser.add_argument( 'project', metavar='', help=_( - 'Project to dissociate from ' 'specified endpoint name or ID)' + 'Project to dissociate from specified endpoint name or ID)' ), ) common.add_project_domain_option_to_parser(parser) @@ -364,28 +398,36 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - endpoint = utils.find_resource( - identity_client.endpoints, parsed_args.endpoint + identity_client = self.app.client_manager.sdk_connection.identity + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False ) - service_id = None + kwargs = {} + if parsed_args.service: - service = common.find_service(identity_client, parsed_args.service) - service_id = service.id - enabled = None + service = common.find_service_sdk( + identity_client, parsed_args.service + ) + kwargs['service_id'] = service.id + if parsed_args.enabled: - enabled = True + kwargs['is_enabled'] = True if parsed_args.disabled: - enabled = False + kwargs['is_enabled'] = False + + if parsed_args.url: + kwargs['url'] = parsed_args.url + + if parsed_args.interface: + kwargs['interface'] = parsed_args.interface + + if parsed_args.region: + kwargs['region_id'] = parsed_args.region - identity_client.endpoints.update( + identity_client.update_endpoint( endpoint.id, - service=service_id, - url=parsed_args.url, - interface=parsed_args.interface, - region=parsed_args.region, - enabled=enabled, + **kwargs, ) @@ -405,16 +447,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - endpoint = utils.find_resource( - identity_client.endpoints, parsed_args.endpoint + identity_client = self.app.client_manager.sdk_connection.identity + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False ) - service = common.find_service(identity_client, endpoint.service_id) + service = common.find_service_sdk(identity_client, endpoint.service_id) - info = {} - endpoint._info.pop('links') - info.update(endpoint._info) - info['service_name'] = get_service_name(service) - info['service_type'] = service.type - return zip(*sorted(info.items())) + return _format_endpoint(endpoint, service) diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py index b5c2631f02..3965f31978 100644 --- a/openstackclient/identity/v3/endpoint_group.py +++ b/openstackclient/identity/v3/endpoint_group.py @@ -16,10 +16,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -168,7 +168,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.endpointgroup) msg = _( - "%(result)s of %(total)s endpointgroups failed " "to delete." + "%(result)s of %(total)s endpointgroups failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 4e980860db..850ec0ac7f 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index d9c3b93b8f..a2c2fd3367 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -17,11 +17,11 @@ import logging -from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command +from openstack import exceptions as sdk_exc from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -29,6 +29,25 @@ LOG = logging.getLogger(__name__) +def _format_group(group): + columns = ( + 'description', + 'domain_id', + 'id', + 'name', + ) + column_headers = ( + 'description', + 'domain_id', + 'id', + 'name', + ) + return ( + column_headers, + utils.get_item_properties(group, columns), + ) + + class AddUserToGroup(command.Command): _description = _("Add user to group") @@ -53,19 +72,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - group_id = common.find_group( + group_id = common.find_group_id_sdk( identity_client, parsed_args.group, parsed_args.group_domain - ).id + ) result = 0 for i in parsed_args.user: try: - user_id = common.find_user( + user_id = common.find_user_id_sdk( identity_client, i, parsed_args.user_domain - ).id - identity_client.users.add_to_group(user_id, group_id) + ) + identity_client.add_user_to_group(user_id, group_id) except Exception as e: result += 1 msg = _("%(user)s not added to group %(group)s: %(e)s") % { @@ -109,32 +128,41 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - user_id = common.find_user( - identity_client, parsed_args.user, parsed_args.user_domain - ).id - group_id = common.find_group( - identity_client, parsed_args.group, parsed_args.group_domain - ).id + user_id = common.find_user_id_sdk( + identity_client, + parsed_args.user, + parsed_args.user_domain, + validate_actor_existence=False, + ) + group_id = common.find_group_id_sdk( + identity_client, + parsed_args.group, + parsed_args.group_domain, + validate_actor_existence=False, + ) + user_in_group = False try: - identity_client.users.check_in_group(user_id, group_id) - except ks_exc.http.HTTPClientError as e: - if e.http_status == 403 or e.http_status == 404: - msg = _("%(user)s not in group %(group)s\n") % { - 'user': parsed_args.user, - 'group': parsed_args.group, - } - self.app.stderr.write(msg) - else: - raise e - else: + user_in_group = identity_client.check_user_in_group( + user_id, group_id + ) + except sdk_exc.ForbiddenException: + # Assume False if forbidden + pass + if user_in_group: msg = _("%(user)s in group %(group)s\n") % { 'user': parsed_args.user, 'group': parsed_args.group, } self.app.stdout.write(msg) + else: + msg = _("%(user)s not in group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + self.app.stderr.write(msg) class CreateGroup(command.ShowOne): @@ -165,29 +193,38 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - domain = None + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + if parsed_args.description: + kwargs['description'] = parsed_args.description if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain).id + kwargs['domain_id'] = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) try: - group = identity_client.groups.create( - name=parsed_args.name, - domain=domain, - description=parsed_args.description, - ) - except ks_exc.Conflict: + group = identity_client.create_group(**kwargs) + except sdk_exc.ConflictException: if parsed_args.or_show: - group = utils.find_resource( - identity_client.groups, parsed_args.name, domain_id=domain - ) + if parsed_args.domain: + group = identity_client.find_group( + parsed_args.name, + domain_id=parsed_args.domain, + ignore_missing=False, + ) + else: + group = identity_client.find_group( + parsed_args.name, + ignore_missing=False, + ) LOG.info(_('Returning existing group %s'), group.name) else: raise - group._info.pop('links') - return zip(*sorted(group._info.items())) + return _format_group(group) class DeleteGroup(command.Command): @@ -209,15 +246,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 for group in parsed_args.groups: try: - group_obj = common.find_group( + group_id = common.find_group_id_sdk( identity_client, group, parsed_args.domain ) - identity_client.groups.delete(group_obj.id) + identity_client.delete_group(group_id) except Exception as e: errors += 1 LOG.error( @@ -230,7 +267,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.groups) - msg = _("%(errors)s of %(total)s groups failed " "to delete.") % { + msg = _("%(errors)s of %(total)s groups failed to delete.") % { 'errors': errors, 'total': total, } @@ -262,30 +299,38 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity domain = None if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain).id + domain = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) + data = [] if parsed_args.user: - user = common.find_user( + user = common.find_user_id_sdk( identity_client, parsed_args.user, parsed_args.user_domain, - ).id + ) + if domain: + # NOTE(0weng): The API doesn't actually support filtering + # additionally by domain_id, so this doesn't really do + # anything. + data = identity_client.user_groups(user, domain_id=domain) + else: + data = identity_client.user_groups(user) else: - user = None + if domain: + data = identity_client.groups(domain_id=domain) + else: + data = identity_client.groups() # List groups + columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ('ID', 'Name', 'Domain ID', 'Description') - else: - columns = ('ID', 'Name') - data = identity_client.groups.list( - domain=domain, - user=user, - ) + columns += ('Domain ID', 'Description') return ( columns, @@ -324,19 +369,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - group_id = common.find_group( + group_id = common.find_group_id_sdk( identity_client, parsed_args.group, parsed_args.group_domain - ).id + ) result = 0 for i in parsed_args.user: try: - user_id = common.find_user( + user_id = common.find_user_id_sdk( identity_client, i, parsed_args.user_domain - ).id - identity_client.users.remove_from_group(user_id, group_id) + ) + identity_client.remove_user_from_group(user_id, group_id) except Exception as e: result += 1 msg = _("%(user)s not removed from group %(group)s: %(e)s") % { @@ -388,8 +433,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - group = common.find_group( + identity_client = self.app.client_manager.sdk_connection.identity + group = common.find_group_id_sdk( identity_client, parsed_args.group, parsed_args.domain ) kwargs = {} @@ -398,7 +443,7 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['description'] = parsed_args.description - identity_client.groups.update(group.id, **kwargs) + identity_client.update_group(group, **kwargs) class ShowGroup(command.ShowOne): @@ -419,13 +464,18 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - group = common.find_group( - identity_client, - parsed_args.group, - domain_name_or_id=parsed_args.domain, - ) + if parsed_args.domain: + domain = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) + group = identity_client.find_group( + parsed_args.group, domain_id=domain, ignore_missing=False + ) + else: + group = identity_client.find_group( + parsed_args.group, ignore_missing=False + ) - group._info.pop('links') - return zip(*sorted(group._info.items())) + return _format_group(group) diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 5e0c8f7c9f..f1af03f05c 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -16,10 +16,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -41,6 +41,7 @@ def get_parser(self, prog_name): identity_remote_id_provider.add_argument( '--remote-id', metavar='', + dest='remote_ids', action='append', help=_( 'Remote IDs to associate with the Identity Provider ' @@ -99,16 +100,15 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + remote_ids: list[str] | None = None if parsed_args.remote_id_file: file_content = utils.read_blob_file_contents( parsed_args.remote_id_file ) remote_ids = file_content.splitlines() remote_ids = list(map(str.strip, remote_ids)) - else: - remote_ids = ( - parsed_args.remote_id if parsed_args.remote_id else None - ) + elif parsed_args.remote_ids: + remote_ids = parsed_args.remote_ids domain_id = None if parsed_args.domain: @@ -133,12 +133,13 @@ def take_action(self, parsed_args): description=parsed_args.description, domain_id=domain_id, enabled=parsed_args.enabled, - **kwargs + **kwargs, ) idp._info.pop('links', None) - remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', [])) - idp._info['remote_ids'] = remote_ids + idp._info['remote_ids'] = format_columns.ListColumn( + idp._info.pop('remote_ids', []) + ) return zip(*sorted(idp._info.items())) @@ -174,8 +175,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.identity_provider) msg = _( - "%(result)s of %(total)s identity providers failed" - " to delete." + "%(result)s of %(total)s identity providers failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -241,6 +241,7 @@ def get_parser(self, prog_name): identity_remote_id_provider.add_argument( '--remote-id', metavar='', + dest='remote_ids', action='append', help=_( 'Remote IDs to associate with the Identity Provider ' @@ -288,8 +289,8 @@ def take_action(self, parsed_args): ) remote_ids = file_content.splitlines() remote_ids = list(map(str.strip, remote_ids)) - elif parsed_args.remote_id: - remote_ids = parsed_args.remote_id + elif parsed_args.remote_ids: + remote_ids = parsed_args.remote_ids # Setup keyword args for the client kwargs = {} @@ -299,7 +300,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False - if parsed_args.remote_id_file or parsed_args.remote_id: + if parsed_args.remote_id_file or parsed_args.remote_ids: kwargs['remote_ids'] = remote_ids # TODO(pas-ha) actually check for 3.14 microversion diff --git a/openstackclient/identity/v3/implied_role.py b/openstackclient/identity/v3/implied_role.py index 3958896b09..c1236ad019 100644 --- a/openstackclient/identity/v3/implied_role.py +++ b/openstackclient/identity/v3/implied_role.py @@ -17,8 +17,8 @@ import logging -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 59cc1398c2..4671c5acb7 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -15,16 +15,38 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as common_utils LOG = logging.getLogger(__name__) +def _format_limit(limit): + columns = ( + "description", + "id", + "project_id", + "region_id", + "resource_limit", + "resource_name", + "service_id", + ) + column_headers = ( + "description", + "id", + "project_id", + "region_id", + "resource_limit", + "resource_name", + "service_id", + ) + return (column_headers, utils.get_item_properties(limit, columns)) + + class CreateLimit(command.ShowOne): _description = _("Create a limit") @@ -67,41 +89,33 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - project = common_utils.find_project( - identity_client, parsed_args.project + identity_client = self.app.client_manager.sdk_connection.identity + + kwargs = { + "resource_name": parsed_args.resource_name, + "resource_limit": parsed_args.resource_limit, + } + if parsed_args.description: + kwargs["description"] = parsed_args.description + + # TODO(0weng): Add --project-domain option + # to support filtering project domain + kwargs["project_id"] = common_utils._find_sdk_id( + identity_client.find_project, + name_or_id=parsed_args.project, ) - service = common_utils.find_service( + kwargs["service_id"] = common_utils.find_service_sdk( identity_client, parsed_args.service - ) - region = None + ).id + if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id - limit = identity_client.limits.create( - project, - service, - parsed_args.resource_name, - parsed_args.resource_limit, - description=parsed_args.description, - region=region, - ) + limit = identity_client.create_limit(**kwargs) - limit._info.pop('links', None) - return zip(*sorted(limit._info.items())) + return _format_limit(limit) class ListLimit(command.Lister): @@ -133,43 +147,31 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - service = None + kwargs = {} if parsed_args.service: - service = common_utils.find_service( + kwargs["service_id"] = common_utils.find_service_sdk( identity_client, parsed_args.service ) - region = None + if parsed_args.region: - region = utils.find_resource( - identity_client.regions, parsed_args.region - ) - val = getattr(parsed_args, 'region', None) - if 'None' not in val: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) - project = None + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id + + # TODO(0weng): Add --project-domain option + # to support filtering project domain if parsed_args.project: - project = utils.find_resource( - identity_client.projects, parsed_args.project + kwargs["project_id"] = common_utils._find_sdk_id( + identity_client.find_project, + name_or_id=parsed_args.project, ) - limits = identity_client.limits.list( - service=service, - resource_name=parsed_args.resource_name, - region=region, - project=project, - ) + if parsed_args.resource_name: + kwargs["resource_name"] = parsed_args.resource_name + + limits = identity_client.limits(**kwargs) columns = ( 'ID', @@ -199,10 +201,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - limit = identity_client.limits.get(parsed_args.limit_id) - limit._info.pop('links', None) - return zip(*sorted(limit._info.items())) + identity_client = self.app.client_manager.sdk_connection.identity + limit = identity_client.get_limit(parsed_args.limit_id) + return _format_limit(limit) class SetLimit(command.ShowOne): @@ -230,17 +231,16 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - limit = identity_client.limits.update( - parsed_args.limit_id, - description=parsed_args.description, - resource_limit=parsed_args.resource_limit, - ) + identity_client = self.app.client_manager.sdk_connection.identity - limit._info.pop('links', None) + kwargs = {} + if parsed_args.description: + kwargs["description"] = parsed_args.description + if parsed_args.resource_limit: + kwargs["resource_limit"] = parsed_args.resource_limit + limit = identity_client.update_limit(parsed_args.limit_id, **kwargs) - return zip(*sorted(limit._info.items())) + return _format_limit(limit) class DeleteLimit(command.Command): @@ -252,27 +252,30 @@ def get_parser(self, prog_name): 'limit_id', metavar='', nargs="+", - help=_('Limit to delete (ID)'), + help=_( + 'Limit to delete (ID) ' + '(repeat option to remove multiple limits)' + ), ) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 for limit_id in parsed_args.limit_id: try: - identity_client.limits.delete(limit_id) + identity_client.delete_limit(limit_id) except Exception as e: errors += 1 LOG.error( - _("Failed to delete limit with ID " "'%(id)s': %(e)s"), + _("Failed to delete limit with ID '%(id)s': %(e)s"), {'id': limit_id, 'e': e}, ) if errors > 0: total = len(parsed_args.limit_id) - msg = _("%(errors)s of %(total)s limits failed to " "delete.") % { + msg = _("%(errors)s of %(total)s limits failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index ff8f2aa317..a041f19e58 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -18,10 +18,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -161,9 +161,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.mapping) - msg = _( - "%(result)s of %(total)s mappings failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s mappings failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 51428e8c92..3554903952 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -92,9 +92,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.policy) - msg = _( - "%(result)s of %(total)s policies failed " "to delete." - ) % { + msg = _("%(result)s of %(total)s policies failed to delete.") % { 'result': result, 'total': total, } @@ -115,12 +113,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('ID', 'Type') + column_headers: tuple[str, ...] = columns if parsed_args.long: - columns = ('ID', 'Type', 'Blob') - column_headers = ('ID', 'Type', 'Rules') - else: - columns = ('ID', 'Type') - column_headers = columns + columns += ('Blob',) + column_headers += ('Rules',) data = self.app.client_manager.identity.policies.list() return ( column_headers, diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index d66fd6121f..e70a8a5011 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -19,10 +19,10 @@ from keystoneauth1 import exceptions as ks_exc from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common from openstackclient.identity.v3 import tag @@ -59,16 +59,21 @@ def get_parser(self, prog_name): enable_group.add_argument( '--enable', action='store_true', + dest='enabled', + default=True, help=_('Enable project'), ) enable_group.add_argument( '--disable', - action='store_true', + action='store_false', + dest='enabled', + default=True, help=_('Disable project'), ) parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Add a property to ' @@ -98,15 +103,9 @@ def take_action(self, parsed_args): parsed_args.parent, ).id - enabled = True - if parsed_args.disable: - enabled = False - - options = common.get_immutable_options(parsed_args) - kwargs = {} - if parsed_args.property: - kwargs = parsed_args.property.copy() + if parsed_args.properties: + kwargs = parsed_args.properties.copy() if 'is_domain' in kwargs.keys(): if kwargs['is_domain'].lower() == "true": kwargs['is_domain'] = True @@ -117,15 +116,19 @@ def take_action(self, parsed_args): kwargs['tags'] = list(set(parsed_args.tags)) + options = {} + if parsed_args.immutable is not None: + options['immutable'] = parsed_args.immutable + try: project = identity_client.projects.create( name=parsed_args.name, domain=domain, parent=parent, description=parsed_args.description, - enabled=enabled, + enabled=parsed_args.enabled, options=options, - **kwargs + **kwargs, ) except ks_exc.Conflict: if parsed_args.or_show: @@ -143,7 +146,14 @@ def take_action(self, parsed_args): class DeleteProject(command.Command): - _description = _("Delete project(s)") + _description = _( + "Delete project(s). This command will remove specified " + "existing project(s) if an active user is authorized to do " + "this. If there are resources managed by other services " + "(for example, Nova, Neutron, Cinder) associated with " + "specified project(s), delete operation will proceed " + "regardless." + ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -190,9 +200,10 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.projects) - msg = _( - "%(errors)s of %(total)s projects failed " "to delete." - ) % {'errors': errors, 'total': total} + msg = _("%(errors)s of %(total)s projects failed to delete.") % { + 'errors': errors, + 'total': total, + } raise exceptions.CommandError(msg) @@ -239,15 +250,28 @@ def get_parser(self, prog_name): 'keys and directions.' ), ) + parser.add_argument( + '--enabled', + action='store_true', + dest='is_enabled', + default=None, + help=_('List only enabled projects'), + ) + parser.add_argument( + '--disabled', + action='store_false', + dest='is_enabled', + default=None, + help=_('List only disabled projects'), + ) tag.add_tag_filtering_option_to_parser(parser, _('projects')) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ('ID', 'Name', 'Domain ID', 'Description', 'Enabled') - else: - columns = ('ID', 'Name') + columns += ('Domain ID', 'Description', 'Enabled') kwargs = {} domain_id = None @@ -277,6 +301,9 @@ def take_action(self, parsed_args): kwargs['user'] = user_id + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled + tag.get_tag_filtering_args(parsed_args, kwargs) if parsed_args.my_projects: @@ -339,16 +366,21 @@ def get_parser(self, prog_name): enable_group.add_argument( '--enable', action='store_true', + dest='enabled', + default=None, help=_('Enable project'), ) enable_group.add_argument( '--disable', - action='store_true', + action='store_false', + dest='enabled', + default=None, help=_('Disable project'), ) parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Set a property on ' @@ -371,15 +403,12 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description - if parsed_args.enable: - kwargs['enabled'] = True - if parsed_args.disable: - kwargs['enabled'] = False - options = common.get_immutable_options(parsed_args) - if options: - kwargs['options'] = options - if parsed_args.property: - kwargs.update(parsed_args.property) + if parsed_args.enabled is not None: + kwargs['enabled'] = parsed_args.enabled + if parsed_args.immutable is not None: + kwargs['options'] = {'immutable': parsed_args.immutable} + if parsed_args.properties: + kwargs.update(parsed_args.properties) tag.update_tags_in_args(parsed_args, project, kwargs) identity_client.projects.update(project.id, **kwargs) diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index ba8c02cf2b..4882c9e9cb 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -15,16 +15,25 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) +def _format_region(region): + columns = ('id', 'description', 'parent_region_id') + column_headers = ('region', 'description', 'parent_region') + return ( + column_headers, + utils.get_item_properties(region, columns), + ) + + class CreateRegion(command.ShowOne): _description = _("Create new region") @@ -50,18 +59,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - region = identity_client.regions.create( + region = identity_client.create_region( id=parsed_args.region, - parent_region=parsed_args.parent_region, + parent_region_id=parsed_args.parent_region, description=parsed_args.description, ) - region._info['region'] = region._info.pop('id') - region._info['parent_region'] = region._info.pop('parent_region_id') - region._info.pop('links', None) - return zip(*sorted(region._info.items())) + return _format_region(region) class DeleteRegion(command.Command): @@ -78,24 +84,21 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.region: try: - identity_client.regions.delete(i) + identity_client.delete_region(i) except Exception as e: result += 1 LOG.error( - _( - "Failed to delete region with " - "ID '%(region)s': %(e)s" - ), + _("Failed to delete region with ID '%(region)s': %(e)s"), {'region': i, 'e': e}, ) if result > 0: total = len(parsed_args.region) - msg = _("%(result)s of %(total)s regions failed " "to delete.") % { + msg = _("%(result)s of %(total)s regions failed to delete.") % { 'result': result, 'total': total, } @@ -115,7 +118,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.parent_region: @@ -124,7 +127,7 @@ def take_action(self, parsed_args): columns_headers = ('Region', 'Parent Region', 'Description') columns = ('ID', 'Parent Region Id', 'Description') - data = identity_client.regions.list(**kwargs) + data = identity_client.regions(**kwargs) return ( columns_headers, ( @@ -161,15 +164,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.parent_region: - kwargs['parent_region'] = parsed_args.parent_region + kwargs['parent_region_id'] = parsed_args.parent_region - identity_client.regions.update(parsed_args.region, **kwargs) + identity_client.update_region(parsed_args.region, **kwargs) class ShowRegion(command.ShowOne): @@ -185,13 +188,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - region = utils.find_resource( - identity_client.regions, parsed_args.region - ) + region = identity_client.get_region(parsed_args.region) - region._info['region'] = region._info.pop('id') - region._info['parent_region'] = region._info.pop('parent_region_id') - region._info.pop('links', None) - return zip(*sorted(region._info.items())) + return _format_region(region) diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index f71ef1f5a3..e0afb4133f 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as common_utils @@ -44,7 +44,10 @@ def get_parser(self, prog_name): '--service', metavar='', required=True, - help=_('Service responsible for the resource to limit (required)'), + help=_( + 'Service responsible for the resource to limit (required) ' + '(name or ID)' + ), ) parser.add_argument( '--default-limit', @@ -68,8 +71,7 @@ def take_action(self, parsed_args): ) region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot @@ -81,6 +83,13 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _( + "Passing 'None' to indicate no region is deprecated. " + "Instead, don't pass --region." + ) + ) registered_limit = identity_client.registered_limits.create( service, @@ -100,10 +109,10 @@ class DeleteRegisteredLimit(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - 'registered_limit_id', - metavar='', + 'registered_limits', + metavar='', nargs="+", - help=_('Registered limit to delete (ID)'), + help=_('Registered limit(s) to delete (ID)'), ) return parser @@ -111,7 +120,7 @@ def take_action(self, parsed_args): identity_client = self.app.client_manager.identity errors = 0 - for registered_limit_id in parsed_args.registered_limit_id: + for registered_limit_id in parsed_args.registered_limits: try: identity_client.registered_limits.delete(registered_limit_id) except Exception as e: @@ -128,10 +137,9 @@ def take_action(self, parsed_args): ) if errors > 0: - total = len(parsed_args.registered_limit_id) + total = len(parsed_args.registered_limits) msg = _( - "%(errors)s of %(total)s registered limits failed to " - "delete." + "%(errors)s of %(total)s registered limits failed to delete." ) % {'errors': errors, 'total': total} raise exceptions.CommandError(msg) @@ -144,7 +152,9 @@ def get_parser(self, prog_name): parser.add_argument( '--service', metavar='', - help=_('Service responsible for the resource to limit'), + help=_( + 'Service responsible for the resource to limit (name or ID)' + ), ) parser.add_argument( '--resource-name', @@ -169,8 +179,7 @@ def take_action(self, parsed_args): ) region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot @@ -182,6 +191,13 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _( + "Passing 'None' to indicate no region is deprecated. " + "Instead, don't pass --region." + ) + ) registered_limits = identity_client.registered_limits.list( service=service, @@ -217,9 +233,9 @@ def get_parser(self, prog_name): '--service', metavar='', help=_( - 'Service to be updated responsible for the resource to ' - 'limit. Either --service, --resource-name or --region must ' - 'be different than existing value otherwise it will be ' + 'Service to be updated responsible for the resource to limit ' + '(name or ID). Either --service, --resource-name or --region ' + 'must be different than existing value otherwise it will be ' 'duplicate entry' ), ) @@ -267,8 +283,7 @@ def take_action(self, parsed_args): region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot @@ -280,6 +295,10 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _("Passing 'None' to indicate no region is deprecated.") + ) registered_limit = identity_client.registered_limits.update( parsed_args.registered_limit_id, diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index c43f389943..3c580d6f8b 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -17,11 +17,11 @@ import logging -from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command +from openstack import exceptions as sdk_exc from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -29,6 +29,25 @@ LOG = logging.getLogger(__name__) +def _format_role(role): + columns = ( + "id", + "name", + "domain_id", + "description", + ) + column_headers = ( + "id", + "name", + "domain_id", + "description", + ) + return ( + column_headers, + utils.get_item_properties(role, columns), + ) + + def _add_identity_and_resource_options_to_parser(parser): system_or_domain_or_project = parser.add_mutually_exclusive_group() system_or_domain_or_project.add_argument( @@ -64,31 +83,58 @@ def _add_identity_and_resource_options_to_parser(parser): def _process_identity_and_resource_options( - parsed_args, identity_client_manager, validate_actor_existence=True + parsed_args, identity_client, validate_actor_existence=True ): def _find_user(): - try: - return common.find_user( - identity_client_manager, - parsed_args.user, - parsed_args.user_domain, - ).id - except exceptions.CommandError: - if not validate_actor_existence: - return parsed_args.user - raise + domain_id = ( + common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.user_domain, + validate_actor_existence=validate_actor_existence, + ) + if parsed_args.user_domain + else None + ) + return common._find_sdk_id( + identity_client.find_user, + name_or_id=parsed_args.user, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) def _find_group(): - try: - return common.find_group( - identity_client_manager, - parsed_args.group, - parsed_args.group_domain, - ).id - except exceptions.CommandError: - if not validate_actor_existence: - return parsed_args.group - raise + domain_id = ( + common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.group_domain, + validate_actor_existence=validate_actor_existence, + ) + if parsed_args.group_domain + else None + ) + return common._find_sdk_id( + identity_client.find_group, + name_or_id=parsed_args.group, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) + + def _find_project(): + domain_id = ( + common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, + validate_actor_existence=validate_actor_existence, + ) + if parsed_args.project_domain + else None + ) + return common._find_sdk_id( + identity_client.find_project, + name_or_id=parsed_args.project, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) kwargs = {} if parsed_args.user and parsed_args.system: @@ -96,34 +142,35 @@ def _find_group(): kwargs['system'] = parsed_args.system elif parsed_args.user and parsed_args.domain: kwargs['user'] = _find_user() - kwargs['domain'] = common.find_domain( - identity_client_manager, - parsed_args.domain, - ).id + kwargs['domain'] = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + validate_actor_existence=validate_actor_existence, + ) elif parsed_args.user and parsed_args.project: kwargs['user'] = _find_user() - kwargs['project'] = common.find_project( - identity_client_manager, - parsed_args.project, - parsed_args.project_domain, - ).id + kwargs['project'] = _find_project() elif parsed_args.group and parsed_args.system: kwargs['group'] = _find_group() kwargs['system'] = parsed_args.system elif parsed_args.group and parsed_args.domain: kwargs['group'] = _find_group() - kwargs['domain'] = common.find_domain( - identity_client_manager, - parsed_args.domain, - ).id + kwargs['domain'] = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + validate_actor_existence=validate_actor_existence, + ) elif parsed_args.group and parsed_args.project: kwargs['group'] = _find_group() - kwargs['project'] = common.find_project( - identity_client_manager, - parsed_args.project, - parsed_args.project_domain, - ).id - kwargs['os_inherit_extension_inherited'] = parsed_args.inherited + kwargs['project'] = _find_project() + else: + msg = _( + "Role not added, incorrect set of arguments " + "provided. See openstack --help for more details" + ) + raise exceptions.CommandError(msg) + + kwargs['inherited'] = parsed_args.inherited return kwargs @@ -145,7 +192,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity if ( not parsed_args.user @@ -161,18 +208,71 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.role_domain: - domain_id = common.find_domain( - identity_client, parsed_args.role_domain - ).id - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + domain_id = common._find_sdk_id( + identity_client.find_domain, name_or_id=parsed_args.role_domain + ) + role = common._find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, + domain_id=domain_id, ) - kwargs = _process_identity_and_resource_options( - parsed_args, self.app.client_manager.identity + add_kwargs = _process_identity_and_resource_options( + parsed_args, identity_client ) - identity_client.roles.grant(role.id, **kwargs) + if add_kwargs.get("domain"): + if add_kwargs.get("user"): + identity_client.assign_domain_role_to_user( + domain=add_kwargs["domain"], + user=add_kwargs["user"], + role=role, + inherited=add_kwargs["inherited"], + ) + if add_kwargs.get("group"): + identity_client.assign_domain_role_to_group( + domain=add_kwargs["domain"], + group=add_kwargs["group"], + role=role, + inherited=add_kwargs["inherited"], + ) + elif add_kwargs.get("project"): + if add_kwargs.get("user"): + identity_client.assign_project_role_to_user( + project=add_kwargs["project"], + user=add_kwargs["user"], + role=role, + inherited=add_kwargs["inherited"], + ) + if add_kwargs.get("group"): + identity_client.assign_project_role_to_group( + project=add_kwargs["project"], + group=add_kwargs["group"], + role=role, + inherited=add_kwargs["inherited"], + ) + elif add_kwargs.get("system"): + if add_kwargs["inherited"]: + LOG.warning( + _( + "'--inherited' was given, which is not supported " + "when adding a system role. This will be an error " + "in a future release." + ) + ) + # TODO(0weng): This should be an error in a future release + if add_kwargs.get("user"): + identity_client.assign_system_role_to_user( + system=add_kwargs["system"], + user=add_kwargs["user"], + role=role, + ) + if add_kwargs.get("group"): + identity_client.assign_system_role_to_group( + system=add_kwargs["system"], + group=add_kwargs["group"], + role=role, + ) class CreateRole(command.ShowOne): @@ -204,37 +304,38 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - domain_id = None + create_kwargs = {} if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + create_kwargs['domain_id'] = common._find_sdk_id( + identity_client.find_domain, name_or_id=parsed_args.domain + ) + + if parsed_args.name: + create_kwargs['name'] = parsed_args.name - options = common.get_immutable_options(parsed_args) + if parsed_args.description: + create_kwargs['description'] = parsed_args.description + + if parsed_args.immutable is not None: + create_kwargs['options'] = {"immutable": parsed_args.immutable} try: - role = identity_client.roles.create( - name=parsed_args.name, - domain=domain_id, - description=parsed_args.description, - options=options, - ) + role = identity_client.create_role(**create_kwargs) - except ks_exc.Conflict: + except sdk_exc.ConflictException: if parsed_args.or_show: - role = utils.find_resource( - identity_client.roles, - parsed_args.name, - domain_id=domain_id, + role = identity_client.find_role( + name_or_id=parsed_args.name, + domain_id=parsed_args.domain, + ignore_missing=False, ) LOG.info(_('Returning existing role %s'), role.name) else: raise - role._info.pop('links') - return zip(*sorted(role._info.items())) + return _format_role(role) class DeleteRole(command.Command): @@ -245,7 +346,7 @@ def get_parser(self, prog_name): parser.add_argument( 'roles', metavar='', - nargs="+", + nargs='+', help=_('Role(s) to delete (name or ID)'), ) parser.add_argument( @@ -256,20 +357,22 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity domain_id = None if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + domain_id = common._find_sdk_id( + identity_client.find_domain, parsed_args.domain + ) errors = 0 for role in parsed_args.roles: try: - role_obj = utils.find_resource( - identity_client.roles, role, domain_id=domain_id + role_id = common._find_sdk_id( + identity_client.find_role, + name_or_id=role, + domain_id=domain_id, ) - identity_client.roles.delete(role_obj.id) + identity_client.delete_role(role=role_id, ignore_missing=False) except Exception as e: errors += 1 LOG.error( @@ -282,7 +385,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.roles) - msg = _("%(errors)s of %(total)s roles failed " "to delete.") % { + msg = _("%(errors)s of %(total)s roles failed to delete.") % { 'errors': errors, 'total': total, } @@ -302,37 +405,34 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity if parsed_args.domain: - domain = common.find_domain( - identity_client, - parsed_args.domain, + domain = identity_client.find_domain( + name_or_id=parsed_args.domain, + ignore_missing=False, ) - columns = ('ID', 'Name', 'Domain') - data = identity_client.roles.list(domain_id=domain.id) - for role in data: - role.domain = domain.name + data = identity_client.roles(domain_id=domain.id) + return ( + ('ID', 'Name', 'Domain'), + ( + utils.get_item_properties(s, ('id', 'name')) + + (domain.name,) + for s in data + ), + ) + else: - columns = ('ID', 'Name') - data = identity_client.roles.list() - - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - formatters={}, - ) - for s in data - ), - ) + data = identity_client.roles() + return ( + ('ID', 'Name'), + (utils.get_item_properties(s, ('id', 'name')) for s in data), + ) class RemoveRole(command.Command): _description = _( - "Removes a role assignment from system/domain/project : " "user/group" + "Removes a role assignment from system/domain/project : user/group" ) def get_parser(self, prog_name): @@ -348,8 +448,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - + identity_client = self.app.client_manager.sdk_connection.identity if ( not parsed_args.user and not parsed_args.domain @@ -364,19 +463,65 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.role_domain: - domain_id = common.find_domain( - identity_client, parsed_args.role_domain - ).id - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.role_domain, + ) + role = common._find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, + domain_id=domain_id, ) - kwargs = _process_identity_and_resource_options( + remove_kwargs = _process_identity_and_resource_options( parsed_args, - self.app.client_manager.identity, + identity_client, validate_actor_existence=False, ) - identity_client.roles.revoke(role.id, **kwargs) + + if remove_kwargs.get("domain"): + if remove_kwargs.get("user"): + identity_client.unassign_domain_role_from_user( + domain=remove_kwargs["domain"], + user=remove_kwargs["user"], + role=role, + inherited=remove_kwargs["inherited"], + ) + if remove_kwargs.get("group"): + identity_client.unassign_domain_role_from_group( + domain=remove_kwargs["domain"], + group=remove_kwargs["group"], + role=role, + inherited=remove_kwargs["inherited"], + ) + elif remove_kwargs.get("project"): + if remove_kwargs.get("user"): + identity_client.unassign_project_role_from_user( + project=remove_kwargs["project"], + user=remove_kwargs["user"], + role=role, + inherited=remove_kwargs["inherited"], + ) + if remove_kwargs.get("group"): + identity_client.unassign_project_role_from_group( + project=remove_kwargs["project"], + group=remove_kwargs["group"], + role=role, + inherited=remove_kwargs["inherited"], + ) + elif remove_kwargs.get("system"): + if remove_kwargs.get("user"): + identity_client.unassign_system_role_from_user( + system=remove_kwargs["system"], + user=remove_kwargs["user"], + role=role, + ) + if remove_kwargs.get("group"): + identity_client.unassign_system_role_from_group( + system=remove_kwargs["system"], + group=remove_kwargs["group"], + role=role, + ) class SetRole(command.Command): @@ -408,25 +553,33 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + + update_kwargs = {} + if parsed_args.description: + update_kwargs["description"] = parsed_args.description + if parsed_args.name: + update_kwargs["name"] = parsed_args.name domain_id = None if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + ) + update_kwargs["domain_id"] = domain_id - options = common.get_immutable_options(parsed_args) - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id - ) + if parsed_args.immutable is not None: + update_kwargs["options"] = {"immutable": parsed_args.immutable} - identity_client.roles.update( - role.id, - name=parsed_args.name, - description=parsed_args.description, - options=options, + role = common._find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, + domain_id=domain_id, ) + update_kwargs["role"] = role + + identity_client.update_role(**update_kwargs) class ShowRole(command.ShowOne): @@ -447,17 +600,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity domain_id = None if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + ) - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + role = identity_client.find_role( + name_or_id=parsed_args.role, + domain_id=domain_id, + ignore_missing=False, ) - role._info.pop('links') - return zip(*sorted(role._info.items())) + return _format_role(role) diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 9753297933..78c010f0e1 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -13,9 +13,7 @@ """Identity v3 Assignment action implementations""" -from openstack import exceptions as sdk_exceptions -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -51,15 +49,6 @@ def _get_ids(attr): ) -def _find_sdk_id(find_command, name_or_id, **kwargs): - try: - return find_command( - name_or_id=name_or_id, ignore_missing=False, **kwargs - ).id - except sdk_exceptions.ForbiddenException: - return name_or_id - - class ListRoleAssignment(command.Lister): _description = _("List role assignments") @@ -135,27 +124,34 @@ def take_action(self, parsed_args): role_id = None role_domain_id = None if parsed_args.role_domain: - role_domain_id = _find_sdk_id( + role_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain, ) if parsed_args.role: - role_id = _find_sdk_id( + role_id = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=role_domain_id, ) + user_domain_id = None + if parsed_args.user_domain: + user_domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.user_domain, + ) + user_id = None if parsed_args.user: - user_id = _find_sdk_id( + user_id = common._find_sdk_id( identity_client.find_user, name_or_id=parsed_args.user, - domain_id=parsed_args.user_domain, + domain_id=user_domain_id, ) elif parsed_args.authuser: if auth_ref: - user_id = _find_sdk_id( + user_id = common._find_sdk_id( identity_client.find_user, name_or_id=auth_ref.user_id, ) @@ -166,33 +162,47 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, + ) + project_id = None if parsed_args.project: - project_id = _find_sdk_id( + project_id = common._find_sdk_id( identity_client.find_project, name_or_id=common._get_token_resource( identity_client, 'project', parsed_args.project ), - domain_id=parsed_args.project_domain, + domain_id=project_domain_id, ) elif parsed_args.authproject: if auth_ref: - project_id = _find_sdk_id( + project_id = common._find_sdk_id( identity_client.find_project, name_or_id=auth_ref.project_id, ) + group_domain_id = None + if parsed_args.group_domain: + group_domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.group_domain, + ) + group_id = None if parsed_args.group: - group_id = _find_sdk_id( + group_id = common._find_sdk_id( identity_client.find_group, name_or_id=parsed_args.group, - domain_id=parsed_args.group_domain, + domain_id=group_domain_id, ) include_names = True if parsed_args.names else None diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index b7d4befc96..53a706299e 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -37,11 +37,11 @@ def _format_service(service): 'description', ) column_headers = ( - 'ID', - 'Name', - 'Type', - 'Enabled', - 'Description', + 'id', + 'name', + 'type', + 'enabled', + 'description', ) return ( @@ -135,9 +135,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.service) - msg = _( - "%(result)s of %(total)s services failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s services failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -157,12 +158,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity + columns: tuple[str, ...] = ('id', 'name', 'type') + column_headers: tuple[str, ...] = ('ID', 'Name', 'Type') if parsed_args.long: - columns = ('id', 'name', 'type', 'description', 'is_enabled') - column_headers = ('ID', 'Name', 'Type', 'Description', 'Enabled') - else: - columns = ('id', 'name', 'type') - column_headers = ('ID', 'Name', 'Type') + columns += ('description', 'is_enabled') + column_headers += ('Description', 'Enabled') data = identity_client.services() @@ -225,7 +225,8 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description - kwargs['is_enabled'] = parsed_args.is_enabled + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled identity_client.update_service(service.id, **kwargs) diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 79869ee60a..02aae66bf8 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -15,16 +15,39 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) +def _format_service_provider(sp): + column_headers = ( + 'id', + 'enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', + ) + columns = ( + 'id', + 'is_enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', + ) + return ( + column_headers, + utils.get_item_properties(sp, columns), + ) + + class CreateServiceProvider(command.ShowOne): _description = _("Create new service provider") @@ -54,22 +77,21 @@ def get_parser(self, prog_name): metavar='', required=True, help=_( - 'A service URL where SAML assertions are being sent ' - '(required)' + 'A service URL where SAML assertions are being sent (required)' ), ) enable_service_provider = parser.add_mutually_exclusive_group() enable_service_provider.add_argument( '--enable', - dest='enabled', + dest='is_enabled', action='store_true', default=True, help=_('Enable the service provider (default)'), ) enable_service_provider.add_argument( '--disable', - dest='enabled', + dest='is_enabled', action='store_false', help=_('Disable the service provider'), ) @@ -77,17 +99,27 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.identity - sp = service_client.federation.service_providers.create( - id=parsed_args.service_provider_id, - auth_url=parsed_args.auth_url, - description=parsed_args.description, - enabled=parsed_args.enabled, - sp_url=parsed_args.service_provider_url, - ) + service_client = self.app.client_manager.sdk_connection.identity + + kwargs = {} + + kwargs = {'id': parsed_args.service_provider_id} - sp._info.pop('links', None) - return zip(*sorted(sp._info.items())) + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled + + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.auth_url: + kwargs['auth_url'] = parsed_args.auth_url + + if parsed_args.service_provider_url: + kwargs['sp_url'] = parsed_args.service_provider_url + + sp = service_client.create_service_provider(**kwargs) + + return _format_service_provider(sp) class DeleteServiceProvider(command.Command): @@ -104,11 +136,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.identity + service_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.service_provider: try: - service_client.federation.service_providers.delete(i) + service_client.delete_service_provider(i) except Exception as e: result += 1 LOG.error( @@ -122,8 +154,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.service_provider) msg = _( - "%(result)s of %(total)s service providers failed" - " to delete." + "%(result)s of %(total)s service providers failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -132,24 +163,32 @@ class ListServiceProvider(command.Lister): _description = _("List service providers") def take_action(self, parsed_args): - service_client = self.app.client_manager.identity - data = service_client.federation.service_providers.list() - - column_headers = ('ID', 'Enabled', 'Description', 'Auth URL') + service_client = self.app.client_manager.sdk_connection.identity + data = service_client.service_providers() + + column_headers = ( + 'ID', + 'Enabled', + 'Description', + 'Auth URL', + 'Service Provider URL', + 'Relay State Prefix', + ) + columns = ( + 'id', + 'is_enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', + ) return ( column_headers, - ( - utils.get_item_properties( - s, - column_headers, - formatters={}, - ) - for s in data - ), + (utils.get_item_properties(s, columns) for s in data), ) -class SetServiceProvider(command.Command): +class SetServiceProvider(command.ShowOne): _description = _("Set service provider properties") def get_parser(self, prog_name): @@ -163,8 +202,7 @@ def get_parser(self, prog_name): '--auth-url', metavar='', help=_( - 'New Authentication URL of remote ' - 'federated service provider' + 'New Authentication URL of remote federated service provider' ), ) @@ -181,33 +219,44 @@ def get_parser(self, prog_name): enable_service_provider = parser.add_mutually_exclusive_group() enable_service_provider.add_argument( '--enable', + dest='is_enabled', action='store_true', + default=None, help=_('Enable the service provider'), ) enable_service_provider.add_argument( '--disable', - action='store_true', + dest='is_enabled', + action='store_false', + default=None, help=_('Disable the service provider'), ) return parser def take_action(self, parsed_args): - federation_client = self.app.client_manager.identity.federation + service_client = self.app.client_manager.sdk_connection.identity - enabled = None - if parsed_args.enable is True: - enabled = True - elif parsed_args.disable is True: - enabled = False + kwargs = {} - federation_client.service_providers.update( + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled + + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.auth_url: + kwargs['auth_url'] = parsed_args.auth_url + + if parsed_args.service_provider_url: + kwargs['sp_url'] = parsed_args.service_provider_url + + service_provider = service_client.update_service_provider( parsed_args.service_provider, - enabled=enabled, - description=parsed_args.description, - auth_url=parsed_args.auth_url, - sp_url=parsed_args.service_provider_url, + **kwargs, ) + return _format_service_provider(service_provider) + class ShowServiceProvider(command.ShowOne): _description = _("Display service provider details") @@ -222,12 +271,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.identity - service_provider = utils.find_resource( - service_client.federation.service_providers, + service_client = self.app.client_manager.sdk_connection.identity + service_provider = service_client.find_service_provider( parsed_args.service_provider, - id=parsed_args.service_provider, + ignore_missing=False, ) - service_provider._info.pop('links', None) - return zip(*sorted(service_provider._info.items())) + return _format_service_provider(service_provider) diff --git a/openstackclient/identity/v3/tag.py b/openstackclient/identity/v3/tag.py index ce1dfe8919..41493c9936 100644 --- a/openstackclient/identity/v3/tag.py +++ b/openstackclient/identity/v3/tag.py @@ -83,7 +83,7 @@ def add_tag_option_to_parser_for_create(parser, resource_name): metavar='', default=[], help=_( - 'Tag to be added to the %s ' '(repeat option to set multiple tags)' + 'Tag to be added to the %s (repeat option to set multiple tags)' ) % resource_name, ) @@ -97,7 +97,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name): metavar='', default=[], help=_( - 'Tag to be added to the %s ' '(repeat option to set multiple tags)' + 'Tag to be added to the %s (repeat option to set multiple tags)' ) % resource_name, ) @@ -114,6 +114,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name): parser.add_argument( '--remove-tag', action='append', + dest='remove_tags', metavar='', default=[], help=_( @@ -128,8 +129,8 @@ def update_tags_in_args(parsed_args, obj, args): if parsed_args.clear_tags: args['tags'] = [] obj.tags = [] - if parsed_args.remove_tag: - args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tag)) + if parsed_args.remove_tags: + args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tags)) return if parsed_args.tags: args['tags'] = sorted(set(obj.tags).union(set(parsed_args.tags))) diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index 8f2dc09405..05e374caf0 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -15,10 +15,10 @@ """Identity v3 Token action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -37,6 +37,7 @@ def get_parser(self, prog_name): parser.add_argument( '--role', metavar='', + dest='roles', action='append', default=[], required=True, @@ -52,7 +53,7 @@ def take_action(self, parsed_args): # NOTE(stevemar): We want a list of role ids roles = [] - for role in parsed_args.role: + for role in parsed_args.roles: role_id = utils.find_resource( identity_client.roles, role, @@ -136,8 +137,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Project that consumer wants to access (name or ID)' - ' (required)' + 'Project that consumer wants to access (name or ID) (required)' ), required=True, ) diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index 33fee4a906..80808aa12b 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -14,13 +14,14 @@ """Identity v3 Trust action implementations""" import datetime +import itertools import logging -from keystoneclient import exceptions as identity_exc -from osc_lib.command import command +from openstack import exceptions as sdk_exceptions from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -28,6 +29,25 @@ LOG = logging.getLogger(__name__) +def _format_trust(trust): + columns = ( + 'expires_at', + 'id', + 'is_impersonation', + 'project_id', + 'redelegated_trust_id', + 'redelegation_count', + 'remaining_uses', + 'roles', + 'trustee_user_id', + 'trustor_user_id', + ) + return ( + columns, + utils.get_item_properties(trust, columns), + ) + + class CreateTrust(command.ShowOne): _description = _("Create new trust") @@ -52,6 +72,7 @@ def get_parser(self, prog_name): parser.add_argument( '--role', metavar='', + dest='roles', action='append', default=[], help=_( @@ -62,7 +83,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--impersonate', - dest='impersonate', + dest='is_impersonation', action='store_true', default=False, help=_( @@ -92,58 +113,91 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + + kwargs = {} # NOTE(stevemar): Find the two users, project and roles that # are necessary for making a trust usable, the API dictates that # trustee, project and role are optional, but that makes the trust # pointless, and trusts are immutable, so let's enforce it at the # client level. - trustor_id = common.find_user( - identity_client, parsed_args.trustor, parsed_args.trustor_domain - ).id - trustee_id = common.find_user( - identity_client, parsed_args.trustee, parsed_args.trustee_domain - ).id - project_id = common.find_project( - identity_client, parsed_args.project, parsed_args.project_domain - ).id - - role_ids = [] - for role in parsed_args.role: + try: + if parsed_args.trustor_domain: + trustor_domain_id = identity_client.find_domain( + parsed_args.trustor_domain, ignore_missing=False + ).id + trustor_id = identity_client.find_user( + parsed_args.trustor, + ignore_missing=False, + domain_id=trustor_domain_id, + ).id + else: + trustor_id = identity_client.find_user( + parsed_args.trustor, ignore_missing=False + ).id + kwargs['trustor_user_id'] = trustor_id + except sdk_exceptions.ForbiddenException: + kwargs['trustor_user_id'] = parsed_args.trustor + + try: + if parsed_args.trustee_domain: + trustee_domain_id = identity_client.find_domain( + parsed_args.trustee_domain, ignore_missing=False + ).id + trustee_id = identity_client.find_user( + parsed_args.trustee, + ignore_missing=False, + domain_id=trustee_domain_id, + ).id + else: + trustee_id = identity_client.find_user( + parsed_args.trustee, ignore_missing=False + ).id + kwargs['trustee_user_id'] = trustee_id + except sdk_exceptions.ForbiddenException: + kwargs['trustee_user_id'] = parsed_args.trustee + + try: + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + parsed_args.project_domain, ignore_missing=False + ).id + project_id = identity_client.find_project( + parsed_args.project, + ignore_missing=False, + domain_id=project_domain_id, + ).id + else: + project_id = identity_client.find_project( + parsed_args.project, ignore_missing=False + ).id + kwargs['project_id'] = project_id + except sdk_exceptions.ForbiddenException: + kwargs['project_id'] = parsed_args.project + + roles = [] + for role in parsed_args.roles: try: - role_id = utils.find_resource( - identity_client.roles, - role, + role_id = identity_client.find_role( + role, ignore_missing=False ).id - except identity_exc.Forbidden: + except sdk_exceptions.ForbiddenException: role_id = role - role_ids.append(role_id) + roles.append({"id": role_id}) + kwargs['roles'] = roles - expires_at = None if parsed_args.expiration: expires_at = datetime.datetime.strptime( parsed_args.expiration, '%Y-%m-%dT%H:%M:%S' ) + kwargs['expires_at'] = expires_at - trust = identity_client.trusts.create( - trustee_id, - trustor_id, - impersonation=parsed_args.impersonate, - project=project_id, - role_ids=role_ids, - expires_at=expires_at, - ) + kwargs['impersonation'] = bool(parsed_args.is_impersonation) - trust._info.pop('roles_links', None) - trust._info.pop('links', None) + trust = identity_client.create_trust(**kwargs) - # Format roles into something sensible - roles = trust._info.pop('roles') - msg = ' '.join(r['name'] for r in roles) - trust._info['roles'] = msg - - return zip(*sorted(trust._info.items())) + return _format_trust(trust) class DeleteTrust(command.Command): @@ -160,13 +214,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 for trust in parsed_args.trust: try: - trust_obj = utils.find_resource(identity_client.trusts, trust) - identity_client.trusts.delete(trust_obj.id) + trust_obj = identity_client.find_trust( + trust, ignore_missing=False + ) + identity_client.delete_trust(trust_obj.id) except Exception as e: errors += 1 LOG.error( @@ -179,7 +235,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.trust) - msg = _("%(errors)s of %(total)s trusts failed " "to delete.") % { + msg = _("%(errors)s of %(total)s trusts failed to delete.") % { 'errors': errors, 'total': total, } @@ -220,7 +276,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity auth_ref = self.app.client_manager.auth_ref if parsed_args.authuser and any( @@ -243,38 +299,70 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.authuser: - if auth_ref: - user = common.find_user(identity_client, auth_ref.user_id) - # We need two calls here as we want trusts with - # either the trustor or the trustee set to current user - # using a single call would give us trusts with both - # trustee and trustor set to current user - data1 = identity_client.trusts.list(trustor_user=user) - data2 = identity_client.trusts.list(trustee_user=user) - data = set(data1 + data2) + # We need two calls here as we want trusts with + # either the trustor or the trustee set to current user + # using a single call would give us trusts with both + # trustee and trustor set to current user + data = list( + { + x.id: x + for x in itertools.chain( + identity_client.trusts( + trustor_user_id=auth_ref.user_id + ), + identity_client.trusts( + trustee_user_id=auth_ref.user_id + ), + ) + }.values() + ) else: trustor = None if parsed_args.trustor: - trustor = common.find_user( - identity_client, - parsed_args.trustor, - parsed_args.trustor_domain, - ) + try: + if parsed_args.trustor_domain: + trustor_domain_id = identity_client.find_domain( + parsed_args.trustor_domain, ignore_missing=False + ).id + trustor_id = identity_client.find_user( + parsed_args.trustor, + ignore_missing=False, + domain_id=trustor_domain_id, + ).id + else: + trustor_id = identity_client.find_user( + parsed_args.trustor, ignore_missing=False + ).id + trustor = trustor_id + except sdk_exceptions.ForbiddenException: + trustor = parsed_args.trustor trustee = None if parsed_args.trustee: - trustee = common.find_user( - identity_client, - parsed_args.trustor, - parsed_args.trustor_domain, - ) - - data = self.app.client_manager.identity.trusts.list( - trustor_user=trustor, - trustee_user=trustee, + try: + if parsed_args.trustee_domain: + trustee_domain_id = identity_client.find_domain( + parsed_args.trustee_domain, ignore_missing=False + ).id + trustee_id = identity_client.find_user( + parsed_args.trustee, + ignore_missing=False, + domain_id=trustee_domain_id, + ).id + else: + trustee_id = identity_client.find_user( + parsed_args.trustee, ignore_missing=False + ).id + trustee = trustee_id + except sdk_exceptions.ForbiddenException: + trustee = parsed_args.trustee + + data = identity_client.trusts( + trustor_user_id=trustor, + trustee_user_id=trustee, ) - columns = ( + column_headers = ( 'ID', 'Expires At', 'Impersonation', @@ -282,9 +370,17 @@ def take_action(self, parsed_args): 'Trustee User ID', 'Trustor User ID', ) + columns = ( + 'id', + 'expires_at', + 'is_impersonation', + 'project_id', + 'trustee_user_id', + 'trustor_user_id', + ) return ( - columns, + column_headers, ( utils.get_item_properties( s, @@ -309,15 +405,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - trust = utils.find_resource(identity_client.trusts, parsed_args.trust) - - trust._info.pop('roles_links', None) - trust._info.pop('links', None) - - # Format roles into something sensible - roles = trust._info.pop('roles') - msg = ' '.join(r['name'] for r in roles) - trust._info['roles'] = msg + identity_client = self.app.client_manager.sdk_connection.identity + trust = identity_client.find_trust( + parsed_args.trust, ignore_missing=False + ) - return zip(*sorted(trust._info.items())) + return _format_trust(trust) diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py index d26035d35e..e1efc15cfa 100644 --- a/openstackclient/identity/v3/unscoped_saml.py +++ b/openstackclient/identity/v3/unscoped_saml.py @@ -17,9 +17,9 @@ the user can list domains and projects they are allowed to access, and request a scoped token.""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 38b9a0ab0c..bec1c620c5 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -17,12 +17,13 @@ import copy import logging +import typing as ty from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -40,6 +41,7 @@ def _format_user(user): 'name', 'description', 'password_expires_at', + 'options', ) column_headers = ( 'default_project_id', @@ -50,6 +52,7 @@ def _format_user(user): 'name', 'description', 'password_expires_at', + 'options', ) return ( column_headers, @@ -58,7 +61,7 @@ def _format_user(user): def _get_options_for_user(identity_client, parsed_args): - options = {} + options: dict[str, ty.Any] = {} if parsed_args.ignore_lockout_failure_attempts: options['ignore_lockout_failure_attempts'] = True if parsed_args.no_ignore_lockout_failure_attempts: @@ -79,9 +82,9 @@ def _get_options_for_user(identity_client, parsed_args): options['multi_factor_auth_enabled'] = True if parsed_args.disable_multi_factor_auth: options['multi_factor_auth_enabled'] = False - if parsed_args.multi_factor_auth_rule: + if parsed_args.multi_factor_auth_rules: auth_rules = [ - rule.split(",") for rule in parsed_args.multi_factor_auth_rule + rule.split(",") for rule in parsed_args.multi_factor_auth_rules ] if auth_rules: options['multi_factor_auth_rules'] = auth_rules @@ -172,7 +175,8 @@ def _add_user_options(parser): parser.add_argument( '--multi-factor-auth-rule', metavar='', - action="append", + dest='multi_factor_auth_rules', + action='append', default=[], help=_( 'Set multi-factor auth rules. For example, to set a rule ' @@ -249,53 +253,75 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity + kwargs = {} + domain_id = None if parsed_args.domain: domain_id = identity_client.find_domain( - name_or_id=parsed_args.domain, + parsed_args.domain, ignore_missing=False, ).id + kwargs['domain_id'] = domain_id - project_id = None if parsed_args.project: - project_id = identity_client.find_project( - name_or_id=parsed_args.project, + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + parsed_args.project_domain, + ignore_missing=False, + ).id + kwargs['default_project_id'] = identity_client.find_project( + parsed_args.project, ignore_missing=False, - domain_id=domain_id, + domain_id=project_domain_id, ).id + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.email: + kwargs['email'] = parsed_args.email + is_enabled = True if parsed_args.disable: is_enabled = False - if parsed_args.password_prompt: - parsed_args.password = utils.get_password(self.app.stdin) - if not parsed_args.password: + password = None + if parsed_args.password: + password = parsed_args.password + elif parsed_args.password_prompt: + password = utils.get_password(self.app.stdin) + + if not password: LOG.warning( _( "No password was supplied, authentication will fail " "when a user does not have a password." ) ) + else: + kwargs['password'] = password + options = _get_options_for_user(identity_client, parsed_args) + if options: + kwargs['options'] = options try: user = identity_client.create_user( - default_project_id=project_id, - description=parsed_args.description, - domain_id=domain_id, - email=parsed_args.email, is_enabled=is_enabled, name=parsed_args.name, - password=parsed_args.password, - options=options, + **kwargs, ) except sdk_exc.ConflictException: if parsed_args.or_show: + kwargs = {} + if domain_id: + kwargs['domain_id'] = domain_id + user = identity_client.find_user( - name_or_id=parsed_args.name, - domain_id=domain_id, + parsed_args.name, ignore_missing=False, + **kwargs, ) LOG.info(_('Returning existing user %s'), user.name) else: @@ -357,7 +383,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.users) - msg = _("%(errors)s of %(total)s users failed " "to delete.") % { + msg = _("%(errors)s of %(total)s users failed to delete.") % { 'errors': errors, 'total': total, } @@ -391,6 +417,26 @@ def get_parser(self, prog_name): default=False, help=_('List additional fields in output'), ) + parser.add_argument( + '--enabled', + action='store_true', + dest='is_enabled', + default=None, + help=_( + 'List only enabled users, does nothing with ' + '--project and --group' + ), + ) + parser.add_argument( + '--disabled', + action='store_false', + dest='is_enabled', + default=None, + help=_( + 'List only disabled users, does nothing with ' + '--project and --group' + ), + ) return parser def take_action(self, parsed_args): @@ -400,6 +446,7 @@ def take_action(self, parsed_args): if parsed_args.domain: domain = identity_client.find_domain( name_or_id=parsed_args.domain, + ignore_missing=False, ).id group = None @@ -410,6 +457,9 @@ def take_action(self, parsed_args): ignore_missing=False, ).id + if parsed_args.is_enabled is not None: + enabled = parsed_args.is_enabled + if parsed_args.project: if domain is not None: project = identity_client.find_project( @@ -423,15 +473,13 @@ def take_action(self, parsed_args): ignore_missing=False, ).id - assignments = identity_client.role_assignments_filter( - project=project - ) - # NOTE(stevemar): If a user has more than one role on a project # then they will have two entries in the returned data. Since we # are looking for any role, let's just track unique user IDs. user_ids = set() - for assignment in assignments: + for assignment in identity_client.role_assignments( + scope_project_id=project + ): if assignment.user: user_ids.add(assignment.user['id']) @@ -448,9 +496,15 @@ def take_action(self, parsed_args): group=group, ) else: - data = identity_client.users( - domain_id=domain, - ) + if parsed_args.is_enabled is not None: + data = identity_client.users( + domain_id=domain, + is_enabled=enabled, + ) + else: + data = identity_client.users( + domain_id=domain, + ) # Column handling if parsed_args.long: @@ -592,10 +646,12 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.project: - project_domain_id = identity_client.find_domain( - name_or_id=parsed_args.project_domain, - ignore_missing=False, - ).id + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + name_or_id=parsed_args.project_domain, + ignore_missing=False, + ).id project_id = identity_client.find_project( name_or_id=parsed_args.project, ignore_missing=False, @@ -636,6 +692,13 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity + conn = self.app.client_manager.sdk_connection + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) # FIXME(gyee): there are two scenarios: # @@ -678,7 +741,9 @@ def take_action(self, parsed_args): ) identity_client.update_user( - current_password=current_password, password=password + user=user_id, + current_password=current_password, + password=password, ) diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index d5ee52b35e..f75b3e6820 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# import logging @@ -21,13 +20,11 @@ LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '2' API_VERSION_OPTION = 'os_image_api_version' API_NAME = 'image' -API_VERSIONS = { - '1': 'openstack.connection.Connection', - '2': 'openstack.connection.Connection', -} +API_VERSIONS = ('1', '2') def make_client(instance): @@ -49,3 +46,8 @@ def build_option_parser(parser): % DEFAULT_API_VERSION, ) return parser + + +def check_api_version(check_version): + # SDK supports auto-negotiation for us: always return True + return True diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index ecbb2050bb..0ea7eca710 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -19,23 +19,18 @@ import logging import os import sys +import typing as ty from cliff import columns as cliff_columns from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ -if os.name == "nt": - import msvcrt -else: - msvcrt = None - - CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' @@ -53,7 +48,6 @@ "ploop", ] - LOG = logging.getLogger(__name__) @@ -74,10 +68,7 @@ def _get_columns(item): ) -_formatters = {} - - -class HumanReadableSizeColumn(cliff_columns.FormattableColumn): +class HumanReadableSizeColumn(cliff_columns.FormattableColumn[int]): def human_readable(self): """Return a formatted visibility string @@ -91,7 +82,7 @@ def human_readable(self): return '' -class VisibilityColumn(cliff_columns.FormattableColumn): +class VisibilityColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): """Return a formatted visibility string @@ -323,8 +314,10 @@ def take_action(self, parsed_args): else: # Read file from stdin if not sys.stdin.isatty(): - if msvcrt: - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + if os.name == "nt": + import msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) # type: ignore if hasattr(sys.stdin, 'buffer'): kwargs['data'] = sys.stdin.buffer else: @@ -345,9 +338,12 @@ def take_action(self, parsed_args): if image: display_columns, columns = _get_columns(image) - _formatters['properties'] = format_columns.DictColumn data = utils.get_item_properties( - image, columns, formatters=_formatters + image, + columns, + formatters={ + 'properties': format_columns.DictColumn, + }, ) return (display_columns, data) elif info: @@ -384,8 +380,7 @@ def take_action(self, parsed_args): except Exception as e: result += 1 msg = _( - "Failed to delete image with name or " - "ID '%(image)s': %(e)s" + "Failed to delete image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) @@ -468,7 +463,7 @@ def take_action(self, parsed_args): kwargs['is_private'] = True if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -481,7 +476,7 @@ def take_action(self, parsed_args): 'owner_id', 'properties', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -499,19 +494,19 @@ def take_action(self, parsed_args): column_headers = columns # List of image data received - data = list(image_client.images(**kwargs)) + images = list(image_client.images(**kwargs)) if parsed_args.property: # NOTE(dtroyer): coerce to a list to subscript it in py3 attr, value = list(parsed_args.property.items())[0] api_utils.simple_filter( - data, + images, attr=attr, value=value, property_field='properties', ) - data = utils.sort_items(data, parsed_args.sort) + data = utils.sort_items(images, parsed_args.sort) return ( column_headers, @@ -534,6 +529,16 @@ class SaveImage(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + "--chunk-size", + type=int, + default=1024, + metavar="", + help=_( + "Size in bytes to read from the wire and buffer at one " + "time (default: 1024)" + ), + ) parser.add_argument( "--file", metavar="", @@ -556,7 +561,12 @@ def take_action(self, parsed_args): if output_file is None: output_file = getattr(sys.stdout, "buffer", sys.stdout) - image_client.download_image(image.id, stream=True, output=output_file) + image_client.download_image( + image.id, + stream=True, + output=output_file, + chunk_size=parsed_args.chunk_size, + ) class SetImage(command.Command): @@ -775,8 +785,10 @@ def take_action(self, parsed_args): # Read file from stdin if sys.stdin.isatty() is not True: if parsed_args.stdin: - if msvcrt: - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + if os.name == "nt": + import msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) # type: ignore if hasattr(sys.stdin, 'buffer'): kwargs['data'] = sys.stdin.buffer else: @@ -828,11 +840,13 @@ def take_action(self, parsed_args): parsed_args.image, ignore_missing=False ) + formatters: dict[ + str, type[cliff_columns.FormattableColumn[ty.Any]] + ] = { + 'properties': format_columns.DictColumn, + } if parsed_args.human_readable: - _formatters['size'] = HumanReadableSizeColumn + formatters['size'] = HumanReadableSizeColumn display_columns, columns = _get_columns(image) - _formatters['properties'] = format_columns.DictColumn - data = utils.get_item_properties( - image, columns, formatters=_formatters - ) + data = utils.get_item_properties(image, columns, formatters=formatters) return (display_columns, data) diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index c69b7b592f..952d9ed01b 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -17,10 +17,10 @@ import datetime import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -37,14 +37,18 @@ def _format_image_cache(cached_images): image_obj = copy.deepcopy(image) image_obj['state'] = 'cached' image_obj['last_accessed'] = ( - datetime.datetime.utcfromtimestamp( - image['last_accessed'] - ).isoformat() + datetime.datetime.fromtimestamp( + image['last_accessed'], tz=datetime.timezone.utc + ) + .replace(tzinfo=None) + .isoformat() ) image_obj['last_modified'] = ( - datetime.datetime.utcfromtimestamp( - image['last_modified'] - ).isoformat() + datetime.datetime.fromtimestamp( + image['last_modified'], tz=datetime.timezone.utc + ) + .replace(tzinfo=None) + .isoformat() ) image_list.append(image_obj) elif item == "queued_images": @@ -131,8 +135,7 @@ def take_action(self, parsed_args): except Exception as e: failures += 1 msg = _( - "Failed to queue image with name or " - "ID '%(image)s': %(e)s" + "Failed to queue image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) @@ -171,8 +174,7 @@ def take_action(self, parsed_args): except Exception as e: failures += 1 msg = _( - "Failed to delete image with name or " - "ID '%(image)s': %(e)s" + "Failed to delete image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 46d374e9ec..cbb6d874de 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -17,31 +17,28 @@ import argparse from base64 import b64encode +import copy import logging import os import sys +import typing as ty +import urllib.parse -from cinderclient import api_versions from openstack import exceptions as sdk_exceptions from openstack.image import image_signer +from openstack import utils as sdk_utils from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.common import progressbar from openstackclient.i18n import _ from openstackclient.identity import common as identity_common -if os.name == "nt": - import msvcrt -else: - msvcrt = None - - CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' @@ -58,9 +55,21 @@ "iso", "ploop", ] +# A list of openstacksdk Image object attributes (values) that named +# differently from actual properties stored by Glance (keys). +IMAGE_ATTRIBUTES_CUSTOM_NAMES = { + 'os_hidden': 'is_hidden', + 'protected': 'is_protected', + 'os_hash_algo': 'hash_algo', + 'os_hash_value': 'hash_value', + 'img_config_drive': 'needs_config_drive', + 'os_secure_boot': 'needs_secure_boot', + 'hw_vif_multiqueue_enabled': 'is_hw_vif_multiqueue_enabled', + 'hw_boot_menu': 'is_hw_boot_menu_enabled', + 'auto_disk_config': 'has_auto_disk_config', +} MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"] - LOG = logging.getLogger(__name__) @@ -89,6 +98,9 @@ def _format_image(image, human_readable=False): 'virtual_size', 'min_ram', 'schema', + 'is_hidden', + 'hash_algo', + 'hash_value', ] # TODO(gtema/anybody): actually it should be possible to drop this method, @@ -154,8 +166,10 @@ def get_data_from_stdin(): image = sys.stdin if hasattr(sys.stdin, 'buffer'): image = sys.stdin.buffer - if msvcrt: - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + if os.name == "nt": + import msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) # type: ignore return image else: @@ -359,8 +373,7 @@ def get_parser(self, prog_name): action="store_true", default=False, help=_( - "Show upload progress bar " - "(ignored if passing data via stdin)" + "Show upload progress bar (ignored if passing data via stdin)" ), ) parser.add_argument( @@ -401,8 +414,7 @@ def get_parser(self, prog_name): metavar="", action='append', help=_( - "Set a tag on this image " - "(repeat option to set multiple tags)" + "Set a tag on this image (repeat option to set multiple tags)" ), ) parser.add_argument( @@ -421,8 +433,8 @@ def get_parser(self, prog_name): identity_common.add_project_domain_option_to_parser(parser) for deadopt in self.deadopts: parser.add_argument( - "--%s" % deadopt, - metavar="<%s>" % deadopt, + f"--{deadopt}", + metavar=f"<{deadopt}>", dest=deadopt.replace('-', '_'), help=argparse.SUPPRESS, ) @@ -434,7 +446,7 @@ def _take_action_image(self, parsed_args): # Build an attribute dict from the parsed args, only include # attributes that were actually set on the command line - kwargs = {'allow_duplicates': True} + kwargs: dict[str, ty.Any] = {'allow_duplicates': True} copy_attrs = ( 'name', 'id', @@ -488,7 +500,7 @@ def _take_action_image(self, parsed_args): fp = open(parsed_args.filename, 'rb') except FileNotFoundError: raise exceptions.CommandError( - '%r is not a valid file' % parsed_args.filename, + f'{parsed_args.filename!r} is not a valid file', ) else: fp = get_data_from_stdin() @@ -539,7 +551,7 @@ def _take_action_image(self, parsed_args): sign_cert_id = parsed_args.sign_cert_id signer = image_signer.ImageSigner() try: - pw = utils.get_password( + pw: str | None = utils.get_password( self.app.stdin, prompt=( "Please enter private key password, leave " @@ -550,12 +562,11 @@ def _take_action_image(self, parsed_args): 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) + signer.load_private_key( + sign_key_path, + password=pw.encode() if pw else None, + ) except Exception: msg = _( "Error during sign operation: private key " @@ -576,10 +587,13 @@ def _take_action_image(self, parsed_args): if parsed_args.filename: fp.close() + # NOTE(pas-ha): create_image returns the image object as it was created + # before the data was uploaded, need a refresh to show the final state + image = image_client.get_image(image) return _format_image(image) def _take_action_volume(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume unsupported_opts = { # 'name', # 'name' is a positional argument and will always exist @@ -610,12 +624,14 @@ def _take_action_volume(self, parsed_args): # version LOG.warning(msg % opt_name) - source_volume = utils.find_resource( - volume_client.volumes, - parsed_args.volume, + source_volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ) - kwargs = {} - if volume_client.api_version < api_versions.APIVersion('3.1'): + kwargs: dict[str, ty.Any] = { + 'visibility': None, + 'protected': None, + } + if not sdk_utils.supports_microversion(volume_client, '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 ' @@ -624,20 +640,18 @@ def _take_action_volume(self, parsed_args): ) raise exceptions.CommandError(msg) else: - kwargs.update( - visibility=parsed_args.visibility or 'private', - protected=parsed_args.is_protected or False, - ) + kwargs['visibility'] = parsed_args.visibility or 'private' + kwargs['protected'] = parsed_args.is_protected or False - response, body = volume_client.volumes.upload_to_image( + response = volume_client.upload_volume_to_image( source_volume.id, - parsed_args.force, parsed_args.name, - parsed_args.container_format, - parsed_args.disk_format, + force=parsed_args.force, + disk_format=parsed_args.disk_format, + container_format=parsed_args.container_format, **kwargs, ) - info = body['os-volume_upload_image'] + info = copy.deepcopy(response) try: info['volume_type'] = info['volume_type']['name'] except TypeError: @@ -702,8 +716,7 @@ def take_action(self, parsed_args): except Exception as e: result += 1 msg = _( - "Failed to delete image with name or " - "ID '%(image)s': %(e)s" + "Failed to delete image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) @@ -881,7 +894,7 @@ def take_action(self, parsed_args): if parsed_args.is_hidden: kwargs['is_hidden'] = parsed_args.is_hidden if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -892,9 +905,11 @@ def take_action(self, parsed_args): 'visibility', 'is_protected', 'owner_id', + 'hash_algo', + 'hash_value', 'tags', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -905,6 +920,8 @@ def take_action(self, parsed_args): 'Visibility', 'Protected', 'Project', + 'Hash Algorithm', + 'Hash Value', 'Tags', ) else: @@ -915,18 +932,19 @@ def take_action(self, parsed_args): if 'limit' in kwargs: # Disable automatic pagination in SDK kwargs['paginated'] = False - data = list(image_client.images(**kwargs)) + + images = list(image_client.images(**kwargs)) if parsed_args.property: for attr, value in parsed_args.property.items(): api_utils.simple_filter( - data, + images, attr=attr, value=value, property_field='properties', ) - data = utils.sort_items(data, parsed_args.sort, str) + data = utils.sort_items(images, parsed_args.sort, str) return ( column_headers, @@ -956,7 +974,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image - columns = ("Image ID", "Member ID", "Status") + columns: tuple[str, ...] = ("Image ID", "Member ID", "Status") image_id = image_client.find_image( parsed_args.image, @@ -1055,6 +1073,16 @@ class SaveImage(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + "--chunk-size", + type=int, + default=1024, + metavar="", + help=_( + "Size in bytes to read from the wire and buffer at one " + "time (default: 1024)" + ), + ) parser.add_argument( "--file", metavar="", @@ -1079,7 +1107,12 @@ def take_action(self, parsed_args): if output_file is None: output_file = getattr(sys.stdout, "buffer", sys.stdout) - image_client.download_image(image.id, stream=True, output=output_file) + image_client.download_image( + image.id, + stream=True, + output=output_file, + chunk_size=parsed_args.chunk_size, + ) class SetImage(command.Command): @@ -1150,8 +1183,7 @@ def get_parser(self, prog_name): default=None, action='append', help=_( - "Set a tag on this image " - "(repeat option to set multiple tags)" + "Set a tag on this image (repeat option to set multiple tags)" ), ) parser.add_argument( @@ -1209,8 +1241,8 @@ def get_parser(self, prog_name): identity_common.add_project_domain_option_to_parser(parser) for deadopt in self.deadopts: parser.add_argument( - "--%s" % deadopt, - metavar="<%s>" % deadopt, + f"--{deadopt}", + metavar=f"<{deadopt}>", dest=f"dead_{deadopt.replace('-', '_')}", help=argparse.SUPPRESS, ) @@ -1361,7 +1393,10 @@ def take_action(self, parsed_args): if parsed_args.visibility is not None: kwargs['visibility'] = parsed_args.visibility - if parsed_args.project: + # Only set owner_id if --project is used WITHOUT membership flags + # When --project is used with --accept/--reject/--pending, it should + # only identify which member's status to update, not change ownership + if parsed_args.project and not parsed_args.membership: # We already did the project lookup above kwargs['owner_id'] = project_id @@ -1454,7 +1489,6 @@ def take_action(self, parsed_args): ignore_missing=False, ) - kwargs = {} tagret = 0 propret = 0 if parsed_args.tags: @@ -1463,10 +1497,11 @@ def take_action(self, parsed_args): image_client.remove_tag(image.id, k) except Exception: LOG.error( - _("tag unset failed, '%s' is a " "nonexistent tag "), k + _("tag unset failed, '%s' is a nonexistent tag "), k ) tagret += 1 + kwargs: dict[str, ty.Any] = {} if parsed_args.properties: for k in parsed_args.properties: if k in image: @@ -1481,6 +1516,11 @@ def take_action(self, parsed_args): ) new_props.pop(k, None) kwargs['properties'] = new_props + elif ( + k in IMAGE_ATTRIBUTES_CUSTOM_NAMES + and IMAGE_ATTRIBUTES_CUSTOM_NAMES[k] in image + ): + delattr(image, IMAGE_ATTRIBUTES_CUSTOM_NAMES[k]) else: LOG.error( _( @@ -1516,7 +1556,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) elif propret > 0: msg = _( - "Failed to unset %(propret)s of %(proptotal)s" " properties." + "Failed to unset %(propret)s of %(proptotal)s properties." ) % {'propret': propret, 'proptotal': proptotal} raise exceptions.CommandError(msg) @@ -1548,8 +1588,7 @@ def get_parser(self, prog_name): action='store_true', default=False, help=_( - 'Show upload progress bar ' - '(ignored if passing data via stdin)' + 'Show upload progress bar (ignored if passing data via stdin)' ), ) parser.add_argument( @@ -1575,12 +1614,12 @@ def take_action(self, parsed_args): fp = open(parsed_args.filename, 'rb') except FileNotFoundError: raise exceptions.CommandError( - '%r is not a valid file' % parsed_args.filename, + f'{parsed_args.filename!r} is not a valid file', ) else: fp = get_data_from_stdin() - kwargs = {} + kwargs: dict[str, ty.Any] = {} if parsed_args.progress and parsed_args.filename: # NOTE(stephenfin): we only show a progress bar if the user @@ -1614,8 +1653,6 @@ def get_parser(self, prog_name): metavar='', help=_('Image to initiate import process for (name or ID)'), ) - # TODO(stephenfin): Uncomment help text when we have this command - # implemented parser.add_argument( '--method', metavar='', @@ -1630,8 +1667,6 @@ def get_parser(self, prog_name): help=_( "Import method used for image import process. " "Not all deployments will support all methods. " - # "Valid values can be retrieved with the 'image import " - # "methods' command. " "The 'glance-direct' method (default) requires images be " "first staged using the 'image-stage' command." ), @@ -1690,7 +1725,8 @@ def get_parser(self, prog_name): "'copy-image' import method)" ), ) - parser.add_argument( + allow_failure_group = parser.add_mutually_exclusive_group() + allow_failure_group.add_argument( '--allow-failure', action='store_true', dest='allow_failure', @@ -1701,9 +1737,9 @@ def get_parser(self, prog_name): 'Only usable with --stores or --all-stores' ), ) - parser.add_argument( + allow_failure_group.add_argument( '--disallow-failure', - action='store_true', + action='store_false', dest='allow_failure', default=True, help=_( @@ -1734,11 +1770,15 @@ def take_action(self, parsed_args): if parsed_args.import_method not in import_methods: msg = _( - "The '%s' import method is not supported by this deployment. " - "Supported: %s" + "The '%(method)s' import method is not supported by this " + "deployment. Supported: %(supported)s" ) raise exceptions.CommandError( - msg % (parsed_args.import_method, ', '.join(import_methods)), + msg + % { + 'method': parsed_args.import_method, + 'supported': ', '.join(import_methods), + }, ) if parsed_args.import_method == 'web-download': @@ -1748,6 +1788,12 @@ def take_action(self, parsed_args): "'--method=web-download'" ) raise exceptions.CommandError(msg) + _parsed = urllib.parse.urlparse(parsed_args.uri) + if not all({_parsed.scheme, _parsed.netloc}): + msg = _("'%(uri)s' is not a valid url") + raise exceptions.CommandError( + msg % {'uri': parsed_args.uri}, + ) else: if parsed_args.uri: msg = _( @@ -1863,8 +1909,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image try: - columns = ("id", "description", "is_default") - column_headers = ("ID", "Description", "Default") + columns: tuple[str, ...] = ("id", "description", "is_default") + column_headers: tuple[str, ...] = ("ID", "Description", "Default") if parsed_args.detail: columns += ("properties",) column_headers += ("Properties",) diff --git a/openstackclient/image/v2/info.py b/openstackclient/image/v2/info.py index 469b15ac29..68848136ea 100644 --- a/openstackclient/image/v2/info.py +++ b/openstackclient/image/v2/info.py @@ -12,8 +12,8 @@ from osc_lib.cli import format_columns -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py index 8294daa02e..af30f718f9 100644 --- a/openstackclient/image/v2/metadef_namespaces.py +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -18,10 +18,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ _formatters = { @@ -169,9 +169,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.namespace) - msg = _( - "%(result)s of %(total)s namespace failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s namespace failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 2dc33968ad..d5cbec1cd9 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -123,8 +123,11 @@ def get_parser(self, prog_name): parser.add_argument( "objects", metavar="", - nargs="+", - help=_("Metadef object(s) to delete (name)"), + nargs="*", + help=_( + "Metadef object(s) to delete (name) " + "(omit this argument to delete all objects in the namespace)" + ), ) return parser @@ -133,6 +136,9 @@ def take_action(self, parsed_args): namespace = parsed_args.namespace + if not parsed_args.objects: + return image_client.delete_all_metadef_objects(namespace) + result = 0 for obj in parsed_args.objects: try: @@ -260,10 +266,12 @@ def take_action(self, parsed_args): prop['name'] = parsed_args.property except KeyError: - msg = _('Property %s not found in object %s.') % ( - parsed_args.property, - parsed_args.object, - ) + msg = _( + 'Property %(property)s not found in object %(object)s.' + ) % { + 'property': parsed_args.property, + 'object': parsed_args.object, + } raise exceptions.CommandError(msg) return zip(*sorted(prop.items())) diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 40440c0299..3a923c5226 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -15,10 +15,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -124,14 +124,22 @@ def get_parser(self, prog_name): parser.add_argument( "properties", metavar="", - nargs="+", - help=_("Metadef propert(ies) to delete (name)"), + nargs="*", + help=_( + "Metadef properties to delete (name) " + "(omit this argument to delete all properties " + "in the namespace)" + ), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image + if not parsed_args.properties: + image_client.delete_all_metadef_properties(parsed_args.namespace) + return + result = 0 for prop in parsed_args.properties: try: diff --git a/openstackclient/image/v2/metadef_resource_type_association.py b/openstackclient/image/v2/metadef_resource_type_association.py index e6461e5da1..4d3ee46689 100644 --- a/openstackclient/image/v2/metadef_resource_type_association.py +++ b/openstackclient/image/v2/metadef_resource_type_association.py @@ -12,20 +12,19 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) def _get_columns(item): - column_map = {} hidden_columns = ['location'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/image/v2/metadef_resource_types.py b/openstackclient/image/v2/metadef_resource_types.py index bbd77636f0..88001b7e1c 100644 --- a/openstackclient/image/v2/metadef_resource_types.py +++ b/openstackclient/image/v2/metadef_resource_types.py @@ -12,9 +12,9 @@ """Image V2 Action Implementations""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -23,8 +23,7 @@ class ListMetadefResourceTypes(command.Lister): def take_action(self, parsed_args): image_client = self.app.client_manager.image - kwargs = {} - data = image_client.metadef_resource_types(**kwargs) + data = image_client.metadef_resource_types() columns = ['Name'] column_headers = columns return ( diff --git a/openstackclient/image/v2/task.py b/openstackclient/image/v2/task.py index 1b7170f63b..a0f1d35ded 100644 --- a/openstackclient/image/v2/task.py +++ b/openstackclient/image/v2/task.py @@ -11,9 +11,9 @@ # under the License. from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ _formatters = { diff --git a/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po b/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po index b44baf889b..f49640ecc2 100644 --- a/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po +++ b/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po @@ -1,32 +1,20 @@ -# Andreas Jaeger , 2017. #zanata # işbaran akçayır , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: python-openstackclient VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2018-02-25 01:10+0000\n" +"POT-Creation-Date: 2025-04-01 18:07+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-08-15 12:09+0000\n" -"Last-Translator: Andreas Jaeger \n" +"PO-Revision-Date: 2017-08-14 07:58+0000\n" +"Last-Translator: Copied by Zanata \n" "Language-Team: Turkish (Turkey)\n" "Language: tr_TR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Zanata 4.3.3\n" "X-POOTLE-MTIME: 1502656444.000000\n" -#, python-format -msgid "" -"\"Create\" rule command for type \"%(rule_type)s\" requires arguments " -"%(args)s" -msgstr "" -"\"%(rule_type)s\" türü için \"create\" kural komutu %(args)s argümanlarını " -"gerektirir" - -msgid "\"Create\" rule command requires argument \"type\"" -msgstr "\"Create\" kural komutu için \"type\" argümanı zorunludur" - #, python-format msgid "%(errors)s of %(total)s groups failed to delete." msgstr "%(total)s gruptan %(errors)s grup silinirken hata oluştu." @@ -51,10 +39,6 @@ msgstr "%(total)s kullanıcıdan %(errors)s kullanıcıyı silme işlemi başar msgid "%(num)s of %(total)s %(resource)ss failed to delete." msgstr "%(total)s %(resource)s'tan %(num)s tanesi silinirken hata oluştu." -#, python-format -msgid "%(result)s of %(total)s %(resource)ss failed to delete." -msgstr "%(total)s'ın %(result)s %(resource)s'ların silinmesi başarısız." - #, python-format msgid "%(result)s of %(total)s EC2 keys failed to delete." msgstr "" @@ -165,10 +149,6 @@ msgstr "%(total)s ağ ajanından %(result)s tanesi silinirken hata oluştu." msgid "%(result)s of %(total)s network segments failed to delete." msgstr "%(total)s ağ dilimlerinin %(result)s tanesi silinirken hata oluştu." -#, python-format -msgid "%(result)s of %(total)s policys failed to delete." -msgstr "%(total)s politikadan %(result)s tanesi silinirken hata oluştu." - #, python-format msgid "%(result)s of %(total)s ports failed to delete." msgstr "" @@ -279,24 +259,12 @@ msgstr "" "Varolan uzak disk bölümünden yeni görüntüsünden yeni disk bölümü anlık " "görüntüsü oluştururken 'force' seçeneği çalışmaz" -msgid "'--retype-policy' option will not work without '--type' option" -msgstr "'--retype-policy' seçeneği '--type' seçeneği olmadan çalışmaz" - msgid "--project is only allowed with --private" msgstr "--project sadece --private ile kullanılabilir" -msgid "" -"--size is a required option if snapshot or source volume is not specified." -msgstr "" -"Anlık görüntü veya kaynak disk bölümü belirtilmezse --size gerekli bir " -"seçenektir." - msgid "A service URL where SAML assertions are being sent (required)" msgstr "SAML bildirimlerinin gönderildiği bir hizmet URL'i (gerekli)" -msgid "Accept the image membership" -msgstr "İmaj üyeliğini kabul et" - msgid "Accept volume transfer request." msgstr "Disk bölümü aktarım isteğini kabul et." @@ -358,27 +326,12 @@ msgstr "L3 aracısına yönlendirici ekleyin" msgid "Add router to an agent" msgstr "Bir ajana yönlendirici ekle" -msgid "Add security group to server" -msgstr "Sunucuya güvenlik grubu ekle" - msgid "Add user to group" msgstr "Gruba kullanıcı ekle" -msgid "Add volume to server" -msgstr "Disk bölümünü sunucuya ekle" - msgid "Add volume(s) to consistency group" msgstr "Uyum grubuna disk bölümleri ekle" -msgid "" -"Additional route for this subnet e.g.: destination=10.10.0.0/16," -"gateway=192.168.71.254 destination: destination subnet (in CIDR notation) " -"gateway: nexthop IP address (repeat option to add multiple routes)" -msgstr "" -"Bu alt ağ için ek yönlendirici örn: hedef=10.10.0.0/16,geçit=192.168.71.254 " -"hedef: hedef alt ağ (CIDR gösteriminde) geçit: bir sonraki durak IP adresi " -"(birden fazla yölendirici eklemek için tekrarlanacak seçenek)" - msgid "Address scope to display (name or ID)" msgstr "Gösterilecek adres kapsamı (isim veya ID)" @@ -388,9 +341,6 @@ msgstr "Değiştirilecek adres kapsamı (ad veya kimlik)" msgid "Address scope(s) to delete (name or ID)" msgstr "Silinecek adres kapsam(lar)ı (isim veya ID)" -msgid "Adds a role assignment to a user or group on a domain or project" -msgstr "Bir alandaki veya projedeki bir kullanıcıya veya gruba rol atama ekler" - msgid "Agent from which router will be removed (ID only)" msgstr "Yönlendiricinin kaldırılacağı ajan (yalnızca ID)" @@ -418,22 +368,6 @@ msgstr "Silinecek küme(ler) (isim veya ID)" msgid "Allocate port on host (ID only)" msgstr " ana bilgisayarında bağlantı noktası ayır (sadece ID)" -msgid "" -"Allocation pool IP addresses for this subnet e.g.: start=192.168.199.2," -"end=192.168.199.254 (repeat option to add multiple IP addresses)" -msgstr "" -"Bu alt ağ için ayırma havuzu IP adresleri örn: başlangıç=192.168.199.2," -"bitiş=192.168.199.254 (birden fazla IP adresi eklemek için seçeneği tekrarla)" - -msgid "" -"Allocation pool IP addresses to be removed from this subnet e.g.: " -"start=192.168.199.2,end=192.168.199.254 (repeat option to unset multiple " -"allocation pools)" -msgstr "" -"Bu altağdan silinecek IP adres tahsis havuzu örn: başlangıç=192.168.199.2," -"bitiş=192.168.199.254 (birden fazla tahsis havuzu ayarını kaldırmak için " -"seçeneği tekrarla)" - msgid "" "Allow to access private flavor (name or ID) (Must be used with --" "private option)" @@ -441,19 +375,9 @@ msgstr "" "'nin özel flavor'a erişmesine izin verin (isim veya ID) (--private " "seçeneği ile beraber kullanılmalı)" -msgid "" -"Allow to access private type (name or ID) (Must be used with --" -"private option)" -msgstr "" -"Özel türe erişimek için 'ye izin ver (isim veya ID) (--private " -"seçeneği ile kullanılması zorunludur)" - msgid "Allow delete in state other than error or available" msgstr "Hata veya kullanılabilirden başka durumda silinmesine izin ver" -msgid "Allow disk over-commit on the destination host" -msgstr "Hedef ana bilgisayarda disk aşırı-işlemeye izin ver" - msgid "Allow image to be deleted (default)" msgstr "İmajın silinmesine izin ver (varsayılan)" @@ -463,29 +387,16 @@ msgstr "Kullanımdaki birimi yedeklemeye izin ver" msgid "Allow to delete in-use QoS specification(s)" msgstr "Kullanımdaki QoS özelliklerini silmeye izin ver" -msgid "Allow volume to be attached more than once (default to False)" -msgstr "Diskin birden fazla eklenmesine izin ver (varsayılan olarak False)" - #, python-format msgid "An error occurred when reading rules from file %(path)s: %(error)s" msgstr "%(path)s dosaysından kurallar okunurken hata oluştu: %(error)s" -msgid "Anchor for paging" -msgstr "Sayfalama için sabitleyici" - msgid "Apply rule to incoming network traffic (default)" msgstr "Kuralı gelen trafiğe uygula (varsayılan)" msgid "Apply rule to outgoing network traffic" msgstr "Giden ağ trafiğine kural uygula" -msgid "" -"Arbitrary scheduler hint key-value pairs to help boot an instance (repeat " -"option to set multiple hints)" -msgstr "" -"İsteğe bağlı bir önyüklemeye yardımcı olmak için keyfi zamanlayıcı ipucu " -"anahtar-değer çiftleri (birden fazla ipucu ayarlamak için seçeneği tekrarla)" - msgid "" "Argument --dst-port not allowed with arguments --icmp-type and --icmp-code" msgstr "" @@ -494,9 +405,6 @@ msgstr "" msgid "Argument --icmp-type required with argument --icmp-code" msgstr "--icmp-type argümanı --icmp-code ile kullanılması zorunlu" -msgid "Assocaite the floating IP with port (name or ID)" -msgstr "Yüzen IP'yi bağlantı noktasıyla ilişkilendirin (ad veya kimlik)" - msgid "Associate a QoS specification to a volume type" msgstr "Bir disk bölümü türüyle QoS özelliklerini ilişkilendir" @@ -532,9 +440,6 @@ msgstr "" msgid "Authentication URL of remote federated service provider (required)" msgstr "Uzak federe servis sağlayıcının kimlik doğrulama URL'si (gerekli)" -msgid "Authentication token to use" -msgstr "Kullanılacak yetkilendirme jetonu" - msgid "Authorize a request token" msgstr "Bir istek jetonu yetkilendir" @@ -615,16 +520,9 @@ msgstr "Mevcut kullanıcının parolasını değiştir" msgid "Check user membership in group" msgstr "Kullanıcının grup üyeliğini kontrol et" -msgid "Clean project resources, but don't delete the project" -msgstr "Projenin kaynaklarını temizle ama projeyi silme" - msgid "Clean resources associated with a project" msgstr "Bir proje ile alakalı kaynakları temizle" -#, python-format -msgid "Clear all tags associated with the %s" -msgstr "%s ile ilişkili tüm etiketleri sil" - msgid "" "Clear associated allocation-pools from the subnet. Specify both --allocation-" "pool and --no-allocation-pool to overwrite the current allocation pool " @@ -642,15 +540,6 @@ msgstr "" "yönlendirme bilgisinin üzerine yazmak için --host-route ve --no-host-route " "seçeneklerinin her ikisini de belirtin." -msgid "" -"Clear existing allowed-address pairs associatedwith this port.(Specify both " -"--allowed-address and --no-allowed-addressto overwrite the current allowed-" -"address pairs)" -msgstr "" -"Bu bağlantı noktasıyla ilişkili mevcut izinli adres çiftlerini temizleyin." -"(Mevcut izinli adres çiftinin üzerinde yazmak için --allowed-address ve --" -"no-allowed-addressto seçeneklerinin her ikisini de belirtiniz)" - msgid "" "Clear existing information of DNS Nameservers. Specify both --dns-nameserver " "and --no-dns-nameserver to overwrite the current DNS Nameserver information." @@ -659,26 +548,6 @@ msgstr "" "bilgisinin üzerine yazmak için --dns-nameserver ve --no-dns-nameserver " "özelliklerini belirle." -msgid "" -"Clear existing information of binding:profile.Specify both --binding-profile " -"and --no-binding-profile to overwrite the current binding:profile " -"information." -msgstr "" -"binding:profile'in mevcut bilgilerini temizle. Mevcut binding:profile " -"bilgisinin üzerine yazmak için --binding-profile ve --no-binding-profile her " -"ikisini de belirtin." - -msgid "Clear existing information of data plane status" -msgstr "Mevcut veri düzlemi durumu bilgilerini temizle" - -msgid "" -"Clear existing information of fixed IP addresses.Specify both --fixed-ip and " -"--no-fixed-ip to overwrite the current fixed IP addresses." -msgstr "" -"Sabit IP adresleri için mevcut bilgileri silin. Geçerli sabit IP " -"adreslerinin üzerine yazmak için hem --fixed-ip hem de --no-fixed-ip " -"belirtin." - msgid "Clear existing security groups associated with this port" msgstr "Bu bağlantı noktasıyla ilişkili mevcut güvenlik gruplarını temizle" @@ -687,21 +556,6 @@ msgstr "" "Yönlendiricinin yüksek kullanılabilirlik özelliğini temizle (sadece devre " "dışı bırakılmış yönlendirici)" -msgid "" -"Clear routes associated with the router. Specify both --route and --no-route " -"to overwrite current value of route." -msgstr "" -"Yönlendirici ile ilişkili yönleri temizle. Mevcut yön değerinin üzerine " -"yazmak için hem --route hem de --no-route seçeneklerini belirtin." - -#, python-format -msgid "" -"Clear tags associated with the %s. Specify both --tag and --no-tag to " -"overwrite current tags" -msgstr "" -"%s ile ilgili etiketleri temizle. Mevcut etiketlerin üzerine yazmak için hem " -"--tag hem de --no-tag seçeneğini belirtin" - msgid "Command Failed: One or more of the operations failed" msgstr "Komut başarısız: Bir veya birden fazla işlem başarısız" @@ -716,12 +570,6 @@ msgstr "Hesaplama API sürümü, varsayılan=%s (Env: OS_COMPUTE_API_VERSION)" msgid "Compute service %(service)s of host %(host)s failed to set." msgstr "Ana bilgisayar %(host)s'ın hesap hizmeti %(service)s ayarlanamadı." -msgid "Compute service(s) to delete (ID only)" -msgstr "Hesaplama servis(ler)ini sil" - -msgid "Confirm server resize is complete" -msgstr "Sunucu yeniden boyutlandırmasının tamamlandığını doğrula" - msgid "Consistency group containing (name or ID)" msgstr "'ü içeren tutarlılık grubu (isim veya ID)" @@ -775,10 +623,6 @@ msgstr "Silinecek alıcı(lar)" msgid "Container for new object" msgstr "Yeni nesne için kap" -#, python-format -msgid "Container name is %s characters long, the default limit is 256" -msgstr "Kap ismi %s karakter uzunluğunda, varsayılan sınır 256'dır" - msgid "Container to display" msgstr "Gösterilecek kap" @@ -817,63 +661,9 @@ msgstr "" "net-id=' parametresi için bir sarmalayıcıdır. Daha gelişmiş " "kullanım durumları için, '--nic' parametresine bakın." -msgid "" -"Create a NIC on the server and connect it to port. Specify option multiple " -"times to create multiple NICs. This is a wrapper for the '--nic port-" -"id=' parameter that provides simple syntax for the standard use case " -"of connecting a new server to a given port. For more advanced use cases, " -"refer to the '--nic' parameter." -msgstr "" -"Sunucuda bir NIC oluşturun ve bağlantı noktasına bağlayın. Birden çok NIC " -"oluşturmak için seçeneği birden çok kez belirtin. Bu, belirli bir bağlantı " -"noktasına yeni bir sunucu bağlamak için standart kullanım örneği için basit " -"sözdizimi sağlayan '--nic port-id=' parametresi için bir " -"sarmalayıcıdır. Daha gelişmiş kullanım durumları için, '--nic' parametresine " -"bakın." - -msgid "" -"Create a NIC on the server. Specify option multiple times to create multiple " -"NICs. Either net-id or port-id must be provided, but not both. net-id: " -"attach NIC to network with this UUID, port-id: attach NIC to port with this " -"UUID, v4-fixed-ip: IPv4 fixed address for NIC (optional), v6-fixed-ip: IPv6 " -"fixed address for NIC (optional), none: (v2.37+) no network is attached, " -"auto: (v2.37+) the compute service will automatically allocate a network. " -"Specifying a --nic of auto or none cannot be used with any other --nic value." -msgstr "" -"Sunucuda bir NIC oluştur. Birden fazla NIC oluşturmak için seçeneği birden " -"fazla kere belirtin. Ya net-id ya da port-id sağlanmalı, ikisi bir arada " -"değil. net-id: NIC'nin ağa ekleneceği UUID, port-id: NIC'nin bağlantı " -"noktasına takılacağı UUID, v4-fixed-ip: NIC için sabit IPv4 adresi " -"(seçimli), v6-fixed-ip: NIC için sabit IPv6 adresi (seçimli), none: (v2.37+) " -"hiç ağ takılmaz, auto: (v2.37+) hesaplama servisi otomatik olarak bir ağ " -"ayırır. Auto veya none'ı bir --nic ile belirtmek başka bir --nic değeri ile " -"kullanılamaz." - msgid "Create a QoS policy" msgstr "QoS politikası oluştur" -msgid "" -"Create a block device on the server.\n" -"Block device mapping in the format\n" -"=:::\n" -": block device name, like: vdb, xvdc (required)\n" -": UUID of the volume or snapshot (required)\n" -": volume or snapshot; default: volume (optional)\n" -": volume size if create from snapshot (optional)\n" -": true or false; default: false (optional)\n" -"(optional extension)" -msgstr "" -"Sunucu üzerinde blok aygıtı oluştur.\n" -"Blok aygıtı eşleşme formatı\n" -"=:::\n" -": blok aygıt ismi, örn: vdb, xvdc (gerekli)\n" -": disk bölümünün veya anlık görüntünün UUID'si (gerekli)\n" -": disk bölümü veya anlık görüntü; varsayılan: disk bölümü (seçimli)\n" -": eğer anlık görüntüden oluşturulduysa disk bölümü boyutu " -"(seçimli)\n" -": true veya false; varsayılan: false (seçimli)\n" -"(seçimli uzantı)" - msgid "Create a centralized router" msgstr "Merkezi bir yönlendirici oluştur" @@ -931,9 +721,6 @@ msgstr "Bir altağ oluşturun" msgid "Create an access token" msgstr "Erişim jetonu oluştur" -msgid "Create compute agent" -msgstr "Hesaplama ajanı oluştur" - msgid "" "Create credentials for user (name or ID; default: current authenticated user)" msgstr "" @@ -947,9 +734,6 @@ msgstr "" "Projede kimlik bilgileri oluştur (isim veya ID; varsayılan: mevcut kimlik " "doğrulama yapılmış proje)" -msgid "Create description for meter" -msgstr "Sayaç için açıklama oluştur" - msgid "Create floating IP" msgstr "Yüzen IP oluştur" @@ -968,9 +752,6 @@ msgstr "Yeni Ağ QoS kuralı oluştur" msgid "Create new QoS specification" msgstr "Yeni QoS özelliği oluştur" -msgid "Create new backup" -msgstr "Yeni yedek oluştur" - msgid "Create new consistency group snapshot." msgstr "Yeni tutarlılık grubu anlık görüntüsü oluştur." @@ -1040,9 +821,6 @@ msgstr "Yeni servis oluştur" msgid "Create new service provider" msgstr "Yeni servis sağlayıcı oluştur" -msgid "Create new snapshot" -msgstr "Yeni anlık görüntü oluştur" - msgid "Create new trust" msgstr "Yeni güven oluştur" @@ -1067,20 +845,6 @@ msgstr "Bu güvenlik grubunda kural oluştur (isim veya ID)" msgid "Create server boot disk from this image (name or ID)" msgstr "Bu imajdan sunucu ön yükleme diski oluştur (isim veya ID)" -msgid "" -"Create server using this volume as the boot disk (name or ID).\n" -"This option automatically creates a block device mapping with a boot index " -"of 0. On many hypervisors (libvirt/kvm for example) this will be device vda. " -"Do not create a duplicate mapping using --block-device-mapping for this " -"volume." -msgstr "" -"Bu disk bölümünü ön yüklenebilir disk olarak kullanarak sunucu oluştur (isim " -"veya ID).\n" -"Bu seçenek otomatik olarak 0 ön yükleme diziniyle bir blok aygıt eşleşmesi " -"oluşturur. Bir çok yönetici arakatman (örneğin libvirt/kvm) üzerinde bu " -"aygıt vda'dir. Bu disk bölümü için --block-device-mapping kullanarak birden " -"fazla eşleşme oluşturmayın." - msgid "Create server with this flavor (name or ID)" msgstr "Bu flavor ile sunucu oluştur (isim veya ID)" @@ -1105,14 +869,6 @@ msgstr "Kimlik bilgilerine erişim anahtarı" msgid "Credentials access key(s)" msgstr "Kimlik bilgilerine erişim anahtar(lar)ı" -msgid "" -"Custom data to be passed as binding:profile. Data may be passed as " -"= or JSON. (repeat option to set multiple binding:profile data)" -msgstr "" -"binding:profile olarak verilecek özel veri. Veri = şeklinde veya " -"JSON olarak verilebilir. (birden fazla binding:profile verisi ayarlamak için " -"seçeneği tekrarlayın)" - msgid "DNS server for this subnet (repeat option to set multiple DNS servers)" msgstr "" "Bu alt ağ için DNS sunucu (birden fazla DNS sunucusu ayarlamak için seçeneği " @@ -1156,22 +912,9 @@ msgstr "Qos Politika(lar/s)ını sil" msgid "Delete address scope(s)" msgstr "Adres kapsam(lar)ını sil" -msgid "" -"Delete auto allocated topology for a given project. Default is the current " -"project" -msgstr "" -"Belirli bir proje için otomatik ayrılan topolojiyi silin. Varsayılan geçerli " -"projedir" - msgid "Delete auto allocated topology for project" msgstr "Projeye otomatik ayrılan topolojiyi sil" -msgid "Delete backup(s)" -msgstr "Yedek(ler)i sil" - -msgid "Delete compute agent(s)" -msgstr "Hesaplama ajan(lar)ını sil" - msgid "Delete compute service(s)" msgstr "Hesaplama servis(ler)ini sil" @@ -1295,9 +1038,6 @@ msgstr "Servis sağlayıcı(ları/yı) sil" msgid "Delete service(s)" msgstr "Servis(ler)i sil" -msgid "Delete snapshot(s)" -msgstr "Anlık görüntü(yü/leri) sil" - msgid "Delete subnet pool(s)" msgstr "Altağ havuzunu sil" @@ -1325,14 +1065,6 @@ msgstr "Disk bölümü türlerini sil" msgid "Delete volume(s)" msgstr "Disk bölümlerini sil" -#, python-format -msgid "Deleting %(resource)s : %(id)s" -msgstr "%(resource)s siliniyor: %(id)s" - -#, python-format -msgid "Deleting project: %s" -msgstr "Proje siliniyor: %s" - msgid "Description for the flavor" msgstr "Flavor için açıklama" @@ -1357,22 +1089,6 @@ msgstr "Bu tutarlılık grubu anlık görüntüsünün açıklaması" msgid "Description of this port" msgstr "Bu bağlantı noktasının açıklaması" -msgid "" -"Desired IP and/or subnet (name or ID)on external gateway: subnet=,ip-" -"address= (repeat option to set multiple fixed IP addresses)" -msgstr "" -"Harici geçit üzerinde istenen IP ve/veya altağ: subnet=,ip-" -"address= (birden fazla sabit IP adres ayarlamak için seçeneği " -"tekrarla)" - -msgid "" -"Desired IP and/or subnet for filtering ports (name or ID): subnet=," -"ip-address= (repeat option to set multiple fixed IP addresses)" -msgstr "" -"Bağlantı noktalarını filtrelemek için tasarlanan IP ve/veya alt ağ (isim " -"veya ID): subnet=,ip-address= (birden fazla sabit IP " -"adresi ayarlamak için seçeneği tekrarlayın)" - msgid "" "Desired IP and/or subnet for this port (name or ID): subnet=,ip-" "address= (repeat option to set multiple fixed IP addresses)" @@ -1390,22 +1106,6 @@ msgstr "" "veya ID): subnet=,ip-address= (birden fazla sabit IP " "adresini kaldırmak için seçeneği tekrarlayın)" -msgid "" -"Desired allowed-address pair which should be removed from this port: ip-" -"address= [,mac-address=] (repeat option to set " -"multiple allowed-address pairs)" -msgstr "" -"Bu bağlantıdan kaldırılması gereken tasarlanan erişilebilir adres çifti: ip-" -"address= [,mac-address=] (birden fazla izin verilen " -"adres çifti ayarlamak için seçeneği tekrarlayın)" - -msgid "" -"Desired key which should be removed from binding:profile(repeat option to " -"unset multiple binding:profile data)" -msgstr "" -"binding:profile'den çıkarılması gereken istenen anahtar (çoklu binding:" -"profile verisinin ayarını kaldırmak için seçeneği tekrarlayın)" - msgid "" "Destination filename (defaults to object name); using '-' as the filename " "will print the file to stdout" @@ -1416,9 +1116,6 @@ msgstr "" msgid "Destination host (takes the form: host@backend-name#pool)" msgstr "Hedef ana bilgisayar (biçemi: anabilgisayar@artalanismi#havuz)" -msgid "Destination port (ssh -p option)" -msgstr "Hedef bağlantı noktası (ssh -p seçeneği)" - msgid "" "Destination port, may be a single port or a starting and ending port range: " "137:139. Required for IP protocols TCP and UDP. Ignored for ICMP IP " @@ -1539,9 +1236,6 @@ msgstr "Adres kapsam detaylarını göster" msgid "Display aggregate details" msgstr "Küme detaylarını göster" -msgid "Display backup details" -msgstr "Yedek detaylarını göster" - msgid "Display configuration details" msgstr "Yapılandırma detaylarını göster" @@ -1584,9 +1278,6 @@ msgstr "Yüzen IP ayrıntılarını görüntüle" msgid "Display group details" msgstr "Grup detaylarını göster" -msgid "Display host details" -msgstr "Sunucu detaylarını göster" - msgid "Display hypervisor details" msgstr "Yönetici ara katman detaylarını göster" @@ -1671,9 +1362,6 @@ msgstr "Servis detaylarını göster" msgid "Display service provider details" msgstr "Servis sağlayıcı detaylarını göster" -msgid "Display snapshot details" -msgstr "Anlık görüntü detaylarını göster" - msgid "Display subnet details" msgstr "Altağ detaylarını göster" @@ -1701,9 +1389,6 @@ msgstr "Disk bölümü türü detaylarını göster" msgid "Do not make the network VLAN transparent" msgstr "Ağ VLAN'ını transparan yapma" -msgid "Do not over-commit disk on the destination host (default)" -msgstr "Hedef ana bilgisayarda disk aşırı işleme yapma (varsayılan)" - msgid "Do not share meter between projects" msgstr "Ölçümleri projeler arasında paylaşma" @@ -1750,10 +1435,6 @@ msgstr "" "Grubun ait olduğu alan (isim veya ID). Grup isimlerinde bir çatışma olması " "durumunda kullanılabilir." -msgid "Domain the project belongs to (name or ID) [only valid with --absolute]" -msgstr "" -"Projenin ait olduğu alan (isim veya ID) [sadece --absolute ile geçerli]" - msgid "" "Domain the project belongs to (name or ID). This can be used in case " "collisions between project names exist." @@ -1898,11 +1579,6 @@ msgstr "" msgid "Enable port security for this port" msgstr "Bu bağlantı noktası için bağlantı noktası güvenliğini etkinleştir" -msgid "Enable port security for this port (Default)" -msgstr "" -"Bu bağlantı noktası için bağlantı noktası güvenliğini etkinleştir " -"(Varsayılan)" - msgid "Enable project" msgstr "Projeyi etkinleştir" @@ -1962,9 +1638,6 @@ msgstr "Silinecek uç nokta(lar) (sadece ID)" msgid "Ephemeral disk size in GB (default 0G)" msgstr "GB cinsinden geçici disk boyutu (varsayılan 0G)" -msgid "Error creating server\n" -msgstr "Sunucu oluşturulurken hata\n" - #, python-format msgid "Error creating server backup: %s" msgstr "Sunucu yedeği oluşturulurken hata: %s" @@ -1977,55 +1650,30 @@ msgstr "Sunucu imajı oluşturma hatası: %s" msgid "Error creating server: %s" msgstr "Sunucu oluşturma başarısız: %s" -msgid "Error deleting server\n" -msgstr "Sunucu silinirken hata\n" - #, python-format msgid "Error deleting server: %s" msgstr "Sunucu silinirken hata: %s" -msgid "Error migrating server\n" -msgstr "Sunucu göçü sırasında hata\n" - #, python-format msgid "Error migrating server: %s" msgstr "Sunucu göçü sırasında hata: %s" -msgid "Error rebooting server\n" -msgstr "Sunucu yeniden başlatma hatası\n" - #, python-format msgid "Error rebooting server: %s" msgstr "Sunucu yeniden başlatma hatası: %s" -msgid "Error rebuilding server\n" -msgstr "Sunucu yeniden yapılandırması sırasında hata\n" - #, python-format msgid "Error rebuilding server: %s" msgstr "Sunucu yeniden yapılandırması sırasında hata: %s" -msgid "Error resizing server\n" -msgstr "Sunucunun yeniden boyutlandırması sırasında hata\n" - #, python-format msgid "Error resizing server: %s" msgstr "Sunucu yeniden boyutlandırma hatası: %s" -msgid "Error retrieving diagnostics data\n" -msgstr "Teşhis verisi alınırken hata oluştu\n" - #, python-format msgid "Error while executing command: %s" msgstr "Komut çalıştırılırken hata oluştu: %s" -msgid "" -"Error: If a user or group is specified, either --domain or --project must " -"also be specified to list role grants." -msgstr "" -"Hata: Bir kullanıcı veya grup belirtilmişse, rol izinlerini listelemek için " -"--domain veya --project de belirtilmelidir." - msgid "" "Ethertype of network traffic (IPv4, IPv6; default: based on IP protocol)" msgstr "" @@ -2079,9 +1727,6 @@ msgstr "Hesaplama API'ı uzantıların listelemeyi desteklemiyor" msgid "Extensions list not supported by Identity API" msgstr "Kimlik API için uzantıların listlenmesi desteklenmiyor" -msgid "External Network used as router's gateway (name or ID)" -msgstr "Yönlendirici geçidi olarak kullanılan Harici Ağ (isim veya ID)" - #, python-format msgid "Failed to add project %(project)s access to flavor: %(e)s" msgstr "%(project)s projesinin flavor'a erişimi başarısız: %(e)s" @@ -2098,22 +1743,10 @@ msgstr "Anlık görüntü özellikleri silinirken hata oluştu: %s" msgid "Failed to clean volume properties: %s" msgstr "Disk bölümü özelliklerini silme başarısız: %s" -#, python-format -msgid "Failed to clear flavor property: %s" -msgstr "Flavor özelliğinin silinmesi başarısız: %s" - #, python-format msgid "Failed to create Network QoS rule: %(e)s" msgstr "Ağ QoS kuralı oluşturulurken hata oluştu: %(e)s" -#, python-format -msgid "Failed to delete %(dresult)s of %(total)s images." -msgstr "%(total)s imajdan %(dresult)s tanesi silinirken hata oluştu." - -#, python-format -msgid "Failed to delete %(resource)s with ID '%(id)s': %(e)s" -msgstr "'%(id)s' ID'li %(resource)s silinemedi: %(e)s" - #, python-format msgid "Failed to delete %(resource)s with name or ID '%(name_or_id)s': %(e)s" msgstr "" @@ -2355,9 +1988,6 @@ msgstr "" "'%(flavor)s' flavor için erişen projelerin listesinin alınma işlemi " "başarısız: %(e)s" -msgid "Failed to get an image file." -msgstr "İmaj dosyası alma başarısız." - #, python-format msgid "Failed to remove flavor access from project: %s" msgstr "Projeden flavor erişiminin kaldırılması başarısız: %s" @@ -2389,10 +2019,6 @@ msgstr "Bu disk bölümü türü için şifreleme bilgisini ayarlama başarısı msgid "Failed to set flavor access to project: %s" msgstr "Projeye flavor erişiminin ayarlanması başarısız: %s" -#, python-format -msgid "Failed to set flavor property: %s" -msgstr "Flavor özelliğinin ayarlanması başarısız: %s" - #, python-format msgid "Failed to set image property: %s" msgstr "İmaj özelliği ayarlarken hata oluştu: %s" @@ -2429,10 +2055,6 @@ msgstr "Disk bölümü durumunu ayarlama başarısız: %s" msgid "Failed to set volume type access to project: %s" msgstr "Projeye erişim için disk bölümü türü ayarlama başarısız: %s" -#, python-format -msgid "Failed to set volume type property: %s" -msgstr "Disk bölümü türü özelliğini ayarlarken hata oluştu: %s" - #, python-format msgid "Failed to set volume type: %s" msgstr "Disk bölümü türü ayarlanırken hata oluştu: %s" @@ -2469,20 +2091,6 @@ msgstr "İmaj özelliği ayarını kaldırma başarısız: %s" msgid "Failed to unset volume property: %s" msgstr "Disk bölümü özelliği ayarlarını kaldırma başarısız: %s" -#, python-format -msgid "Failed to unset volume type property: %s" -msgstr "Disk bölümü türü özelliğini kaldırma işlemi başarısız: %s" - -#, python-format -msgid "Failed to update backup name or description: %s" -msgstr "Yedek ismi ve açıklaması güncellenirken hata oluştu: %s" - -#, python-format -msgid "Failed to update snapshot display name or display description: %s" -msgstr "" -"Anlık görüntü görünür ismi veya görünür açıklamasını güncelleme işlemi " -"başarısız: %s" - #, python-format msgid "Failed to update snapshot name or description: %s" msgstr "Anlık görüntü adı veya açıklaması güncellenemedi: %s" @@ -2508,23 +2116,12 @@ msgstr "Düzenlenecek federasyon protokolü (isim veya ID)" msgid "Federation protocol(s) to delete (name or ID)" msgstr "Silinecek federasyon protokol(ü/leri) (isim veya ID)" -msgid "" -"File to inject into image before boot (repeat option to set multiple files)" -msgstr "" -"Önyüklemeden önce imaja enjekte etmek için dosya (birden çok dosyayı " -"ayarlamak için seçeneği tekrar edin)" - msgid "" "Filename for private key to save. If not used, print private key in console." msgstr "" "Kapalı anahtarın kaydedileceği dosyanın ismi. Kullanılmazsa, kapalı anahtar " "konsola basılır." -msgid "Filename for public key to add. If not used, creates a private key." -msgstr "" -"Eklenecek açık anahtarın dosya ismi. Kullanılmazsa, bir kapalı anahtar " -"oluşturur." - msgid "Filename that contains a new set of mapping rules" msgstr "Eşleşme kurallarının yeni bir kümesini içeren dosya adı" @@ -2552,10 +2149,6 @@ msgstr "Grup listesini 'e göre filtrele (isim veya ID)" msgid "Filter group list by (name or ID)" msgstr "'ya göre grup listesini filtrele (isim veya ID)" -msgid "Filter hypervisors using substring" -msgstr "" -" alt karakter dizisini kullanarak yönetici arakatmanları filtrele" - msgid "Filter images based on name." msgstr "İmajları isme göre filtrele." @@ -2589,12 +2182,6 @@ msgstr "Sonuçları kullanıcıya göre filtrele (isim veya ID) (sadece yönetic msgid "Filter results by volume name" msgstr "Sonuçları disk bölümü ismine göre filtrele" -msgid "Filter roles by (name or ID)" -msgstr "Rolleri 'ye göre filtrele (isim veya ID)" - -msgid "Filter roles by (name or ID)" -msgstr "Rolleri 'ya göre filtrele (isim veya ID)" - msgid "Filter users by (name or ID)" msgstr "Kullanıcıları 'e göre filtrele (isim veya ID)" @@ -2620,35 +2207,18 @@ msgstr "" "Sonuçları bir duruma göre filtrele (\"available\", \"error\", \"creating\", " "\"deleting\" veya \"error_deleting\")" -msgid "" -"Filters results by a status. ('available', 'error', 'creating', 'deleting' " -"or 'error-deleting')" -msgstr "" -"Bir duruma göre sonuçları filtrele. ('kullanılabilir', 'hata', " -"'oluşturuluyor', 'siliniyor' veya 'silinirken hata')" - msgid "Filters results by a volume (name or ID)." msgstr "Bir disk bölümüne göre sonuçları filtrele (isim veya ID)." msgid "Filters results by the backup name" msgstr "Yedek ismine göre sonuçları listele" -msgid "" -"Filters results by the backup status ('creating', 'available', 'deleting', " -"'error', 'restoring' or 'error_restoring')" -msgstr "" -"Yedek durumuna göre sonuçları filtrele ('oluşturuluyor', 'kullanılabilir', " -"'siliniyor', 'hata' veya 'geri yüklenirken hata')" - msgid "Filters results by the volume which they backup (name or ID)" msgstr "Yedeklenen disk bölümüne göre sonuçları filtrele (isim veya ID)" msgid "Fixed IP address mapped to the floating IP" msgstr "Sabit IP adres yüzen IP adresi ile eşleşti" -msgid "Fixed IP address to associate with this floating IP address" -msgstr "Bu kayan IP adresi ile ilişkili sabit IP adresi" - msgid "Fixed IP address to remove from the server (IP only)" msgstr "Sunucudan kaldırılacak sabit IP adresi (sadece IP)" @@ -2687,15 +2257,9 @@ msgstr "Silinecek flavor(lar) (isim veya ID)" msgid "Floating IP address" msgstr "Yüzen IP adresi" -msgid "Floating IP address to assign to server (IP only)" -msgstr "Sunucuya atanacak kayan IP adresi (sadece IP)" - msgid "Floating IP address to remove from server (IP only)" msgstr "Sunucudan kaldırılacak kayan IP adresi (sadece IP)" -msgid "Floating IP to associate (IP address or ID)" -msgstr "İlişkilendirilecek yüzen IP adresi (IP adres veya ID)" - msgid "Floating IP to disassociate (IP address or ID)" msgstr "Bağlantıyı kesmek için kayan IP (IP adresi veya kimliği)" @@ -2709,9 +2273,6 @@ msgid "Floating ip pool operations are only available for Compute v2 network." msgstr "" "Yüzen IP havuz işlemleri sadece Hesaplama v2 ağı için kullanılabilirdir." -msgid "Force down service" -msgstr "Servisi durmaya zorla" - msgid "Force image change if volume is in use (only meaningful with --volume)" msgstr "" "Disk bölümü kullanımda ise imajı değişmeye zorla (sadece --volume ile " @@ -2723,9 +2284,6 @@ msgstr "" "Disk bölümü kullanımda ise imaj oluşturmaya zorla (sadece --volume ile " "anlamlıdır)" -msgid "Force up service" -msgstr "Servisi açılmaya zorla" - msgid "Freeze and disable the specified volume host" msgstr "Belirtilen disk bölümü sunucusunu dondur ve devre dışı bırak" @@ -2753,9 +2311,6 @@ msgstr "Silinecek grup(lar) (isim veya ID)" msgid "Helper class capable of reading rules from files" msgstr "Yardımcı sınıf kuralları dosyadan okuyabilir" -msgid "Hints for the scheduler (optional extension)" -msgstr "Zamanlayıcı için ipuçları (isteğe bağlı eklenti)" - msgid "Host to add to " msgstr " için eklenecek sunucu" @@ -2802,25 +2357,12 @@ msgstr "Bu imajı oluşturmak için kullanılan sunucu örneği kimliği" msgid "ID of the agent" msgstr "Ajanın ID'si" -msgid "IP address to add to server (name only)" -msgstr "Sunucuya eklenecek IP adresi (sadece isim)" - -msgid "IP address to remove from server (name only)" -msgstr "Sunucudan kaldırılacak IP adresi (sadece isim)" - msgid "IP protocol (icmp, tcp, udp; default: tcp)" msgstr "IP protokolü (icmp, tcp, udp; varsayılan: tcp)" msgid "IP version (default is 4)" msgstr "IP sürümü (varsayılan 4)" -msgid "" -"IP version (default is 4). Note that when subnet pool is specified, IP " -"version is determined from the subnet pool and this option is ignored." -msgstr "" -"IP sürümü (varsayılan 4). Alt ağ havuzu belirtildiğinde, IP sürümü alt ağ " -"havuzundan belirlenir ve bu seçenek göz ardı edilir." - msgid "IPv4 subnet for fixed IPs (in CIDR notation)" msgstr "Sabit IP'ler için IPv4 alt ağı (CIDR gösteriminde)" @@ -2871,16 +2413,6 @@ msgstr "" "Belirtilirse, disk bölümü durumu kilitlenir ve geçiş işleminin iptal " "edilmesine izin vermeyecektir (muhtemelen başka bir işlemle)" -msgid "" -"If specified, the volume state will not be locked and the a migration can be " -"aborted (default) (possibly by another operation)" -msgstr "" -"Belirtilirse, disk bölümü durumu kilitlenmez ve bir taşıma işlemi iptal " -"edilebilir (varsayılan) (muhtemelen başka bir işlemle)" - -msgid "If topology exists returns the topology's information (Default)" -msgstr "Topoloji var ise, topolojinin bilgisini döndürür (Varsayılan)" - #, python-format msgid "Image %(id)s was %(status)s." msgstr "%(id)s imaj %(status)s idi." @@ -2892,9 +2424,6 @@ msgstr "İmaj API sürümü, öntanımlı=%s (Env: OS_IMAGE_API_VERSION)" msgid "Image ID to reserve" msgstr "Ayrılacak imaj ID'si" -msgid "Image can be shared" -msgstr "İmaj paylaşılabilir" - #, python-format msgid "" "Image container format. The supported options are: %(option_list)s. The " @@ -2925,9 +2454,6 @@ msgstr "" msgid "Image hash used for verification" msgstr "Doğrulama için kullanılan imaj özeti" -msgid "Image is accessible to the community" -msgstr "İmaj topluluk tarafından erişilebilir" - msgid "Image is accessible to the public" msgstr "İmaj genele açıktır" @@ -2974,9 +2500,6 @@ msgstr "Tüm projeleri dahil et (sadece yönetici)" msgid "Include remote IP prefix from traffic count (default)" msgstr "Trafik sayımından uzak IP önekini ekle (varsayılan)" -msgid "Include reservations count [only valid with --absolute]" -msgstr "Rezervasyon sayısını ekle [sadece --absolute ile geçerlidir]" - msgid "" "Incorrect set of arguments provided. See openstack --help for more details" msgstr "" @@ -2986,22 +2509,10 @@ msgstr "" msgid "Ingress traffic direction from the project point of view" msgstr "Projenin bakış açısından gelen trafik yönü" -#, python-format -msgid "Invalid argument %s, characters ',' and '=' are not allowed" -msgstr "%s geçersiz değişken, ',' ve '=' karakterlerine izin verilmiyor" - #, python-format msgid "Invalid changes-since value: %s" msgstr "Geçersiz değişiklikler-şu değerden beri:%s" -#, python-format -msgid "" -"Invalid nic argument '%s'. Nic arguments must be of the form --nic ." -msgstr "" -"Geçersiz '%s' nic argümanı, Nic argümanları --nic biçiminde olmalıdır." - msgid "Issue new token" msgstr "Yeni jeton yayınla" @@ -3013,18 +2524,9 @@ msgstr "%(private_key)s anahtar dosyası kaydedilemedi: %(exception)s" msgid "Key file %(public_key)s not found: %(exception)s" msgstr "%(public_key)s anahtar dosyası bulunamadı: %(exception)s" -msgid "Keypair to inject into this server (optional extension)" -msgstr "Bu sunucuya enjekte etmek için anahtarlık (isteğe bağlı uzantı)" - msgid "Label to associate with this metering rule (name or ID)" msgstr "Bu ölçüm kuralı ile ilişkili etiket (isim veya ID)" -msgid "Limit the number of containers returned" -msgstr "İade edilen kap sayısını sınırla" - -msgid "Limit the number of objects returned" -msgstr "Döndürülen nesnelerin sayısını sınırla" - #, python-format msgid "List %s which have all given tag(s) (Comma-separated list of tags)" msgstr "" @@ -3069,9 +2571,6 @@ msgstr "QoS özelliklerini listele" msgid "List Service Providers" msgstr "Servis Sağlayıcıları listele" -msgid "List a project's resources" -msgstr "Bir projenin kaynaklarını listele" - msgid "List accessible domains" msgstr "Erişilebilir alanları listele" @@ -3126,21 +2625,12 @@ msgstr "Kullanılabilirlik alanlarını ve durumlarını listele" msgid "List available images" msgstr "Kullanılabilir imajlar listesi" -msgid "List backups" -msgstr "Yedekleri listele" - -msgid "List compute agents" -msgstr "Hesaplama ajanlarını listele" - msgid "List compute availability zones" msgstr "Hesaplama kullanılabilirlik alanlarını listele" msgid "List compute quota" msgstr "Hesaplama kotasını listele" -msgid "List compute services" -msgstr "Hesaplama servislerini listele" - msgid "List consistency group snapshots." msgstr "Tutarlılık grubu anlık görüntülerini listele." @@ -3219,9 +2709,6 @@ msgstr "Verilen duruma göre yüzen IP(ler) listesi ('AKTİF', 'KAPALI')" msgid "List groups" msgstr "Grupları listele" -msgid "List hosts" -msgstr "Sunucuları listele" - msgid "List hypervisors" msgstr "Yönetici arakatman listesi" @@ -3253,13 +2740,6 @@ msgstr "" "Verilen eyleme göre ağ RBAC ilkelerini listele (\"access_as_external\" veya " "\"access_as_shared\")" -msgid "" -"List network RBAC policies according to given object type (\"qos_policy\" or " -"\"network\")" -msgstr "" -"Verilen nesne türüne göre Ağ RBAC politikalarının listesi (\"qos_policy\" " -"veya \"network\")" - msgid "List network agents" msgstr "Ağ ajanlarını listele" @@ -3300,13 +2780,6 @@ msgstr "Ağları fiziksel ağın adına göre listeleme" msgid "List networks according to their name" msgstr "Ağları isimlerine göre listele" -msgid "" -"List networks according to their physical mechanisms. The supported options " -"are: flat, geneve, gre, local, vlan, vxlan." -msgstr "" -"Ağları fiziksel mekanizmalarına göre listeleyin. Desteklenen seçenekler " -"şunlardır: flat, geneve, gre, local, vlan, vxlan." - msgid "List networks according to their project (name or ID)" msgstr "Projelerine göre ağları listele (isim veya ID)" @@ -3333,15 +2806,6 @@ msgstr "Çıktıda verilen adın sadece adres kapsamlarını listeleyin" msgid "List only agents running on the specified host" msgstr "Yalnızca belirtilen ana bilgisayarda çalışan ajanları listele" -msgid "" -"List only agents with the specified agent type. The supported agent types " -"are: bgp, dhcp, open-vswitch, linux-bridge, ofa, l3, loadbalancer, metering, " -"metadata, macvtap, nic." -msgstr "" -"Yalnızca belirtilen aracı türü olan aracıları listeleyin. Desteklenen aracı " -"türleri şunlardır: bgp, dhcp, open-vswitch, linux-bridge, ofa, l3, " -"loadbalancer, metering, metadata, macvtap, nic." - msgid "List only ports attached to this router (name or ID)" msgstr "" "Yalnızca bu yönlendiriciye bağlı bağlantı noktalarını listeleyin (adı veya " @@ -3382,16 +2846,6 @@ msgstr "Sadece genele açık imajları listele" msgid "List only public types" msgstr "Sadece genele açık türleri listele" -msgid "" -"List only servers changed after a certain point of time. The provided time " -"should be an ISO 8061 formatted time. ex 2016-03-04T06:27:59Z ." -msgstr "" -"Yalnızca belirli bir zaman noktasından sonra sunucuları listeleyin. Verilen " -"süre bir ISO 8061 biçiminde olmalıdır. Örn 2016-03-04T06: 27: 59Z ." - -msgid "List only shared images" -msgstr "Sadece paylaşılan imajları listele" - msgid "List only specified service (name only)" msgstr "Sadece belirtilen servisleri listele (sadece isim)" @@ -3403,36 +2857,12 @@ msgstr "" msgid "List only subnet pools of given name in output" msgstr "Verilen isimdeki sadece altağ havuzlarını çıktıda listele" -msgid "" -"List only subnets of a given service type in output e.g.: network:" -"floatingip_agent_gateway. Must be a valid device owner value for a network " -"port (repeat option to list multiple service types)" -msgstr "" -"Çıktıda verilen servisin sadece altağlarını listele örn: network:" -"floatingip_agent_gateway. Bir ağ bağlantı noktası için geçerli bir aygıt " -"sahibi değeri olmalıdır (birden fazla servis türü listelemek için seçeneği " -"tekrarla)" - -msgid "" -"List only subnets of given IP version in output.Allowed values for IP " -"version are 4 and 6." -msgstr "" -"Verilen IP sürümünün sadece altağlarını çıktıda listele. IP sürümü için izin " -"verilen sürümler 4 ve 6." - msgid "List only subnets of given gateway IP in output" msgstr "Verilen geçit IP'sinin sadece alt ağlarını çıktıda listele" msgid "List only subnets of given name in output" msgstr "Verilen isimdeki sadece altağları çıktıda listele" -msgid "" -"List only subnets of given subnet range (in CIDR notation) in output e.g.: --" -"subnet-range 10.10.0.0/16" -msgstr "" -"Verilen alt ağ aralığında (CIDR notasyonu ile) sadece altağları çıktıda " -"listele örn: --subnet-range 10.10.0.0/16" - msgid "" "List only subnets which belong to a given network in output (name or ID)" msgstr "Verilen bir ağa ait sadece altağları çıktıda listele (isim veya ID)" @@ -3462,22 +2892,6 @@ msgstr "" "Yetkilendirilmiş kullanıcı için projeleri listele. Diğer filtrelerin yerini " "alır." -msgid "List qos policies according to their project (name or ID)" -msgstr "Projelerine göre QoS politikalarını listele (isim veya ID)" - -msgid "List qos policies not shared between projects" -msgstr "Projeler arasında paylaştırılmayan qos ilkelerini listele" - -msgid "List qos policies shared between projects" -msgstr "Projeler arasında paylaşılan qos politikalarını listele" - -msgid "List quotas for all projects with non-default quota values" -msgstr "" -"Varsayılan olmayan kota değerlerine sahip tüm projelerin kotalarını listeleme" - -msgid "List recent events of a server" -msgstr "Bir sunucunun son olaylarını listele" - msgid "List recognized commands by group" msgstr "Grup tarafından tanınan komutları listele" @@ -3511,15 +2925,6 @@ msgstr "Gelen ağ trafiğine uygulanan kurallar listesi" msgid "List rules applied to outgoing network traffic" msgstr "Giden ağ trafiğine uygulanan kuralları listele" -msgid "" -"List rules by the IP protocol (ah, dhcp, egp, esp, gre, icmp, igmp, ipv6-" -"encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, " -"rsvp, sctp, tcp, udp, udplite, vrrp and integer representations [0-255])." -msgstr "" -"Kuralları IP protokolüne göre listele (ah, dhcp, egp, esp, gre, icmp, igmp, " -"ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, " -"pgm, rsvp, sctp, tcp, udp, udplite, vrrp ve tam sayı gösterimleri [0-255])" - msgid "List security group rules" msgstr "Güvenlik grubu kurallarını listele" @@ -3547,9 +2952,6 @@ msgstr "Servis katalogundaki servisleri listele" msgid "List services on specified host (name only)" msgstr "Belirtilen sunucu üzerindeki servisleri listele (sadece isim)" -msgid "List snapshots" -msgstr "Anlık görüntüleri listele" - msgid "List subnet pools" msgstr "Altağ havuzlarını listele" @@ -3585,9 +2987,6 @@ msgstr "Varsayılan disk bölümü türünü listele" msgid "List trusts" msgstr "Güvenleri listele" -msgid "List user-role assignments" -msgstr "Kullanıcı rol atamalarını listele" - msgid "List users" msgstr "Kullanıcıları listele" @@ -3609,76 +3008,12 @@ msgstr "Disk bölümü türlerini listele" msgid "List volumes" msgstr "Disk bölümlerini listele" -msgid "" -"Listing assignments using role list is deprecated as of the Newton release. " -"Use role assignment list --user --project --names " -"instead." -msgstr "" -"Rol listesi kullanarak liste atamaları, Newton sürümünden itibaren " -"kullanımdan kaldırılmıştır. Onun yerine rol atama listesi --user " -"--project --names komutunu kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--group --domain --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Onun yerine " -"role assignment list --group --domain --names " -"kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--group --project --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Onun yerine " -"role assignment list --group --project --names " -"kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--user --domain --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Bunun " -"yerine role assignment list --user --domain --" -"names kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--user --domain default --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Onun yerine " -"role assignment list --user --domain default --names kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--user --project --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Bunun " -"yerine, role assignment list --user --project --" -"names kullanın." - -msgid "" -"Listing assignments using user role list is deprecated as of the Newton " -"release. Use role assignment list --user --project --names instead." -msgstr "" -"Atamaları kullanıcı rol listesi kullanarak listelemek Newton sürümünden " -"itibaren kullanımdan kaldırılmıştır. Onun yerine role assignment list --user " -" --project --names komutunu kullanın." - msgid "Lists all volume transfer requests." msgstr "Tüm disk bölümü aktarım isteklerini listele." msgid "Local filename(s) to upload" msgstr "Yüklenecek yerel dosya ad(lar)ı" -msgid "Lock server(s). A non-admin user will not be able to execute actions" -msgstr "" -"Sunucu(ları/yu) kilitle. Yönetici olmayan kullanıcılar işlem yapamayacaktır." - -msgid "Login name (ssh -l option)" -msgstr "Giriş adı (ssh -l seçeneği)" - msgid "MAC address of this port (admin only)" msgstr "Bu bağlantı noktasının MAC adresi (yalnızca yönetici)" @@ -3729,47 +3064,14 @@ msgstr "Disk bölümünü ön yüklemesiz olarak işaretle (varsayılan)" msgid "Maximum bandwidth in kbps" msgstr "En büyük bant genişliği kbps cinsinden" -msgid "Maximum burst in kilobits, 0 means automatic" -msgstr "Kilo bit cinsinden en büyük atış, 0 otomatik anlamına gelir" +msgid "Maximum number of servers to launch (default=1)" +msgstr "Başlatılması gereken en fazla sunucu sayısı (varsayılan=1)" -msgid "Maximum number of backups to display" -msgstr "En fazla gösterilecek yedek sayısı " +msgid "Memory size in MB (default 256M)" +msgstr "MB cinsinden bellek boyutu (varsayılan 256M)" -msgid "Maximum number of flavors to display" -msgstr "Gösterilecek en fazla flavor sayısı" - -msgid "Maximum number of images to display." -msgstr "Gösterilecek en fazla imaj sayısı." - -msgid "" -"Maximum number of servers to display. If limit equals -1, all servers will " -"be displayed. If limit is greater than 'osapi_max_limit' option of Nova API, " -"'osapi_max_limit' will be used instead." -msgstr "" -"Gösterilecek en fazla sunucu sayısı. Eğer sınır -1'e eşitse, tüm sunucular " -"gösterilir. Eğer limit Nova API'nın 'osapi_max_limit' seçeneğinden daha " -"büyükse, onun yerine 'osapi_max_limit' kullanılacaktır." - -msgid "Maximum number of servers to launch (default=1)" -msgstr "Başlatılması gereken en fazla sunucu sayısı (varsayılan=1)" - -msgid "Maximum number of snapshots to display" -msgstr "Gösterilecek en büyük anlık görüntü sayısı" - -msgid "Maximum number of volumes to display" -msgstr "Gösterilecek en çok disk bölümü sayısı" - -msgid "Memory size in MB (default 256M)" -msgstr "MB cinsinden bellek boyutu (varsayılan 256M)" - -msgid "" -"Metainfo for the flavor profile. This becomes required if --driver is " -"missing and vice versa" -msgstr "" -"Flavor profili için Metainfo. --driver eksikse bu gereklidir ya da tam tersi" - -msgid "Meter rule (ID only)" -msgstr "Ölçek kuralı (sadece ID)" +msgid "Meter rule (ID only)" +msgstr "Ölçek kuralı (sadece ID)" msgid "Meter rule to delete (ID only)" msgstr "Silinecek ölçüm kuralı (sadece ID)" @@ -3780,9 +3082,6 @@ msgstr "Silinecek ölçek (isim veya ID)" msgid "Meter to display (name or ID)" msgstr "Gösterilecek ölçek (isim veya ID)" -msgid "Migrate server to different host" -msgstr "Farklı konakçıya sunucuyu göç ettir" - msgid "Migrate volume to a new host" msgstr "Disk bölümünü yeni bir sunucuya göç ettir" @@ -3872,9 +3171,6 @@ msgid "" "Name of the physical network over which the virtual network is implemented" msgstr "Sanal ağın üzerinde uygulandığı fiziksel ağın adı" -msgid "Name of the snapshot" -msgstr "Anlık görüntü ismi" - msgid "Name of this port" msgstr "Bu bağlantı noktasının ismi" @@ -3884,12 +3180,6 @@ msgstr "Disk bölümü anabilgisayarının ismi" msgid "Name or ID of project to show usage for" msgstr "Kullanımı gösterilecek projenin isim veya ID'si" -msgid "Name or ID of security group to remove from server" -msgstr "Sunucudan kaldırılacak güvenlik grubunun isim veya ID'si" - -msgid "Name or ID of server to use" -msgstr "Kullanılacak sunucunun isim veya ID'si" - #, python-format msgid "Network API version, default=%s (Env: OS_NETWORK_API_VERSION)" msgstr "Ağ API sürümü, varsayılan=%s (Env: OS_NETWORK_API_VERSION)" @@ -3948,9 +3238,6 @@ msgstr "Bir ajandan kaldırılacak ağ (isim veya ID)" msgid "Network to display (name or ID)" msgstr "Gösterilecek ağ (isim veya ID)" -msgid "Network to fetch an IP address from (name or ID)" -msgstr "IP adresi çekilecek ağ (isim veya ID)" - msgid "Network to modify (name or ID)" msgstr "Düzenlenecek ağ (isim veya ID)" @@ -3981,21 +3268,6 @@ msgstr "Yeni adres kapsam ismi" msgid "New aggregate name" msgstr "Yeni küme ismi" -msgid "New backup description" -msgstr "Yeni yedek açıklaması" - -msgid "New backup name" -msgstr "Yeni yedek ismi" - -msgid "" -"New backup state (\"available\" or \"error\") (admin only) (This option " -"simply changes the state of the backup in the database with no regard to " -"actual status, exercise caution when using)" -msgstr "" -"Yeni yedek durumu (\"kullanılabilir\" veya \"hata\") (sadece yönetici) (Bu " -"seçenek, veritabanındaki yedeklemenin durumunu gerçek durumu dikkate almadan " -"değiştirir, kullanırken dikkatli olun.)" - msgid "New consistency group description" msgstr "Yeni tutarlılık grubu açıklaması" @@ -4109,9 +3381,6 @@ msgstr "Yeni sunucu grup ismi" msgid "New server name" msgstr "Yeni sunucu ismi" -msgid "New server state (valid value: active, error)" -msgstr "Yeni sunucu durumu (geçerli değer: aktif, hata)" - msgid "New service description" msgstr "Yeni servis tanımı" @@ -4218,10 +3487,6 @@ msgstr "'%s' türünde, isminde veya ID'si ile bir servis kataloğu yok." msgid "No service with a type, name or ID of '%s' exists." msgstr "'%s' türünde, isminde veya ID'si ile bir servis bulunamadı." -#, python-format -msgid "No tags associated with the %s" -msgstr "%s ile ilişkilendirilmiş hiç etiket yok" - msgid "Number of backups to keep (default: 1)" msgstr "Tutulacak yedekleme sayısı (varsayılan: 1)" @@ -4256,9 +3521,6 @@ msgstr "Bir veya birden fazla ayar kaldırma işlemi başarısız" msgid "Only an authorized user may issue a new token." msgstr "Yalnızca yetkili bir kullanıcı yeni bir jeton verebilir." -msgid "Only display deleted servers (Admin only)." -msgstr "Sadece silinen sunucuları göster (sadece admin)." - msgid "Only return hosts in the availability zone" msgstr "Sadece kullanılabilirlik bölgesindeki sunucuları döndürür" @@ -4277,9 +3539,6 @@ msgstr "Işletim sistemi dağıtım sürümü" msgid "Optional backup container name" msgstr "Seçimli yedek kap ismi" -msgid "Options in ssh_config(5) format (ssh -o option)" -msgstr "ssh_config(5) biçemi içerisindeki seçenekler (ssh -o seçeneği)" - msgid "Original user password" msgstr "Orijinal kullanıcı parolası" @@ -4298,18 +3557,12 @@ msgstr "Parolalar eşleşmedi, parola değiştirilmedi" msgid "Pause server(s)" msgstr "Sunucu(ları/yu) durdur" -msgid "Perform a block live migration" -msgstr "Bir blok gerçek göç gerçekleştir" - msgid "Perform a hard or soft server reboot" msgstr "Sert veya yumuşak sunucu yeniden başlatmayı gerçekleştir" msgid "Perform a hard reboot" msgstr "Sert yeniden başlatmayı gerçekleştir" -msgid "Perform a shared live migration (default)" -msgstr "Paylaşımlı canlı göç gerçekleştir (varsayılan)" - msgid "Perform a soft reboot" msgstr "Yumuşak yeniden başlatmayı gerçekleştir" @@ -4380,9 +3633,6 @@ msgstr "İmajı silinmekten koru" msgid "Print image size in a human-friendly format." msgstr "İmaj boyutunu insan dostu bir biçimde bastırın." -msgid "Private key file (ssh -i option)" -msgstr "Gizli anahtar dosyası (ssh -i seçeneği)" - msgid "Project and User must be specified" msgstr "Proje ve kullanıcı belirtilmeli" @@ -4392,15 +3642,9 @@ msgstr "Devredilen proje (isim veya ID) (gerekli)" msgid "Project description" msgstr "Proje açıklaması" -msgid "Project must be specified" -msgstr "Proje belirtilmek zorunda" - msgid "Project that consumer wants to access (name or ID) (required)" msgstr "Alıcının erişmek istediği proje (isim veya ID) (gerekli)" -msgid "Project to associate with image (name or ID)" -msgstr "İmaj ile ilişkili proje (isim veya ID)" - msgid "Project to clean (name or ID)" msgstr "Temizlenecek proje (isim veya ID)" @@ -4525,16 +3769,6 @@ msgstr "" msgid "Public or private key to display (name only)" msgstr "Gösterilecek açık veya kapalı anahtar (sadece isim)" -msgid "Put server in rescue mode" -msgstr "Sunucuyu kurtarma kipine getir" - -msgid "" -"Python module path to driver. This becomes required if --metainfo is missing " -"and vice versa" -msgstr "" -"Python modülünün sürücüye yolu. --metainfo eksikse bu gereklidir ya da tam " -"tersi" - msgid "QoS policy that contains the rule (name or ID)" msgstr "Kuralı içeren QoS politikası (isim veya ID)" @@ -4607,18 +3841,12 @@ msgstr "Düzenlenecek bölge" msgid "Regular expression to match IP addresses" msgstr "IP adresleriyle eşleşen düzenli ifadeler" -msgid "Regular expression to match IPv6 addresses" -msgstr "IPv6 adresleriyle eşleşen düzenli ifadeler" - msgid "Regular expression to match instance name (admin only)" msgstr "Sunucu adıyla eşleşen düzenli ifadeler (yalnızca yönetici)" msgid "Regular expression to match names" msgstr "Adları eşleştirmek için düzenli ifadeler" -msgid "Reject the image membership" -msgstr "İmaj üyeliğini red et" - msgid "" "Remote IDs to associate with the Identity Provider (repeat option to provide " "multiple values)" @@ -4626,13 +3854,6 @@ msgstr "" "Kimlik Sağlayıcısı ile ilişkilendirilecek uzak kimlikler (birden fazla değer " "sağlamak için seçeneği tekrarlayın)" -msgid "" -"Remote IP address block (may use CIDR notation; default for IPv4 rule: " -"0.0.0.0/0)" -msgstr "" -"Uzak IP adres bloğu (CIDR notasyonu kullanılabilir; IPv4 kuralı için " -"varsayılan: 0.0.0.0/0)" - msgid "Remote security group (name or ID)" msgstr "Uzak güvenlik grubu (isim veya ID)" @@ -4740,43 +3961,24 @@ msgstr "Sunucudan güvenlik grubunu kaldır" msgid "Remove service profile from network flavor" msgstr "Servis profilini ağ flavor'ından kaldır" -msgid "" -"Remove subnet pool prefixes (in CIDR notation). (repeat option to unset " -"multiple prefixes)." -msgstr "" -"Alt ağ havuzu öneklerini kaldır (CIDR gösteriminde). (Birden fazla önekin " -"ayarını kaldırmak için seçeneği tekrarlayın)." - msgid "Remove the QoS policy attached to the port" msgstr "Bağlantı noktasına eklenmiş QoS politikasını kaldır" msgid "Remove the QoS policy attached to this network" msgstr "Bu ağa bağlı QoS politikasını kaldırın" -msgid "Remove the encryption type for this volume type (admin oly)" -msgstr "Bu disk bölümü için şifreleme türünü sil (sadece yönetici)" - msgid "Remove the encryption type for this volume type (admin only)" msgstr "Bu disk bölümü türü için şifreleme türü kaldırıldı (sadece yönetici)" msgid "Remove user from group" msgstr "Kullanıcıyı gruptan kaldır" -msgid "Remove volume from server" -msgstr "Disk bölümünü sunucudan kaldır" - msgid "Remove volume(s) from consistency group" msgstr "Tutarlılık grubundan disk bölümlerini sil" -msgid "Removes a role assignment from domain/project : user/group" -msgstr "Bir rol atamasını alan/proje : kullanıcı/gruptan kaldırır." - msgid "Removes volume type access to project (name or ID) (admin only)" msgstr "Projeye disk türü erişimini kaldırır (isim veya ID) (sadece yönetici)" -msgid "Replicated volume to clone (name or ID)" -msgstr "Klonlanacak yinelenmiş disk bölümü (isim veya ID)" - msgid "Request ID of the event to show (ID only)" msgstr "Gösterilecek olayın ID'sini iste (sadece ID)" @@ -4795,15 +3997,9 @@ msgstr "İmaj üyeliğini 'beklemede' olarak sıfırlayın" msgid "Resize server to specified flavor" msgstr "Sunucuyu belirtilen flavor'a yeniden boyutlandır" -msgid "Restore backup" -msgstr "Yedeği yeniden yükle" - msgid "Restore server from rescue mode" msgstr "Kurtarma kipinden sunucuyu onar" -msgid "Restore server state before resize" -msgstr "Sunucu durumunu yeniden boyutlandırmadan önceki haline geri getir" - msgid "Restore server(s)" msgstr "Sunucu(ları/yu) onar " @@ -4828,13 +4024,6 @@ msgstr "Mevcut rolü döndür" msgid "Return existing user" msgstr "Mevcut kullanıcıyı döndür" -msgid "" -"Return the auto allocated topology for a given project. Default is current " -"project" -msgstr "" -"Belirli bir proje için otomatik olarak ayrılan topolojiyi döndürür. " -"Varsayılan geçerli projedir" - #, python-format msgid "Returning existing domain %s" msgstr "Mevcut alanı döndür %s" @@ -4909,15 +4098,6 @@ msgstr "" msgid "Roll up items with " msgstr "Elemanları ile toparla" -msgid "" -"Route to be removed from this subnet e.g.: destination=10.10.0.0/16," -"gateway=192.168.71.254 destination: destination subnet (in CIDR notation) " -"gateway: nexthop IP address (repeat option to unset multiple host routes)" -msgstr "" -"Bu altağdan silinecek yön örn: hedef=10.10.0.0/16,geçit=192.168.71.254 hedef:" -"hedef altağ (CIDR notasyonunda) geçit: bir sonraki uğranacak IP adresi " -"(birden fazla sunucu yönü ayarı kaldırmak için seçeneği tekrar et)" - #, python-format msgid "Router does not contain route %s" msgstr "Yönlendirici %s yönünü içermiyor" @@ -4949,23 +4129,6 @@ msgstr "Alt ağın ekleneceği yönlendirici (isim veya ID)" msgid "Router(s) to delete (name or ID)" msgstr "Silinecek yönlendirici(ler) (isim veya ID)" -msgid "" -"Routes associated with the router destination: destination subnet (in CIDR " -"notation) gateway: nexthop IP address (repeat option to set multiple routes)" -msgstr "" -"Yönlendirici hedefi ile ilişkili yönlendiriciler: hedef altağ (CIDR " -"notasyonunda) geçit: bir sonraki atlanacak IP adresi (birden fazla " -"yönlendirici ayarlamak için seçeneği tekrarla)" - -msgid "" -"Routes to be removed from the router destination: destination subnet (in " -"CIDR notation) gateway: nexthop IP address (repeat option to unset multiple " -"routes)" -msgstr "" -"Yönlendirici hedefinden kaldırılacak yönlendiriciler: hedef alt ağ (CIDR " -"notasyonuyla) geçit: atlanacak sonraki IP adresi (birden fazla " -"yönlendiricinin ayarını kaldırmak için seçeneği tekrarla)" - #, python-format msgid "Rule ID %(rule_id)s not found" msgstr "%(rule_id)s kural ID'si bulunamadı" @@ -4976,10 +4139,6 @@ msgstr "Gelen ağ trafiğine uygulanacak kural (varsayılan)" msgid "Rule applies to outgoing network traffic" msgstr "Dışarıya doğru ağ trafiğine uygulanacak kural" -#, python-format -msgid "Rule type \"%(rule_type)s\" only requires arguments %(args)s" -msgstr "\"%(rule_type)s\" kural türü sadece %(args)s argümanlarını gerektirir" - msgid "SSH to server" msgstr "Sunucuya SSH ile bağlan" @@ -4992,29 +4151,6 @@ msgstr "Kap içeriğini yerel olarak kaydet" msgid "Save object locally" msgstr "Nesneyi yerel olarak kaydet" -msgid "" -"Scale server to a new flavor.\n" -"\n" -"A resize operation is implemented by creating a new server and copying the\n" -"contents of the original disk into a new one. It is also a two-step process " -"for\n" -"the user: the first is to perform the resize, the second is to either " -"confirm\n" -"(verify) success and release the old server, or to declare a revert to " -"release\n" -"the new server and restart the old one." -msgstr "" -"Sunucuyu yeni bir flavor'a ölçekle.\n" -"\n" -"Bir yeniden boyutlandırma işlemi yeni bir sunucu oluşturma ve orijinal \n" -"diskteki içeriğin yeni bir tanesine kopyalanması ile gerçekleştirilir. Bu " -"ayrıca\n" -"kullanıcı için iki adımlı bir süreçtir: birincisi yeniden boyutlandırmayı " -"gerçekleştirmek,\n" -"ikincisi ya başarılı olduğunu doğrulamak ve eski sunucuyu silmek, ya da " -"yenisini silip\n" -"eskisini yeniden başlatma isteğini belirtmek." - msgid "Search by flavor (name or ID)" msgstr "Flavor'a göre ara (isim veya ID)" @@ -5030,9 +4166,6 @@ msgstr "Projeye göre ara (sadece yönetici) (isim veya ID)" msgid "Search by server status" msgstr "Sunucu durumuna göre ara" -msgid "Search by user (admin only) (name or ID)" -msgstr "Kullanıcıya göre ara (sadece yönetici) (isim veya ID)" - msgid "Secret associated with (required)" msgstr " ile ilişkili gizli anahtar (gerekli)" @@ -5045,16 +4178,6 @@ msgstr "Gösterilecek güvenlik grubu kuralları (sadece ID)" msgid "Security group rule(s) to delete (ID only)" msgstr "Silinecek güvenlik grubu kuralları (sadece ID)" -msgid "Security group to add (name or ID)" -msgstr "Eklenecek güvenlik grubu (isim veya ID)" - -msgid "" -"Security group to assign to this server (name or ID) (repeat option to set " -"multiple groups)" -msgstr "" -"Bu sunucuya atanan güvenlik grubu (isim veya ID) (birden fazla grup " -"ayarlamak için seçeneği tekrar edin)" - msgid "" "Security group to associate with this port (name or ID) (repeat option to " "set multiple security groups)" @@ -5086,9 +4209,6 @@ msgstr "" "Ağ türüne göre bölüm belirteci, vlan ağ türü için VLAN ID, geneve, gre ve " "vxlan ağ türleri için tünel ID" -msgid "Select an availability zone for the server" -msgstr "Sunucu için kullanılabilirlik bölgesi seçin" - msgid "Server (name or ID)" msgstr "Sunucu (isim veya ID)" @@ -5107,18 +4227,12 @@ msgstr "İmaj oluşturulacak sunucu (isim veya ID)" msgid "Server to list events (name or ID)" msgstr "Olayları listelenecek sunucu (isim veya ID)" -msgid "Server to receive the IP address (name or ID)" -msgstr "IP adresin alınacağı sunucu (isim veya ID)" - msgid "Server to receive the fixed IP address (name or ID)" msgstr "Sabit IP adresleri alınacak sunucu (isim veya ID)" msgid "Server to receive the floating IP address (name or ID)" msgstr "Kayan IP adresi alınacak sunucu (isim veya ID)" -msgid "Server to remove the IP address from (name or ID)" -msgstr "IP adresin kaldırılacağı sunucu (isim veya ID)" - msgid "Server to remove the fixed IP address from (name or ID)" msgstr "Sabit IP adresin silineceği sunucu (isim veya ID)" @@ -5200,39 +4314,9 @@ msgstr "Gösterilecek servis (tür, isim veya ID)" msgid "Service to modify (type, name or ID)" msgstr "Düzenlenecek servis (tür, isim veya ID)" -msgid "" -"Service type for this subnet e.g.: network:floatingip_agent_gateway. Must be " -"a valid device owner value for a network port (repeat option to set multiple " -"service types)" -msgstr "" -"Bu alt ağ için servis türü örn: network:floatingip_agent_gateway. Bir ağ " -"bağlantı noktası için geçerli bir cihaz sahibi değeri olmalıdır (birden " -"fazla servis türü ayarlamak için seçeneği tekrarlayın)" - -msgid "" -"Service type to be removed from this subnet e.g.: network:" -"floatingip_agent_gateway. Must be a valid device owner value for a network " -"port (repeat option to unset multiple service types)" -msgstr "" -"Bu altağdan kaldırılacak servis türü örn: network:floatingip_agent_gateway. " -"Bir ağ bağlantı noktası için geçerli bir aygıt sahibi değeri olmalıdır " -"(birden fazla servis türünün ayarını kaldırmak için seçeneği tekrarlayın)" - -msgid "" -"Service type to which the flavor applies to: e.g. VPN (See openstack network " -"service provider list for loaded examples.)" -msgstr "" -"Flavor'ın uygulanacağı hizmet türü: ör. VPN (Yüklü sunucular için açık devre " -"ağ servis sağlayıcısı listesine bakın.)" - msgid "Service(s) to delete (type, name or ID)" msgstr "Silinecek servis(ler) (tür, isim veya ID)" -msgid "Set DNS name to this port (requires DNS integration extension)" -msgstr "" -"DNS adını bu bağlantı noktasına ayarlayın (DNS entegrasyon uzantısı " -"gerektirir)" - msgid "Set Network QoS rule properties" msgstr "Ağ QoS kural özelliklerini ayarla" @@ -5353,9 +4437,6 @@ msgstr "" msgid "Set availability zone name" msgstr "Kullanılabilirlik bölge adını ayarla" -msgid "Set compute agent properties" -msgstr "Hesaplama ajanı özelliklerini ayarla" - msgid "Set compute service properties" msgstr "Hesaplama servisi özelliklerini ayarla" @@ -5371,24 +4452,9 @@ msgstr "Kap özelliklerini ayarla" msgid "Set credential properties" msgstr "Kimlik bilgileri özelliklerini ayarla" -msgid "" -"Set data plane status of this port (ACTIVE | DOWN). Unset it to None with " -"the 'port unset' command (requires data plane status extension)" -msgstr "" -"Bu bağlantı noktasının veri düzlemi durumunu ayarlayın (ACTIVE | DOWN). " -"'port unset' komutu ile None'a getirin (veri düzlemi durum uzantısı " -"gerektirir)" - msgid "Set default project (name or ID)" msgstr "Varsayılan projeyi ayarla (isim veya ID)" -msgid "" -"Set default quota for subnet pool as the number ofIP addresses allowed in a " -"subnet" -msgstr "" -"Bir alt ağda izin verilen IP adreslerinin sayısı olarak alt ağ havuzu için " -"varsayılan kota ayarla" - msgid "Set domain properties" msgstr "Alan özelliklerini ayarla" @@ -5416,9 +4482,6 @@ msgstr "Yüzen IP açıklaması ayarla" msgid "Set group properties" msgstr "Grup özelliklerini ayarla" -msgid "Set host properties" -msgstr "Sunucu özelliklerini ayarla" - msgid "Set identity provider description" msgstr "Kimlik sağlayıcı tanımlarını ayarla" @@ -5467,9 +4530,6 @@ msgstr "Ağ bölüm ismi ayarla" msgid "Set network segment properties" msgstr "Ağ kesimi özelliklerini ayarla" -msgid "Set new root password (interactive only)" -msgstr "Yeni root parolası ayarla (sadece interaktif)" - msgid "Set object properties" msgstr "Nesne özelliklerini ayarla" @@ -5491,18 +4551,12 @@ msgstr "Proje ismini ayarla" msgid "Set project properties" msgstr "Proje özelliklerini ayarla" -msgid "Set quotas for " -msgstr " için kotaları ayarla" - msgid "Set quotas for a specific " msgstr "Belirli bir için kota ayarla" msgid "Set quotas for project or class" msgstr "Proje veya sınıf için kotaları ayarla" -msgid "Set quotas for this project or class (name/ID)" -msgstr "Bu proje veya sınıf için (isim/ID) kotaları ayarla" - msgid "Set region properties" msgstr "Bölgenin özelliklerini ayarla" @@ -5546,9 +4600,6 @@ msgstr "Servis özelliklerini ayarla" msgid "Set service provider properties" msgstr "Servis sağlayıcı özelliklerini ayarlayın" -msgid "Set snapshot properties" -msgstr "Anlık görüntü özelliklerini ayarlayın" - msgid "Set subnet description" msgstr "Alt ağ açıklaması ayarla" @@ -5580,32 +4631,6 @@ msgstr "Alt ağ havuzu özellikleri ayarla" msgid "Set subnet properties" msgstr "Altağ özelliklerini ayarla" -msgid "" -"Set the class that provides encryption support for this volume type (e.g " -"\"LuksEncryptor\") (admin only) (This option is required when setting " -"encryption type of a volume for the first time. Consider using other " -"encryption options such as: \"--encryption-cipher\", \"--encryption-key-size" -"\" and \"--encryption-control-location\")" -msgstr "" -"Bu birim türü için şifreleme desteği sağlayan sınıfı ayarlayın (örn " -"\"LuksEncryptor\") (sadece yönetici) (Bu seçenek, bir disk bölümü şifreleme " -"türünü ilk kez ayarlarken gereklidir. Şu diğer şifreleme seçeneklerini " -"kullanmayı düşünün: \"--encryption-cipher\", \"--encryption-key-size\" ve " -"\"--encryption-control-location\")" - -msgid "" -"Set the class that provides encryption support for this volume type (e.g " -"\"LuksEncryptor\") (admin only) (This option is required when setting " -"encryption type of a volume. Consider using other encryption options such " -"as: \"--encryption-cipher\", \"--encryption-key-size\" and \"--encryption-" -"control-location\")" -msgstr "" -"Bu disk bölümü türü için şifreleme desteği sağlayan sınıfı ayarla (örn " -"\"LuksEncryptor\") (sadece yönetici) (Bir disk bölümünün şifreleme türü " -"ayarlanırken bu seçenek zorunludur. \"--encryption-cipher\", \"--encryption-" -"key-size\" ve \"--encryption-control-location\" gibi diğer şifreleme " -"seçeneklerini kullanmayı göz önünde bulundurun)" - msgid "" "Set the encryption algorithm or mode for this volume type (e.g \"aes-xts-" "plain64\") (admin only)" @@ -5642,9 +4667,6 @@ msgstr "" "encryption-cipher\", \"--encryption-key-size\" ve \"--encryption-provider\" " "gibi diğer şifreleme seçeneklerini kullanmayı göz önünde bulundurun)" -msgid "Set the password on the rebuilt instance" -msgstr "Yeniden oluşturulmuş sunucudaki parolayı ayarla" - msgid "Set the router as highly available (disabled router only)" msgstr "" "Yönlendiriciyi yüksek kullanılabilirlikli olarak ayarla (sadece " @@ -5672,17 +4694,6 @@ msgstr "Bunu varsayılan olmayan bir ağ QoS politikası olarak ayarla" msgid "Set this as a non-default subnet pool" msgstr "Bunu varsayılan olmayan altağ havuzu olarak ayarla" -msgid "" -"Set this network as an external network (external-net extension required)" -msgstr "" -"Bu şebekeyi harici bir ağ olarak ayarlayın (harici ağ uzantısı gerekir)" - -msgid "Set this network as an internal network" -msgstr "Bu şebekeyi dahili bir şebeke olarak ayarlayın" - -msgid "Set this network as an internal network (default)" -msgstr "Bu ağı dahili bir ağ olarak ayarlayın (varsayılan)" - msgid "Set this subnet pool as not shared" msgstr "Bu alt ağ havuzunu paylaşılmayan olarak ayarlayın" @@ -5754,9 +4765,6 @@ msgstr "Projeler arasında adres kapsamını paylaş" msgid "Share the network between projects" msgstr "Ağları projeler arasında paylaşın" -msgid "Shelve server(s)" -msgstr "Sunucu(yu/ları) raftan çıkart" - msgid "Show API extension" msgstr "API uzantısını göster" @@ -5804,12 +4812,6 @@ msgstr "" "Tüm projeler için detayları göster. Sadece yönetici. (varsayılan olarak " "False)" -msgid "" -"Show limits for a specific project (name or ID) [only valid with --absolute]" -msgstr "" -"Belirli bir proje için sınırları göster (isim veya ID) [sadece --absolute " -"ile geçerli]" - msgid "Show network IP availability details" msgstr "Ağ IP kullanılabilirlik detaylarını göster" @@ -5832,30 +4834,12 @@ msgstr "Parolaları düz metin olarak göster" msgid "Show project's subtree (children) as a list" msgstr "Projenin alt projelerini (çocuklarını) bir liste olarak göster" -msgid "Show quotas for " -msgstr " için kotaları göster" - -msgid "Show quotas for project or class" -msgstr "Proje veya sınıf için kotaları göster" - -msgid "Show quotas for this project or class (name or ID)" -msgstr "Bu proje veya sınıf (isim veaya ID) için kotaları göster" - -msgid "Show rate limits" -msgstr "Oran sınırları göster" - msgid "Show resource usage for a single project" msgstr "Tek bir proje için kaynak kullanımını göster" msgid "Show serial console URL" msgstr "serial konsol URL'ini göster" -msgid "Show server details" -msgstr "Sunucu detaylarını göster" - -msgid "Show server event details" -msgstr "Sunucu olay detaylarını listele" - msgid "Show server's console output" msgstr "Sunucunun konsol çıktısını göster" @@ -5868,9 +4852,6 @@ msgstr "Servis katalog bilgisini göster" msgid "Show the project's parents as a list" msgstr "Projenin üst projelerini bir liste olarak göster" -msgid "Show volume details" -msgstr "Disk bölümü detaylarını göster" - msgid "Show volume transfer request details." msgstr "Disk bölümü aktarım isteği detaylarını göster." @@ -5880,9 +4861,6 @@ msgstr "xvpvnc konsol URL'ini göster" msgid "Size of image data (in bytes)" msgstr "İmaj verisinin boyutu (bayt cinsinden)" -msgid "Skip flavor and image name lookup." -msgstr "Flavor veya imaj ismi aramasını atla." - msgid "Snapshot to backup (name or ID)" msgstr "Yedeği alınacak anlık görüntü (isim veya ID)" @@ -5911,40 +4889,10 @@ msgstr "" "(varsayılan: ad:artan), virgülle ayrılmış olarak birden çok anahtar ve yön " "belirlenebilir" -msgid "Specific service endpoint to use" -msgstr "Kullanılacak belirli bir servis uç noktası" - msgid "Specifies if the role grant is inheritable to the sub projects" msgstr "" "Rol atamasının alt projelere miras bırakılıp bırakılmayacağını belirler" -msgid "" -"Specify a gateway for the subnet. The three options are: : " -"Specific IP address to use as the gateway, 'auto': Gateway address should " -"automatically be chosen from within the subnet itself, 'none': This subnet " -"will not use a gateway, e.g.: --gateway 192.168.9.1, --gateway auto, --" -"gateway none (default is 'auto')." -msgstr "" -"Alt ağ için bir geçit belirle. Üç seçenek: : geçit olarak " -"kullanılacak belirli bir IP adresi, 'auto': Geçit adresi ağdan otomatik " -"olarak seçilmelidir, 'none': Bu alt ağ bir geçit kullanmayacak, örn: --" -"gateway 192.168.9.1, --gateway auto, --gateway none (varsayılan 'auto')." - -msgid "" -"Specify a gateway for the subnet. The options are: : Specific IP " -"address to use as the gateway, 'none': This subnet will not use a gateway, e." -"g.: --gateway 192.168.9.1, --gateway none." -msgstr "" -"Alt ağ için bir geçit belirle. Seçenekler: : Geçit olarak " -"kullanılacak belirli bir IP adresi, 'none': Bu alt ağ geçit olarak " -"kullanılmayacak, örn: --gateway 192.168.9.1, --gateway none." - -msgid "Specify an alternate project (name or ID)" -msgstr "Alternatif bir proje belirle (isim veya ID)" - -msgid "Specify an alternate user (name or ID)" -msgstr "Alternatif bir kullanıcı belirle (isim veya ID)" - msgid "Specify if this network should be used as the default external network" msgstr "" "Bu ağın varsayılan dış ağ olarak kullanılması gerekip gerekmediğini " @@ -5957,19 +4905,6 @@ msgstr "" "auto veya none --nic'i belirtmek başka bir --nic, --network veya --port " "değeriyle kullanılamaz." -msgid "" -"Specifying the auth-key as a positional argument has been deprecated. " -"Please use the --auth-key option in the future." -msgstr "" -"Yetki anahtarını pozisyonel argüman olarak belirleme kullanımdan " -"kaldırıldı. Lütfen gelecekte --auth-key seçeneğini kullanın." - -msgid "Start server(s)." -msgstr "Sunucu(ları/yu) başlat." - -msgid "Stop server(s)." -msgstr "Sunucu(ları/yu) durdur." - #, python-format msgid "Subnet does not contain %(option)s %(value)s" msgstr "Ağ %(option)s %(value)s'yü içermiyor" @@ -5977,10 +4912,6 @@ msgstr "Ağ %(option)s %(value)s'yü içermiyor" msgid "Subnet on which you want to create the floating IP (name or ID)" msgstr "Yüzen IP'yi oluşturmak istediğiniz alt ağ (isim veya ID)" -#, python-format -msgid "Subnet pool does not contain prefix %s" -msgstr "%s önekini içermeyen altağ havuzu" - msgid "Subnet pool from which this subnet will obtain a CIDR (Name or ID)" msgstr "Bu alt ağın bir CIDR alacağı alt ağ havuzu (isim veya ID)" @@ -6024,242 +4955,27 @@ msgstr "" "%s'e eklenecek etiketler (birden fazla etiket ayarlamak için seçeneği tekrar " "et)" -#, python-format -msgid "Tag to be removed from the %s (repeat option to remove multiple tags)" -msgstr "" -"%s'den kaldırılacak etiket (birden fazla etiket silmek için seçenekleri " -"tekrarlayın)" - -msgid "Target hostname" -msgstr "Hedef sunucu adı" - msgid "Thaw and enable the specified volume host" msgstr "Belirtilen disk bölümü barındırıcısını çöz ve etkinleştir" -#, python-format -msgid "The %(old)s option is deprecated, please use %(new)s instead." -msgstr "" -"%(old)s seçeneği kullanımdan kaldırıldı, lütfen onun yerine %(new)s kullanın." - -msgid "The --clear-routes option is deprecated, please use --no-route instead." -msgstr "" -"--clear-routes seçeneği kullanımdan kaldırıldı, onun yerine --no-route " -"kullanın." - -msgid "The --device-id option is deprecated, please use --device instead." -msgstr "" -"--device-id seçeneği kullanımdan kaldırıldı, lütfen onun yerine --device " -"komutunu kullanın." - -msgid "The --host-id option is deprecated, please use --host instead." -msgstr "" -"--host-id seçeneği kullanımdan kaldırıldı, lütfen onun yerine --host " -"komutunu kullanın." - -msgid "The --owner option is deprecated, please use --project instead." -msgstr "" -"--owner seçeneği kullanımdan kaldırıldı, onun yerine lütfen --project " -"seçeneğini kullanın." - msgid "" "The ID of the volume backend replication target where the host will failover " "to (required)" msgstr "" "Ana bilgisayarın üstleneceği disk bölümü arkaplan kopyası ID'si (zorunlu)" -msgid "" -"The argument --type is deprecated, use service create --name " -"type instead." -msgstr "" -"--type argümanı kullanımdan kaldırılmıştır, onun yerine service create --" -"name type komutunu kullanın." - -msgid "" -"The attribute(s) of the exsiting 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'" -msgstr "" -"Varolan uzak birimin anlık görüntüsünün nitelikleri (yönetici gerekli) " -"(birden fazla özellik belirlemek için seçeneği tekrarlayın) örn: '--remote-" -"source source-name=test_name --remote-source source-id=test_id'" - -msgid "The last backup of the previous page (name or ID)" -msgstr "Bir önceki sayfanın son yedeği (isim veya ID)" - -msgid "The last flavor ID of the previous page" -msgstr "Bir önceki sayfanın son flavor ID'si" - -msgid "" -"The last image of the previous page. Display list of images after marker. " -"Display all images if not specified. (name or ID)" -msgstr "" -"Bir önceki sayfanın son imajı. İşaretçiden sonraki imajların listesini " -"görüntüle. Belirtilmemişse tüm imajları görüntüleyin. (isim veya ID)" - -msgid "" -"The last server of the previous page. Display list of servers after marker. " -"Display all servers if not specified. (name or ID)" -msgstr "" -"Bir önceki sayfanın son sunucusu. İşaretçi sonrasındaki sunucuların " -"listesini görüntüle. Belirtilmemişse tüm sunucuları görüntüleyin. (isim veya " -"ID)" - -msgid "The last snapshot ID of the previous page" -msgstr "Bir önceki sayfanın son anlık görüntü ID'si" - -msgid "The last volume ID of the previous page" -msgstr "Bir önceki sayfanın son disk bölümü ID'si" - msgid "The object to which this RBAC policy affects (name or ID)" msgstr "Bu RBAC politikasının etkilediği nesne (ad veya kimlik)" msgid "The owner project (name or ID)" msgstr "Projenin sahibi (isim veya ID)" -msgid "" -"The physical mechanism by which the virtual network is implemented. For " -"example: flat, geneve, gre, local, vlan, vxlan." -msgstr "" -"Sanal ağın uygulandığı fiziksel mekanizma. Örneğin: düz, geneve, gre, yerel, " -"vlan, vxlan." - msgid "The project to which the RBAC policy will be enforced (name or ID)" msgstr "RBAC politikası dayatılacak proje (isim veya ID)" msgid "The remote IP prefix to associate with this rule" msgstr "Bu kural ile ilişkilendirilecek uzak IP ön eki" -msgid "" -"This command has been deprecated. Please use \"floating ip create\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"floating ip create\" " -"kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip delete\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen yerine \"floating ip delete\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip list\" instead." -msgstr "" -"Bu komut kullanımdan kalktı. Onun yerine lütfen \"floating ip list\" " -"kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip pool list\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen, onun yerine \"floating ip pool list" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip show\" instead." -msgstr "" -"Bu komut önerilmiyor. Lütfen bunun yerine \"floating ip show\"ı kullanın." - -msgid "" -"This command has been deprecated. Please use \"server add fixed ip\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"server add fixed ip\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"server add floating ip\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırılmıştır. Onun yerine lütfen \"server add " -"floating ip\" kullanın." - -msgid "" -"This command has been deprecated. Please use \"server remove fixed ip\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"server remove fixed ip" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"server remove floating ip\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"server remove floating " -"ip\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup create\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"volume backup create\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup delete\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup delete\" " -"kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup list\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup list\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup restore\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup restore" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup show\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup show\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot create\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot create" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot delete\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot delete" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot list\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"volume snapshot list\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot set\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen bunun yerine \"volume snapshot set\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot show\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot show\" " -"komutunu kullan." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot unset\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot unset" -"\" komutunu kullanın." - msgid "Token to be deleted" msgstr "Silinecek jeton" @@ -6283,20 +4999,12 @@ msgstr "Mimari türü" msgid "Type of hypervisor" msgstr "Arakatman türü" -msgid "" -"Type of the object that RBAC policy affects (\"qos_policy\" or \"network\")" -msgstr "" -"RBAC politikasını etkileyen nesne türü (\"qos_policy\" veya \"network\")" - msgid "URL" msgstr "URL" msgid "URL of the agent" msgstr "Ajanın URL'i" -msgid "Unique flavor ID; 'auto' creates a UUID (default: auto)" -msgstr "Benzersiz flavor ID'si; 'auto' bir UUID oluşturur (varsayılan: auto)" - msgid "Unlock server(s)" msgstr "Sunucu(ların/nun) kilidini kaldır" @@ -6344,12 +5052,6 @@ msgstr "Proje özelliklerini kaldır" msgid "Unset router properties" msgstr "Yönlendirici özelliklerini kaldır" -msgid "Unset server properties" -msgstr "Sunucu özelliklerini kaldır" - -msgid "Unset snapshot properties" -msgstr "Anlık görüntü özelliklerinin ayarını kaldır" - msgid "Unset subnet pool properties" msgstr "Altağ havuz özelliklerinin ayarlarını kaldır" @@ -6432,12 +5134,6 @@ msgstr "Gizli IP adresi kullan" msgid "Use public IP address" msgstr "Genel IP adresi kullan" -msgid "" -"Use specified volume as the config drive, or 'True' to use an ephemeral drive" -msgstr "" -"Belirtilen birimi yapılandırma sürücüsü olarak kullanın veya kısa ömürlü bir " -"sürücüyü kullanmak için 'True' kullanın" - msgid "" "Used to populate the backup_type property of the backup image (default: " "empty)" @@ -6451,9 +5147,6 @@ msgstr "Üst veri sunucusundan sunmak için kullanıcı veri dosyası" msgid "User description" msgstr "Kullanıcı tanımı" -msgid "User must be specified" -msgstr "Kullanıcı belirtilmeli" - msgid "User that is assuming authorization (name or ID)" msgstr "Yetki varsayan kullanıcı (adı veya kimliği)" @@ -6472,9 +5165,6 @@ msgstr "Gösterilecek kullanıcı (isim veya ID)" msgid "User to filter (name or ID)" msgstr "Filtrelenecek kullanıcı (isim veya ID)" -msgid "User to list (name or ID)" -msgstr "Listelenecek kullanıcı (isim veya ID)" - msgid "User to modify (name or ID)" msgstr "Düzenlenecek kullanıcı (isim veya ID)" @@ -6497,13 +5187,6 @@ msgstr "" msgid "VLAN ID for VLAN networks or Tunnel ID for GENEVE/GRE/VXLAN networks" msgstr "VLAN ağları için VLAN ID veya GENEVA/GRE/VXLAN ağları için Tünel ID" -msgid "" -"VNIC type for this port (direct | direct-physical | macvtap | normal | " -"baremetal | virtio-forwarder, default: normal)" -msgstr "" -"Bu bağlantı noktası için VNIC türü (direct | direct-physical | macvtap | " -"normal | baremetal | virtio-forwarder, varsayılan: normal)" - msgid "" "Validate the requirements for auto allocated topology. Does not return a " "topology." @@ -6536,25 +5219,6 @@ msgstr "" msgid "Volume name" msgstr "Disk bölümü ismi" -msgid "" -"Volume or snapshot (name or ID) must be specified if --block-device-mapping " -"is specified" -msgstr "" -"Disk bölümü veya anlık görüntü (isim veya ID), eğer --block-device-mapping " -"belirtildiyse, belirtilmek zorundadır." - -msgid "Volume size in GB (Required unless --snapshot or --source is specified)" -msgstr "" -"GB cinsinden disk bölümü boyutu (--snapshot veya --source belirtilmezse " -"gereklidir)" - -msgid "" -"Volume size in GB (Required unless --snapshot or --source or --source-" -"replicated is specified)" -msgstr "" -"GB cinsinden disk bölümü boyutu (--snapshot veya --source veya --source-" -"replicated belirtilmedikçe gereklidir)" - msgid "Volume to add (name or ID)" msgstr "Eklenecek disk bölümü (isim veya ID)" @@ -6576,12 +5240,6 @@ msgstr "Düzenlenecek disk bölümü (isim veya ID)" msgid "Volume to remove (name or ID)" msgstr "Kaldırılacak disk bölümü (isim veya ID)" -msgid "Volume to restore to (name or ID)" -msgstr "Geri yüklenecek disk bölümü (isim veya ID)" - -msgid "Volume to snapshot (name or ID)" -msgstr "Anlık görüntüsü oluşturulacak disk bölümü (isim veya ID)" - msgid "Volume to snapshot (name or ID) (default is )" msgstr "" "Anlık görüntüsü alınacak disk bölümü (isim veya ID) (varsayılan _argparse.ArgumentParser: LOG.debug('get_parser(%s)', prog_name) - parser = super().get_parser(prog_name) + parser = super().get_parser(prog_name) # type: ignore parser = self.update_parser_common(parser) LOG.debug('common parser: %s', parser) if self.is_neutron or self.is_docs_build: @@ -157,11 +160,13 @@ def update_parser_compute(self, parser): def take_action(self, parsed_args): if self.is_neutron: return self.take_action_network( - self.app.client_manager.network, parsed_args + self.app.client_manager.network, # type: ignore + parsed_args, ) elif self.is_nova_network: return self.take_action_compute( - self.app.client_manager.sdk_connection.compute, parsed_args + self.app.client_manager.compute, # type: ignore + parsed_args, ) def take_action_network(self, client, parsed_args): @@ -197,6 +202,8 @@ class NetworkAndComputeDelete(NetworkAndComputeCommand, metaclass=abc.ABCMeta): following the rules in doc/source/command-errors.rst. """ + resource: str + def take_action(self, parsed_args): ret = 0 resources = getattr(parsed_args, self.resource, []) @@ -210,7 +217,7 @@ def take_action(self, parsed_args): ) else: self.take_action_compute( - self.app.client_manager.sdk_connection.compute, + self.app.client_manager.compute, parsed_args, ) except Exception as e: @@ -268,7 +275,7 @@ def take_action(self, parsed_args): ) else: return self.take_action_compute( - self.app.client_manager.sdk_connection.compute, parsed_args + self.app.client_manager.compute, parsed_args ) except openstack.exceptions.HttpException as exc: msg = _("Error while executing command: %s") % exc.message @@ -294,16 +301,15 @@ class NeutronCommandWithExtraArgs(command.Command): } def _get_property_converter(self, _property): - if 'type' not in _property: - converter = str - else: + if 'type' in _property: converter = self._allowed_types_dict.get(_property['type']) + else: + converter = str if not converter: raise exceptions.CommandError( _( - "Type {property_type} of property {name} " - "is not supported" + "Type {property_type} of property {name} is not supported" ).format( property_type=_property['type'], name=_property['name'] ) @@ -311,7 +317,7 @@ def _get_property_converter(self, _property): return converter def _parse_extra_properties(self, extra_properties): - result = {} + result: dict[str, ty.Any] = {} if extra_properties: for _property in extra_properties: converter = self._get_property_converter(_property) @@ -344,7 +350,7 @@ def get_parser(self, prog_name): class NeutronUnsetCommandWithExtraArgs(NeutronCommandWithExtraArgs): def _parse_extra_properties(self, extra_properties): - result = {} + result: dict[str, ty.Any] = {} if extra_properties: for _property in extra_properties: result[_property['name']] = None diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py index 9cb3e1a368..30c0da0645 100644 --- a/openstackclient/network/utils.py +++ b/openstackclient/network/utils.py @@ -23,7 +23,7 @@ def transform_compute_security_group_rule(sg_rule): from_port = info.pop('from_port') to_port = info.pop('to_port') if isinstance(from_port, int) and isinstance(to_port, int): - port_range = {'port_range': "%u:%u" % (from_port, to_port)} + port_range = {'port_range': f"{from_port}:{to_port}"} elif from_port is None and to_port is None: port_range = {'port_range': ""} else: @@ -58,12 +58,12 @@ def str2list(strlist): return result -def str2dict(strdict): +def str2dict(strdict: str) -> dict[str, str]: """Convert key1:value1;key2:value2;... string into dictionary. :param strdict: string in the form of key1:value1;key2:value2 """ - result = {} + result: dict[str, str] = {} if not strdict: return result i = 0 diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index fee3d143c7..178c3afbf3 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -16,10 +16,10 @@ import logging import netaddr -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -76,8 +75,7 @@ def get_parser(self, prog_name): action='append', default=[], help=_( - "IP address or CIDR " - "(repeat option to set multiple addresses)" + "IP address or CIDR (repeat option to set multiple addresses)" ), ) parser.add_argument( @@ -139,7 +137,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.address_group) msg = _( - "%(result)s of %(total)s address groups failed " "to delete." + "%(result)s of %(total)s address groups failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -153,13 +151,13 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only address groups of given name in output"), + help=_("List only address groups with the specified name"), ) parser.add_argument( '--project', metavar="", help=_( - "List address groups according to their project " + "List only address groups with the specified project " "(name or ID)" ), ) @@ -233,8 +231,7 @@ def get_parser(self, prog_name): action='append', default=[], help=_( - "IP address or CIDR " - "(repeat option to set multiple addresses)" + "IP address or CIDR (repeat option to set multiple addresses)" ), ) return parser diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index f28215462f..8a38dab4d5 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -144,7 +144,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.address_scope) msg = _( - "%(result)s of %(total)s address scopes failed " "to delete." + "%(result)s of %(total)s address scopes failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -160,7 +160,7 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only address scopes of given name in output"), + help=_("List only address scopes with the specified name"), ) parser.add_argument( '--ip-version', @@ -169,14 +169,15 @@ def get_parser(self, prog_name): metavar='', dest='ip_version', help=_( - "List address scopes of given IP version networks (4 or 6)" + "List only address scopes with the specified IP version " + "networks (4 or 6)" ), ) parser.add_argument( '--project', metavar="", help=_( - "List address scopes according to their project " + "List only address scopes with the specified project " "(name or ID)" ), ) @@ -186,12 +187,12 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List address scopes shared between projects"), + help=_("List only address scopes shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List address scopes not shared between projects"), + help=_("List only address scopes not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index 9c39b3f9ca..24475852fb 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -16,10 +16,10 @@ import logging from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common from openstackclient.network import utils as network_utils @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'name', 'revision_number'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -151,7 +150,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network # Build the create attributes. attrs = {} attrs['protocol'] = network_utils.get_protocol(parsed_args) @@ -249,7 +248,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): result = 0 - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network for r in parsed_args.rule: try: obj = client.find_default_security_group_rule( @@ -301,7 +300,8 @@ def get_parser(self, prog_name): metavar='', type=network_utils.convert_to_lowercase, help=_( - "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " + "List only default rules with the specified IP protocol " + "(ah, dhcp, egp, esp, gre, " "icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, " "ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, " "sctp, tcp, udp, udplite, vrrp and integer " @@ -320,7 +320,7 @@ def get_parser(self, prog_name): '--ingress', action='store_true', help=_( - "List default rules which will be applied to incoming " + "List only default rules which will be applied to incoming " "network traffic" ), ) @@ -328,14 +328,14 @@ def get_parser(self, prog_name): '--egress', action='store_true', help=_( - "List default rules which will be applied to outgoing " + "List only default rules which will be applied to outgoing " "network traffic" ), ) return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network column_headers = ( 'ID', 'IP Protocol', @@ -404,7 +404,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network obj = client.find_default_security_group_rule( parsed_args.rule, ignore_missing=False ) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 301ae716df..76b91a0ecd 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -12,6 +12,8 @@ """IP Floating action implementations""" +from openstack import exceptions as sdk_exceptions +from osc_lib.cli import format_columns from osc_lib import utils from osc_lib.utils import tags as _tag @@ -21,15 +23,14 @@ from openstackclient.network import common _formatters = { - 'port_details': utils.format_dict, + 'port_details': format_columns.DictColumn, } def _get_network_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -122,7 +123,7 @@ def update_parser_network(self, parser): '--port', metavar='', help=self.enhance_help_neutron( - _("Port to be associated with the floating IP " "(name or ID)") + _("Port to be associated with the floating IP (name or ID)") ), ) parser.add_argument( @@ -242,25 +243,37 @@ def update_parser_network(self, parser): parser.add_argument( '--network', metavar='', + dest='networks', + action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to " - "given network (name or ID)" + "List only floating IP(s) with the specified network " + "(name or ID) " + "(repeat option to fiter on multiple networks)" ) ), ) parser.add_argument( '--port', metavar='', + dest='ports', + action='append', help=self.enhance_help_neutron( - _("List floating IP(s) according to given port (name or ID)") + _( + "List only floating IP(s) with the specified port " + "(name or ID) " + "(repeat option to fiter on multiple ports)" + ) ), ) parser.add_argument( '--fixed-ip-address', metavar='', help=self.enhance_help_neutron( - _("List floating IP(s) according to given fixed IP address") + _( + "List only floating IP(s) with the specified fixed IP " + "address" + ) ), ) parser.add_argument( @@ -268,27 +281,19 @@ def update_parser_network(self, parser): metavar='', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given floating IP " + "List only floating IP(s) with the specified floating IP " "address" ) ), ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=self.enhance_help_neutron( - _("List additional fields in output") - ), - ) parser.add_argument( '--status', metavar='', choices=['ACTIVE', 'DOWN'], help=self.enhance_help_neutron( _( - "List floating IP(s) according to given status ('ACTIVE', " - "'DOWN')" + "List only floating IP(s) with the specified status " + "('ACTIVE', 'DOWN')" ) ), ) @@ -297,8 +302,8 @@ def update_parser_network(self, parser): metavar='', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given project (name or " - "ID)" + "List only floating IP(s) with the specified project " + "(name or ID)" ) ), ) @@ -306,16 +311,27 @@ def update_parser_network(self, parser): parser.add_argument( '--router', metavar='', + dest='routers', + action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given router (name or " - "ID)" + "List only floating IP(s) with the specified router " + "(name or ID) " + "(repeat option to fiter on multiple routers)" ) ), ) _tag.add_tag_filtering_option_to_parser( parser, _('floating IP'), enhance_help=self.enhance_help_neutron ) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=self.enhance_help_neutron( + _("List additional fields in output") + ), + ) return parser @@ -323,7 +339,7 @@ def take_action_network(self, client, parsed_args): network_client = self.app.client_manager.network identity_client = self.app.client_manager.identity - columns = ( + columns: tuple[str, ...] = ( 'id', 'floating_ip_address', 'fixed_ip_address', @@ -331,7 +347,7 @@ def take_action_network(self, client, parsed_args): 'floating_network_id', 'project_id', ) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Floating IP Address', 'Fixed IP Address', @@ -340,7 +356,7 @@ def take_action_network(self, client, parsed_args): 'Project', ) if parsed_args.long: - columns = columns + ( + columns += ( 'router_id', 'status', 'description', @@ -348,7 +364,7 @@ def take_action_network(self, client, parsed_args): 'dns_name', 'dns_domain', ) - headers = headers + ( + headers += ( 'Router', 'Status', 'Description', @@ -359,22 +375,33 @@ def take_action_network(self, client, parsed_args): query = {} - if parsed_args.network is not None: - network = network_client.find_network( - parsed_args.network, ignore_missing=False - ) - query['floating_network_id'] = network.id - if parsed_args.port is not None: - port = network_client.find_port( - parsed_args.port, ignore_missing=False - ) - query['port_id'] = port.id + if parsed_args.networks is not None: + network_ids = [] + for network in parsed_args.networks: + network_id = network_client.find_network( + network, ignore_missing=False + ).id + network_ids.append(network_id) + query['floating_network_id'] = network_ids + + if parsed_args.ports is not None: + port_ids = [] + for port in parsed_args.ports: + port_id = network_client.find_port( + port, ignore_missing=False + ).id + port_ids.append(port_id) + query['port_id'] = port_ids + if parsed_args.fixed_ip_address is not None: query['fixed_ip_address'] = parsed_args.fixed_ip_address + if parsed_args.floating_ip_address is not None: query['floating_ip_address'] = parsed_args.floating_ip_address + if parsed_args.status: query['status'] = parsed_args.status + if parsed_args.project is not None: project = identity_common.find_project( identity_client, @@ -382,15 +409,22 @@ def take_action_network(self, client, parsed_args): parsed_args.project_domain, ) query['project_id'] = project.id - if parsed_args.router is not None: - router = network_client.find_router( - parsed_args.router, ignore_missing=False - ) - query['router_id'] = router.id + + if parsed_args.routers is not None: + router_ids = [] + for router in parsed_args.routers: + router_id = network_client.find_router( + router, ignore_missing=False + ).id + router_ids.append(router_id) + query['router_id'] = router_ids _tag.get_tag_filtering_args(parsed_args, query) - data = client.ips(**query) + try: + data = list(client.ips(**query)) + except sdk_exceptions.NotFoundException: + data = [] return ( headers, @@ -405,14 +439,14 @@ def take_action_network(self, client, parsed_args): ) def take_action_compute(self, client, parsed_args): - columns = ( + columns: tuple[str, ...] = ( 'ID', 'IP', 'Fixed IP', 'Instance ID', 'Pool', ) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Floating IP Address', 'Fixed IP Address', @@ -448,14 +482,13 @@ def get_parser(self, prog_name): '--port', metavar='', help=_("Associate the floating IP with port (name or ID)"), - ), + ) parser.add_argument( '--fixed-ip-address', metavar='', dest='fixed_ip_address', help=_( - "Fixed IP of the port " - "(required only if port has multiple IPs)" + "Fixed IP of the port (required only if port has multiple IPs)" ), ) parser.add_argument( @@ -574,7 +607,7 @@ def take_action(self, parsed_args): parsed_args.floating_ip, ignore_missing=False, ) - attrs = {} + attrs: dict[str, None] = {} if parsed_args.port: attrs['port_id'] = None if parsed_args.qos_policy: diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index e4a9aa511d..cf770c2cdc 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -14,11 +14,12 @@ """Floating IP Port Forwarding action implementations""" import logging +import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -85,10 +86,9 @@ def validate_port(port): def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -144,12 +144,12 @@ def get_parser(self, prog_name): "The protocol used in the floating IP " "port forwarding, for instance: TCP, UDP" ), - ), + ) parser.add_argument( '--description', metavar='', help=_( - "A text to describe/contextualize the use of the " + "Text to describe/contextualize the use of the " "port forwarding configuration" ), ) @@ -165,7 +165,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} client = self.app.client_manager.network floating_ip = client.find_ip( parsed_args.floating_ip, @@ -243,7 +243,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.port_forwarding_id) msg = _( - "%(result)s of %(total)s Port forwarding failed " "to delete." + "%(result)s of %(total)s Port forwarding failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -265,8 +265,8 @@ def get_parser(self, prog_name): '--port', metavar='', help=_( - "Filter the list result by the ID or name of " - "the internal network port" + "List only floating IP port forwardings with the " + "specified internal network port (name or ID)" ), ) parser.add_argument( @@ -274,14 +274,17 @@ def get_parser(self, prog_name): metavar='', dest='external_protocol_port', help=_( - "Filter the list result by the " - "protocol port number of the floating IP" + "List only floating IP port forwardings with the " + "specified external protocol port number" ), ) parser.add_argument( '--protocol', - metavar='protocol', - help=_("Filter the list result by the port protocol"), + metavar='', + help=_( + "List only floating IP port forwardings with the " + "specified protocol number" + ), ) return parser @@ -404,12 +407,12 @@ def get_parser(self, prog_name): metavar='', choices=['tcp', 'udp'], help=_("The IP protocol used in the floating IP port forwarding"), - ), + ) parser.add_argument( '--description', metavar='', help=_( - "A text to describe/contextualize the use of " + "Text to describe/contextualize the use of " "the port forwarding configuration" ), ) diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index 30b6a0e494..f78c7ec866 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -14,9 +14,9 @@ """IP Availability Info implementations""" from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -26,10 +26,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['id', 'name', 'location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -48,14 +47,17 @@ def get_parser(self, prog_name): metavar='', dest='ip_version', help=_( - "List IP availability of given IP version " - "networks (default is 4)" + "List only IP availability with the specified IP version " + "networks (4 or 6, default is 4)" ), ) parser.add_argument( '--project', metavar='', - help=_("List IP availability of given project (name or ID)"), + help=_( + "List only IP availability with the specified project " + "(name or ID)" + ), ) identity_common.add_project_domain_option_to_parser(parser) return parser diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index 598ced9979..742c263964 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -15,20 +15,19 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -66,8 +65,7 @@ def get_parser(self, prog_name): required=True, metavar='', help=_( - 'The network protocol for the netfilter conntrack target ' - 'rule' + 'The network protocol for the netfilter conntrack target rule' ), ) parser.add_argument( @@ -99,7 +97,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( 'conntrack_helper_id', @@ -147,25 +145,31 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( '--helper', metavar='', - help=_('The netfilter conntrack helper module'), + help=_( + 'List only helpers using the specified netfilter conntrack ' + 'helper module' + ), ) parser.add_argument( '--protocol', metavar='', help=_( - 'The network protocol for the netfilter conntrack target ' - 'rule' + 'List only helpers with the specified network protocol for ' + 'the netfilter conntrack target rule' ), ) parser.add_argument( '--port', metavar='', - help=_('The network port for the netfilter conntrack target rule'), + help=_( + 'List only helpers with the specified network port for ' + 'the netfilter conntrack target rule (name or ID)' + ), ) return parser @@ -210,7 +214,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( 'conntrack_helper_id', @@ -226,8 +230,7 @@ def get_parser(self, prog_name): '--protocol', metavar='', help=_( - 'The network protocol for the netfilter conntrack target ' - 'rule' + 'The network protocol for the netfilter conntrack target rule' ), ) parser.add_argument( @@ -245,7 +248,7 @@ def take_action(self, parsed_args): client.update_conntrack_helper( parsed_args.conntrack_helper_id, attrs.pop('router_id'), - **attrs + **attrs, ) @@ -257,7 +260,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( 'conntrack_helper_id', diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index 0d92f2c111..dde6ccd0cd 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -74,30 +73,32 @@ class CreateLocalIP(command.ShowOne): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - '--name', metavar="", help=_("New local IP name") + '--name', metavar="", help=_("New Local IP name") ) parser.add_argument( '--description', metavar="", - help=_("New local IP description"), + help=_("Description for Local IP"), ) parser.add_argument( '--network', metavar='', - help=_("Network to allocate Local IP (name or ID)"), + help=_("Network to allocate Local IP from (name or ID)"), ) parser.add_argument( '--local-port', metavar='', - help=_("Port to allocate Local IP (name or ID)"), + help=_("Port to allocate Local IP from (name or ID)"), ) parser.add_argument( "--local-ip-address", metavar="", - help=_("IP address or CIDR "), + help=_("IP address or CIDR for Local IP"), ) parser.add_argument( - '--ip-mode', metavar='', help=_("local IP ip mode") + '--ip-mode', + metavar='', + help=_("IP mode to use for Local IP"), ) identity_common.add_project_domain_option_to_parser(parser) @@ -116,7 +117,7 @@ def take_action(self, parsed_args): class DeleteLocalIP(command.Command): - _description = _("Delete local IP(s)") + _description = _("Delete Local IP(s)") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -149,14 +150,15 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.local_ip) - msg = _( - "%(result)s of %(total)s local IPs failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s local IPs failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) class SetLocalIP(command.Command): - _description = _("Set local ip properties") + _description = _("Set Local IP properties") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -171,7 +173,7 @@ def get_parser(self, prog_name): parser.add_argument( '--description', metavar="", - help=_('Set local IP description'), + help=_('Set Local IP description'), ) return parser @@ -188,7 +190,7 @@ def take_action(self, parsed_args): class ListLocalIP(command.Lister): - _description = _("List local IPs") + _description = _("List Local IPs") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -196,36 +198,38 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only local IPs of given name in output"), + help=_("List only local IP(s) with the specified name"), ) parser.add_argument( '--project', metavar="", help=_( - "List Local IPs according to their project " "(name or ID)" + "List only local IP(s) with the specified project (name or ID)" ), ) parser.add_argument( '--network', metavar='', help=_( - "List Local IP(s) according to " "given network (name or ID)" + "List only local IP(s) with the specified network (name or ID)" ), ) parser.add_argument( '--local-port', metavar='', - help=_("List Local IP(s) according to " "given port (name or ID)"), + help=_( + "List only local IP(s) with the specified port (name or ID)" + ), ) parser.add_argument( '--local-ip-address', metavar='', - help=_("List Local IP(s) according to " "given Local IP Address"), + help=_("List only local IP(s) with the specified IP address"), ) parser.add_argument( '--ip-mode', metavar='', - help=_("List Local IP(s) according to " "given IP mode"), + help=_("List only local IP(s) with the specified IP mode"), ) identity_common.add_project_domain_option_to_parser(parser) @@ -295,7 +299,7 @@ def take_action(self, parsed_args): class ShowLocalIP(command.ShowOne): - _description = _("Display local IP details") + _description = _("Display Local IP details") def get_parser(self, prog_name): parser = super().get_parser(prog_name) diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index 9f8325be40..123faa67ce 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'name', 'id', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -44,7 +43,7 @@ def get_parser(self, prog_name): 'local_ip', metavar='', help=_( - "Local IP that the port association belongs to " "(Name or ID)" + "Local IP that the port association belongs to (Name or ID)" ), ) parser.add_argument( @@ -90,7 +89,7 @@ def get_parser(self, prog_name): 'local_ip', metavar="", help=_( - "Local IP that the port association belongs to " "(Name or ID)" + "Local IP that the port association belongs to (Name or ID)" ), ) parser.add_argument( @@ -151,18 +150,21 @@ def get_parser(self, prog_name): '--fixed-port', metavar='', help=_( - "Filter the list result by the ID or name of " "the fixed port" + "List only local IP assocations with the specified fixed IP " + "port (name or ID)" ), ) parser.add_argument( '--fixed-ip', metavar='', - help=_("Filter the list result by fixed ip"), + help=_( + "List only local IP associations with the specified fixed IP" + ), ) parser.add_argument( '--host', metavar='', - help=_("Filter the list result by given host"), + help=_("List only local IP associations with the specified host"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 3b6caab66c..78a7ae9117 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -14,12 +14,13 @@ # under the License. """Router NDP proxy action implementations""" + import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -28,10 +29,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -68,7 +68,7 @@ def get_parser(self, prog_name): '--description', metavar='', help=_( - "A text to describe/contextualize the use of the " + "Text to describe/contextualize the use of the " "NDP proxy configuration" ), ) @@ -123,13 +123,13 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _("Failed to delete NDP proxy " "'%(ndp_proxy)s': %(e)s"), + _("Failed to delete NDP proxy '%(ndp_proxy)s': %(e)s"), {'ndp_proxy': ndp_proxy, 'e': e}, ) if result > 0: total = len(parsed_args.ndp_proxy) msg = _( - "%(result)s of %(total)s NDP proxies failed " "to delete." + "%(result)s of %(total)s NDP proxies failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -143,30 +143,37 @@ def get_parser(self, prog_name): '--router', metavar='', help=_( - "List only NDP proxies belonging to this router (name or ID)" + "List only NDP proxies associated with the specifed router " + "(name or ID)" ), ) parser.add_argument( '--port', metavar='', help=_( - "List only NDP proxies associated to this port (name or ID)" + "List only NDP proxies associated with the specified port " + "(name or ID)" ), ) parser.add_argument( '--ip-address', - metavar='ip-address', - help=_("List only NDP proxies according to their IPv6 address"), + metavar='', + help=_( + "List only NDP proxies associated with the specified " + "IPv6 address" + ), ) parser.add_argument( '--project', metavar='', - help=_("List NDP proxies according to their project (name or ID)"), + help=_( + "List only NDP proxies with the specified project (name or ID)" + ), ) parser.add_argument( '--name', metavar='', - help=_("List NDP proxies according to their name"), + help=_("List only NDP proxies with the specified name"), ) identity_common.add_project_domain_option_to_parser(parser) @@ -246,7 +253,7 @@ def get_parser(self, prog_name): '--description', metavar='', help=_( - "A text to describe/contextualize the use of " + "Text to describe/contextualize the use of " "the NDP proxy configuration" ), ) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index de1910ea7c..c8e1f360a7 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -14,6 +14,7 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns +from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag @@ -23,12 +24,12 @@ from openstackclient.network import common -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' -class RouterExternalColumn(cliff_columns.FormattableColumn): +class RouterExternalColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'External' if self._value else 'Internal' @@ -67,8 +68,7 @@ def _get_columns_network(item): def _get_columns_compute(item): - column_map = {} - return utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, {}) def _get_attrs_network(client_manager, parsed_args): @@ -165,7 +165,7 @@ def _add_additional_network_options(parser): help=_( "The physical mechanism by which the virtual network " "is implemented. For example: " - "flat, geneve, gre, local, vlan, vxlan." + "flat, geneve, gre, local, vlan or vxlan." ), ) parser.add_argument( @@ -291,8 +291,8 @@ def update_parser_network(self, parser): action='store_true', help=self.enhance_help_neutron( _( - "The network has an external routing facility that's not " - "managed by Neutron and can be used as in: " + "The network has an external routing facility that is not " + "managed by Neutron and can be used. For example: " "openstack router set --external-gateway NETWORK " "(external-net extension required)" ) @@ -349,6 +349,22 @@ def update_parser_network(self, parser): ), ) + vlan_qinq_grp = parser.add_mutually_exclusive_group() + vlan_qinq_grp.add_argument( + '--qinq-vlan', + action='store_true', + help=self.enhance_help_neutron( + _("Enable VLAN QinQ (S-Tag ethtype 0x8a88) for the network") + ), + ) + vlan_qinq_grp.add_argument( + '--no-qinq-vlan', + action='store_true', + help=self.enhance_help_neutron( + _("Disable VLAN QinQ (S-Tag ethtype 0x8a88) for the network") + ), + ) + _add_additional_network_options(parser) _tag.add_tag_option_to_parser_for_create( parser, _('network'), enhance_help=self.enhance_help_neutron @@ -372,6 +388,29 @@ def take_action_network(self, client, parsed_args): attrs['vlan_transparent'] = True if parsed_args.no_transparent_vlan: attrs['vlan_transparent'] = False + + if parsed_args.qinq_vlan: + attrs['vlan_qinq'] = True + if parsed_args.no_qinq_vlan: + attrs['vlan_qinq'] = False + + if attrs.get('vlan_transparent') and attrs.get('vlan_qinq'): + msg = _( + "--transparent-vlan and --qinq-vlan can not be both enabled " + "for the network." + ) + raise exceptions.CommandError(msg) + + if ( + parsed_args.segmentation_id + and not parsed_args.provider_network_type + ): + msg = _( + "--provider-segment requires --provider-network-type " + "to be specified." + ) + raise exceptions.CommandError(msg) + attrs.update( self._parse_extra_properties(parsed_args.extra_properties) ) @@ -430,12 +469,12 @@ def update_parser_network(self, parser): router_ext_group.add_argument( '--external', action='store_true', - help=self.enhance_help_neutron(_("List external networks")), + help=self.enhance_help_neutron(_("List only external networks")), ) router_ext_group.add_argument( '--internal', action='store_true', - help=self.enhance_help_neutron(_("List internal networks")), + help=self.enhance_help_neutron(_("List only internal networks")), ) parser.add_argument( '--long', @@ -448,24 +487,26 @@ def update_parser_network(self, parser): '--name', metavar='', help=self.enhance_help_neutron( - _("List networks according to their name") + _("List only networks with the specified name") ), ) admin_state_group = parser.add_mutually_exclusive_group() admin_state_group.add_argument( '--enable', action='store_true', - help=self.enhance_help_neutron(_("List enabled networks")), + help=self.enhance_help_neutron(_("List only enabled networks")), ) admin_state_group.add_argument( '--disable', action='store_true', - help=self.enhance_help_neutron(_("List disabled networks")), + help=self.enhance_help_neutron(_("List only disabled networks")), ) parser.add_argument( '--project', metavar='', - help=_("List networks according to their project (name or ID)"), + help=_( + "List only networks with the specified project (name or ID)" + ), ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron @@ -475,14 +516,14 @@ def update_parser_network(self, parser): '--share', action='store_true', help=self.enhance_help_neutron( - _("List networks shared between projects") + _("List only networks shared between projects") ), ) shared_group.add_argument( '--no-share', action='store_true', help=self.enhance_help_neutron( - _("List networks not shared between projects") + _("List only networks not shared between projects") ), ) parser.add_argument( @@ -491,7 +532,7 @@ def update_parser_network(self, parser): choices=['ACTIVE', 'BUILD', 'DOWN', 'ERROR'], help=self.enhance_help_neutron( _( - "List networks according to their status " + "List only networks with the specified status " "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" ) ), @@ -502,9 +543,10 @@ def update_parser_network(self, parser): choices=['flat', 'geneve', 'gre', 'local', 'vlan', 'vxlan'], help=self.enhance_help_neutron( _( - "List networks according to their physical mechanisms. " + "List only networks with the specified physical " + "mechanisms. " "The supported options are: flat, geneve, gre, local, " - "vlan, vxlan." + "vlan and vxlan." ) ), ) @@ -513,7 +555,10 @@ def update_parser_network(self, parser): metavar='', dest='physical_network', help=self.enhance_help_neutron( - _("List networks according to name of the physical network") + _( + "List only networks with the specified physical network " + "name" + ) ), ) parser.add_argument( @@ -522,8 +567,9 @@ def update_parser_network(self, parser): dest='segmentation_id', help=self.enhance_help_neutron( _( - "List networks according to VLAN ID for VLAN networks or " - "Tunnel ID for GENEVE/GRE/VXLAN networks" + "List only networks with the specified provider segment " + "ID (VLAN ID for VLAN networks or " + "Tunnel ID for GENEVE/GRE/VXLAN networks)" ) ), ) @@ -532,7 +578,7 @@ def update_parser_network(self, parser): metavar='', dest='agent_id', help=self.enhance_help_neutron( - _('List networks hosted by agent (ID only)') + _('List only networks hosted the specified agent (ID only)') ), ) _tag.add_tag_filtering_option_to_parser( @@ -543,7 +589,7 @@ def update_parser_network(self, parser): def take_action_network(self, client, parsed_args): identity_client = self.app.client_manager.identity if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'status', @@ -556,7 +602,7 @@ def take_action_network(self, client, parsed_args): 'availability_zones', 'tags', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', @@ -756,10 +802,10 @@ def get_parser(self, prog_name): '--external', action='store_true', help=_( - "The network has an external routing facility that's not " - "managed by Neutron and can be used as in: " + "The network has an external routing facility that is not " + "managed by Neutron and can be used. For example: " "openstack router set --external-gateway NETWORK " - "(external-net extension required)" + "(external-net extension required)." ), ) external_router_grp.add_argument( diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index d31bd3f561..806422b102 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -17,21 +17,21 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class AliveColumn(cliff_columns.FormattableColumn): +class AliveColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return ":-)" if self._value else "XXX" -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' @@ -89,9 +89,7 @@ def take_action(self, parsed_args): try: client.add_dhcp_agent_to_network(agent, network) except Exception: - msg = 'Failed to add {} to {}'.format( - network.name, agent.agent_type - ) + msg = f'Failed to add {network.name} to {agent.agent_type}' exceptions.CommandError(msg) @@ -157,7 +155,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.network_agent) msg = _( - "%(result)s of %(total)s network agents failed " "to delete." + "%(result)s of %(total)s network agents failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -166,32 +164,37 @@ def take_action(self, parsed_args): # OSC minimum requirements include SDK 1.0. class ListNetworkAgent(command.Lister): _description = _("List network agents") + _supported_agents = { + 'bgp': 'BGP dynamic routing agent', + 'dhcp': 'DHCP agent', + 'open-vswitch': 'Open vSwitch agent', + 'linux-bridge': 'Linux bridge agent', + 'ofa': 'OFA driver agent', + 'l3': 'L3 agent', + 'loadbalancer': 'Loadbalancer agent', + 'metering': 'Metering agent', + 'metadata': 'Metadata agent', + 'macvtap': 'Macvtap agent', + 'nic': 'NIC Switch agent', + 'baremetal': 'Baremetal Node', + 'ovn-controller': 'OVN Controller agent', + 'ovn-controller-gateway': 'OVN Controller Gateway agent', + 'ovn-metadata': 'OVN Metadata agent', + 'ovn-agent': 'OVN Neutron agent', + } def get_parser(self, prog_name): parser = super().get_parser(prog_name) + supported_agents = ','.join(self._supported_agents.keys()) parser.add_argument( '--agent-type', metavar='', - choices=[ - "bgp", - "dhcp", - "open-vswitch", - "linux-bridge", - "ofa", - "l3", - "loadbalancer", - "metering", - "metadata", - "macvtap", - "nic", - "baremetal", - ], + choices=list(self._supported_agents.keys()), help=_( "List only agents with the specified agent type. " - "The supported agent types are: bgp, dhcp, open-vswitch, " - "linux-bridge, ofa, l3, loadbalancer, metering, " - "metadata, macvtap, nic, baremetal." - ), + "The supported agent types are: %(supported_agents)s." + ) + % {'supported_agents': supported_agents}, ) parser.add_argument( '--host', @@ -202,12 +205,12 @@ def get_parser(self, prog_name): agent_type_group.add_argument( '--network', metavar='', - help=_('List agents hosting a network (name or ID)'), + help=_('List agents hosting the specified network (name or ID)'), ) agent_type_group.add_argument( '--router', metavar='', - help=_('List agents hosting this router (name or ID)'), + help=_('List agents hosting the specified router (name or ID)'), ) parser.add_argument( '--long', @@ -220,7 +223,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - columns = ( + columns: tuple[str, ...] = ( 'id', 'agent_type', 'host', @@ -229,7 +232,7 @@ def take_action(self, parsed_args): 'is_admin_state_up', 'binary', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Agent Type', 'Host', @@ -239,21 +242,6 @@ def take_action(self, parsed_args): 'Binary', ) - key_value = { - 'bgp': 'BGP dynamic routing agent', - 'dhcp': 'DHCP agent', - 'open-vswitch': 'Open vSwitch agent', - 'linux-bridge': 'Linux bridge agent', - 'ofa': 'OFA driver agent', - 'l3': 'L3 agent', - 'loadbalancer': 'Loadbalancer agent', - 'metering': 'Metering agent', - 'metadata': 'Metadata agent', - 'macvtap': 'Macvtap agent', - 'nic': 'NIC Switch agent', - 'baremetal': 'Baremetal Node', - } - filters = {} if parsed_args.network is not None: @@ -271,7 +259,9 @@ def take_action(self, parsed_args): data = client.routers_hosting_l3_agents(router) else: if parsed_args.agent_type is not None: - filters['agent_type'] = key_value[parsed_args.agent_type] + filters['agent_type'] = self._supported_agents[ + parsed_args.agent_type + ] if parsed_args.host is not None: filters['host'] = parsed_args.host @@ -321,9 +311,7 @@ def take_action(self, parsed_args): try: client.remove_dhcp_agent_from_network(agent, network) except Exception: - msg = 'Failed to remove {} to {}'.format( - network.name, agent.agent_type - ) + msg = f'Failed to remove {network.name} to {agent.agent_type}' exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index 9f382eacfb..1107cb709d 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -15,9 +15,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -25,10 +25,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['name', 'location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -71,7 +70,7 @@ def get_parser(self, prog_name): metavar='', help=_( "Return the auto allocated topology for a given project. " - "Default is current project" + "Default is current project." ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -89,7 +88,7 @@ def get_parser(self, prog_name): default=True, help=_( "If topology exists returns the topology's " - "information (Default)" + "information (default)" ), ) @@ -132,7 +131,7 @@ def get_parser(self, prog_name): metavar='', help=_( 'Delete auto allocated topology for a given project. ' - 'Default is the current project' + 'Default is the current project.' ), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index 8bc5262015..99993f64e8 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -102,7 +102,7 @@ def get_parser(self, prog_name): metavar="", required=True, help=_( - 'Service type to which the flavor applies to: e.g. VPN ' + 'Service type to which the flavor applies. For example: VPN ' '(See openstack network service provider list for loaded ' 'examples.)' ), diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index 1fb5a80118..9792b010a3 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -13,12 +13,11 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ -from openstackclient.identity import common as identity_common from openstackclient.network import common LOG = logging.getLogger(__name__) @@ -29,7 +28,7 @@ def _get_columns(item): 'is_enabled': 'enabled', } - hidden_columns = ['location', 'name', 'tenant_id'] + hidden_columns = ['location', 'name', 'tenant_id', 'project_id'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) @@ -47,14 +46,6 @@ def _get_attrs(client_manager, parsed_args): attrs['enabled'] = True if parsed_args.disable: attrs['enabled'] = False - if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity - project_id = identity_common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['project_id'] = project_id return attrs @@ -68,12 +59,6 @@ class CreateNetworkFlavorProfile( def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument( - '--project', - metavar="", - help=_("Owner's project (name or ID)"), - ) - identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--description', metavar="", @@ -94,14 +79,14 @@ def get_parser(self, prog_name): '--driver', help=_( "Python module path to driver. This becomes " - "required if --metainfo is missing and vice versa" + "required if --metainfo is missing and vice-versa." ), ) parser.add_argument( '--metainfo', help=_( "Metainfo for the flavor profile. This becomes " - "required if --driver is missing and vice versa" + "required if --driver is missing and vice-versa." ), ) @@ -161,7 +146,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.flavor_profile) msg = _( - "%(result)s of %(total)s flavor_profiles failed " "to delete." + "%(result)s of %(total)s flavor_profiles failed to delete." ) % {"result": result, "total": total} raise exceptions.CommandError(msg) @@ -212,7 +197,6 @@ def get_parser(self, prog_name): metavar="", help=_("Flavor profile to update (ID only)"), ) - identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--description', metavar="", @@ -233,14 +217,14 @@ def get_parser(self, prog_name): '--driver', help=_( "Python module path to driver. This becomes " - "required if --metainfo is missing and vice versa" + "required if --metainfo is missing and vice-versa." ), ) parser.add_argument( '--metainfo', help=_( "Metainfo for the flavor profile. This becomes " - "required if --driver is missing and vice versa" + "required if --driver is missing and vice-versa." ), ) diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index 5a6a8f2f21..bb3bf94a18 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -70,7 +70,7 @@ def get_parser(self, prog_name): parser.add_argument( '--description', metavar='', - help=_("Create description for meter"), + help=_("Description for meter"), ) parser.add_argument( '--project', @@ -139,12 +139,12 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _("Failed to delete meter with " "ID '%(meter)s': %(e)s"), + _("Failed to delete meter with ID '%(meter)s': %(e)s"), {"meter": meter, "e": e}, ) if result > 0: total = len(parsed_args.meter) - msg = _("%(result)s of %(total)s meters failed " "to delete.") % { + msg = _("%(result)s of %(total)s meters failed to delete.") % { "result": result, "total": total, } diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index ea1bea269c..7a7c231dd7 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -14,11 +14,12 @@ """Meter Rule Implementations""" import logging +import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -27,15 +28,14 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) def _get_attrs(client_manager, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.exclude: attrs['excluded'] = True @@ -168,17 +168,14 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _( - "Failed to delete meter rule with " - "ID '%(id)s': %(e)s" - ), + _("Failed to delete meter rule with ID '%(id)s': %(e)s"), {"id": id, "e": e}, ) if result > 0: total = len(parsed_args.meter_rule_id) msg = _( - "%(result)s of %(total)s meter rules failed " "to delete." + "%(result)s of %(total)s meter rules failed to delete." ) % {"result": result, "total": total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 5023ea9880..ab620b8490 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -14,12 +14,13 @@ # under the License. import logging +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -27,7 +28,7 @@ LOG = logging.getLogger(__name__) -class RulesColumn(cliff_columns.FormattableColumn): +class RulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return '\n'.join(str(v) for v in self._value) @@ -174,7 +175,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.policy) msg = _( - "%(result)s of %(total)s QoS policies failed " "to delete." + "%(result)s of %(total)s QoS policies failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -190,7 +191,8 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - "List qos policies according to their project (name or ID)" + "List only QoS policies with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -198,12 +200,12 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List qos policies shared between projects"), + help=_("List only QoS policies shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List qos policies not shared between projects"), + help=_("List only QoS policies not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 70f4d28432..b2ddd823ab 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -15,10 +15,10 @@ import itertools -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -73,10 +73,9 @@ def _get_columns(item): - column_map = {} - hidden_columns = ['location', 'tenant_id'] + hidden_columns = ['location', 'name', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -149,20 +148,9 @@ def _get_attrs(network_client, parsed_args, is_create=False): return attrs -def _get_item_properties(item, fields): - """Return a tuple containing the item properties.""" - row = [] - for field in fields: - row.append(item.get(field, '')) - return tuple(row) - - def _rule_action_call(client, action, rule_type): rule_type = rule_type.replace('-', '_') - func_name = '{action}_qos_{rule_type}_rule'.format( - action=action, - rule_type=rule_type, - ) + func_name = f'{action}_qos_{rule_type}_rule' return getattr(client, func_name) @@ -311,7 +299,7 @@ def take_action(self, parsed_args): ) rule_type = _find_rule_type(qos, rule_id) if not rule_type: - raise Exception('Rule %s not found' % rule_id) + raise Exception(f'Rule {rule_id} not found') _rule_action_call(network_client, ACTION_DELETE, rule_type)( rule_id, qos.id ) @@ -361,10 +349,10 @@ def take_action(self, parsed_args): qos = client.find_qos_policy( parsed_args.qos_policy, ignore_missing=False ) - data = qos.rules + return ( column_headers, - (_get_item_properties(s, columns) for s in data), + (utils.get_dict_properties(s, columns) for s in qos.rules), ) @@ -381,7 +369,7 @@ def get_parser(self, prog_name): parser.add_argument( 'id', metavar='', - help=_('Network QoS rule to delete (ID)'), + help=_('Network QoS rule to set (ID)'), ) _add_rule_arguments(parser) return parser @@ -427,7 +415,7 @@ def get_parser(self, prog_name): parser.add_argument( 'id', metavar='', - help=_('Network QoS rule to delete (ID)'), + help=_('Network QoS rule to show (ID)'), ) return parser @@ -445,7 +433,7 @@ def take_action(self, parsed_args): rule_id, qos.id ) except Exception as e: - msg = _('Failed to set Network QoS rule ID "%(rule)s": %(e)s') % { + msg = _('Failed to show Network QoS rule ID "%(rule)s": %(e)s') % { 'rule': rule_id, 'e': e, } diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py index fd4da84af0..c79c5fe017 100644 --- a/openstackclient/network/v2/network_qos_rule_type.py +++ b/openstackclient/network/v2/network_qos_rule_type.py @@ -13,9 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index e3e5aff19e..200175ac94 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -147,7 +147,7 @@ def get_parser(self, prog_name): target_project_group.add_argument( '--target-all-projects', action='store_true', - help=_('Allow creating RBAC policy for all projects.'), + help=_('Allow creating RBAC policy for all projects'), ) parser.add_argument( '--target-project-domain', @@ -212,7 +212,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.rbac_policy) msg = _( - "%(result)s of %(total)s RBAC policies failed " "to delete." + "%(result)s of %(total)s RBAC policies failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -234,8 +234,8 @@ def get_parser(self, prog_name): 'network', ], help=_( - 'List network RBAC policies according to ' - 'given object type ("address_group", "address_scope", ' + 'List only network RBAC policies with the specified ' + 'object type ("address_group", "address_scope", ' '"security_group", "subnetpool", "qos_policy" or ' '"network")' ), @@ -245,14 +245,17 @@ def get_parser(self, prog_name): metavar='', choices=['access_as_external', 'access_as_shared'], help=_( - 'List network RBAC policies according to given ' + 'List only network RBAC policies with the specified ' 'action ("access_as_external" or "access_as_shared")' ), ) parser.add_argument( '--target-project', metavar='', - help=_('List network RBAC policies for a specific target project'), + help=_( + 'List only network RBAC policies with the specified ' + 'target project (name or ID)' + ), ) parser.add_argument( '--long', @@ -265,12 +268,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - columns = ( + columns: tuple[str, ...] = ( 'id', 'object_type', 'object_id', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Object Type', 'Object ID', diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 7409523152..c2e7becfa8 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -26,10 +26,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -141,7 +140,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.network_segment) msg = _( - "%(result)s of %(total)s network segments failed " "to delete." + "%(result)s of %(total)s network segments failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -161,7 +160,7 @@ def get_parser(self, prog_name): '--network', metavar='', help=_( - 'List network segments that belong to this ' + 'List only network segments associated with the specified ' 'network (name or ID)' ), ) @@ -178,14 +177,14 @@ def take_action(self, parsed_args): filters = {'network_id': _network.id} data = network_client.segments(**filters) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Name', 'Network', 'Network Type', 'Segment', ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'network_id', @@ -193,8 +192,8 @@ def take_action(self, parsed_args): 'segmentation_id', ) if parsed_args.long: - headers = headers + ('Physical Network',) - columns = columns + ('physical_network',) + headers += ('Physical Network',) + columns += ('physical_network',) return ( headers, diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index aa0509969f..c8b9a0e4c4 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -18,11 +18,12 @@ import itertools import logging +import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -32,19 +33,18 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) def _get_ranges(item): item = sorted([int(i) for i in item]) for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]): - b = list(b) + c = list(b) yield ( - f"{b[0][1]}-{b[-1][1]}" if b[0][1] != b[-1][1] else str(b[0][1]) + f"{c[0][1]}-{c[-1][1]}" if c[0][1] != c[-1][1] else str(c[0][1]) ) @@ -59,7 +59,7 @@ def _is_prop_empty(columns, props, prop_name): def _exchange_dict_keys_with_values(orig_dict): - updated_dict = dict() + updated_dict: dict[str, ty.Any] = {} for k, v in orig_dict.items(): k = [k] if not updated_dict.get(v): @@ -105,8 +105,7 @@ def get_parser(self, prog_name): dest="private", action="store_true", help=_( - 'Network segment range is assigned specifically to the ' - 'project' + 'Network segment range is assigned specifically to the project' ), ) shared_group.add_argument( @@ -125,7 +124,7 @@ def get_parser(self, prog_name): metavar='', help=_( 'Network segment range owner (name or ID). Optional when ' - 'the segment range is shared' + 'the segment range is shared.' ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -196,8 +195,7 @@ def take_action(self, parsed_args): and parsed_args.physical_network ): msg = _( - "--physical-network is only allowed with --network-type " - "vlan" + "--physical-network is only allowed with --network-type vlan" ) raise exceptions.CommandError(msg) @@ -317,25 +315,32 @@ def get_parser(self, prog_name): used_group.add_argument( '--used', action='store_true', - help=_('List network segment ranges that have segments in use'), + help=_( + 'List only network segment ranges that have segments in use' + ), ) used_group.add_argument( '--unused', action='store_true', help=_( - 'List network segment ranges that have segments ' 'not in use' + 'List only network segment ranges that have segments ' + 'not in use' ), ) available_group = parser.add_mutually_exclusive_group() available_group.add_argument( '--available', action='store_true', - help=_('List network segment ranges that have available segments'), + help=_( + 'List only network segment ranges that have available segments' + ), ) available_group.add_argument( '--unavailable', action='store_true', - help=_('List network segment ranges without available segments'), + help=_( + 'List only network segment ranges without available segments' + ), ) return parser @@ -353,10 +358,9 @@ def take_action(self, parsed_args): ) % {'e': e} raise exceptions.CommandError(msg) - filters = {} - data = network_client.network_segment_ranges(**filters) + data = network_client.network_segment_ranges() - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Name', 'Default', @@ -367,7 +371,7 @@ def take_action(self, parsed_args): 'Minimum ID', 'Maximum ID', ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'default', @@ -389,16 +393,16 @@ def take_action(self, parsed_args): # should be listed in output. parsed_args.long = True if parsed_args.long: - headers = headers + ( + headers += ( 'Used', 'Available', ) - columns = columns + ( + columns += ( 'used', 'available', ) - display_props = tuple() + display_props: tuple[ty.Any, ...] = tuple() for s in data: props = utils.get_item_properties(s, columns) if ( @@ -457,8 +461,7 @@ def take_action(self, parsed_args): ) except Exception as e: msg = _( - 'Network segment range set not supported by ' - 'Network API: %(e)s' + 'Network segment range set not supported by Network API: %(e)s' ) % {'e': e} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_service_provider.py b/openstackclient/network/v2/network_service_provider.py index f743bfa6e2..1433c097ae 100644 --- a/openstackclient/network/v2/network_service_provider.py +++ b/openstackclient/network/v2/network_service_provider.py @@ -13,9 +13,9 @@ """Network Service Providers Implementation""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 2b00d03ec5..974a997636 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -15,16 +15,18 @@ # """Network trunk and subports action implementations""" + import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import identity as identity_utils from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -34,7 +36,7 @@ SUB_PORTS = 'sub_ports' -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' @@ -67,9 +69,9 @@ def get_parser(self, prog_name): required_keys=['port'], help=_( "Subport to add. Subport is of form " - "\'port=,segmentation-type=," - "segmentation-id=\' (--subport) option " - "can be repeated" + "'port=,segmentation-type=," + "segmentation-id=' (repeat option " + "to add multiple subports)" ), ) admin_group = parser.add_mutually_exclusive_group() @@ -86,9 +88,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) - obj = client.create_trunk(**attrs) + network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity + attrs = _get_attrs_for_trunk( + network_client, identity_client, parsed_args + ) + obj = network_client.create_trunk(**attrs) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( obj, columns, formatters=_formatters @@ -110,12 +115,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network + network_client = self.app.client_manager.network result = 0 for trunk in parsed_args.trunk: try: - trunk_id = client.find_trunk(trunk).id - client.delete_trunk(trunk_id) + trunk_id = network_client.find_trunk( + trunk, + ignore_missing=False, + ).id + network_client.delete_trunk(trunk_id) except Exception as e: result += 1 LOG.error( @@ -127,7 +135,7 @@ def take_action(self, parsed_args): ) if result > 0: total = len(parsed_args.trunk) - msg = _("%(result)s of %(total)s trunks failed " "to delete.") % { + msg = _("%(result)s of %(total)s trunks failed to delete.") % { 'result': result, 'total': total, } @@ -148,10 +156,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - data = client.trunks() - headers = ('ID', 'Name', 'Parent Port', 'Description') - columns = ('id', 'name', 'port_id', 'description') + network_client = self.app.client_manager.network + data = network_client.trunks() + headers: tuple[str, ...] = ('ID', 'Name', 'Parent Port', 'Description') + columns: tuple[str, ...] = ('id', 'name', 'port_id', 'description') if parsed_args.long: headers += ( 'Status', @@ -198,9 +206,9 @@ def get_parser(self, prog_name): required_keys=['port'], help=_( "Subport to add. Subport is of form " - "\'port=,segmentation-type=" - ",segmentation-id=\' (--subport) option " - "can be repeated" + "'port=,segmentation-type=," + "segmentation-id=' (repeat option " + "to add multiple subports)" ), ) admin_group = parser.add_mutually_exclusive_group() @@ -213,11 +221,17 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk) - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) + network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) + attrs = _get_attrs_for_trunk( + network_client, identity_client, parsed_args + ) try: - client.update_trunk(trunk_id, **attrs) + network_client.update_trunk(trunk_id, **attrs) except Exception as e: msg = _("Failed to set trunk '%(t)s': %(e)s") % { 't': parsed_args.trunk, @@ -226,10 +240,10 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.set_subports: subport_attrs = _get_attrs_for_subports( - self.app.client_manager, parsed_args + network_client, parsed_args ) try: - client.add_trunk_subports(trunk_id, subport_attrs) + network_client.add_trunk_subports(trunk_id, subport_attrs) except Exception as e: msg = _("Failed to add subports to trunk '%(t)s': %(e)s") % { 't': parsed_args.trunk, @@ -249,9 +263,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk).id - obj = client.get_trunk(trunk_id) + network_client = self.app.client_manager.network + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ).id + obj = network_client.get_trunk(trunk_id) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( obj, columns, formatters=_formatters @@ -268,16 +285,27 @@ def get_parser(self, prog_name): '--trunk', required=True, metavar="", - help=_("List subports belonging to this trunk (name or ID)"), + help=_("List only subports belonging to this trunk (name or ID)"), ) return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk) - data = client.get_trunk_subports(trunk_id) - headers = ('Port', 'Segmentation Type', 'Segmentation ID') - columns = ('port_id', 'segmentation_type', 'segmentation_id') + network_client = self.app.client_manager.network + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) + data = network_client.get_trunk_subports(trunk_id) + headers: tuple[str, ...] = ( + 'Port', + 'Segmentation Type', + 'Segmentation ID', + ) + columns: tuple[str, ...] = ( + 'port_id', + 'segmentation_type', + 'segmentation_id', + ) return ( headers, ( @@ -307,17 +335,20 @@ def get_parser(self, prog_name): action='append', dest='unset_subports', help=_( - "Subport to delete (name or ID of the port) " - "(--subport) option can be repeated" + "Subport to unset (name or ID of the port) " + "(repeat option to unset multiple subports)" ), ) return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) - trunk_id = client.find_trunk(parsed_args.trunk) - client.delete_trunk_subports(trunk_id, attrs) + network_client = self.app.client_manager.network + attrs = _get_attrs_for_subports(network_client, parsed_args) + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) + network_client.delete_trunk_subports(trunk_id, attrs) _formatters = { @@ -327,15 +358,14 @@ def take_action(self, parsed_args): def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return osc_utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) -def _get_attrs_for_trunk(client_manager, parsed_args): - attrs = {} +def _get_attrs_for_trunk(network_client, identity_client, parsed_args): + attrs: dict[str, ty.Any] = {} if parsed_args.name is not None: attrs['name'] = str(parsed_args.name) if parsed_args.description is not None: @@ -345,18 +375,18 @@ def _get_attrs_for_trunk(client_manager, parsed_args): if parsed_args.disable: attrs['admin_state_up'] = False if 'parent_port' in parsed_args and parsed_args.parent_port is not None: - port_id = client_manager.network.find_port(parsed_args.parent_port)[ - 'id' - ] + port_id = network_client.find_port( + parsed_args.parent_port, + ignore_missing=False, + ).id attrs['port_id'] = port_id if 'add_subports' in parsed_args and parsed_args.add_subports is not None: attrs[SUB_PORTS] = _format_subports( - client_manager, parsed_args.add_subports + network_client, parsed_args.add_subports ) # "trunk set" command doesn't support setting project. if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity project_id = identity_utils.find_project( identity_client, parsed_args.project, @@ -367,12 +397,15 @@ def _get_attrs_for_trunk(client_manager, parsed_args): return attrs -def _format_subports(client_manager, subports): +def _format_subports(network_client, subports): attrs = [] for subport in subports: subport_attrs = {} if subport.get('port'): - port_id = client_manager.network.find_port(subport['port'])['id'] + port_id = network_client.find_port( + subport['port'], + ignore_missing=False, + ).id subport_attrs['port_id'] = port_id if subport.get('segmentation-id'): try: @@ -391,21 +424,20 @@ def _format_subports(client_manager, subports): return attrs -def _get_attrs_for_subports(client_manager, parsed_args): - attrs = {} +def _get_attrs_for_subports(network_client, parsed_args): + attrs = [] if 'set_subports' in parsed_args and parsed_args.set_subports is not None: - attrs = _format_subports(client_manager, parsed_args.set_subports) + attrs = _format_subports(network_client, parsed_args.set_subports) if ( 'unset_subports' in parsed_args and parsed_args.unset_subports is not None ): subports_list = [] for subport in parsed_args.unset_subports: - port_id = client_manager.network.find_port(subport)['id'] + port_id = network_client.find_port( + subport, + ignore_missing=False, + )['id'] subports_list.append({'port_id': port_id}) attrs = subports_list return attrs - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, str(id_or_name))['id'] diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index b1e60df4e8..e1205153e6 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -17,15 +17,16 @@ import copy import json import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -33,11 +34,27 @@ LOG = logging.getLogger(__name__) -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' +class SubPortColumn(format_columns.ListDictColumn): + _value: ty.Any + + def _retrieve_subports(self): + if isinstance(self._value, dict): + self._value = self._value['sub_ports'] + + def human_readable(self): + self._retrieve_subports() + return super().human_readable() + + def machine_readable(self): + self._retrieve_subports() + return super().machine_readable() + + _formatters = { 'admin_state_up': AdminStateColumn, 'is_admin_state_up': AdminStateColumn, @@ -52,6 +69,8 @@ def human_readable(self): 'security_group_ids': format_columns.ListColumn, 'tags': format_columns.ListColumn, } +_list_formatters = copy.deepcopy(_formatters) +_list_formatters.update({'trunk_details': SubPortColumn}) def _get_columns(item): @@ -93,6 +112,7 @@ def _get_columns(item): 'status': 'status', 'tags': 'tags', 'trunk_details': 'trunk_details', + 'trusted': 'trusted', 'updated_at': 'updated_at', } return ( @@ -222,6 +242,10 @@ def _get_attrs(client_manager, parsed_args): and parsed_args.hardware_offload_type ): attrs['hardware_offload_type'] = parsed_args.hardware_offload_type + if parsed_args.not_trusted: + attrs['trusted'] = False + if parsed_args.trusted: + attrs['trusted'] = True return attrs @@ -274,13 +298,13 @@ def _prepare_filter_fixed_ips(client_manager, parsed_args): _subnet = client.find_subnet( subnet_name_id, ignore_missing=False ) - ips.append('subnet_id=%s' % _subnet.id) + ips.append(f'subnet_id={_subnet.id}') if 'ip-address' in ip_spec: - ips.append('ip_address=%s' % ip_spec['ip-address']) + ips.append('ip_address={}'.format(ip_spec['ip-address'])) if 'ip-substring' in ip_spec: - ips.append('ip_address_substr=%s' % ip_spec['ip-substring']) + ips.append('ip_address_substr={}'.format(ip_spec['ip-substring'])) return ips @@ -326,8 +350,8 @@ def _add_updatable_args(parser, create=False): help=_( "VNIC type for this port (direct | direct-physical | " "macvtap | normal | baremetal | virtio-forwarder | vdpa | " - "remote-managed, " - "default: normal)" + "remote-managed) " + "(default: normal)" ), ) parser.add_argument( @@ -347,8 +371,7 @@ def _add_updatable_args(parser, create=False): '--dns-name', metavar='', help=_( - "Set DNS name for this port " - "(requires DNS integration extension)" + "Set DNS name for this port (requires DNS integration extension)" ), ) numa_affinity_policy_group = parser.add_mutually_exclusive_group() @@ -385,7 +408,26 @@ def _add_updatable_args(parser, create=False): '(requires port-hints extension) ' '(requires port-hint-ovs-tx-steering extension for alias: ' 'ovs-tx-steering) ' - '(repeat option to set multiple hints)' + '(repeat option to set multiple hints).' + ), + ) + port_trusted = parser.add_mutually_exclusive_group() + port_trusted.add_argument( + '--trusted', + action='store_true', + help=_( + "Set port to be trusted. This will be populated into the " + "'binding:profile' dictionary and passed to the services " + "which expect it in this dictionary (for example, Nova)." + ), + ) + port_trusted.add_argument( + '--not-trusted', + action='store_true', + help=_( + "Set port to be not trusted. This will be populated into the " + "'binding:profile' dictionary and passed to the services " + "which expect it in this dictionary (for example, Nova)." ), ) @@ -472,7 +514,7 @@ def get_parser(self, prog_name): fixed_ip.add_argument( '--no-fixed-ip', action='store_true', - help=_("No IP or subnet for this port."), + help=_("No IP or subnet set for this port"), ) parser.add_argument( '--binding-profile', @@ -480,8 +522,8 @@ def get_parser(self, prog_name): action=JSONKeyValueAction, help=_( "Custom data to be passed as binding:profile. Data may " - "be passed as = or JSON. " - "(repeat option to set multiple binding:profile data)" + "be passed as = or JSON " + "(repeat option to set multiple binding:profile data)." ), ) admin_group = parser.add_mutually_exclusive_group() @@ -498,12 +540,12 @@ def get_parser(self, prog_name): uplink_status_group.add_argument( '--enable-uplink-status-propagation', action='store_true', - help=_("Enable uplink status propagate"), + help=_("Enable uplink status propagation (default)"), ) uplink_status_group.add_argument( '--disable-uplink-status-propagation', action='store_true', - help=_("Disable uplink status propagate (default)"), + help=_("Disable uplink status propagation"), ) parser.add_argument( '--project', @@ -534,7 +576,7 @@ def get_parser(self, prog_name): '--security-group', metavar='', action='append', - dest='security_group', + dest='security_groups', help=_( "Security group to associate with this port (name or ID) " "(repeat option to set multiple security groups)" @@ -542,8 +584,9 @@ def get_parser(self, prog_name): ) secgroups.add_argument( '--no-security-group', - dest='no_security_group', - action='store_true', + action='store_const', + const=[], + dest='security_groups', help=_("Associate no security groups with this port"), ) parser.add_argument( @@ -555,7 +598,7 @@ def get_parser(self, prog_name): port_security.add_argument( '--enable-port-security', action='store_true', - help=_("Enable port security for this port (Default)"), + help=_("Enable port security for this port (default)"), ) port_security.add_argument( '--disable-port-security', @@ -578,7 +621,7 @@ def get_parser(self, prog_name): parser.add_argument( '--device-profile', metavar='', - help=_('Cyborg port device profile'), + help=_('Port device profile'), ) parser.add_argument( '--hardware-offload-type', @@ -593,11 +636,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - _network = client.find_network( + network_client = self.app.client_manager.network + network = network_client.find_network( parsed_args.network, ignore_missing=False ) - parsed_args.network = _network.id + parsed_args.network = network.id _prepare_fixed_ips(self.app.client_manager, parsed_args) attrs = _get_attrs(self.app.client_manager, parsed_args) @@ -609,13 +652,11 @@ def take_action(self, parsed_args): elif parsed_args.no_fixed_ip: attrs['fixed_ips'] = [] - if parsed_args.security_group: + if parsed_args.security_groups is not None: attrs['security_group_ids'] = [ - client.find_security_group(sg, ignore_missing=False).id - for sg in parsed_args.security_group + network_client.find_security_group(sg, ignore_missing=False).id + for sg in parsed_args.security_groups ] - elif parsed_args.no_security_group: - attrs['security_group_ids'] = [] if parsed_args.allowed_address_pairs: attrs['allowed_address_pairs'] = _convert_address_pairs( @@ -626,7 +667,7 @@ def take_action(self, parsed_args): attrs["extra_dhcp_opts"] = _convert_extra_dhcp_options(parsed_args) if parsed_args.qos_policy: - attrs['qos_policy_id'] = client.find_qos_policy( + attrs['qos_policy_id'] = network_client.find_qos_policy( parsed_args.qos_policy, ignore_missing=False ).id @@ -634,7 +675,9 @@ def take_action(self, parsed_args): _validate_port_hints(parsed_args.hint) expanded_hints = _expand_port_hint_aliases(parsed_args.hint) try: - client.find_extension('port-hints', ignore_missing=False) + network_client.find_extension( + 'port-hints', ignore_missing=False + ) except Exception as e: msg = _('Not supported by Network API: %(e)s') % {'e': e} raise exceptions.CommandError(msg) @@ -645,7 +688,7 @@ def take_action(self, parsed_args): in expanded_hints['openvswitch']['other_config'] ): try: - client.find_extension( + network_client.find_extension( 'port-hint-ovs-tx-steering', ignore_missing=False ) except Exception as e: @@ -654,7 +697,9 @@ def take_action(self, parsed_args): attrs['hints'] = expanded_hints set_tags_in_post = bool( - client.find_extension('tag-ports-during-bulk-creation') + network_client.find_extension( + 'tag-ports-during-bulk-creation', ignore_missing=True + ) ) if set_tags_in_post: if parsed_args.no_tag: @@ -666,14 +711,12 @@ def take_action(self, parsed_args): self._parse_extra_properties(parsed_args.extra_properties) ) - with common.check_missing_extension_if_error( - self.app.client_manager.network, attrs - ): - obj = client.create_port(**attrs) + with common.check_missing_extension_if_error(network_client, attrs): + obj = network_client.create_port(**attrs) if not set_tags_in_post: # tags cannot be set when created, so tags need to be set later. - _tag.update_tags_for_set(client, obj, parsed_args) + _tag.update_tags_for_set(network_client, obj, parsed_args) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) @@ -714,7 +757,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.port) - msg = _("%(result)s of %(total)s ports failed " "to delete.") % { + msg = _("%(result)s of %(total)s ports failed to delete.") % { 'result': result, 'total': total, } @@ -767,7 +810,7 @@ def get_parser(self, prog_name): parser.add_argument( '--mac-address', metavar='', - help=_("List only ports with this MAC address"), + help=_("List only ports with the specified MAC address"), ) parser.add_argument( '--long', @@ -778,12 +821,12 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar='', - help=_("List ports according to their project (name or ID)"), + help=_("List only ports with the specified project (name or ID)"), ) parser.add_argument( '--name', metavar='', - help=_("List ports according to their name"), + help=_("List only ports with the specified name"), ) parser.add_argument( '--security-group', @@ -792,6 +835,19 @@ def get_parser(self, prog_name): metavar='', help=_("List only ports associated with this security group"), ) + # the API sadly reports these in upper case and while it would be + # wonderful to plaster over this ugliness client-side, there are + # already users in the wild doing this in upper case that we need to + # support + parser.add_argument( + '--status', + metavar='', + choices=('ACTIVE', 'BUILD', 'DOWN', 'ERROR'), + help=_( + "List only ports with the specified status " + "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" + ), + ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--fixed-ip', @@ -805,7 +861,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet for filtering ports " "(name or ID): subnet=,ip-address=," "ip-substring= " - "(repeat option to set multiple fixed IP addresses)" + "(repeat option to filter multiple fixed IP addresses)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('ports')) @@ -815,25 +871,30 @@ def take_action(self, parsed_args): network_client = self.app.client_manager.network identity_client = self.app.client_manager.identity - columns = ( + columns = [ 'id', 'name', 'mac_address', 'fixed_ips', 'status', - ) - column_headers = ( + ] + column_headers = [ 'ID', 'Name', 'MAC Address', 'Fixed IP Addresses', 'Status', - ) + ] filters = {} if parsed_args.long: - columns += ('security_group_ids', 'device_owner', 'tags') - column_headers += ('Security Groups', 'Device Owner', 'Tags') + columns.extend( + ['security_groups', 'device_owner', 'tags', 'trunk_details'] + ) + column_headers.extend( + ['Security Groups', 'Device Owner', 'Tags', 'Trunk subports'] + ) + if parsed_args.device_owner is not None: filters['device_owner'] = parsed_args.device_owner if parsed_args.device_id is not None: @@ -844,7 +905,7 @@ def take_action(self, parsed_args): ) filters['device_id'] = _router.id if parsed_args.server: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False, @@ -859,6 +920,8 @@ def take_action(self, parsed_args): filters['network_id'] = network.id if parsed_args.mac_address: filters['mac_address'] = parsed_args.mac_address + if parsed_args.status: + filters['status'] = parsed_args.status if parsed_args.project: project_id = identity_common.find_project( identity_client, @@ -879,16 +942,19 @@ def take_action(self, parsed_args): data = network_client.ports(fields=columns, **filters) - headers, attrs = utils.calculate_header_and_attrs( - column_headers, columns, parsed_args - ) + if parsed_args.long: + columns = [ + 'security_group_ids' if item == 'security_groups' else item + for item in columns + ] + return ( - headers, + column_headers, ( utils.get_item_properties( s, - attrs, - formatters=_formatters, + columns, + formatters=_list_formatters, ) for s in data ), @@ -931,7 +997,7 @@ def get_parser(self, prog_name): '--no-fixed-ip', action='store_true', help=_( - "Clear existing information of fixed IP addresses." + "Clear existing information of fixed IP addresses. " "Specify both --fixed-ip and --no-fixed-ip " "to overwrite the current fixed IP addresses." ), @@ -942,8 +1008,8 @@ def get_parser(self, prog_name): action=JSONKeyValueAction, help=_( "Custom data to be passed as binding:profile. Data may " - "be passed as = or JSON. " - "(repeat option to set multiple binding:profile data)" + "be passed as = or JSON " + "(repeat option to set multiple binding:profile data)." ), ) parser.add_argument( @@ -967,7 +1033,7 @@ def get_parser(self, prog_name): '--security-group', metavar='', action='append', - dest='security_group', + dest='security_groups', help=_( "Security group to associate with this port (name or ID) " "(repeat option to set multiple security groups)" @@ -1010,8 +1076,8 @@ def get_parser(self, prog_name): help=_( "Clear existing allowed-address pairs associated " "with this port. " - "(Specify both --allowed-address and --no-allowed-address " - "to overwrite the current allowed-address pairs)" + "Specify both --allowed-address and --no-allowed-address " + "to overwrite the current allowed-address pairs." ), ) parser.add_argument( @@ -1035,9 +1101,21 @@ def get_parser(self, prog_name): help=_( "Set data plane status of this port (ACTIVE | DOWN). " "Unset it to None with the 'port unset' command " - "(requires data plane status extension)" + "(requires data plane status extension)." ), ) + uplink_status_group = parser.add_mutually_exclusive_group() + uplink_status_group.add_argument( + '--enable-uplink-status-propagation', + action='store_true', + help=_('Enable uplink status propagation'), + ) + uplink_status_group.add_argument( + '--disable-uplink-status-propagation', + action='store_true', + help=_('Disable uplink status propagation'), + ) + _tag.add_tag_option_to_parser_for_set(parser, _('port')) return parser @@ -1068,7 +1146,7 @@ def take_action(self, parsed_args): if parsed_args.no_security_group: attrs['security_group_ids'] = [] - if parsed_args.security_group: + if parsed_args.security_groups: if 'security_group_ids' not in attrs: # NOTE(dtroyer): Get existing security groups, iterate the # list to force a new list object to be @@ -1079,7 +1157,7 @@ def take_action(self, parsed_args): ] attrs['security_group_ids'].extend( client.find_security_group(sg, ignore_missing=False).id - for sg in parsed_args.security_group + for sg in parsed_args.security_groups ) if parsed_args.no_allowed_address_pair: @@ -1122,6 +1200,11 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) attrs['hints'] = expanded_hints + if parsed_args.not_trusted: + attrs['trusted'] = False + if parsed_args.trusted: + attrs['trusted'] = True + attrs.update( self._parse_extra_properties(parsed_args.extra_properties) ) @@ -1180,14 +1263,14 @@ def get_parser(self, prog_name): action='append', help=_( "Desired key which should be removed from binding:profile " - "(repeat option to unset multiple binding:profile data)" + "(repeat option to unset multiple binding:profile keys)" ), ) parser.add_argument( '--security-group', metavar='', action='append', - dest='security_group_ids', + dest='security_groups', help=_( "Security group which should be removed this port (name " "or ID) (repeat option to unset multiple security groups)" @@ -1216,7 +1299,7 @@ def get_parser(self, prog_name): parser.add_argument( '--data-plane-status', action='store_true', - help=_("Clear existing information of data plane status"), + help=_("Clear existing data plane status information"), ) parser.add_argument( '--numa-policy', @@ -1227,13 +1310,25 @@ def get_parser(self, prog_name): '--host', action='store_true', default=False, - help=_("Clear host binding for the port."), + help=_("Clear host binding for the port"), ) parser.add_argument( '--hints', action='store_true', default=False, - help=_("Clear hints for the port."), + help=_("Clear hints for the port"), + ) + parser.add_argument( + '--device', + action='store_true', + default=False, + help=_("Clear device ID for the port."), + ) + parser.add_argument( + '--device-owner', + action='store_true', + default=False, + help=_("Clear device owner for the port."), ) _tag.add_tag_option_to_parser_for_unset(parser, _('port')) parser.add_argument( @@ -1272,9 +1367,9 @@ def take_action(self, parsed_args): msg = _("Port does not contain binding-profile %s") % key raise exceptions.CommandError(msg) attrs['binding:profile'] = tmp_binding_profile - if parsed_args.security_group_ids: + if parsed_args.security_groups: try: - for sg in parsed_args.security_group_ids: + for sg in parsed_args.security_groups: sg_id = client.find_security_group( sg, ignore_missing=False ).id @@ -1301,6 +1396,10 @@ def take_action(self, parsed_args): attrs['binding:host_id'] = None if parsed_args.hints: attrs['hints'] = None + if parsed_args.device: + attrs['device_id'] = '' + if parsed_args.device_owner: + attrs['device_owner'] = '' attrs.update( self._parse_extra_properties(parsed_args.extra_properties) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 8527426ee5..939167d850 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -18,15 +18,16 @@ import copy import json import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -34,12 +35,12 @@ LOG = logging.getLogger(__name__) -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' -class RouterInfoColumn(cliff_columns.FormattableColumn): +class RouterInfoColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): try: return json.dumps(self._value) @@ -47,7 +48,7 @@ def human_readable(self): return '' -class RoutesColumn(cliff_columns.FormattableColumn): +class RoutesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): # Map the route keys to match --route option. for route in self._value or []: @@ -75,7 +76,7 @@ def _get_columns(item): } if hasattr(item, 'interfaces_info'): column_map['interfaces_info'] = 'interfaces_info' - invisible_columns = ['location'] + invisible_columns = ['location', 'tenant_id'] if item.is_ha is None: invisible_columns.append('is_ha') column_map.pop('is_ha') @@ -88,7 +89,12 @@ def _get_columns(item): def is_multiple_gateways_supported(n_client): - return n_client.find_extension("external-gateway-multihoming") is not None + return ( + n_client.find_extension( + "external-gateway-multihoming", ignore_missing=True + ) + is not None + ) def _passed_multiple_gateways(extension_supported, external_gateways): @@ -104,21 +110,21 @@ def _passed_multiple_gateways(extension_supported, external_gateways): def _get_external_gateway_attrs(client_manager, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.external_gateways: external_gateways: collections.defaultdict[str, list[dict]] = ( collections.defaultdict(list) ) n_client = client_manager.network - first_network_id = None + first_network_id = '' for gw_net_name_or_id in parsed_args.external_gateways: gateway_info = {} gw_net = n_client.find_network( gw_net_name_or_id, ignore_missing=False ) - if first_network_id is None: + if not first_network_id: first_network_id = gw_net.id gateway_info['network_id'] = gw_net.id if 'disable_snat' in parsed_args and parsed_args.disable_snat: @@ -146,7 +152,7 @@ def _get_external_gateway_attrs(client_manager, parsed_args): for ip_spec in parsed_args.fixed_ips: # If there is only one gateway, this value will represent the # network ID for it, otherwise it will be overridden. - ip_net_id = first_network_id + ip_net_id: str = first_network_id if ip_spec.get('subnet', False): subnet_name_id = ip_spec.pop('subnet') @@ -164,10 +170,13 @@ def _get_external_gateway_attrs(client_manager, parsed_args): 'subnet_id' in ip_spec and ip_net_id not in external_gateways ): - msg = _( - 'Subnet %s does not belong to any of the networks ' - 'provided for --external-gateway.' - ) % (ip_spec['subnet_id']) + msg = ( + _( + 'Subnet %s does not belong to any of the networks ' + 'provided for --external-gateway.' + ) + % (ip_spec['subnet_id']) + ) raise exceptions.CommandError(msg) for gw_info in external_gateways[ip_net_id]: if 'external_fixed_ips' not in gw_info: @@ -232,7 +241,12 @@ def _get_attrs(client_manager, parsed_args): # "router set" command doesn't support setting flavor_id. if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None: - flavor = n_client.find_flavor(parsed_args.flavor_id) + flavor = n_client.find_flavor( + parsed_args.flavor_id, ignore_missing=False + ) + attrs['flavor_id'] = flavor.id + elif 'flavor' in parsed_args and parsed_args.flavor is not None: + flavor = n_client.find_flavor(parsed_args.flavor, ignore_missing=False) attrs['flavor_id'] = flavor.id for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'): @@ -252,7 +266,7 @@ def _parser_add_bfd_ecmp_arguments(parser): action='store_true', help=_( "Enable BFD sessions for default routes inferred from " - "the external gateway port subnets for this router." + "the external gateway port subnets for this router" ), ) parser.add_argument( @@ -262,7 +276,7 @@ def _parser_add_bfd_ecmp_arguments(parser): action='store_false', help=_( "Disable BFD sessions for default routes inferred from " - "the external gateway port subnets for this router." + "the external gateway port subnets for this router" ), ) parser.add_argument( @@ -272,7 +286,7 @@ def _parser_add_bfd_ecmp_arguments(parser): action='store_true', help=_( "Add ECMP default routes if multiple are available via " - "different gateway ports." + "different gateway ports" ), ) parser.add_argument( @@ -280,7 +294,7 @@ def _parser_add_bfd_ecmp_arguments(parser): dest='enable_default_route_ecmp', default=None, action='store_false', - help=_("Add default route only for first gateway port."), + help=_("Add default route only for first gateway port"), ) @@ -361,7 +375,7 @@ def get_parser(self, prog_name): metavar='', help=_( "Router to which extra static routes " - "will be added (name or ID)." + "will be added (name or ID)" ), ) parser.add_argument( @@ -376,7 +390,7 @@ def get_parser(self, prog_name): "destination: destination subnet (in CIDR notation), " "gateway: nexthop IP address. " "Repeat option to add multiple routes. " - "Trying to add a route that's already present " + "Trying to add a route that is already present " "(exactly, including destination and nexthop) " "in the routing table is allowed and is considered " "a successful operation." @@ -412,7 +426,7 @@ def get_parser(self, prog_name): metavar='', help=_( "Router from which extra static routes " - "will be removed (name or ID)." + "will be removed (name or ID)" ), ) parser.add_argument( @@ -427,7 +441,7 @@ def get_parser(self, prog_name): "destination: destination subnet (in CIDR notation), " "gateway: nexthop IP address. " "Repeat option to remove multiple routes. " - "Trying to remove a route that's already missing " + "Trying to remove a route that is already missing " "(fully, including destination and nexthop) " "from the routing table is allowed and is considered " "a successful operation." @@ -519,9 +533,9 @@ def get_parser(self, prog_name): metavar="", action='append', help=_( - "External Network used as router's gateway (name or ID). " + "External Network used as router's gateway (name or ID) " "(repeat option to set multiple gateways per router " - "if the L3 service plugin in use supports it)." + "if the L3 service plugin in use supports it)" ), dest='external_gateways', ) @@ -535,7 +549,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet (name or ID) " "on external gateway: " "subnet=,ip-address= " - "(repeat option to set multiple fixed IP addresses)." + "(repeat option to set multiple fixed IP addresses)" ), ) snat_group = parser.add_mutually_exclusive_group() @@ -575,6 +589,11 @@ def get_parser(self, prog_name): help=argparse.SUPPRESS, ) _parser_add_bfd_ecmp_arguments(parser) + parser.add_argument( + '--qos-policy', + metavar='', + help=_('Attach QoS policy to router gateway IPs'), + ) return parser @@ -597,6 +616,13 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) + if parsed_args.qos_policy and not parsed_args.external_gateways: + msg = _( + "You must specify '--external-gateway' in order " + "to define a QoS policy" + ) + raise exceptions.CommandError(msg) + if parsed_args.enable_ndp_proxy is not None: attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy @@ -665,7 +691,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.router) - msg = _("%(result)s of %(total)s routers failed " "to delete.") % { + msg = _("%(result)s of %(total)s routers failed to delete.") % { 'result': result, 'total': total, } @@ -700,13 +726,17 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar='', - help=_("List routers according to their project (name or ID)"), + help=_( + "List only routers with the specified project (name or ID)" + ), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--agent', metavar='', - help=_("List routers hosted by an agent (ID only)"), + help=_( + "List only routers hosted by the specified agent (ID only)" + ), ) _tag.add_tag_filtering_option_to_parser(parser, _('routers')) @@ -716,14 +746,14 @@ def take_action(self, parsed_args): identity_client = self.app.client_manager.identity client = self.app.client_manager.network - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'status', 'is_admin_state_up', 'project_id', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', @@ -769,27 +799,27 @@ def take_action(self, parsed_args): d.is_distributed is not None and 'is_distributed' not in columns ): - columns = columns + ('is_distributed',) - column_headers = column_headers + ('Distributed',) + columns += ('is_distributed',) + column_headers += ('Distributed',) if d.is_ha is not None and 'is_ha' not in columns: - columns = columns + ('is_ha',) - column_headers = column_headers + ('HA',) + columns += ('is_ha',) + column_headers += ('HA',) if parsed_args.long: - columns = columns + ( + columns += ( 'routes', 'external_gateway_info', ) - column_headers = column_headers + ( + column_headers += ( 'Routes', 'External gateway info', ) # availability zone will be available only when # router_availability_zone extension is enabled if client.find_extension("router_availability_zone"): - columns = columns + ('availability_zones',) - column_headers = column_headers + ('Availability zones',) - columns = columns + ('tags',) - column_headers = column_headers + ('Tags',) + columns += ('availability_zones',) + column_headers += ('Availability zones',) + columns += ('tags',) + column_headers += ('Tags',) return ( column_headers, @@ -921,7 +951,7 @@ def get_parser(self, prog_name): default=None, required_keys=['destination', 'gateway'], help=_( - "Add routes to the router " + "Add routes to the router. " "destination: destination subnet (in CIDR notation) " "gateway: nexthop IP address " "(repeat option to add multiple routes). " @@ -945,7 +975,7 @@ def get_parser(self, prog_name): '--ha', action='store_true', help=_( - "Set the router as highly available " "(disabled router only)" + "Set the router as highly available (disabled router only)" ), ) routes_ha.add_argument( @@ -961,7 +991,7 @@ def get_parser(self, prog_name): metavar="", action='append', help=_( - "External Network used as router's gateway (name or ID). " + "External Network used as router's gateway (name or ID) " "(repeat option to set multiple gateways per router " "if the L3 service plugin in use supports it)." ), @@ -977,7 +1007,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet (name or ID) " "on external gateway: " "subnet=,ip-address= " - "(repeat option to set multiple fixed IP addresses)." + "(repeat option to set multiple fixed IP addresses)" ), ) snat_group = parser.add_mutually_exclusive_group() @@ -1157,7 +1187,7 @@ def get_parser(self, prog_name): default=None, required_keys=['destination', 'gateway'], help=_( - "Routes to be removed from the router " + "Routes to be removed from the router. " "destination: destination subnet (in CIDR notation) " "gateway: nexthop IP address " "(repeat option to unset multiple routes)" @@ -1210,7 +1240,7 @@ def take_action(self, parsed_args): ): pass except (KeyError, TypeError): - msg = _("Router does not have external network or qos policy") + msg = _("Router does not have external network or QoS policy") raise exceptions.CommandError(msg) else: attrs['external_gateway_info'] = { @@ -1247,13 +1277,12 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar="", - help=_("Router to modify (name or ID)."), + help=_("Router to modify (name or ID)"), ) parser.add_argument( metavar="", help=_( - "External Network to a attach a router gateway to (name or " - "ID)." + "External Network to a attach a router gateway to (name or ID)" ), dest='external_gateways', # The argument is stored in a list in order to reuse the @@ -1270,7 +1299,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet (name or ID) " "on external gateway: " "subnet=,ip-address= " - "(repeat option to set multiple fixed IP addresses)." + "(repeat option to set multiple fixed IP addresses)" ), ) return parser @@ -1320,8 +1349,7 @@ def get_parser(self, prog_name): parser.add_argument( metavar="", help=_( - "External Network to remove a router gateway from (name or " - "ID)." + "External Network to remove a router gateway from (name or ID)" ), dest='external_gateways', # The argument is stored in a list in order to reuse the @@ -1338,7 +1366,7 @@ def get_parser(self, prog_name): "IP and/or subnet (name or ID) on the external gateway " "which is used to identify a particular gateway if multiple " "are attached to the same network: subnet=," - "ip-address=." + "ip-address=" ), ) return parser diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 556eff2c0d..c6930de78d 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -14,13 +14,14 @@ """Security Group action implementations""" import argparse +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import utils from osc_lib.utils import tags as _tag from openstackclient.api import compute_v2 +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -65,12 +66,12 @@ def _format_compute_security_group_rules(sg_rules): return utils.format_list(rules, separator='\n') -class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn): +class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return _format_network_security_group_rules(self._value) -class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn): +class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return _format_compute_security_group_rules(self._value) @@ -89,9 +90,8 @@ def _get_columns(item): # We still support Nova managed security groups, where we have tenant_id. column_map = { 'security_group_rules': 'rules', - 'tenant_id': 'project_id', } - hidden_columns = ['location'] + hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) @@ -126,7 +126,7 @@ def update_parser_network(self, parser): "--stateful", action='store_true', default=None, - help=_("Security group is stateful (Default)"), + help=_("Security group is stateful (default)"), ) stateful_group.add_argument( "--stateless", @@ -186,7 +186,8 @@ def take_action_compute(self, client, parsed_args): parsed_args.name, description, ) - display_columns, property_columns = _get_columns(obj) + display_columns = ('description', 'id', 'name', 'project_id', 'rules') + property_columns = ('description', 'id', 'name', 'tenant_id', 'rules') data = utils.get_dict_properties( obj, property_columns, formatters=_formatters_compute ) @@ -222,7 +223,14 @@ def take_action_compute(self, client, parsed_args): # the OSC minimum requirements include SDK 1.0. class ListSecurityGroup(common.NetworkAndComputeLister): _description = _("List security groups") - FIELDS_TO_RETRIEVE = ['id', 'name', 'description', 'project_id', 'tags'] + FIELDS_TO_RETRIEVE = [ + 'id', + 'name', + 'description', + 'project_id', + 'tags', + 'shared', + ] def update_parser_network(self, parser): if not self.is_docs_build: @@ -240,14 +248,31 @@ def update_parser_network(self, parser): metavar='', help=self.enhance_help_neutron( _( - "List security groups according to the project (name or " - "ID)" + "List only security groups with the specified project " + "(name or ID)" ) ), ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron ) + + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( + '--share', + action='store_true', + dest='shared', + default=None, + help=_("List only security groups shared between projects"), + ) + shared_group.add_argument( + '--no-share', + action='store_false', + dest='shared', + default=None, + help=_("List only security groups not shared between projects"), + ) + _tag.add_tag_filtering_option_to_parser( parser, _('security group'), enhance_help=self.enhance_help_neutron ) @@ -275,13 +300,30 @@ def take_action_network(self, client, parsed_args): ).id filters['project_id'] = project_id + if parsed_args.shared is not None: + filters['shared'] = parsed_args.shared + _tag.get_tag_filtering_args(parsed_args, filters) data = client.security_groups( fields=self.FIELDS_TO_RETRIEVE, **filters ) - columns = ("ID", "Name", "Description", "Project ID", "tags") - column_headers = ("ID", "Name", "Description", "Project", "Tags") + columns = ( + "id", + "name", + "description", + "project_id", + "tags", + "is_shared", + ) + column_headers = ( + "ID", + "Name", + "Description", + "Project", + "Tags", + "Shared", + ) return ( column_headers, ( @@ -300,15 +342,11 @@ def take_action_compute(self, client, parsed_args): all_projects=parsed_args.all_projects, ) - columns = ( - "ID", - "Name", - "Description", - ) - column_headers = columns + columns: tuple[str, ...] = ("id", "name", "description") + column_headers: tuple[str, ...] = ("ID", "Name", "Description") if parsed_args.all_projects: - columns = columns + ('Tenant ID',) - column_headers = column_headers + ('Project',) + columns += ('tenant_id',) + column_headers += ('Project',) return ( column_headers, ( @@ -345,7 +383,7 @@ def update_parser_common(self, parser): "--stateful", action='store_true', default=None, - help=_("Security group is stateful (Default)"), + help=_("Security group is stateful (default)"), ) stateful_group.add_argument( "--stateless", @@ -427,7 +465,8 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): obj = compute_v2.find_security_group(client, parsed_args.group) - display_columns, property_columns = _get_columns(obj) + display_columns = ('description', 'id', 'name', 'project_id', 'rules') + property_columns = ('description', 'id', 'name', 'tenant_id', 'rules') data = utils.get_dict_properties( obj, property_columns, formatters=_formatters_compute ) diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 80e5ff0859..f6baac3cb7 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -30,10 +30,9 @@ def _get_columns(item): - column_map = {} - hidden_columns = ['location', 'tenant_id'] + hidden_columns = ['location', 'name', 'tenant_id', 'tags'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -388,7 +387,8 @@ def update_parser_network(self, parser): type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( _( - "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " + "List only rules with the specified IP protocol " + "(ah, dhcp, egp, esp, gre, " "icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, " "ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, " "sctp, tcp, udp, udplite, vrrp and integer " @@ -402,7 +402,10 @@ def update_parser_network(self, parser): metavar='', type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( - _("List rules by the Ethertype (IPv4 or IPv6)") + _( + "List only rules with the specified Ethertype " + "(IPv4 or IPv6)" + ) ), ) direction_group = parser.add_mutually_exclusive_group() @@ -410,14 +413,14 @@ def update_parser_network(self, parser): '--ingress', action='store_true', help=self.enhance_help_neutron( - _("List rules applied to incoming network traffic") + _("List only rules applied to incoming network traffic") ), ) direction_group.add_argument( '--egress', action='store_true', help=self.enhance_help_neutron( - _("List rules applied to outgoing network traffic") + _("List only rules applied to outgoing network traffic") ), ) parser.add_argument( @@ -428,6 +431,16 @@ def update_parser_network(self, parser): _("**Deprecated** This argument is no longer needed") ), ) + parser.add_argument( + '--project', + metavar='', + help=self.enhance_help_neutron( + _("List only rules with the specified project (name or ID)") + ), + ) + identity_common.add_project_domain_option_to_parser( + parser, enhance_help=self.enhance_help_neutron + ) return parser def update_parser_compute(self, parser): @@ -451,7 +464,7 @@ def update_parser_compute(self, parser): return parser def _get_column_headers(self, parsed_args): - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'IP Protocol', 'Ethertype', @@ -461,9 +474,9 @@ def _get_column_headers(self, parsed_args): 'Remote Security Group', ) if self.is_neutron: - column_headers = column_headers + ('Remote Address Group',) + column_headers += ('Remote Address Group',) if parsed_args.group is None: - column_headers = column_headers + ('Security Group',) + column_headers += ('Security Group',) return column_headers def take_action_network(self, client, parsed_args): @@ -474,7 +487,7 @@ def take_action_network(self, client, parsed_args): self.log.warning(msg) column_headers = self._get_column_headers(parsed_args) - columns = ( + columns: tuple[str, ...] = ( 'id', 'protocol', 'ether_type', @@ -496,7 +509,7 @@ def take_action_network(self, client, parsed_args): ).id query = {'security_group_id': security_group_id} else: - columns = columns + ('security_group_id',) + columns += ('security_group_id',) if parsed_args.ingress: query['direction'] = 'ingress' @@ -504,6 +517,15 @@ def take_action_network(self, client, parsed_args): query['direction'] = 'egress' if parsed_args.protocol is not None: query['protocol'] = parsed_args.protocol + if parsed_args.project is not None: + identity_client = self.app.client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + query['tenant_id'] = project_id + query['project_id'] = project_id rules = [ self._format_network_security_group_rule(r) @@ -523,7 +545,7 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): column_headers = self._get_column_headers(parsed_args) - columns = ( + columns: tuple[str, ...] = ( "ID", "IP Protocol", "Ethertype", @@ -539,7 +561,7 @@ def take_action_compute(self, client, parsed_args): ) rules_to_list = security_group['rules'] else: - columns = columns + ('parent_group_id',) + columns += ('parent_group_id',) for security_group in compute_v2.list_security_groups( client, all_projects=parsed_args.all_projects ): @@ -608,7 +630,7 @@ def take_action_compute(self, client, parsed_args): if obj is None: msg = ( - _("Could not find security group rule " "with ID '%s'") + _("Could not find security group rule with ID '%s'") % parsed_args.rule ) raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 3d172adb18..3357f8930e 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -15,15 +15,16 @@ import copy import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -43,7 +44,7 @@ def _update_arguments(obj_list, parsed_args_list, option): raise exceptions.CommandError(msg) -class AllocationPoolsColumn(cliff_columns.FormattableColumn): +class AllocationPoolsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): pool_formatted = [ '{}-{}'.format(pool.get('start', ''), pool.get('end', '')) @@ -52,7 +53,7 @@ def human_readable(self): return ','.join(pool_formatted) -class HostRoutesColumn(cliff_columns.FormattableColumn): +class HostRoutesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): # Map the host route keys to match --host-route option. return utils.format_list_of_dicts( @@ -60,7 +61,7 @@ def human_readable(self): ) -class UnsortedListColumn(cliff_columns.FormattableColumn): +class UnsortedListColumn(cliff_columns.FormattableColumn[list[ty.Any]]): # format_columns.ListColumn sorts the output, but for things like # DNS server addresses the order matters def human_readable(self): @@ -84,8 +85,8 @@ def _get_common_parse_arguments(parser, is_create=True): action=parseractions.MultiKeyValueAction, required_keys=['start', 'end'], help=_( - "Allocation pool IP addresses for this subnet " - "e.g.: start=192.168.199.2,end=192.168.199.254 " + "Allocation pool IP addresses for this subnet, " + "for example, start=192.168.199.2,end=192.168.199.254 " "(repeat option to add multiple IP addresses)" ), ) @@ -127,10 +128,10 @@ def _get_common_parse_arguments(parser, is_create=True): action=parseractions.MultiKeyValueAction, required_keys=['destination', 'gateway'], help=_( - "Additional route for this subnet " - "e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 " + "Additional route for this subnet, " + "for example, destination=10.10.0.0/16,gateway=192.168.71.254 " "destination: destination subnet (in CIDR notation) " - "gateway: nexthop IP address " + "gateway: next-hop IP address " "(repeat option to add multiple routes)" ), ) @@ -150,8 +151,8 @@ def _get_common_parse_arguments(parser, is_create=True): action='append', dest='service_types', help=_( - "Service type for this subnet " - "e.g.: network:floatingip_agent_gateway. " + "Service type for this subnet, " + "for example, network:floatingip_agent_gateway. " "Must be a valid device owner value for a network port " "(repeat option to set multiple service types)" ), @@ -365,8 +366,8 @@ def get_parser(self, prog_name): ": Specific IP address to use as the gateway, " "'auto': Gateway address should automatically be chosen " "from within the subnet itself, 'none': This subnet will " - "not use a gateway, e.g.: --gateway 192.168.9.1, " - "--gateway auto, --gateway none (default is 'auto')." + "not use a gateway. For example, --gateway 192.168.9.1, " + "--gateway auto or --gateway none (default is 'auto')." ), ) parser.add_argument( @@ -375,7 +376,7 @@ def get_parser(self, prog_name): default=4, choices=[4, 6], help=_( - "IP version (default is 4). Note that when subnet pool is " + "IP version (default is 4). Note that when subnet pool is " "specified, IP version is determined from the subnet pool " "and this option is ignored." ), @@ -400,7 +401,7 @@ def get_parser(self, prog_name): '--network-segment', metavar='', help=_( - "Network segment to associate with this subnet " "(name or ID)" + "Network segment to associate with this subnet (name or ID)" ), ) parser.add_argument( @@ -465,7 +466,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.subnet) - msg = _("%(result)s of %(total)s subnets failed " "to delete.") % { + msg = _("%(result)s of %(total)s subnets failed to delete.") % { 'result': result, 'total': total, } @@ -492,7 +493,7 @@ def get_parser(self, prog_name): metavar='', dest='ip_version', help=_( - "List only subnets of given IP version in output. " + "List only subnets with the specified IP version. " "Allowed values for IP version are 4 and 6." ), ) @@ -500,12 +501,12 @@ def get_parser(self, prog_name): dhcp_enable_group.add_argument( '--dhcp', action='store_true', - help=_("List subnets which have DHCP enabled"), + help=_("List only subnets which have DHCP enabled"), ) dhcp_enable_group.add_argument( '--no-dhcp', action='store_true', - help=_("List subnets which have DHCP disabled"), + help=_("List only subnets which have DHCP disabled"), ) parser.add_argument( '--service-type', @@ -513,18 +514,17 @@ def get_parser(self, prog_name): action='append', dest='service_types', help=_( - "List only subnets of a given service type in output " - "e.g.: network:floatingip_agent_gateway. " + "List only subnets with the specified service type, " + "for example, network:floatingip_agent_gateway. " "Must be a valid device owner value for a network port " - "(repeat option to list multiple service types)" + "(repeat option to list multiple service types)." ), ) parser.add_argument( '--project', metavar='', help=_( - "List only subnets which belong to a given project " - "in output (name or ID)" + "List only subnets with the specified project (name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -532,35 +532,35 @@ def get_parser(self, prog_name): '--network', metavar='', help=_( - "List only subnets which belong to a given network " - "in output (name or ID)" + "List only subnets which belong to the specified network " + "(name or ID)" ), ) parser.add_argument( '--gateway', metavar='', - help=_("List only subnets of given gateway IP in output"), + help=_("List only subnets with the specified gateway IP"), ) parser.add_argument( '--name', metavar='', - help=_("List only subnets of given name in output"), + help=_("List only subnets with the specified name"), ) parser.add_argument( '--subnet-range', metavar='', help=_( - "List only subnets of given subnet range " - "(in CIDR notation) in output " - "e.g.: --subnet-range 10.10.0.0/16" + "List only subnets with the specified subnet range " + "(in CIDR notation). " + "For example, --subnet-range 10.10.0.0/16" ), ) parser.add_argument( '--subnet-pool', metavar='', help=_( - "List only subnets which belong to a given subnet pool " - "in output (Name or ID)" + "List only subnets which belong to the specified subnet pool " + "(name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnets')) @@ -606,8 +606,8 @@ def take_action(self, parsed_args): _tag.get_tag_filtering_args(parsed_args, filters) data = network_client.subnets(**filters) - headers = ('ID', 'Name', 'Network', 'Subnet') - columns = ('id', 'name', 'network_id', 'cidr') + headers: tuple[str, ...] = ('ID', 'Name', 'Network', 'Subnet') + columns: tuple[str, ...] = ('id', 'name', 'network_id', 'cidr') if parsed_args.long: headers += ( 'Project', @@ -684,8 +684,8 @@ def get_parser(self, prog_name): help=_( "Specify a gateway for the subnet. The options are: " ": Specific IP address to use as the gateway, " - "'none': This subnet will not use a gateway, " - "e.g.: --gateway 192.168.9.1, --gateway none." + "'none': This subnet will not use a gateway. " + "For example, --gateway 192.168.9.1 or --gateway none." ), ) parser.add_argument( @@ -694,7 +694,7 @@ def get_parser(self, prog_name): help=_( "Network segment to associate with this subnet (name or " "ID). It is only allowed to set the segment if the current " - "value is `None`, the network must also have only one " + "value is `None`. The network must also have only one " "segment and only one subnet can exist on the network." ), ) @@ -774,7 +774,7 @@ def get_parser(self, prog_name): required_keys=['start', 'end'], help=_( 'Allocation pool IP addresses to be removed from this ' - 'subnet e.g.: start=192.168.199.2,end=192.168.199.254 ' + 'subnet, for example, start=192.168.199.2,end=192.168.199.254 ' '(repeat option to unset multiple allocation pools)' ), ) @@ -800,10 +800,10 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, required_keys=['destination', 'gateway'], help=_( - 'Route to be removed from this subnet ' - 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 ' + 'Route to be removed from this subnet, ' + 'for example, destination=10.10.0.0/16,gateway=192.168.71.254 ' 'destination: destination subnet (in CIDR notation) ' - 'gateway: nexthop IP address ' + 'gateway: next-hop IP address ' '(repeat option to unset multiple host routes)' ), ) @@ -813,8 +813,8 @@ def get_parser(self, prog_name): action='append', dest='service_types', help=_( - 'Service type to be removed from this subnet ' - 'e.g.: network:floatingip_agent_gateway. ' + 'Service type to be removed from this subnet, ' + 'for example, network:floatingip_agent_gateway. ' 'Must be a valid device owner value for a network port ' '(repeat option to unset multiple service types)' ), @@ -831,7 +831,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.network obj = client.find_subnet(parsed_args.subnet, ignore_missing=False) - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.gateway: attrs['gateway_ip'] = None if parsed_args.dns_nameservers: diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index c6d92a6b42..399ce483f3 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -17,11 +17,11 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -201,7 +201,7 @@ def get_parser(self, prog_name): "as the number of IP addresses that can be allocated " "from the subnet pool" ), - ), + ) _tag.add_tag_option_to_parser_for_create(parser, _('subnet pool')) return parser @@ -256,7 +256,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.subnet_pool) msg = _( - "%(result)s of %(total)s subnet pools failed " "to delete." + "%(result)s of %(total)s subnet pools failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -278,26 +278,27 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List subnet pools shared between projects"), + help=_("List only subnet pools shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List subnet pools not shared between projects"), + help=_("List only subnet pools not shared between projects"), ) default_group = parser.add_mutually_exclusive_group() default_group.add_argument( '--default', action='store_true', help=_( - "List subnet pools used as the default external " "subnet pool" + "List only subnet pools used as the default external " + "subnet pool" ), ) default_group.add_argument( '--no-default', action='store_true', help=_( - "List subnet pools not used as the default external " + "List only subnet pools not used as the default external " "subnet pool" ), ) @@ -305,21 +306,22 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - "List subnet pools according to their project (name or ID)" + "List only subnet pools with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--name', metavar='', - help=_("List only subnet pools of given name in output"), + help=_("List only subnet pools with the specified name"), ) parser.add_argument( '--address-scope', metavar='', help=_( - "List only subnet pools of given address scope " - "in output (name or ID)" + "List only subnet pools with the specified address scope " + "(name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnet pools')) @@ -356,8 +358,8 @@ def take_action(self, parsed_args): _tag.get_tag_filtering_args(parsed_args, filters) data = network_client.subnet_pools(**filters) - headers = ('ID', 'Name', 'Prefixes') - columns = ('id', 'name', 'prefixes') + headers: tuple[str, ...] = ('ID', 'Name', 'Prefixes') + columns: tuple[str, ...] = ('id', 'name', 'prefixes') if parsed_args.long: headers += ( 'Default Prefix Length', @@ -433,7 +435,7 @@ def get_parser(self, prog_name): "as the number of IP addresses that can be allocated " "from the subnet pool" ), - ), + ) _tag.add_tag_option_to_parser_for_set(parser, _('subnet pool')) return parser diff --git a/openstackclient/tests/functional/volume/v1/__init__.py b/openstackclient/network/v2/taas/__init__.py similarity index 100% rename from openstackclient/tests/functional/volume/v1/__init__.py rename to openstackclient/network/v2/taas/__init__.py diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py new file mode 100644 index 0000000000..206fbde7fa --- /dev/null +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -0,0 +1,245 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import format_columns +from osc_lib.cli import identity as identity_utils +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient import command +from openstackclient.i18n import _ +from openstackclient.identity import common +from openstackclient.network.v2.taas import tap_service + +LOG = logging.getLogger(__name__) + +TAP_FLOW = 'tap_flow' +TAP_FLOWS = f'{TAP_FLOW}s' + +_attr_map = [ + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('source_port', 'source_port', column_util.LIST_BOTH), + ('tap_service_id', 'tap_service_id', column_util.LIST_BOTH), + ('direction', 'Direction', column_util.LIST_BOTH), +] + +_formatters = { + 'vlan_filter': format_columns.ListColumn, +} + + +def _add_updatable_args(parser): + parser.add_argument('--name', help=_('Name of the tap flow.')) + parser.add_argument( + '--description', help=_('Description of the tap flow.') + ) + + +class CreateTapFlow(command.ShowOne): + _description = _("Create a new tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + required=True, + metavar="SOURCE_PORT", + help=_('Source port (name or ID) to monitor.'), + ) + parser.add_argument( + '--tap-service', + required=True, + metavar="TAP_SERVICE", + help=_( + 'Tap service (name or ID) to associate with this tap flow.' + ), + ) + parser.add_argument( + '--direction', + required=True, + metavar="DIRECTION", + choices=['IN', 'OUT', 'BOTH'], + type=lambda s: s.upper(), + help=_( + 'Direction of the Tap flow. Valid options are: ' + 'IN, OUT and BOTH' + ), + ) + parser.add_argument( + '--vlan-filter', + required=False, + metavar="VLAN_FILTER", + help=_('VLAN IDs to mirror in the form of range string.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port is not None: + source_port = client.find_port( + parsed_args.port, ignore_missing=False + ).id + attrs['source_port'] = source_port + if parsed_args.tap_service is not None: + tap_service_id = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + attrs['tap_service_id'] = tap_service_id + if parsed_args.direction is not None: + attrs['direction'] = parsed_args.direction + if parsed_args.vlan_filter is not None: + attrs['vlan_filter'] = parsed_args.vlan_filter + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_flow(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapFlow(command.Lister): + _description = _("List tap flows.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_flows(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + ( + osc_utils.get_dict_properties( + s, columns, formatters=_formatters + ) + for s in objs + ), + ) + + +class ShowTapFlow(command.ShowOne): + _description = _("Show tap flow details.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + help=_("Tap flow to display (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_flow( + parsed_args.tap_flow, ignore_missing=False + ).id + obj = client.get_tap_flow(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapFlow(command.Command): + _description = _("Delete a tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + nargs="+", + help=_("Tap flow to delete (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_flow: + try: + id = client.find_tap_flow(id_or_name, ignore_missing=False).id + client.delete_tap_flow(id) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete tap flow with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s tap flow.") % { + 'fails': fails, + 'total': len(parsed_args.tap_flow), + } + raise exceptions.CommandError(msg) + + +class UpdateTapFlow(command.ShowOne): + _description = _("Update a tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + help=_("Tap flow to modify (name or ID)."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_f = client.find_tap_flow( + parsed_args.tap_flow, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_flow(original_t_f, **attrs) + columns, display_columns = column_util.get_columns(obj, _attr_map) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py new file mode 100644 index 0000000000..876109dc46 --- /dev/null +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -0,0 +1,237 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import identity as identity_utils +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient import command +from openstackclient.i18n import _ +from openstackclient.identity import common +from openstackclient.network.v2 import port as osc_port +from openstackclient.network.v2.taas import tap_service + +LOG = logging.getLogger(__name__) + +TAP_MIRROR = 'tap_mirror' +TAP_MIRRORS = f'{TAP_MIRROR}s' + +_attr_map = [ + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('directions', 'Directions', column_util.LIST_LONG_ONLY), + ('remote_ip', 'Remote IP', column_util.LIST_BOTH), + ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY), +] + + +def _get_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateTapMirror(command.ShowOne): + _description = _("Create a new tap mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + tap_service._add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port (name or ID) to which the Tap Mirror is connected.'), + ) + parser.add_argument( + '--directions', + dest='directions', + action=osc_port.JSONKeyValueAction, + required=True, + help=_( + 'Dictionary of direction and tunnel_id. Valid directions are: ' + 'IN and OUT.' + ), + ) + parser.add_argument( + '--remote-ip', + dest='remote_ip', + required=True, + help=_( + 'Remote IP address for the tap mirror (remote end of the ' + 'GRE or ERSPAN v1 tunnel).' + ), + ) + parser.add_argument( + '--mirror-type', + dest='mirror_type', + required=True, + help=_('Mirror type. Valid values are: gre and erspanv1.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_id is not None: + port_id = client.find_port( + parsed_args.port_id, ignore_missing=False + ).id + attrs['port_id'] = port_id + if parsed_args.directions is not None: + attrs['directions'] = parsed_args.directions + if parsed_args.remote_ip is not None: + attrs['remote_ip'] = parsed_args.remote_ip + if parsed_args.mirror_type is not None: + attrs['mirror_type'] = parsed_args.mirror_type + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_mirror(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapMirror(command.Lister): + _description = _("List tap mirrors.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_mirrors(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + (osc_utils.get_dict_properties(s, columns) for s in objs), + ) + + +class ShowTapMirror(command.ShowOne): + _description = _("Show tap mirror details.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + help=_("Tap mirror to display (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_mirror( + parsed_args.tap_mirror, ignore_missing=False + ).id + obj = client.get_tap_mirror(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapMirror(command.Command): + _description = _("Delete a tap mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + nargs="+", + help=_("Tap mirror to delete (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_mirror: + try: + id = client.find_tap_mirror( + id_or_name, ignore_missing=False + ).id + + client.delete_tap_mirror(id) + LOG.warning("Tap Mirror %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete Tap Mirror with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s Tap Mirror.") % { + 'fails': fails, + 'total': len(parsed_args.tap_mirror), + } + raise exceptions.CommandError(msg) + + +class UpdateTapMirror(command.ShowOne): + _description = _("Update a tap mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + help=_("Tap mirror to modify (name or ID)."), + ) + tap_service._add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_mirror( + parsed_args.tap_mirror, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_mirror(original_t_s, **attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py new file mode 100644 index 0000000000..df27658f5d --- /dev/null +++ b/openstackclient/network/v2/taas/tap_service.py @@ -0,0 +1,211 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import identity as identity_utils +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient import command +from openstackclient.i18n import _ +from openstackclient.identity import common + +LOG = logging.getLogger(__name__) + +TAP_SERVICE = 'tap_service' +TAP_SERVICES = f'{TAP_SERVICE}s' + +_attr_map = [ + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), +] + + +def _add_updatable_args(parser): + parser.add_argument('--name', help=_('Name of the tap service.')) + parser.add_argument( + '--description', help=_('Description of the tap service.') + ) + + +def _get_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateTapService(command.ShowOne): + _description = _("Create a new tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port (name or ID) to connect to the tap service.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_id is not None: + port_id = client.find_port( + parsed_args.port_id, ignore_missing=False + ).id + attrs['port_id'] = port_id + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_service(**attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapService(command.Lister): + _description = _("List tap services.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_services(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + (osc_utils.get_dict_properties(s, columns) for s in objs), + ) + + +class ShowTapService(command.ShowOne): + _description = _("Show tap service details.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + help=_("Tap service to display (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + obj = client.get_tap_service(id) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapService(command.Command): + _description = _("Delete a tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + nargs="+", + help=_("Tap service to delete (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_service: + try: + id = client.find_tap_service( + id_or_name, ignore_missing=False + ).id + + client.delete_tap_service(id) + LOG.warning("Tap service %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete tap service with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s tap service.") % { + 'fails': fails, + 'total': len(parsed_args.tap_service), + } + raise exceptions.CommandError(msg) + + +class UpdateTapService(command.ShowOne): + _description = _("Update a tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + help=_("Tap service to modify (name or ID)."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_service(original_t_s, **attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/object/client.py b/openstackclient/object/client.py index 9eb7ad7baa..466f132ec2 100644 --- a/openstackclient/object/client.py +++ b/openstackclient/object/client.py @@ -19,12 +19,11 @@ from openstackclient.api import object_store_v1 +# global variables used when building the shell DEFAULT_API_VERSION = '1' API_VERSION_OPTION = 'os_object_api_version' API_NAME = 'object_store' -API_VERSIONS = { - '1': 'openstackclient.object.client.ObjectClientv1', -} +API_VERSIONS = ('1',) def make_client(instance): diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py index 686fdcc62c..199e5222fd 100644 --- a/openstackclient/object/v1/account.py +++ b/openstackclient/object/v1/account.py @@ -15,8 +15,8 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index c4d0e42b9d..b0f92c7619 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -19,9 +19,9 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ @@ -148,10 +148,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Name',) if parsed_args.long: - columns = ('Name', 'Bytes', 'Count') - else: - columns = ('Name',) + columns += ('Bytes', 'Count') kwargs = {} if parsed_args.prefix: diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index 4edb314930..e8ee0fc692 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -19,10 +19,10 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ @@ -162,16 +162,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Name',) if parsed_args.long: - columns = ( - 'Name', - 'Bytes', - 'Hash', - 'Content Type', - 'Last Modified', - ) - else: - columns = ('Name',) + columns += ('Bytes', 'Hash', 'Content Type', 'Last Modified') kwargs = {} if parsed_args.prefix: diff --git a/openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml b/openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml new file mode 100644 index 0000000000..974d88ed9c --- /dev/null +++ b/openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Listing volume backups now shows the created_at column. diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 5c64cb7cb1..743ed2bc82 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -26,16 +26,27 @@ import openstackclient from openstackclient.common import clientmanager - DEFAULT_DOMAIN = 'default' +# list of modules that were originally out-of-tree and are now in +# core OSC +IGNORED_MODULES = ( + 'neutron_taas.taas_client.osc', + 'neutronclient.osc.v2.taas', +) class OpenStackShell(shell.OpenStackShell): + client_manager: clientmanager.ClientManager + def __init__(self): + command_manager = commandmanager.CommandManager( + 'openstack.cli', ignored_modules=IGNORED_MODULES + ) + super().__init__( description=__doc__.strip(), version=openstackclient.__version__, - command_manager=commandmanager.CommandManager('openstack.cli'), + command_manager=command_manager, deferred_help=True, ) @@ -48,8 +59,10 @@ def __init__(self): # about them warnings.filterwarnings('ignore', module='openstack') - def build_option_parser(self, description, version): - parser = super().build_option_parser(description, version) + def build_option_parser(self, description, version, argparse_kwargs=None): + parser = super().build_option_parser( + description, version, argparse_kwargs + ) parser = clientmanager.build_plugin_option_parser(parser) parser = auth.build_auth_plugins_option_parser(parser) return parser @@ -65,10 +78,7 @@ def _final_defaults(self): self._auth_type = 'password' def _load_plugins(self): - """Load plugins via stevedore - - osc-lib has no opinion on what plugins should be loaded - """ + """Load plugins via stevedore.""" # Loop through extensions to get API versions for mod in clientmanager.PLUGIN_MODULES: default_version = getattr(mod, 'DEFAULT_API_VERSION', None) @@ -88,16 +98,34 @@ def _load_plugins(self): # this throws an exception if invalid skip_old_check = mod_check_api_version(version_opt) + # NOTE(stephenfin): API_VERSIONS has traditionally been a + # dictionary but the values are only used internally and are + # ignored for the modules using SDK. So we now support tuples + # instead. mod_versions = getattr(mod, 'API_VERSIONS', None) - if not skip_old_check and mod_versions: + if mod_versions is not None and not isinstance( + mod_versions, dict | tuple + ): + raise TypeError( + f'Plugin {mod} has incompatible API_VERSIONS. ' + f'Expected: tuple, dict. Got: {type(mod_versions)}. ' + f'Please report this to your package maintainer.' + ) + + if mod_versions and not skip_old_check: if version_opt not in mod_versions: sorted_versions = sorted( - mod.API_VERSIONS.keys(), + list(mod.API_VERSIONS), key=lambda s: list(map(int, s.split('.'))), ) self.log.warning( - "%s version %s is not in supported versions: %s" - % (api, version_opt, ', '.join(sorted_versions)) + "%(name)s API version %(version)s is not in " + "supported versions: %(supported)s", + { + 'name': api, + 'version': version_opt, + 'supported': ', '.join(sorted_versions), + }, ) # Command groups deal only with major versions diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 2539c56fc3..96c9accf6b 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -36,8 +36,8 @@ def execute(cmd, *, fail_ok=False): proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr, env=env) - result_out, result_err = proc.communicate() - result_out = result_out.decode('utf-8') + result_out_b, result_err = proc.communicate() + result_out = result_out_b.decode('utf-8') LOG.debug('stdout: %s', result_out) LOG.debug('stderr: %s', result_err) @@ -97,7 +97,11 @@ def openstack( ) if parse_output: - return json.loads(output) + try: + return json.loads(output) + except json.JSONDecodeError: + print(f'failed to decode: {output}') + raise else: return output diff --git a/openstackclient/tests/functional/common/test_help.py b/openstackclient/tests/functional/common/test_help.py index f1aea8ee98..c44a906091 100644 --- a/openstackclient/tests/functional/common/test_help.py +++ b/openstackclient/tests/functional/common/test_help.py @@ -21,7 +21,7 @@ class HelpTests(base.TestCase): """Functional tests for openstackclient help output.""" SERVER_COMMANDS = [ - ('server add security group', 'Add security group to server'), + ('server add security group', 'Add security group(s) to server'), ('server add volume', 'Add volume to server'), ('server backup create', 'Create a server backup image'), ('server create', 'Create a new server'), @@ -60,15 +60,9 @@ def test_server_commands_main_help(self): """Check server commands in main help message.""" raw_output = self.openstack('help') for command, description in self.SERVER_COMMANDS: - msg = 'Command: {} not found in help output:\n{}'.format( - command, - raw_output, - ) + msg = f'Command: {command} not found in help output:\n{raw_output}' self.assertIn(command, raw_output, msg) - msg = 'Description: {} not found in help output:\n{}'.format( - description, - raw_output, - ) + msg = f'Description: {description} not found in help output:\n{raw_output}' self.assertIn(description, raw_output, msg) def test_server_only_help(self): diff --git a/openstackclient/tests/functional/common/test_module.py b/openstackclient/tests/functional/common/test_module.py index 1506683499..41486d1ff5 100644 --- a/openstackclient/tests/functional/common/test_module.py +++ b/openstackclient/tests/functional/common/test_module.py @@ -59,7 +59,7 @@ def test_command_list_with_group(self): input_groups = ['volume', 'network', 'image', 'identity', 'compute.v2'] for each_input in input_groups: cmd_output = self.openstack( - 'command list --group %s' % each_input, + f'command list --group {each_input}', parse_output=True, ) group_names = [each.get('Command Group') for each in cmd_output] diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index 8aa93a9294..373b178c15 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -25,7 +25,7 @@ class QuotaTests(base.TestCase): test runs as these may run in parallel and otherwise step on each other. """ - PROJECT_NAME = None + PROJECT_NAME: str @classmethod def setUpClass(cls): @@ -165,8 +165,7 @@ def test_quota_set_network(self): # 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) + f'network create --project {self.PROJECT_NAME} {uuid.uuid4().hex}' ) self.assertRaises( @@ -211,8 +210,7 @@ def test_quota_set_network_with_force(self): # 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) + f'network create --project {self.PROJECT_NAME} {uuid.uuid4().hex}' ) self.openstack('quota set --networks 1 --force ' + self.PROJECT_NAME) @@ -252,6 +250,8 @@ def test_quota_show_usage_option(self): row_headers = [str(r) for r in row.keys()] self.assertEqual(sorted(expected_headers), sorted(row_headers)) resources.append(row['Resource']) + for header in expected_headers[1:]: + self.assertIsInstance(row[header], int) # Ensure that returned quota has network quota... self.assertIn("networks", resources) # ...and compute quota diff --git a/openstackclient/tests/functional/compute/v2/common.py b/openstackclient/tests/functional/compute/v2/common.py index ef59d458a1..5892ee3a0b 100644 --- a/openstackclient/tests/functional/compute/v2/common.py +++ b/openstackclient/tests/functional/compute/v2/common.py @@ -22,9 +22,9 @@ class ComputeTestCase(base.TestCase): """Common functional test bits for Compute commands""" - flavor_name = None - image_name = None - network_arg = None + flavor_name: str + image_name: str + network_arg: str def setUp(self): """Select common resources""" @@ -34,7 +34,7 @@ def setUp(self): self.network_arg = self.get_network() @classmethod - def get_flavor(cls): + def get_flavor(cls) -> str: # NOTE(rtheis): Get cirros256 or m1.tiny flavors since functional # tests may create other flavors. flavors = cls.openstack("flavor list", parse_output=True) @@ -43,10 +43,13 @@ def get_flavor(cls): if flavor['Name'] in ['m1.tiny', 'cirros256']: server_flavor = flavor['Name'] break + + assert server_flavor is not None + return server_flavor @classmethod - def get_image(cls): + def get_image(cls) -> str: # NOTE(rtheis): Get first Cirros image since functional tests may # create other images. Image may be named '-uec' or # '-disk'. @@ -59,10 +62,13 @@ def get_image(cls): ): server_image = image['Name'] break + + assert server_image is not None + return server_image @classmethod - def get_network(cls): + def get_network(cls) -> str: try: # NOTE(rtheis): Get private network since functional tests may # create other networks. @@ -132,9 +138,7 @@ def wait_for_status( print(f'Server {name} now has status {status}') break print( - 'Server {}: Waiting for {}, current status: {}'.format( - name, expected_status, status - ) + f'Server {name}: Waiting for {expected_status}, current status: {status}' ) self.assertNotIn(status, failures) time.sleep(interval) diff --git a/openstackclient/tests/functional/compute/v2/test_hypervisor.py b/openstackclient/tests/functional/compute/v2/test_hypervisor.py index 7e90b743cb..0ed4e904bc 100644 --- a/openstackclient/tests/functional/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/functional/compute/v2/test_hypervisor.py @@ -37,8 +37,8 @@ def test_hypervisor_list(self): for i in ids1: cmd_output = json.loads( self.openstack( - "hypervisor show %s -f json " - " --os-compute-api-version 2.1" % (i) + f"hypervisor show {i} -f json " + " --os-compute-api-version 2.1" ) ) self.assertIsNotNone(cmd_output) @@ -47,6 +47,6 @@ def test_hypervisor_list(self): # Show test - latest microversion for i in ids2: cmd_output = json.loads( - self.openstack("hypervisor show %s -f json" % (i)) + self.openstack(f"hypervisor show {i} -f json") ) self.assertIsNotNone(cmd_output) diff --git a/openstackclient/tests/functional/compute/v2/test_keypair.py b/openstackclient/tests/functional/compute/v2/test_keypair.py index ebc78abdae..2e01b1bed2 100644 --- a/openstackclient/tests/functional/compute/v2/test_keypair.py +++ b/openstackclient/tests/functional/compute/v2/test_keypair.py @@ -21,12 +21,18 @@ class KeypairBase(base.TestCase): """Methods for functional tests.""" - def keypair_create(self, name=data_utils.rand_uuid()): + def keypair_create(self, name=data_utils.rand_uuid(), user=None): """Create keypair and add cleanup.""" - raw_output = self.openstack('keypair create ' + name) - self.addCleanup(self.keypair_delete, name, True) + cmd = 'keypair create ' + name + if user is not None: + cmd += ' --user ' + user + raw_output = self.openstack(cmd) + self.addCleanup( + self.keypair_delete, name, ignore_exceptions=True, user=user + ) if not raw_output: self.fail('Keypair has not been created!') + return name def keypair_list(self, params=''): """Return dictionary with list of keypairs.""" @@ -34,10 +40,13 @@ def keypair_list(self, params=''): keypairs = self.parse_show_as_object(raw_output) return keypairs - def keypair_delete(self, name, ignore_exceptions=False): + def keypair_delete(self, name, ignore_exceptions=False, user=None): """Try to delete keypair by name.""" try: - self.openstack('keypair delete ' + name) + cmd = 'keypair delete ' + name + if user is not None: + cmd += ' --user ' + user + self.openstack(cmd) except exceptions.CommandFailed: if not ignore_exceptions: raise @@ -96,7 +105,7 @@ def test_keypair_create_public_key(self): f.flush() raw_output = self.openstack( - 'keypair create --public-key %s tmpkey' % f.name, + f'keypair create --public-key {f.name} tmpkey', ) self.addCleanup( self.openstack, @@ -113,7 +122,7 @@ def test_keypair_create_private_key(self): """ with tempfile.NamedTemporaryFile(mode='w+') as f: cmd_output = self.openstack( - 'keypair create --private-key %s tmpkey' % f.name, + f'keypair create --private-key {f.name} tmpkey', parse_output=True, ) self.addCleanup(self.openstack, 'keypair delete tmpkey') @@ -200,3 +209,30 @@ def test_keypair_show(self): items = self.parse_listing(raw_output) self.assert_table_structure(items, HEADERS) self.assertInOutput(self.KPName, raw_output) + + def test_keypair_list_by_project(self): + """Test keypair list by project. + + Test steps: + 1) Create keypair for admin project in setUp + 2) Create a new project + 3) Create a new user + 4) Associate the new user with the new project + 5) Create keypair for the new user + 6) List keypairs by the new project + 7) Check that only the keypair from step 5 is returned + """ + project_name = data_utils.rand_name('TestProject') + self.openstack(f'project create {project_name}') + self.addCleanup(self.openstack, f'project delete {project_name}') + user_name = data_utils.rand_name('TestUser') + self.openstack(f'user create {user_name}') + self.addCleanup(self.openstack, f'user delete {user_name}') + self.openstack( + f'role add --user {user_name} --project {project_name} member' + ) + keypair_name = self.keypair_create(user=user_name) + raw_output = self.openstack(f'keypair list --project {project_name}') + items = self.parse_listing(raw_output) + self.assertEqual(1, len(items)) + self.assertEqual(keypair_name, items[0]['Name']) diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index d6638d2f71..6afa2c7c0e 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -111,7 +111,7 @@ def test_server_list_with_marker_and_deleted(self): ) except exceptions.CommandFailed as e: self.assertIn( - 'marker [%s] not found' % (name2), e.stderr.decode('utf-8') + f'marker [{name2}] not found', e.stderr.decode('utf-8') ) def test_server_list_with_changes_before(self): @@ -156,7 +156,7 @@ def test_server_list_with_changes_since(self): server_name3 = cmd_output['name'] cmd_output = self.openstack( - 'server list ' '--changes-since ' + updated_at2, + 'server list --changes-since ' + updated_at2, parse_output=True, ) @@ -852,8 +852,7 @@ def _test_server_boot_with_bdm_image(self, use_legacy): # it to the server at /dev/vdb and delete the volume when the # server is deleted. bdm_arg = ( - f'--block-device-mapping ' - f'vdb={self.image_name}:image:1:true ' + f'--block-device-mapping vdb={self.image_name}:image:1:true ' ) else: # get image ID @@ -1295,7 +1294,7 @@ def test_server_add_remove_port(self): ) if ip_address in cmd_output['addresses']['private']: # Hang out for a bit and try again - print('retrying add port check') + print('retrying remove port check') wait_time += 10 time.sleep(10) else: @@ -1367,6 +1366,92 @@ def test_server_add_fixed_ip(self): addresses = cmd_output['addresses']['private'] self.assertIn(ip_address, addresses) + def test_server_add_remove_security_group(self): + name = uuid.uuid4().hex + cmd_output = self.openstack( + 'server create ' + + '--network private ' + + '--flavor ' + + self.flavor_name + + ' ' + + '--image ' + + self.image_name + + ' ' + + '--wait ' + + name, + parse_output=True, + ) + + self.assertIsNotNone(cmd_output['id']) + self.assertEqual(name, cmd_output['name']) + self.addCleanup(self.openstack, 'server delete --wait ' + name) + + # create security group + security_group_name = uuid.uuid4().hex + + cmd_output = self.openstack( + 'security group list', + parse_output=True, + ) + self.assertNotIn(security_group_name, cmd_output) + + cmd_output = self.openstack( + 'security group create ' + security_group_name, + parse_output=True, + ) + self.assertIsNotNone(cmd_output['id']) + self.addCleanup( + self.openstack, 'security group delete ' + security_group_name + ) + + # add security group to server, assert the name of the security group + # appears + self.openstack( + 'server add security group ' + name + ' ' + security_group_name + ) + + wait_time = 0 + while wait_time < 60: + cmd_output = self.openstack( + 'server show ' + name, + parse_output=True, + ) + if security_group_name not in [ + x['name'] for x in cmd_output['security_groups'] + ]: + # Hang out for a bit and try again + print('retrying add security group check') + wait_time += 10 + time.sleep(10) + else: + break + security_groups = [x['name'] for x in cmd_output['security_groups']] + self.assertIn(security_group_name, security_groups) + + # remove security group, assert the name of the security group doesn't + # appear + self.openstack( + 'server remove security group ' + name + ' ' + security_group_name + ) + + wait_time = 0 + while wait_time < 60: + cmd_output = self.openstack( + 'server show ' + name, + parse_output=True, + ) + if security_group_name not in [ + x['name'] for x in cmd_output['security_groups'] + ]: + # Hang out for a bit and try again + print('retrying remove security group check') + wait_time += 10 + time.sleep(10) + else: + break + security_groups = [x['name'] for x in cmd_output['security_groups']] + self.assertNotIn(security_group_name, security_groups) + def test_server_add_remove_volume(self): volume_wait_for = volume_common.BaseVolumeTests.wait_for_status diff --git a/openstackclient/tests/functional/compute/v2/test_server_event.py b/openstackclient/tests/functional/compute/v2/test_server_event.py index 790aec69d7..0457985a08 100644 --- a/openstackclient/tests/functional/compute/v2/test_server_event.py +++ b/openstackclient/tests/functional/compute/v2/test_server_event.py @@ -93,7 +93,7 @@ def test_server_event_list_and_show_deleted_server(self): # And verify we can get the event list after it's deleted # Test 'server event list' for deleting cmd_output = self.openstack( - '--os-compute-api-version 2.21 ' 'server event list ' + server_id, + '--os-compute-api-version 2.21 server event list ' + server_id, parse_output=True, ) request_id = None diff --git a/openstackclient/tests/functional/identity/v2/common.py b/openstackclient/tests/functional/identity/v2/common.py index a78f3b0315..dd2e271933 100644 --- a/openstackclient/tests/functional/identity/v2/common.py +++ b/openstackclient/tests/functional/identity/v2/common.py @@ -67,13 +67,9 @@ def setUpClass(cls): cls.openstack( '--os-identity-api-version 2 ' 'project create ' - '--description %(description)s ' + f'--description {cls.project_description} ' '--enable ' - '%(name)s' - % { - 'description': cls.project_description, - 'name': cls.project_name, - } + f'{cls.project_name}' ) except tempest_exceptions.CommandFailed: # Good chance this is due to Identity v2 admin not being enabled @@ -87,7 +83,7 @@ def tearDownClass(cls): try: cls.openstack( '--os-identity-api-version 2 ' - 'project delete %s' % cls.project_name + f'project delete {cls.project_name}' ) finally: super().tearDownClass() @@ -111,14 +107,13 @@ def _create_dummy_project(self, add_clean_up=True): project_description = data_utils.rand_name('description') raw_output = self.openstack( 'project create ' - '--description %(description)s ' - '--enable %(name)s' - % {'description': project_description, 'name': project_name} + f'--description {project_description} ' + f'--enable {project_name}' ) project = self.parse_show_as_object(raw_output) if add_clean_up: self.addCleanup( - self.openstack, 'project delete %s' % project['id'] + self.openstack, 'project delete {}'.format(project['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.PROJECT_FIELDS) @@ -130,22 +125,18 @@ def _create_dummy_user(self, add_clean_up=True): email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( 'user create ' - '--project %(project)s ' - '--password %(password)s ' - '--email %(email)s ' + f'--project {self.project_name} ' + f'--password {password} ' + f'--email {email} ' '--enable ' - '%(name)s' - % { - 'project': self.project_name, - 'email': email, - 'password': password, - 'name': username, - } + f'{username}' ) if add_clean_up: self.addCleanup( self.openstack, - 'user delete %s' % self.parse_show_as_object(raw_output)['id'], + 'user delete {}'.format( + self.parse_show_as_object(raw_output)['id'] + ), ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) @@ -153,10 +144,12 @@ def _create_dummy_user(self, add_clean_up=True): def _create_dummy_role(self, add_clean_up=True): role_name = data_utils.rand_name('TestRole') - raw_output = self.openstack('role create %s' % role_name) + raw_output = self.openstack(f'role create {role_name}') role = self.parse_show_as_object(raw_output) if add_clean_up: - self.addCleanup(self.openstack, 'role delete %s' % role['id']) + self.addCleanup( + self.openstack, 'role delete {}'.format(role['id']) + ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) self.assertEqual(role_name, role['name']) @@ -168,7 +161,7 @@ def _create_dummy_ec2_credentials(self, add_clean_up=True): access_key = ec2_credentials['access'] if add_clean_up: self.addCleanup( - self.openstack, 'ec2 credentials delete %s' % access_key + self.openstack, f'ec2 credentials delete {access_key}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.EC2_CREDENTIALS_FIELDS) @@ -178,7 +171,9 @@ def _create_dummy_token(self, add_clean_up=True): raw_output = self.openstack('token issue') token = self.parse_show_as_object(raw_output) if add_clean_up: - self.addCleanup(self.openstack, 'token revoke %s' % token['id']) + self.addCleanup( + self.openstack, 'token revoke {}'.format(token['id']) + ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.TOKEN_FIELDS) return token['id'] @@ -189,19 +184,14 @@ def _create_dummy_service(self, add_clean_up=True): type_name = data_utils.rand_name('TestType') raw_output = self.openstack( 'service create ' - '--name %(name)s ' - '--description %(description)s ' - '%(type)s' - % { - 'name': service_name, - 'description': description, - 'type': type_name, - } + f'--name {service_name} ' + f'--description {description} ' + f'{type_name}' ) if add_clean_up: service = self.parse_show_as_object(raw_output) self.addCleanup( - self.openstack, 'service delete %s' % service['id'] + self.openstack, 'service delete {}'.format(service['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) @@ -215,23 +205,16 @@ def _create_dummy_endpoint(self, add_clean_up=True): internal_url = data_utils.rand_url() raw_output = self.openstack( 'endpoint create ' - '--publicurl %(publicurl)s ' - '--adminurl %(adminurl)s ' - '--internalurl %(internalurl)s ' - '--region %(region)s ' - '%(service)s' - % { - 'publicurl': public_url, - 'adminurl': admin_url, - 'internalurl': internal_url, - 'region': region_id, - 'service': service_name, - } + f'--publicurl {public_url} ' + f'--adminurl {admin_url} ' + f'--internalurl {internal_url} ' + f'--region {region_id} ' + f'{service_name}' ) endpoint = self.parse_show_as_object(raw_output) if add_clean_up: self.addCleanup( - self.openstack, 'endpoint delete %s' % endpoint['id'] + self.openstack, 'endpoint delete {}'.format(endpoint['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_catalog.py b/openstackclient/tests/functional/identity/v2/test_catalog.py index 1cef314f74..67c6c36945 100644 --- a/openstackclient/tests/functional/identity/v2/test_catalog.py +++ b/openstackclient/tests/functional/identity/v2/test_catalog.py @@ -35,7 +35,7 @@ def test_catalog_show(self): | type | identity | +-----------+-------------------------------------------+ """ - raw_output = self.openstack('catalog show %s' % 'identity') + raw_output = self.openstack('catalog show {}'.format('identity')) items = self.parse_show(raw_output) # items may have multiple endpoint urls with empty key self.assert_show_fields(items, ['endpoints', 'name', 'type', '']) diff --git a/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py b/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py index 6d25bdae2b..b72dc40145 100644 --- a/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py +++ b/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py @@ -20,7 +20,7 @@ def test_ec2_credentials_create(self): def test_ec2_credentials_delete(self): access_key = self._create_dummy_ec2_credentials(add_clean_up=False) raw_output = self.openstack( - 'ec2 credentials delete %s' % access_key, + f'ec2 credentials delete {access_key}', ) self.assertEqual(0, len(raw_output)) @@ -41,7 +41,7 @@ def test_ec2_credentials_list(self): def test_ec2_credentials_show(self): access_key = self._create_dummy_ec2_credentials() show_output = self.openstack( - 'ec2 credentials show %s' % access_key, + f'ec2 credentials show {access_key}', ) items = self.parse_show(show_output) self.assert_show_fields(items, self.EC2_CREDENTIALS_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_endpoint.py b/openstackclient/tests/functional/identity/v2/test_endpoint.py index bafbfdaed9..8a0077b7f2 100644 --- a/openstackclient/tests/functional/identity/v2/test_endpoint.py +++ b/openstackclient/tests/functional/identity/v2/test_endpoint.py @@ -19,7 +19,7 @@ def test_endpoint_create(self): def test_endpoint_delete(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) - raw_output = self.openstack('endpoint delete %s' % endpoint_id) + raw_output = self.openstack(f'endpoint delete {endpoint_id}') self.assertEqual(0, len(raw_output)) def test_endpoint_multi_delete(self): @@ -39,6 +39,6 @@ def test_endpoint_list(self): def test_endpoint_show(self): endpoint_id = self._create_dummy_endpoint() - raw_output = self.openstack('endpoint show %s' % endpoint_id) + raw_output = self.openstack(f'endpoint show {endpoint_id}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_project.py b/openstackclient/tests/functional/identity/v2/test_project.py index 1ad0edaa31..2dfc95edb8 100644 --- a/openstackclient/tests/functional/identity/v2/test_project.py +++ b/openstackclient/tests/functional/identity/v2/test_project.py @@ -21,13 +21,13 @@ def test_project_create(self): description = data_utils.rand_name('description') raw_output = self.openstack( 'project create ' - '--description %(description)s ' + f'--description {description} ' '--enable ' '--property k1=v1 ' '--property k2=v2 ' - '%(name)s' % {'description': description, 'name': project_name} + f'{project_name}' ) - self.addCleanup(self.openstack, 'project delete %s' % project_name) + self.addCleanup(self.openstack, f'project delete {project_name}') items = self.parse_show(raw_output) show_fields = list(self.PROJECT_FIELDS) show_fields.extend(['k1', 'k2']) @@ -38,7 +38,7 @@ def test_project_create(self): def test_project_delete(self): project_name = self._create_dummy_project(add_clean_up=False) - raw_output = self.openstack('project delete %s' % project_name) + raw_output = self.openstack(f'project delete {project_name}') self.assertEqual(0, len(raw_output)) def test_project_list(self): @@ -51,14 +51,14 @@ def test_project_set(self): new_project_name = data_utils.rand_name('NewTestProject') raw_output = self.openstack( 'project set ' - '--name %(new_name)s ' + f'--name {new_project_name} ' '--disable ' '--property k0=v0 ' - '%(name)s' % {'new_name': new_project_name, 'name': project_name} + f'{project_name}' ) self.assertEqual(0, len(raw_output)) # check project details - raw_output = self.openstack('project show %s' % new_project_name) + raw_output = self.openstack(f'project show {new_project_name}') items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) fields.extend(['properties']) @@ -70,7 +70,7 @@ def test_project_set(self): def test_project_show(self): project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) fields.extend(['properties']) diff --git a/openstackclient/tests/functional/identity/v2/test_role.py b/openstackclient/tests/functional/identity/v2/test_role.py index fb22f0181e..ec6134012f 100644 --- a/openstackclient/tests/functional/identity/v2/test_role.py +++ b/openstackclient/tests/functional/identity/v2/test_role.py @@ -19,7 +19,7 @@ def test_role_create(self): def test_role_delete(self): role_name = self._create_dummy_role(add_clean_up=False) - raw_output = self.openstack('role delete %s' % role_name) + raw_output = self.openstack(f'role delete {role_name}') self.assertEqual(0, len(raw_output)) def test_role_list(self): @@ -30,7 +30,7 @@ def test_role_list(self): def test_role_show(self): role_name = self._create_dummy_role() - raw_output = self.openstack('role show %s' % role_name) + raw_output = self.openstack(f'role show {role_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) @@ -39,26 +39,16 @@ def test_role_add(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) @@ -68,25 +58,15 @@ def test_role_remove(self): username = self._create_dummy_user() add_raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) del_raw_output = self.openstack( 'role remove ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) items = self.parse_show(add_raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_service.py b/openstackclient/tests/functional/identity/v2/test_service.py index 7afa967d4b..5f1611cafd 100644 --- a/openstackclient/tests/functional/identity/v2/test_service.py +++ b/openstackclient/tests/functional/identity/v2/test_service.py @@ -19,7 +19,7 @@ def test_service_create(self): def test_service_delete(self): service_name = self._create_dummy_service(add_clean_up=False) - raw_output = self.openstack('service delete %s' % service_name) + raw_output = self.openstack(f'service delete {service_name}') self.assertEqual(0, len(raw_output)) def test_service_multi_delete(self): @@ -38,6 +38,6 @@ def test_service_list(self): def test_service_show(self): service_name = self._create_dummy_service() - raw_output = self.openstack('service show %s' % service_name) + raw_output = self.openstack(f'service show {service_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_token.py b/openstackclient/tests/functional/identity/v2/test_token.py index d2ef78e385..51be24319a 100644 --- a/openstackclient/tests/functional/identity/v2/test_token.py +++ b/openstackclient/tests/functional/identity/v2/test_token.py @@ -19,5 +19,5 @@ def test_token_issue(self): def test_token_revoke(self): token_id = self._create_dummy_token(add_clean_up=False) - raw_output = self.openstack('token revoke %s' % token_id) + raw_output = self.openstack(f'token revoke {token_id}') self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v2/test_user.py b/openstackclient/tests/functional/identity/v2/test_user.py index d30e3c4567..c92fc87927 100644 --- a/openstackclient/tests/functional/identity/v2/test_user.py +++ b/openstackclient/tests/functional/identity/v2/test_user.py @@ -22,7 +22,7 @@ def test_user_create(self): def test_user_delete(self): username = self._create_dummy_user(add_clean_up=False) - raw_output = self.openstack('user delete %s' % username) + raw_output = self.openstack(f'user delete {username}') self.assertEqual(0, len(raw_output)) def test_user_list(self): @@ -32,26 +32,24 @@ def test_user_list(self): def test_user_set(self): username = self._create_dummy_user() - raw_output = self.openstack('user show %s' % username) + raw_output = self.openstack(f'user show {username}') user = self.parse_show_as_object(raw_output) new_username = data_utils.rand_name('NewTestUser') new_email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( - 'user set ' - '--email %(email)s ' - '--name %(new_name)s ' - '%(id)s' - % {'email': new_email, 'new_name': new_username, 'id': user['id']} + 'user set --email {email} --name {new_name} {id}'.format( + email=new_email, new_name=new_username, id=user['id'] + ) ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('user show %s' % new_username) + raw_output = self.openstack(f'user show {new_username}') new_user = self.parse_show_as_object(raw_output) self.assertEqual(user['id'], new_user['id']) self.assertEqual(new_email, new_user['email']) def test_user_show(self): username = self._create_dummy_user() - raw_output = self.openstack('user show %s' % username) + raw_output = self.openstack(f'user show {username}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index cc7adcc461..9f21374ff5 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -48,8 +48,8 @@ class IdentityTests(base.TestCase): 'parent_id', ] ROLE_FIELDS = ['id', 'name', 'domain_id', 'description'] - SERVICE_FIELDS = ['ID', 'Enabled', 'Name', 'Type', 'Description'] - REGION_FIELDS = ['description', 'enabled', 'parent_region', 'region'] + SERVICE_FIELDS = ['id', 'enabled', 'name', 'type', 'description'] + REGION_FIELDS = ['description', 'parent_region', 'region'] ENDPOINT_FIELDS = [ 'id', 'region', @@ -156,10 +156,9 @@ def setUpClass(cls): cls.openstack( '--os-identity-api-version 3 ' 'domain create ' - '--description %(description)s ' + f'--description {cls.domain_description} ' '--enable ' - '%(name)s' - % {'description': cls.domain_description, 'name': cls.domain_name} + f'{cls.domain_name}' ) # create dummy project @@ -168,15 +167,10 @@ def setUpClass(cls): cls.openstack( '--os-identity-api-version 3 ' 'project create ' - '--domain %(domain)s ' - '--description %(description)s ' + f'--domain {cls.domain_name} ' + f'--description {cls.project_description} ' '--enable ' - '%(name)s' - % { - 'domain': cls.domain_name, - 'description': cls.project_description, - 'name': cls.project_name, - } + f'{cls.project_name}' ) @classmethod @@ -185,16 +179,15 @@ def tearDownClass(cls): # delete dummy project cls.openstack( '--os-identity-api-version 3 ' - 'project delete %s' % cls.project_name + f'project delete {cls.project_name}' ) # disable and delete dummy domain cls.openstack( '--os-identity-api-version 3 ' - 'domain set --disable %s' % cls.domain_name + f'domain set --disable {cls.domain_name}' ) cls.openstack( - '--os-identity-api-version 3 ' - 'domain delete %s' % cls.domain_name + f'--os-identity-api-version 3 domain delete {cls.domain_name}' ) finally: super().tearDownClass() @@ -220,28 +213,21 @@ def _create_dummy_user(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'user create ' - '--domain %(domain)s ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--password %(password)s ' - '--email %(email)s ' - '--description %(description)s ' + f'--domain {self.domain_name} ' + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--password {password} ' + f'--email {email} ' + f'--description {description} ' '--enable ' - '%(name)s' - % { - 'domain': self.domain_name, - 'project': self.project_name, - 'project_domain': self.domain_name, - 'email': email, - 'password': password, - 'description': description, - 'name': username, - } + f'{username}' ) if add_clean_up: self.addCleanup( self.openstack, - 'user delete %s' % self.parse_show_as_object(raw_output)['id'], + 'user delete {}'.format( + self.parse_show_as_object(raw_output)['id'] + ), ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) @@ -249,10 +235,12 @@ def _create_dummy_user(self, add_clean_up=True): def _create_dummy_role(self, add_clean_up=True): role_name = data_utils.rand_name('TestRole') - raw_output = self.openstack('role create %s' % role_name) + raw_output = self.openstack(f'role create {role_name}') role = self.parse_show_as_object(raw_output) if add_clean_up: - self.addCleanup(self.openstack, 'role delete %s' % role['id']) + self.addCleanup( + self.openstack, 'role delete {}'.format(role['id']) + ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) self.assertEqual(role_name, role['name']) @@ -263,8 +251,8 @@ def _create_dummy_implied_role(self, add_clean_up=True): implied_role_name = self._create_dummy_role(add_clean_up) self.openstack( 'implied role create ' - '--implied-role %(implied_role)s ' - '%(role)s' % {'implied_role': implied_role_name, 'role': role_name} + f'--implied-role {implied_role_name} ' + f'{role_name}' ) return implied_role_name, role_name @@ -274,21 +262,14 @@ def _create_dummy_group(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'group create ' - '--domain %(domain)s ' - '--description %(description)s ' - '%(name)s' - % { - 'domain': self.domain_name, - 'description': description, - 'name': group_name, - } + f'--domain {self.domain_name} ' + f'--description {description} ' + f'{group_name}' ) if add_clean_up: self.addCleanup( self.openstack, - 'group delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': group_name}, + f'group delete --domain {self.domain_name} {group_name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.GROUP_FIELDS) @@ -299,14 +280,13 @@ def _create_dummy_domain(self, add_clean_up=True): domain_description = data_utils.rand_name('description') self.openstack( 'domain create ' - '--description %(description)s ' - '--enable %(name)s' - % {'description': domain_description, 'name': domain_name} + f'--description {domain_description} ' + f'--enable {domain_name}' ) if add_clean_up: - self.addCleanup(self.openstack, 'domain delete %s' % domain_name) + self.addCleanup(self.openstack, f'domain delete {domain_name}') self.addCleanup( - self.openstack, 'domain set --disable %s' % domain_name + self.openstack, f'domain set --disable {domain_name}' ) return domain_name @@ -315,22 +295,14 @@ def _create_dummy_project(self, add_clean_up=True): project_description = data_utils.rand_name('description') self.openstack( 'project create ' - '--domain %(domain)s ' - '--description %(description)s ' - '--enable %(name)s' - % { - 'domain': self.domain_name, - 'description': project_description, - 'name': project_name, - } + f'--domain {self.domain_name} ' + f'--description {project_description} ' + f'--enable {project_name}' ) if add_clean_up: self.addCleanup( self.openstack, - 'project delete ' - '--domain %(domain)s ' - '%(name)s' - % {'domain': self.domain_name, 'name': project_name}, + f'project delete --domain {self.domain_name} {project_name}', ) return project_name @@ -339,20 +311,15 @@ def _create_dummy_region(self, parent_region=None, add_clean_up=True): description = data_utils.rand_name('description') parent_region_arg = '' if parent_region is not None: - parent_region_arg = '--parent-region %s' % parent_region + parent_region_arg = f'--parent-region {parent_region}' raw_output = self.openstack( 'region create ' - '%(parent_region_arg)s ' - '--description %(description)s ' - '%(id)s' - % { - 'parent_region_arg': parent_region_arg, - 'description': description, - 'id': region_id, - } + f'{parent_region_arg} ' + f'--description {description} ' + f'{region_id}' ) if add_clean_up: - self.addCleanup(self.openstack, 'region delete %s' % region_id) + self.addCleanup(self.openstack, f'region delete {region_id}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.REGION_FIELDS) return region_id @@ -363,20 +330,15 @@ def _create_dummy_service(self, add_clean_up=True): type_name = data_utils.rand_name('TestType') raw_output = self.openstack( 'service create ' - '--name %(name)s ' - '--description %(description)s ' + f'--name {service_name} ' + f'--description {description} ' '--enable ' - '%(type)s' - % { - 'name': service_name, - 'description': description, - 'type': type_name, - } + f'{type_name}' ) if add_clean_up: service = self.parse_show_as_object(raw_output) self.addCleanup( - self.openstack, 'service delete %s' % service['ID'] + self.openstack, 'service delete {}'.format(service['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) @@ -388,22 +350,16 @@ def _create_dummy_endpoint(self, interface='public', add_clean_up=True): endpoint_url = data_utils.rand_url() raw_output = self.openstack( 'endpoint create ' - '--region %(region)s ' + f'--region {region_id} ' '--enable ' - '%(service)s ' - '%(interface)s ' - '%(url)s' - % { - 'region': region_id, - 'service': service_name, - 'interface': interface, - 'url': endpoint_url, - } + f'{service_name} ' + f'{interface} ' + f'{endpoint_url}' ) endpoint = self.parse_show_as_object(raw_output) if add_clean_up: self.addCleanup( - self.openstack, 'endpoint delete %s' % endpoint['id'] + self.openstack, 'endpoint delete {}'.format(endpoint['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) @@ -414,15 +370,14 @@ def _create_dummy_idp(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'identity provider create ' - ' %(name)s ' - '--description %(description)s ' + f' {identity_provider} ' + f'--description {description} ' '--enable ' - % {'name': identity_provider, 'description': description} ) if add_clean_up: self.addCleanup( self.openstack, - 'identity provider delete %s' % identity_provider, + f'identity provider delete {identity_provider}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.IDENTITY_PROVIDER_FIELDS) @@ -433,16 +388,15 @@ def _create_dummy_sp(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'service provider create ' - ' %(name)s ' - '--description %(description)s ' + f' {service_provider} ' + f'--description {description} ' '--auth-url https://sp.example.com:35357 ' '--service-provider-url https://sp.example.com:5000 ' '--enable ' - % {'name': service_provider, 'description': description} ) if add_clean_up: self.addCleanup( - self.openstack, 'service provider delete %s' % service_provider + self.openstack, f'service provider delete {service_provider}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_PROVIDER_FIELDS) @@ -458,9 +412,9 @@ def _create_dummy_registered_limit(self, add_clean_up=True): } raw_output = self.openstack( 'registered limit create' - ' --service %(service_name)s' - ' --default-limit %(default_limit)s' - ' %(resource_name)s' % params, + ' --service {service_name}' + ' --default-limit {default_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -469,7 +423,7 @@ def _create_dummy_registered_limit(self, add_clean_up=True): if add_clean_up: self.addCleanup( self.openstack, - 'registered limit delete %s' % registered_limit_id, + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -486,7 +440,7 @@ def _create_dummy_limit(self, add_clean_up=True): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %s' % registered_limit_id, + f'registered limit show {registered_limit_id}', cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -495,7 +449,7 @@ def _create_dummy_limit(self, add_clean_up=True): resource_limit = 15 project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) project_id = self._extract_value_from_items('id', items) @@ -508,10 +462,10 @@ def _create_dummy_limit(self, add_clean_up=True): raw_output = self.openstack( 'limit create' - ' --project %(project_id)s' - ' --service %(service_id)s' - ' --resource-limit %(resource_limit)s' - ' %(resource_name)s' % params, + ' --project {project_id}' + ' --service {service_id}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -520,7 +474,7 @@ def _create_dummy_limit(self, add_clean_up=True): if add_clean_up: self.addCleanup( self.openstack, - 'limit delete %s' % limit_id, + f'limit delete {limit_id}', cloud=SYSTEM_CLOUD, ) diff --git a/openstackclient/tests/functional/identity/v3/test_access_rule.py b/openstackclient/tests/functional/identity/v3/test_access_rule.py new file mode 100644 index 0000000000..4f7225801f --- /dev/null +++ b/openstackclient/tests/functional/identity/v3/test_access_rule.py @@ -0,0 +1,86 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import ast +import json + +from tempest.lib.common.utils import data_utils + +from openstackclient.tests.functional.identity.v3 import common + + +class AccessRuleTests(common.IdentityTests): + ACCESS_RULE_FIELDS = [ + 'ID', + 'Service', + 'Method', + 'Path', + ] + ACCESS_RULE_LIST_HEADERS = [ + 'ID', + 'Service', + 'Method', + 'Path', + ] + + def setUp(self): + super().setUp() + + application_credential_name = data_utils.rand_name('name') + access_rules = json.dumps( + [ + { + 'method': 'GET', + 'path': '/v2.1/servers', + 'service': 'compute', + }, + { + 'method': 'GET', + 'path': '/v2.0/networks', + 'service': 'networking', + }, + ] + ) + raw_output = self.openstack( + f"application credential create {application_credential_name} " + f"--access-rules '{access_rules}'" + ) + # we immediately delete the application credential since it will leave + # the access rules around + self.openstack( + f'application credential delete {application_credential_name}' + ) + + items = self.parse_show_as_object(raw_output) + self.access_rule_ids = [ + x['id'] for x in ast.literal_eval(items['Access Rules']) + ] + self.addCleanup( + self.openstack, + 'access rule delete ' + + ' '.join([x for x in self.access_rule_ids]), + ) + + def test_access_rule(self): + # list + + raw_output = self.openstack('access rule list') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ACCESS_RULE_LIST_HEADERS) + + # show + + raw_output = self.openstack( + f'access rule show {self.access_rule_ids[0]}' + ) + items = self.parse_show(raw_output) + self.assert_show_fields(items, self.ACCESS_RULE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index 9c8b0462ff..20315c4e76 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -50,69 +50,55 @@ def test_application_credential_create(self): def _create_role_assignments(self): try: user = self.openstack( - 'configuration show -f value' ' -c auth.username' + 'configuration show -f value -c auth.username' ) except Exception: user = self.openstack( - 'configuration show -f value' ' -c auth.user_id' + 'configuration show -f value -c auth.user_id' ) try: user_domain = self.openstack( - 'configuration show -f value' ' -c auth.user_domain_name' + 'configuration show -f value -c auth.user_domain_name' ) except Exception: user_domain = self.openstack( - 'configuration show -f value' ' -c auth.user_domain_id' + 'configuration show -f value -c auth.user_domain_id' ) try: project = self.openstack( - 'configuration show -f value' ' -c auth.project_name' + 'configuration show -f value -c auth.project_name' ) except Exception: project = self.openstack( - 'configuration show -f value' ' -c auth.project_id' + 'configuration show -f value -c auth.project_id' ) try: project_domain = self.openstack( - 'configuration show -f value' ' -c auth.project_domain_name' + 'configuration show -f value -c auth.project_domain_name' ) except Exception: project_domain = self.openstack( - 'configuration show -f value' ' -c auth.project_domain_id' + 'configuration show -f value -c auth.project_domain_id' ) role1 = self._create_dummy_role() role2 = self._create_dummy_role() for role in role1, role2: self.openstack( 'role add' - ' --user %(user)s' - ' --user-domain %(user_domain)s' - ' --project %(project)s' - ' --project-domain %(project_domain)s' - ' %(role)s' - % { - 'user': user, - 'user_domain': user_domain, - 'project': project, - 'project_domain': project_domain, - 'role': role, - } + f' --user {user}' + f' --user-domain {user_domain}' + f' --project {project}' + f' --project-domain {project_domain}' + f' {role}' ) self.addCleanup( self.openstack, 'role remove' - ' --user %(user)s' - ' --user-domain %(user_domain)s' - ' --project %(project)s' - ' --project-domain %(project_domain)s' - ' %(role)s' - % { - 'user': user, - 'user_domain': user_domain, - 'project': project, - 'project_domain': project_domain, - 'role': role, - }, + f' --user {user}' + f' --user-domain {user_domain}' + f' --project {project}' + f' --project-domain {project_domain}' + f' {role}', ) return role1, role2 @@ -121,25 +107,18 @@ def test_application_credential_create_with_options(self): secret = data_utils.rand_name('secret') description = data_utils.rand_name('description') tomorrow = ( - datetime.datetime.utcnow() + datetime.timedelta(days=1) + datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) + + datetime.timedelta(days=1) ).strftime('%Y-%m-%dT%H:%M:%S%z') role1, role2 = self._create_role_assignments() raw_output = self.openstack( - 'application credential create %(name)s' - ' --secret %(secret)s' - ' --description %(description)s' - ' --expiration %(tomorrow)s' - ' --role %(role1)s' - ' --role %(role2)s' + f'application credential create {name}' + f' --secret {secret}' + f' --description {description}' + f' --expiration {tomorrow}' + f' --role {role1}' + f' --role {role2}' ' --unrestricted' - % { - 'name': name, - 'secret': secret, - 'description': description, - 'tomorrow': tomorrow, - 'role1': role1, - 'role2': role2, - } ) self.addCleanup( self.openstack, @@ -151,9 +130,7 @@ def test_application_credential_create_with_options(self): def test_application_credential_delete(self): name = data_utils.rand_name('name') self.openstack(f'application credential create {name}') - raw_output = self.openstack( - 'application credential delete ' '%(name)s' % {'name': name} - ) + raw_output = self.openstack(f'application credential delete {name}') self.assertEqual(0, len(raw_output)) def test_application_credential_list(self): @@ -170,8 +147,6 @@ def test_application_credential_show(self): self.openstack, f'application credential delete {name}', ) - raw_output = self.openstack( - 'application credential show ' '%(name)s' % {'name': name} - ) + raw_output = self.openstack(f'application credential show {name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_catalog.py b/openstackclient/tests/functional/identity/v3/test_catalog.py index 9b6df2d194..429f94ac36 100644 --- a/openstackclient/tests/functional/identity/v3/test_catalog.py +++ b/openstackclient/tests/functional/identity/v3/test_catalog.py @@ -10,35 +10,54 @@ # License for the specific language governing permissions and limitations # under the License. + from openstackclient.tests.functional.identity.v3 import common class CatalogTests(common.IdentityTests): - def test_catalog_list(self): + """Functional tests for catalog commands""" + + def test_catalog(self): + """Test catalog list and show functionality""" + # Create a test service for isolated testing + _dummy_service_name = self._create_dummy_service(add_clean_up=True) + + # list catalogs raw_output = self.openstack('catalog list') items = self.parse_listing(raw_output) self.assert_table_structure(items, ['Name', 'Type', 'Endpoints']) - def test_catalog_show(self): - """test catalog show command - - The output example: - +-----------+----------------------------------------+ - | Field | Value | - +-----------+----------------------------------------+ - | endpoints | test1 | - | | public: http://localhost:5000/v2.0 | - | | test1 | - | | internal: http://localhost:5000/v2.0 | - | | test1 | - | | admin: http://localhost:35357/v2.0 | - | | | - | id | e1e68b5ba21a43a39ff1cf58e736c3aa | - | name | keystone | - | type | identity | - +-----------+----------------------------------------+ - """ - raw_output = self.openstack('catalog show %s' % 'identity') + # Verify created service appears in catalog + service_names = [ + item.get('Name') for item in items if item.get('Name') + ] + self.assertIn( + _dummy_service_name, + service_names, + "Created dummy service should be present in catalog", + ) + + # show service (by name) + raw_output = self.openstack(f'catalog show {_dummy_service_name}') items = self.parse_show(raw_output) - # items may have multiple endpoint urls with empty key - self.assert_show_fields(items, ['endpoints', 'name', 'type', '', 'id']) + self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id']) + + # Extract the type from the dummy service + _dummy_service_type = next( + (item['type'] for item in items if 'type' in item), None + ) + + # show service (by type) + raw_output = self.openstack(f'catalog show {_dummy_service_type}') + items = self.parse_show(raw_output) + self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id']) + + # show service (non-existent) + result = self.openstack( + 'catalog show nonexistent-service-xyz', fail_ok=True + ) + self.assertEqual( + '', + result.strip(), + "Non-existent service should return empty result", + ) diff --git a/openstackclient/tests/functional/identity/v3/test_domain.py b/openstackclient/tests/functional/identity/v3/test_domain.py index 08e0e07901..867db91df9 100644 --- a/openstackclient/tests/functional/identity/v3/test_domain.py +++ b/openstackclient/tests/functional/identity/v3/test_domain.py @@ -19,12 +19,10 @@ class DomainTests(common.IdentityTests): def test_domain_create(self): domain_name = data_utils.rand_name('TestDomain') - raw_output = self.openstack('domain create %s' % domain_name) + raw_output = self.openstack(f'domain create {domain_name}') # disable domain first before deleting it - self.addCleanup(self.openstack, 'domain delete %s' % domain_name) - self.addCleanup( - self.openstack, 'domain set --disable %s' % domain_name - ) + self.addCleanup(self.openstack, f'domain delete {domain_name}') + self.addCleanup(self.openstack, f'domain set --disable {domain_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.DOMAIN_FIELDS) @@ -37,18 +35,18 @@ def test_domain_list(self): def test_domain_delete(self): domain_name = self._create_dummy_domain(add_clean_up=False) # cannot delete enabled domain, disable it first - raw_output = self.openstack('domain set --disable %s' % domain_name) + raw_output = self.openstack(f'domain set --disable {domain_name}') self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('domain delete %s' % domain_name) + raw_output = self.openstack(f'domain delete {domain_name}') self.assertEqual(0, len(raw_output)) def test_domain_multi_delete(self): domain_1 = self._create_dummy_domain(add_clean_up=False) domain_2 = self._create_dummy_domain(add_clean_up=False) # cannot delete enabled domain, disable it first - raw_output = self.openstack('domain set --disable %s' % domain_1) + raw_output = self.openstack(f'domain set --disable {domain_1}') self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('domain set --disable %s' % domain_2) + raw_output = self.openstack(f'domain set --disable {domain_2}') self.assertEqual(0, len(raw_output)) raw_output = self.openstack(f'domain delete {domain_1} {domain_2}') self.assertEqual(0, len(raw_output)) @@ -59,11 +57,11 @@ def test_domain_delete_failure(self): self.assertRaises( exceptions.CommandFailed, self.openstack, - 'domain delete %s' % domain_name, + f'domain delete {domain_name}', ) def test_domain_show(self): domain_name = self._create_dummy_domain() - raw_output = self.openstack('domain show %s' % domain_name) + raw_output = self.openstack(f'domain show {domain_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.DOMAIN_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_endpoint.py b/openstackclient/tests/functional/identity/v3/test_endpoint.py index 1fff6abeb4..0441fcb6ec 100644 --- a/openstackclient/tests/functional/identity/v3/test_endpoint.py +++ b/openstackclient/tests/functional/identity/v3/test_endpoint.py @@ -23,7 +23,7 @@ def test_endpoint_create(self): def test_endpoint_delete(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) - raw_output = self.openstack('endpoint delete %s' % endpoint_id) + raw_output = self.openstack(f'endpoint delete {endpoint_id}') self.assertEqual(0, len(raw_output)) def test_endpoint_multi_delete(self): @@ -45,20 +45,15 @@ def test_endpoint_list_filter(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) project_id = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'endpoint add project ' - '%(endpoint_id)s ' - '%(project_id)s' - % {'project_id': project_id, 'endpoint_id': endpoint_id} + f'endpoint add project {endpoint_id} {project_id}' ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'endpoint list --endpoint %s' % endpoint_id - ) + raw_output = self.openstack(f'endpoint list --endpoint {endpoint_id}') self.assertIn(project_id, raw_output) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ENDPOINT_LIST_PROJECT_HEADERS) - raw_output = self.openstack('endpoint list --project %s' % project_id) + raw_output = self.openstack(f'endpoint list --project {project_id}') self.assertIn(endpoint_id, raw_output) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS) @@ -68,18 +63,17 @@ def test_endpoint_set(self): new_endpoint_url = data_utils.rand_url() raw_output = self.openstack( 'endpoint set ' - '--interface %(interface)s ' - '--url %(url)s ' + '--interface {interface} ' + '--url {url} ' '--disable ' - '%(endpoint_id)s' - % { - 'interface': 'admin', - 'url': new_endpoint_url, - 'endpoint_id': endpoint_id, - } + '{endpoint_id}'.format( + interface='admin', + url=new_endpoint_url, + endpoint_id=endpoint_id, + ) ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('endpoint show %s' % endpoint_id) + raw_output = self.openstack(f'endpoint show {endpoint_id}') endpoint = self.parse_show_as_object(raw_output) self.assertEqual('admin', endpoint['interface']) self.assertEqual(new_endpoint_url, endpoint['url']) @@ -87,7 +81,7 @@ def test_endpoint_set(self): def test_endpoint_show(self): endpoint_id = self._create_dummy_endpoint() - raw_output = self.openstack('endpoint show %s' % endpoint_id) + raw_output = self.openstack(f'endpoint show {endpoint_id}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) @@ -95,17 +89,11 @@ def test_endpoint_add_remove_project(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) project_id = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'endpoint add project ' - '%(endpoint_id)s ' - '%(project_id)s' - % {'project_id': project_id, 'endpoint_id': endpoint_id} + f'endpoint add project {endpoint_id} {project_id}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'endpoint remove project ' - '%(endpoint_id)s ' - '%(project_id)s' - % {'project_id': project_id, 'endpoint_id': endpoint_id} + f'endpoint remove project {endpoint_id} {project_id}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_group.py b/openstackclient/tests/functional/identity/v3/test_group.py index 95d74cb76c..a2e41d813a 100644 --- a/openstackclient/tests/functional/identity/v3/test_group.py +++ b/openstackclient/tests/functional/identity/v3/test_group.py @@ -28,9 +28,7 @@ def test_group_list(self): def test_group_list_with_domain(self): group_name = self._create_dummy_group() - raw_output = self.openstack( - 'group list --domain %s' % self.domain_name - ) + raw_output = self.openstack(f'group list --domain {self.domain_name}') items = self.parse_listing(raw_output) self.assert_table_structure(items, common.BASIC_LIST_HEADERS) self.assertIn(group_name, raw_output) @@ -38,18 +36,14 @@ def test_group_list_with_domain(self): def test_group_delete(self): group_name = self._create_dummy_group(add_clean_up=False) raw_output = self.openstack( - 'group delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': group_name} + f'group delete --domain {self.domain_name} {group_name}' ) self.assertEqual(0, len(raw_output)) def test_group_show(self): group_name = self._create_dummy_group() raw_output = self.openstack( - 'group show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': group_name} + f'group show --domain {self.domain_name} {group_name}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.GROUP_FIELDS) @@ -59,34 +53,22 @@ def test_group_set(self): new_group_name = data_utils.rand_name('NewTestGroup') raw_output = self.openstack( 'group set ' - '--domain %(domain)s ' - '--name %(new_group)s ' - '%(group)s' - % { - 'domain': self.domain_name, - 'new_group': new_group_name, - 'group': group_name, - } + f'--domain {self.domain_name} ' + f'--name {new_group_name} ' + f'{group_name}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'group show ' - '--domain %(domain)s ' - '%(group)s' % {'domain': self.domain_name, 'group': new_group_name} + f'group show --domain {self.domain_name} {new_group_name}' ) group = self.parse_show_as_object(raw_output) self.assertEqual(new_group_name, group['name']) # reset group name to make sure it will be cleaned up raw_output = self.openstack( 'group set ' - '--domain %(domain)s ' - '--name %(new_group)s ' - '%(group)s' - % { - 'domain': self.domain_name, - 'new_group': group_name, - 'group': new_group_name, - } + f'--domain {self.domain_name} ' + f'--name {group_name} ' + f'{new_group_name}' ) self.assertEqual(0, len(raw_output)) @@ -95,28 +77,16 @@ def test_group_add_user(self): username = self._create_dummy_user() raw_output = self.openstack( 'group add user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.addCleanup( self.openstack, 'group remove user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - }, + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}', ) self.assertOutput('', raw_output) @@ -125,45 +95,26 @@ def test_group_contains_user(self): username = self._create_dummy_user() raw_output = self.openstack( 'group add user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.addCleanup( self.openstack, 'group remove user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - }, + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}', ) self.assertOutput('', raw_output) raw_output = self.openstack( 'group contains user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.assertEqual( - '%(user)s in group %(group)s\n' - % {'user': username, 'group': group_name}, + f'{username} in group {group_name}\n', raw_output, ) @@ -172,27 +123,15 @@ def test_group_remove_user(self): username = self._create_dummy_user() add_raw_output = self.openstack( 'group add user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) remove_raw_output = self.openstack( 'group remove user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.assertOutput('', add_raw_output) self.assertOutput('', remove_raw_output) diff --git a/openstackclient/tests/functional/identity/v3/test_idp.py b/openstackclient/tests/functional/identity/v3/test_idp.py index 05f3ee84ee..c9ef01d0f1 100644 --- a/openstackclient/tests/functional/identity/v3/test_idp.py +++ b/openstackclient/tests/functional/identity/v3/test_idp.py @@ -24,7 +24,7 @@ def test_idp_create(self): def test_idp_delete(self): identity_provider = self._create_dummy_idp(add_clean_up=False) raw_output = self.openstack( - 'identity provider delete %s' % identity_provider + f'identity provider delete {identity_provider}' ) self.assertEqual(0, len(raw_output)) @@ -39,7 +39,7 @@ def test_idp_multi_delete(self): def test_idp_show(self): identity_provider = self._create_dummy_idp(add_clean_up=True) raw_output = self.openstack( - 'identity provider show %s' % identity_provider + f'identity provider show {identity_provider}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.IDENTITY_PROVIDER_FIELDS) @@ -54,17 +54,13 @@ def test_idp_set(self): identity_provider = self._create_dummy_idp(add_clean_up=True) new_remoteid = data_utils.rand_name('newRemoteId') raw_output = self.openstack( - 'identity provider set ' - '%(identity-provider)s ' - '--remote-id %(remote-id)s ' - % { - 'identity-provider': identity_provider, - 'remote-id': new_remoteid, - } + f'identity provider set ' + f'{identity_provider} ' + f'--remote-id {new_remoteid}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'identity provider show %s' % identity_provider + f'identity provider show {identity_provider}' ) updated_value = self.parse_show_as_object(raw_output) self.assertIn(new_remoteid, updated_value['remote_ids']) diff --git a/openstackclient/tests/functional/identity/v3/test_limit.py b/openstackclient/tests/functional/identity/v3/test_limit.py index d096762d2e..8c0bbcd6a9 100644 --- a/openstackclient/tests/functional/identity/v3/test_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_limit.py @@ -23,19 +23,19 @@ class LimitTestCase(common.IdentityTests): def test_limit_create_with_service_name(self): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %s' % registered_limit_id, + f'registered limit show {registered_limit_id}', cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) service_id = self._extract_value_from_items('service_id', items) resource_name = self._extract_value_from_items('resource_name', items) - raw_output = self.openstack('service show %s' % service_id) + raw_output = self.openstack(f'service show {service_id}') items = self.parse_show(raw_output) - service_name = self._extract_value_from_items('Name', items) + service_name = self._extract_value_from_items('name', items) project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) project_id = self._extract_value_from_items('id', items) @@ -47,16 +47,16 @@ def test_limit_create_with_service_name(self): } raw_output = self.openstack( 'limit create' - ' --project %(project_id)s' - ' --service %(service_name)s' - ' --resource-limit %(resource_limit)s' - ' %(resource_name)s' % params, + ' --project {project_id}' + ' --service {service_name}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) limit_id = self._extract_value_from_items('id', items) self.addCleanup( - self.openstack, 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -64,16 +64,16 @@ def test_limit_create_with_service_name(self): def test_limit_create_with_project_name(self): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %s' % registered_limit_id, + f'registered limit show {registered_limit_id}', cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) service_id = self._extract_value_from_items('service_id', items) resource_name = self._extract_value_from_items('resource_name', items) - raw_output = self.openstack('service show %s' % service_id) + raw_output = self.openstack(f'service show {service_id}') items = self.parse_show(raw_output) - service_name = self._extract_value_from_items('Name', items) + service_name = self._extract_value_from_items('name', items) project_name = self._create_dummy_project() @@ -85,16 +85,16 @@ def test_limit_create_with_project_name(self): } raw_output = self.openstack( 'limit create' - ' --project %(project_name)s' - ' --service %(service_name)s' - ' --resource-limit %(resource_limit)s' - ' %(resource_name)s' % params, + ' --project {project_name}' + ' --service {service_name}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) limit_id = self._extract_value_from_items('id', items) self.addCleanup( - self.openstack, 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -117,8 +117,8 @@ def test_limit_create_with_options(self): raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --region %(region_id)s' % params, + ' {registered_limit_id}' + ' --region {region_id}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -126,7 +126,7 @@ def test_limit_create_with_options(self): resource_name = self._extract_value_from_items('resource_name', items) project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) project_id = self._extract_value_from_items('id', items) description = data_utils.arbitrary_string() @@ -141,18 +141,18 @@ def test_limit_create_with_options(self): } raw_output = self.openstack( 'limit create' - ' --project %(project_id)s' - ' --service %(service_id)s' - ' --resource-limit %(resource_limit)s' - ' --region %(region_id)s' - ' --description %(description)s' - ' %(resource_name)s' % params, + ' --project {project_id}' + ' --service {service_id}' + ' --resource-limit {resource_limit}' + ' --region {region_id}' + ' --description {description}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) limit_id = self._extract_value_from_items('id', items) self.addCleanup( - self.openstack, 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -160,7 +160,7 @@ def test_limit_create_with_options(self): def test_limit_show(self): limit_id = self._create_dummy_limit() raw_output = self.openstack( - 'limit show %s' % limit_id, cloud=SYSTEM_CLOUD + f'limit show {limit_id}', cloud=SYSTEM_CLOUD ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -174,9 +174,9 @@ def test_limit_set_description(self): } raw_output = self.openstack( - 'limit set' - ' --description %(description)s' - ' %(limit_id)s' % params, + 'limit set --description {description} {limit_id}'.format( + **params + ), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -188,9 +188,9 @@ def test_limit_set_resource_limit(self): params = {'resource_limit': 5, 'limit_id': limit_id} raw_output = self.openstack( - 'limit set' - ' --resource-limit %(resource_limit)s' - ' %(limit_id)s' % params, + 'limit set --resource-limit {resource_limit} {limit_id}'.format( + **params + ), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -205,6 +205,6 @@ def test_limit_list(self): def test_limit_delete(self): limit_id = self._create_dummy_limit(add_clean_up=False) raw_output = self.openstack( - 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_project.py b/openstackclient/tests/functional/identity/v3/test_project.py index d91ba87a88..7a66c18518 100644 --- a/openstackclient/tests/functional/identity/v3/test_project.py +++ b/openstackclient/tests/functional/identity/v3/test_project.py @@ -21,23 +21,16 @@ def test_project_create(self): description = data_utils.rand_name('description') raw_output = self.openstack( 'project create ' - '--domain %(domain)s ' - '--description %(description)s ' + f'--domain {self.domain_name} ' + f'--description {description} ' '--enable ' '--property k1=v1 ' '--property k2=v2 ' - '%(name)s' - % { - 'domain': self.domain_name, - 'description': description, - 'name': project_name, - } + f'{project_name}' ) self.addCleanup( self.openstack, - 'project delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': project_name}, + f'project delete --domain {self.domain_name} {project_name}', ) items = self.parse_show(raw_output) show_fields = list(self.PROJECT_FIELDS) @@ -50,9 +43,7 @@ def test_project_create(self): def test_project_delete(self): project_name = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'project delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': project_name} + f'project delete --domain {self.domain_name} {project_name}' ) self.assertEqual(0, len(raw_output)) @@ -64,7 +55,7 @@ def test_project_list(self): def test_project_list_with_domain(self): project_name = self._create_dummy_project() raw_output = self.openstack( - 'project list --domain %s' % self.domain_name + f'project list --domain {self.domain_name}' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, common.BASIC_LIST_HEADERS) @@ -76,17 +67,15 @@ def test_project_set(self): new_project_name = data_utils.rand_name('NewTestProject') raw_output = self.openstack( 'project set ' - '--name %(new_name)s ' + f'--name {new_project_name} ' '--disable ' '--property k0=v0 ' - '%(name)s' % {'new_name': new_project_name, 'name': project_name} + f'{project_name}' ) self.assertEqual(0, len(raw_output)) # check project details raw_output = self.openstack( - 'project show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': new_project_name} + f'project show --domain {self.domain_name} {new_project_name}' ) items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) @@ -98,18 +87,12 @@ def test_project_set(self): self.assertEqual('v0', project['k0']) # reset project to make sure it will be cleaned up self.openstack( - 'project set ' - '--name %(new_name)s ' - '--enable ' - '%(name)s' % {'new_name': project_name, 'name': new_project_name} + f'project set --name {project_name} --enable {new_project_name}' ) def test_project_show(self): raw_output = self.openstack( - 'project show ' - '--domain %(domain)s ' - '%(name)s' - % {'domain': self.domain_name, 'name': self.project_name} + f'project show --domain {self.domain_name} {self.project_name}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.PROJECT_FIELDS) @@ -118,9 +101,8 @@ def test_project_show_with_parents_children(self): output = self.openstack( 'project show ' '--parents --children ' - '--domain %(domain)s ' - '%(name)s' - % {'domain': self.domain_name, 'name': self.project_name}, + f'--domain {self.domain_name} ' + f'{self.project_name}', parse_output=True, ) for attr_name in self.PROJECT_FIELDS + ['parents', 'subtree']: diff --git a/openstackclient/tests/functional/identity/v3/test_region.py b/openstackclient/tests/functional/identity/v3/test_region.py index 48ca837814..49471002ad 100644 --- a/openstackclient/tests/functional/identity/v3/test_region.py +++ b/openstackclient/tests/functional/identity/v3/test_region.py @@ -23,7 +23,7 @@ def test_region_create_with_parent_region(self): def test_region_delete(self): region_id = self._create_dummy_region(add_clean_up=False) - raw_output = self.openstack('region delete %s' % region_id) + raw_output = self.openstack(f'region delete {region_id}') self.assertEqual(0, len(raw_output)) def test_region_multi_delete(self): @@ -43,27 +43,24 @@ def test_region_set(self): new_parent_region_id = self._create_dummy_region() region_id = self._create_dummy_region(parent_region_id) # check region details - raw_output = self.openstack('region show %s' % region_id) + raw_output = self.openstack(f'region show {region_id}') region = self.parse_show_as_object(raw_output) self.assertEqual(parent_region_id, region['parent_region']) self.assertEqual(region_id, region['region']) # update parent-region raw_output = self.openstack( - 'region set ' - '--parent-region %(parent_region)s ' - '%(region)s' - % {'parent_region': new_parent_region_id, 'region': region_id} + f'region set --parent-region {new_parent_region_id} {region_id}' ) self.assertEqual(0, len(raw_output)) # check updated region details - raw_output = self.openstack('region show %s' % region_id) + raw_output = self.openstack(f'region show {region_id}') region = self.parse_show_as_object(raw_output) self.assertEqual(new_parent_region_id, region['parent_region']) self.assertEqual(region_id, region['region']) def test_region_show(self): region_id = self._create_dummy_region() - raw_output = self.openstack('region show %s' % region_id) + raw_output = self.openstack(f'region show {region_id}') region = self.parse_show_as_object(raw_output) self.assertEqual(region_id, region['region']) self.assertEqual('None', region['parent_region']) diff --git a/openstackclient/tests/functional/identity/v3/test_registered_limit.py b/openstackclient/tests/functional/identity/v3/test_registered_limit.py index ba7f8cbd4d..54ee7f4f8f 100644 --- a/openstackclient/tests/functional/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_registered_limit.py @@ -25,31 +25,26 @@ def test_registered_limit_create_with_service_name(self): def test_registered_limit_create_with_service_id(self): service_name = self._create_dummy_service() - raw_output = self.openstack( - 'service show' ' %(service_name)s' % {'service_name': service_name} - ) + raw_output = self.openstack(f'service show {service_name}') service_items = self.parse_show(raw_output) - service_id = self._extract_value_from_items('ID', service_items) + service_id = self._extract_value_from_items('id', service_items) raw_output = self.openstack( 'registered limit create' - ' --service %(service_id)s' - ' --default-limit %(default_limit)s' - ' %(resource_name)s' - % { - 'service_id': service_id, - 'default_limit': 10, - 'resource_name': 'cores', - }, + ' --service {service_id}' + ' --default-limit {default_limit}' + ' {resource_name}'.format( + service_id=service_id, + default_limit=10, + resource_name='cores', + ), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) registered_limit_id = self._extract_value_from_items('id', items) self.addCleanup( self.openstack, - 'registered limit delete' - ' %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id}, + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -68,19 +63,18 @@ def test_registered_limit_create_with_options(self): raw_output = self.openstack( 'registered limit create' - ' --description \'%(description)s\'' - ' --region %(region_id)s' - ' --service %(service_name)s' - ' --default-limit %(default_limit)s' - ' %(resource_name)s' % params, + ' --description \'{description}\'' + ' --region {region_id}' + ' --service {service_name}' + ' --default-limit {default_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) registered_limit_id = self._extract_value_from_items('id', items) self.addCleanup( self.openstack, - 'registered limit delete %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id}, + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -89,8 +83,7 @@ def test_registered_limit_create_with_options(self): def test_registered_limit_show(self): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id} + f'registered limit show {registered_limit_id}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS) @@ -105,8 +98,8 @@ def test_registered_limit_set_region_id(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --region %(region_id)s' % params, + ' {registered_limit_id}' + ' --region {region_id}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -120,8 +113,8 @@ def test_registered_limit_set_description(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --description \'%(description)s\'' % params, + ' {registered_limit_id}' + ' --description \'{description}\''.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -136,8 +129,8 @@ def test_registered_limit_set_service(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --service %(service)s' % params, + ' {registered_limit_id}' + ' --service {service}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -151,8 +144,8 @@ def test_registered_limit_set_default_limit(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --default-limit %(default_limit)s' % params, + ' {registered_limit_id}' + ' --default-limit {default_limit}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -167,8 +160,8 @@ def test_registered_limit_set_resource_name(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --resource-name %(resource_name)s' % params, + ' {registered_limit_id}' + ' --resource-name {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -185,9 +178,7 @@ def test_registered_limit_delete(self): add_clean_up=False ) raw_output = self.openstack( - 'registered limit delete' - ' %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id}, + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py index 9a1aa648f6..3237c0bfb4 100644 --- a/openstackclient/tests/functional/identity/v3/test_role.py +++ b/openstackclient/tests/functional/identity/v3/test_role.py @@ -23,12 +23,10 @@ def test_role_create_with_description(self): role_name = data_utils.rand_name('TestRole') description = data_utils.rand_name('description') raw_output = self.openstack( - 'role create ' - '--description %(description)s ' - '%(name)s' % {'description': description, 'name': role_name} + f'role create --description {description} {role_name}' ) role = self.parse_show_as_object(raw_output) - self.addCleanup(self.openstack, 'role delete %s' % role['id']) + self.addCleanup(self.openstack, 'role delete {}'.format(role['id'])) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) self.assertEqual(description, role['description']) @@ -36,7 +34,7 @@ def test_role_create_with_description(self): def test_role_delete(self): role_name = self._create_dummy_role(add_clean_up=False) - raw_output = self.openstack('role delete %s' % role_name) + raw_output = self.openstack(f'role delete {role_name}') self.assertEqual(0, len(raw_output)) def test_role_list(self): @@ -47,7 +45,7 @@ def test_role_list(self): def test_role_show(self): role_name = self._create_dummy_role() - raw_output = self.openstack('role show %s' % role_name) + raw_output = self.openstack(f'role show {role_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) @@ -58,7 +56,7 @@ def test_role_set(self): f'role set --name {new_role_name} {role_name}' ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('role show %s' % new_role_name) + raw_output = self.openstack(f'role show {new_role_name}') role = self.parse_show_as_object(raw_output) self.assertEqual(new_role_name, role['name']) @@ -69,7 +67,7 @@ def test_role_set_description(self): f'role set --description {description} {role_name}' ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('role show %s' % role_name) + raw_output = self.openstack(f'role show {role_name}') role = self.parse_show_as_object(raw_output) self.assertEqual(description, role['description']) @@ -78,34 +76,44 @@ def test_role_add(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}', + ) + self.assertEqual(0, len(raw_output)) + + def test_role_add_inherited(self): + role_name = self._create_dummy_role() + username = self._create_dummy_user() + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + '--inherited ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + '--inherited ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) @@ -114,33 +122,19 @@ def test_role_remove(self): username = self._create_dummy_user() add_raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}' ) remove_raw_output = self.openstack( 'role remove ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}' ) self.assertEqual(0, len(add_raw_output)) self.assertEqual(0, len(remove_raw_output)) @@ -165,15 +159,15 @@ def test_implied_role_create(self): implied_role_name = self._create_dummy_role() self.openstack( 'implied role create ' - '--implied-role %(implied_role)s ' - '%(role)s' % {'implied_role': implied_role_name, 'role': role_name} + f'--implied-role {implied_role_name} ' + f'{role_name}' ) def test_implied_role_delete(self): implied_role_name, role_name = self._create_dummy_implied_role() raw_output = self.openstack( 'implied role delete ' - '--implied-role %(implied_role)s ' - '%(role)s' % {'implied_role': implied_role_name, 'role': role_name} + f'--implied-role {implied_role_name} ' + f'{role_name}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py index a3927e7e1e..1255841afe 100644 --- a/openstackclient/tests/functional/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -24,45 +24,25 @@ def test_role_assignment_list_user_role_system(self): username = self._create_dummy_user() system = 'all' raw_output = self.openstack( - 'role add ' - '--user %(user)s ' - '--system %(system)s ' - '%(role)s' - % { - 'user': username, - 'system': system, - 'role': role_name, - } + f'role add --user {username} --system {system} {role_name}' ) self.addCleanup( self.openstack, - 'role remove ' - '--user %(user)s ' - '--system %(system)s ' - '%(role)s' - % { - 'user': username, - 'system': system, - 'role': role_name, - }, + f'role remove --user {username} --system {system} {role_name}', ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'role assignment list ' '--user %(user)s ' % {'user': username} - ) + raw_output = self.openstack(f'role assignment list --user {username} ') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) raw_output = self.openstack( - 'role assignment list ' '--role %(role)s ' % {'role': role_name} + f'role assignment list --role {role_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) - raw_output = self.openstack( - 'role assignment list ' '--system %(system)s ' % {'system': system} - ) + raw_output = self.openstack(f'role assignment list --system {system} ') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -70,116 +50,250 @@ def test_role_assignment_list_group(self): role_name = self._create_dummy_role() group = self._create_dummy_group() system = 'all' + raw_output = self.openstack( + f'role add --group {group} --system {system} {role_name}' + ) + self.addCleanup( + self.openstack, + f'role remove --group {group} --system {system} {role_name}', + ) + self.assertEqual(0, len(raw_output)) + raw_output = self.openstack(f'role assignment list --group {group} ') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_group_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + group_name = 'group_name' + self.openstack(f'group create --domain {domain_name_A} {group_name}') + self.addCleanup( + self.openstack, + f'group delete --domain {domain_name_A} {group_name}', + ) + self.openstack(f'group create --domain {domain_name_B} {group_name}') + self.addCleanup( + self.openstack, + f'group delete --domain {domain_name_B} {group_name}', + ) raw_output = self.openstack( 'role add ' - '--group %(group)s ' - '--system %(system)s ' - '%(role)s' - % { - 'group': group, - 'system': system, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--group {group_name} --group-domain {domain_name_A} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--group %(group)s ' - '--system %(system)s ' - '%(role)s' - % { - 'group': group, - 'system': system, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--group {group_name} --group-domain {domain_name_A} ' + f'{role_name}', ) - self.assertEqual(0, len(raw_output)) + self.assertEqual('', raw_output.strip()) raw_output = self.openstack( - 'role assignment list ' '--group %(group)s ' % {'group': group} + f'role assignment list ' + f'--group {group_name} --group-domain {domain_name_A} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--group {group_name} --group-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) def test_role_assignment_list_domain(self): role_name = self._create_dummy_role() username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--domain %(domain)s ' - '--user %(user)s ' - '%(role)s' - % { - 'domain': self.domain_name, - 'user': username, - 'role': role_name, - } + f'--domain {self.domain_name} ' + f'--user {username} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--domain %(domain)s ' - '--user %(user)s ' - '%(role)s' - % { - 'domain': self.domain_name, - 'user': username, - 'role': role_name, - }, + f'--domain {self.domain_name} ' + f'--user {username} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' - '--domain %(domain)s ' % {'domain': self.domain_name} + f'role assignment list --domain {self.domain_name} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_user_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + username = 'username' + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + self.openstack(f'user create --domain {domain_name_B} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_B} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--user {username} --user-domain {domain_name_A} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--user {username} --user-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + + def test_role_assignment_list_role_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = 'role_name' + username = 'username' + self.openstack(f'role create --domain {domain_name_A} {role_name}') + self.addCleanup( + self.openstack, f'role delete --domain {domain_name_A} {role_name}' + ) + self.openstack(f'role create --domain {domain_name_B} {role_name}') + self.addCleanup( + self.openstack, f'role delete --domain {domain_name_B} {role_name}' + ) + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--user {username} --domain {domain_name_A} ' + f'--role-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--user {username} --domain {domain_name_A} ' + f'--role-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--role {role_name} --role-domain {domain_name_A}' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--role {role_name} --role-domain {domain_name_B}' + ) + items = self.parse_listing(raw_output) + self.assertEqual('', raw_output.strip()) def test_role_assignment_list_project(self): role_name = self._create_dummy_role() username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' - '--project %(project)s ' % {'project': self.project_name} + f'role assignment list --project {self.project_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_project_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + project_name = 'project_name' + username = 'username' + self.openstack( + f'project create --domain {domain_name_A} {project_name}' + ) + self.addCleanup( + self.openstack, + f'project delete --domain {domain_name_A} {project_name}', + ) + self.openstack( + f'project create --domain {domain_name_B} {project_name}' + ) + self.addCleanup( + self.openstack, + f'project delete --domain {domain_name_B} {project_name}', + ) + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--project {project_name} --project-domain {domain_name_A} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {project_name} --project-domain {domain_name_A} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--project {project_name} --project-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--project {project_name} --project-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_effective(self): - raw_output = self.openstack('role assignment list ' '--effective') + raw_output = self.openstack('role assignment list --effective') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) def test_role_assignment_list_auth_user(self): - raw_output = self.openstack('role assignment list ' '--auth-user') + raw_output = self.openstack('role assignment list --auth-user') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) def test_role_assignment_list_auth_project(self): - raw_output = self.openstack('role assignment list ' '--auth-project') + raw_output = self.openstack('role assignment list --auth-project') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -188,15 +302,18 @@ def test_role_assignment_list_inherited(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' + f'--project {self.project_name} ' + f'--user {username} ' + '--inherited ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--user {username} ' '--inherited ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'{role_name}', ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_service.py b/openstackclient/tests/functional/identity/v3/test_service.py index 76009bf48b..7e102bacb7 100644 --- a/openstackclient/tests/functional/identity/v3/test_service.py +++ b/openstackclient/tests/functional/identity/v3/test_service.py @@ -21,7 +21,7 @@ def test_service_create(self): def test_service_delete(self): service_name = self._create_dummy_service(add_clean_up=False) - raw_output = self.openstack('service delete %s' % service_name) + raw_output = self.openstack(f'service delete {service_name}') self.assertEqual(0, len(raw_output)) def test_service_multi_delete(self): @@ -44,29 +44,23 @@ def test_service_set(self): new_service_type = data_utils.rand_name('NewTestType') raw_output = self.openstack( 'service set ' - '--type %(type)s ' - '--name %(name)s ' - '--description %(description)s ' + f'--type {new_service_type} ' + f'--name {new_service_name} ' + f'--description {new_service_description} ' '--disable ' - '%(service)s' - % { - 'type': new_service_type, - 'name': new_service_name, - 'description': new_service_description, - 'service': service_name, - } + f'{service_name}' ) self.assertEqual(0, len(raw_output)) # get service details - raw_output = self.openstack('service show %s' % new_service_name) + raw_output = self.openstack(f'service show {new_service_name}') # assert service details service = self.parse_show_as_object(raw_output) - self.assertEqual(new_service_type, service['Type']) - self.assertEqual(new_service_name, service['Name']) - self.assertEqual(new_service_description, service['Description']) + self.assertEqual(new_service_type, service['type']) + self.assertEqual(new_service_name, service['name']) + self.assertEqual(new_service_description, service['description']) def test_service_show(self): service_name = self._create_dummy_service() - raw_output = self.openstack('service show %s' % service_name) + raw_output = self.openstack(f'service show {service_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_service_provider.py b/openstackclient/tests/functional/identity/v3/test_service_provider.py index b7ab14bc43..330f938c8b 100644 --- a/openstackclient/tests/functional/identity/v3/test_service_provider.py +++ b/openstackclient/tests/functional/identity/v3/test_service_provider.py @@ -24,7 +24,7 @@ def test_sp_create(self): def test_sp_delete(self): service_provider = self._create_dummy_sp(add_clean_up=False) raw_output = self.openstack( - 'service provider delete %s' % service_provider + f'service provider delete {service_provider}' ) self.assertEqual(0, len(raw_output)) @@ -37,7 +37,7 @@ def test_sp_multi_delete(self): def test_sp_show(self): service_provider = self._create_dummy_sp(add_clean_up=True) raw_output = self.openstack( - 'service provider show %s' % service_provider + f'service provider show {service_provider}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_PROVIDER_FIELDS) @@ -52,17 +52,9 @@ def test_sp_set(self): service_provider = self._create_dummy_sp(add_clean_up=True) new_description = data_utils.rand_name('newDescription') raw_output = self.openstack( - 'service provider set ' - '%(service-provider)s ' - '--description %(description)s ' - % { - 'service-provider': service_provider, - 'description': new_description, - } - ) - self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'service provider show %s' % service_provider + f'service provider set ' + f'{service_provider} ' + f'--description {new_description}' ) updated_value = self.parse_show_as_object(raw_output) - self.assertIn(new_description, updated_value['description']) + self.assertEqual(new_description, updated_value.get('description')) diff --git a/openstackclient/tests/functional/identity/v3/test_user.py b/openstackclient/tests/functional/identity/v3/test_user.py index 917da4a3f6..dd56293e63 100644 --- a/openstackclient/tests/functional/identity/v3/test_user.py +++ b/openstackclient/tests/functional/identity/v3/test_user.py @@ -22,9 +22,7 @@ def test_user_create(self): def test_user_delete(self): username = self._create_dummy_user(add_clean_up=False) raw_output = self.openstack( - 'user delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + f'user delete --domain {self.domain_name} {username}' ) self.assertEqual(0, len(raw_output)) @@ -36,25 +34,19 @@ def test_user_list(self): def test_user_set(self): username = self._create_dummy_user() raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + f'user show --domain {self.domain_name} {username}' ) user = self.parse_show_as_object(raw_output) new_username = data_utils.rand_name('NewTestUser') new_email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( - 'user set ' - '--email %(email)s ' - '--name %(new_name)s ' - '%(id)s' - % {'email': new_email, 'new_name': new_username, 'id': user['id']} + 'user set --email {email} --name {new_name} {id}'.format( + email=new_email, new_name=new_username, id=user['id'] + ) ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': new_username} + f'user show --domain {self.domain_name} {new_username}' ) updated_user = self.parse_show_as_object(raw_output) self.assertEqual(user['id'], updated_user['id']) @@ -65,36 +57,29 @@ def test_user_set_default_project_id(self): project_name = self._create_dummy_project() # get original user details raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + f'user show --domain {self.domain_name} {username}' ) user = self.parse_show_as_object(raw_output) # update user raw_output = self.openstack( 'user set ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '%(id)s' - % { - 'project': project_name, - 'project_domain': self.domain_name, - 'id': user['id'], - } + '--project {project} ' + '--project-domain {project_domain} ' + '{id}'.format( + project=project_name, + project_domain=self.domain_name, + id=user['id'], + ) ) self.assertEqual(0, len(raw_output)) # get updated user details raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + f'user show --domain {self.domain_name} {username}' ) updated_user = self.parse_show_as_object(raw_output) # get project details raw_output = self.openstack( - 'project show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': project_name} + f'project show --domain {self.domain_name} {project_name}' ) project = self.parse_show_as_object(raw_output) # check updated user details @@ -104,9 +89,7 @@ def test_user_set_default_project_id(self): def test_user_show(self): username = self._create_dummy_user() raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + f'user show --domain {self.domain_name} {username}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) diff --git a/openstackclient/tests/functional/image/v2/test_cache.py b/openstackclient/tests/functional/image/v2/test_cache.py new file mode 100644 index 0000000000..58245e5ca8 --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_cache.py @@ -0,0 +1,54 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from openstackclient.tests.functional.image import base + + +class CacheTests(base.BaseImageTests): + """Functional tests for Cache commands""" + + def test_cached_image(self): + """Test cached image operations including queue and clear""" + # Create test image + name = uuid.uuid4().hex + output = self.openstack( + f'image create {name}', + parse_output=True, + ) + image_id = output["id"] + self.assertOutput(name, output['name']) + + # Register cleanup for created image + self.addCleanup( + self.openstack, 'cached image delete ' + image_id, fail_ok=True + ) + self.addCleanup(self.openstack, 'image delete ' + image_id) + + # Queue image for caching + self.openstack('cached image queue ' + image_id) + + # Verify queuing worked + cache_output = self.openstack('cached image list', parse_output=True) + self.assertIsInstance(cache_output, list) + image_ids = [img['ID'] for img in cache_output] + self.assertIn(image_id, image_ids) + + # Clear cached images + self.openstack('cached image clear') + + # Verify clearing worked + output = self.openstack('cached image list', parse_output=True) + if output: + image_ids = [img['ID'] for img in output] + self.assertNotIn(image_id, image_ids) diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py index 93251ea53b..828488c42c 100644 --- a/openstackclient/tests/functional/image/v2/test_image.py +++ b/openstackclient/tests/functional/image/v2/test_image.py @@ -30,9 +30,7 @@ def setUp(self): self.image_tag = 'my_tag' self.image_tag1 = 'random' output = self.openstack( - 'image create --tag {tag} {name}'.format( - tag=self.image_tag, name=self.name - ), + f'image create --tag {self.image_tag} {self.name}', parse_output=True, ) self.image_id = output["id"] @@ -220,17 +218,39 @@ def test_image_members(self): 'image remove project ' + self.name + ' ' + my_project_id ) - # else: - # # Test not shared - # self.assertRaises( - # image_exceptions.HTTPForbidden, - # self.openstack, - # 'image add project ' + - # self.name + ' ' + - # my_project_id - # ) - # self.openstack( - # 'image set ' + - # '--share ' + - # self.name - # ) + def test_image_hidden(self): + # Test image is shown in list + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertIn( + self.name, + [img['Name'] for img in output], + ) + + # Hide the image and test image not show in the list + self.openstack('image set ' + '--hidden ' + self.name) + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertNotIn(self.name, [img['Name'] for img in output]) + + # Test image show in the list with flag + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertNotIn(self.name, [img['Name'] for img in output]) + + # Unhide the image and test image is again visible in regular list + self.openstack('image set ' + '--unhidden ' + self.name) + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertIn( + self.name, + [img['Name'] for img in output], + ) diff --git a/openstackclient/tests/functional/image/v2/test_metadef_objects.py b/openstackclient/tests/functional/image/v2/test_metadef_objects.py new file mode 100644 index 0000000000..5216c933fd --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_metadef_objects.py @@ -0,0 +1,69 @@ +# 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 openstackclient.tests.functional import base + + +class MetadefObjectTests(base.TestCase): + def setUp(self): + super().setUp() + self.obj_name = self.getUniqueString('metadef-obj') + self.ns_name = self.getUniqueString('metadef-ns') + self.openstack(f"image metadef namespace create {self.ns_name}") + self.addCleanup( + lambda: self.openstack( + f"image metadef namespace delete {self.ns_name}" + ) + ) + + def test_metadef_objects(self): + # CREATE + created = self.openstack( + ( + "image metadef object create " + f"--namespace {self.ns_name} " + f"{self.obj_name}" + ), + parse_output=True, + ) + self.addCleanup( + lambda: self.openstack( + f"image metadef object delete {self.ns_name} {self.obj_name}" + ) + ) + self.assertEqual(self.obj_name, created["name"]) + self.assertEqual(self.ns_name, created["namespace_name"]) + + # UPDATE + new_name = f"{self.obj_name}-updated" + self.openstack( + "image metadef object update " + f"{self.ns_name} {self.obj_name} " + f"--name {new_name}" + ) + self.obj_name = new_name + + # READ (get) + shown = self.openstack( + f"image metadef object show {self.ns_name} {self.obj_name}", + parse_output=True, + ) + self.assertEqual(self.obj_name, shown["name"]) + self.assertEqual(self.ns_name, shown["namespace_name"]) + + # READ (list) + rows = self.openstack( + f"image metadef object list {self.ns_name}", + parse_output=True, + ) + names = {row["name"] for row in rows} + self.assertIn(self.obj_name, names) diff --git a/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py b/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py new file mode 100644 index 0000000000..ab8dc13ef0 --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py @@ -0,0 +1,55 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from openstackclient.tests.functional.image import base + + +class ImageMetadefResourceTypeTests(base.BaseImageTests): + """Functional tests for image metadef resource type commands.""" + + def setUp(self): + super().setUp() + + # Create unique namespace name using UUID + self.namespace_name = 'test-mdef-ns-' + uuid.uuid4().hex + self.resource_type_name = 'test-mdef-rt-' + uuid.uuid4().hex + + # Create namespace + self.openstack('image metadef namespace create ' + self.namespace_name) + self.addCleanup( + self.openstack, + 'image metadef namespace delete ' + self.namespace_name, + ) + + def test_metadef_resource_type(self): + """Test image metadef resource type commands""" + + self.openstack( + 'image metadef resource type association create ' + f'{self.namespace_name} {self.resource_type_name}', + ) + self.addCleanup( + self.openstack, + 'image metadef resource type association delete ' + f'{self.namespace_name} {self.resource_type_name}', + ) + + output = self.openstack( + 'image metadef resource type list', + parse_output=True, + ) + + self.assertIn( + self.resource_type_name, [item['Name'] for item in output] + ) diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index f7c7212a1a..248758a843 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -33,7 +33,7 @@ def setUp(self): class NetworkTagTests(NetworkTests): """Functional tests with tag operation""" - base_command = None + base_command: str def test_tag_operation(self): # Get project IDs @@ -56,7 +56,7 @@ def test_tag_operation(self): 'set', name1, '--tag red --tag green', ['red', 'green'] ) - list_expected = ( + list_expected: tuple[tuple[str, list[str]], ...] = ( (name1, ['red', 'green']), (name2, ['red', 'blue']), (name3, []), @@ -80,9 +80,7 @@ def test_tag_operation(self): def _list_tag_check(self, project_id, expected): cmd_output = self.openstack( - '{} list --long --project {}'.format( - self.base_command, project_id - ), + f'{self.base_command} list --long --project {project_id}', parse_output=True, ) for name, tags in expected: @@ -95,7 +93,11 @@ def _create_resource_for_tag_test(self, name, args): parse_output=True, ) - def _create_resource_and_tag_check(self, args, expected): + def _create_resource_and_tag_check( + self, + args: str, + expected: list[str], + ) -> str: name = uuid.uuid4().hex cmd_output = self._create_resource_for_tag_test(name, args) self.addCleanup(self.openstack, f'{self.base_command} delete {name}') diff --git a/openstackclient/tests/functional/network/v2/test_address_group.py b/openstackclient/tests/functional/network/v2/test_address_group.py index 8146112a3c..6bc13c75d0 100644 --- a/openstackclient/tests/functional/network/v2/test_address_group.py +++ b/openstackclient/tests/functional/network/v2/test_address_group.py @@ -75,6 +75,10 @@ def test_address_group_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex cmd_output = self.openstack( 'address group create ' + name1, diff --git a/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py b/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py index d6ce95c1a8..1481c9a417 100644 --- a/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py @@ -30,14 +30,9 @@ def setUp(self): # Create the default security group rule. cmd_output = self.openstack( 'default security group rule create ' - '--protocol %(protocol)s ' - '--dst-port %(port)s:%(port)s ' - '--%(direction)s --ethertype IPv4 ' - % { - 'protocol': self.protocol, - 'port': self.port, - 'direction': self.direction, - }, + f'--protocol {self.protocol} ' + f'--dst-port {self.port}:{self.port} ' + f'--{self.direction} --ethertype IPv4 ', parse_output=True, ) self.addCleanup( diff --git a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py index 510ec8b2dd..ab0cb53e8a 100644 --- a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py @@ -41,15 +41,14 @@ def _create_helpers(self, router_id, helpers): created_helpers = [] for helper in helpers: output = self.openstack( - 'network l3 conntrack helper create %(router)s ' - '--helper %(helper)s --protocol %(protocol)s ' - '--port %(port)s ' - % { - 'router': router_id, - 'helper': helper['helper'], - 'protocol': helper['protocol'], - 'port': helper['port'], - }, + 'network l3 conntrack helper create {router} ' + '--helper {helper} --protocol {protocol} ' + '--port {port} '.format( + router=router_id, + helper=helper['helper'], + protocol=helper['protocol'], + port=helper['port'], + ), parse_output=True, ) self.assertEqual(helper['helper'], output['helper']) @@ -70,8 +69,7 @@ def test_l3_conntrack_helper_create_and_delete(self): ct_ids = " ".join([ct['id'] for ct in created_helpers]) raw_output = self.openstack( - '--debug network l3 conntrack helper delete %(router)s ' - '%(ct_ids)s' % {'router': router_id, 'ct_ids': ct_ids} + f'--debug network l3 conntrack helper delete {router_id} {ct_ids}' ) self.assertOutput('', raw_output) @@ -87,7 +85,7 @@ def test_l3_conntrack_helper_list(self): router_id = self._create_router() self._create_helpers(router_id, helpers) output = self.openstack( - 'network l3 conntrack helper list %s ' % router_id, + f'network l3 conntrack helper list {router_id} ', parse_output=True, ) for ct in output: @@ -96,42 +94,44 @@ def test_l3_conntrack_helper_list(self): self.assertIn(ct, expected_helpers) def test_l3_conntrack_helper_set_and_show(self): - helper = {'helper': 'tftp', 'protocol': 'udp', 'port': 69} + helper = 'tftp' + proto = 'udp' + port = 69 router_id = self._create_router() - created_helper = self._create_helpers(router_id, [helper])[0] + created_helper = self._create_helpers( + router_id, + [{'helper': helper, 'protocol': proto, 'port': port}], + )[0] output = self.openstack( - 'network l3 conntrack helper show %(router_id)s %(ct_id)s ' - '-f json' - % { - 'router_id': router_id, - 'ct_id': created_helper['id'], - }, + 'network l3 conntrack helper show {router_id} {ct_id} ' + '-f json'.format( + router_id=router_id, + ct_id=created_helper['id'], + ), parse_output=True, ) - self.assertEqual(helper['helper'], output['helper']) - self.assertEqual(helper['protocol'], output['protocol']) - self.assertEqual(helper['port'], output['port']) + self.assertEqual(port, output['port']) + self.assertEqual(helper, output['helper']) + self.assertEqual(proto, output['protocol']) raw_output = self.openstack( - 'network l3 conntrack helper set %(router_id)s %(ct_id)s ' - '--port %(port)s ' - % { - 'router_id': router_id, - 'ct_id': created_helper['id'], - 'port': helper['port'] + 1, - } + 'network l3 conntrack helper set {router_id} {ct_id} ' + '--port {port} '.format( + router_id=router_id, + ct_id=created_helper['id'], + port=port + 1, + ) ) self.assertOutput('', raw_output) output = self.openstack( - 'network l3 conntrack helper show %(router_id)s %(ct_id)s ' - '-f json' - % { - 'router_id': router_id, - 'ct_id': created_helper['id'], - }, + 'network l3 conntrack helper show {router_id} {ct_id} ' + '-f json'.format( + router_id=router_id, + ct_id=created_helper['id'], + ), parse_output=True, ) - self.assertEqual(helper['port'] + 1, output['port']) - self.assertEqual(helper['helper'], output['helper']) - self.assertEqual(helper['protocol'], output['protocol']) + self.assertEqual(port + 1, output['port']) + self.assertEqual(helper, output['helper']) + self.assertEqual(proto, output['protocol']) diff --git a/openstackclient/tests/functional/network/v2/test_local_ip.py b/openstackclient/tests/functional/network/v2/test_local_ip.py index a1553bf3c7..d092be084b 100644 --- a/openstackclient/tests/functional/network/v2/test_local_ip.py +++ b/openstackclient/tests/functional/network/v2/test_local_ip.py @@ -77,6 +77,10 @@ def test_local_ip_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex cmd_output = self.openstack( 'local ip create ' + name1, diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py index 8dbe883b6c..a38d88bdac 100644 --- a/openstackclient/tests/functional/network/v2/test_network.py +++ b/openstackclient/tests/functional/network/v2/test_network.py @@ -206,7 +206,7 @@ def test_network_list(self): 'network create ' + network_options + name1, parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name1) + self.addCleanup(self.openstack, f'network delete {name1}') self.assertIsNotNone(cmd_output["id"]) if self.haz_network: self.assertEqual( @@ -342,33 +342,30 @@ def test_network_dhcp_agent(self): name1 = uuid.uuid4().hex cmd_output = self.openstack( - 'network create --description aaaa %s' % name1, + f'network create --description aaaa {name1}', parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name1) + self.addCleanup(self.openstack, f'network delete {name1}') # Get network ID network_id = cmd_output['id'] # Add Agent to Network self.openstack( - 'network agent add network --dhcp {} {}'.format( - agent_id, network_id - ) + f'network agent add network --dhcp {agent_id} {network_id}' ) # Test network list --agent cmd_output = self.openstack( - 'network list --agent %s' % agent_id, + f'network list --agent {agent_id}', parse_output=True, ) # Cleanup # Remove Agent from Network self.openstack( - 'network agent remove network --dhcp %s %s' - % (agent_id, network_id) + f'network agent remove network --dhcp {agent_id} {network_id}' ) # Assert @@ -388,10 +385,10 @@ def test_network_set(self): '--no-share ' '--internal ' '--no-default ' - '--enable-port-security %s' % name, + f'--enable-port-security {name}', parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name) + self.addCleanup(self.openstack, f'network delete {name}') self.assertIsNotNone(cmd_output["id"]) self.assertEqual( 'aaaa', @@ -416,7 +413,7 @@ def test_network_set(self): '--disable ' '--share ' '--external ' - '--disable-port-security %s' % name + f'--disable-port-security {name}' ) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py index bbe168aeed..013201a40e 100644 --- a/openstackclient/tests/functional/network/v2/test_network_agent.py +++ b/openstackclient/tests/functional/network/v2/test_network_agent.py @@ -42,7 +42,7 @@ def test_network_agent_list_show_set(self): # agent show cmd_output = self.openstack( - 'network agent show %s' % agent_ids[0], + f'network agent show {agent_ids[0]}', parse_output=True, ) self.assertEqual( @@ -57,12 +57,12 @@ def test_network_agent_list_show_set(self): # agent set raw_output = self.openstack( - 'network agent set --disable %s' % agent_ids[0] + f'network agent set --disable {agent_ids[0]}' ) self.assertOutput('', raw_output) cmd_output = self.openstack( - 'network agent show %s' % agent_ids[0], + f'network agent show {agent_ids[0]}', parse_output=True, ) self.assertEqual( @@ -71,12 +71,12 @@ def test_network_agent_list_show_set(self): ) raw_output = self.openstack( - 'network agent set --enable %s' % agent_ids[0] + f'network agent set --enable {agent_ids[0]}' ) self.assertOutput('', raw_output) cmd_output = self.openstack( - 'network agent show %s' % agent_ids[0], + f'network agent show {agent_ids[0]}', parse_output=True, ) self.assertEqual( @@ -112,33 +112,30 @@ def test_network_dhcp_agent_list(self): name1 = uuid.uuid4().hex cmd_output = self.openstack( - 'network create --description aaaa %s' % name1, + f'network create --description aaaa {name1}', parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name1) + self.addCleanup(self.openstack, f'network delete {name1}') # Get network ID network_id = cmd_output['id'] # Add Agent to Network self.openstack( - 'network agent add network --dhcp {} {}'.format( - agent_id, network_id - ) + f'network agent add network --dhcp {agent_id} {network_id}' ) # Test network agent list --network cmd_output = self.openstack( - 'network agent list --network %s' % network_id, + f'network agent list --network {network_id}', parse_output=True, ) # Cleanup # Remove Agent from Network self.openstack( - 'network agent remove network --dhcp %s %s' - % (agent_id, network_id) + f'network agent remove network --dhcp {agent_id} {network_id}' ) # Assert @@ -153,11 +150,11 @@ def test_network_agent_list_routers(self): name = uuid.uuid4().hex cmd_output = self.openstack( - 'router create %s' % name, + f'router create {name}', parse_output=True, ) - self.addCleanup(self.openstack, 'router delete %s' % name) + self.addCleanup(self.openstack, f'router delete {name}') # Get router ID router_id = cmd_output['id'] # Get l3 agent id @@ -175,7 +172,7 @@ def test_network_agent_list_routers(self): # Test router list --agent cmd_output = self.openstack( - 'network agent list --router %s' % router_id, + f'network agent list --router {router_id}', parse_output=True, ) @@ -184,12 +181,10 @@ def test_network_agent_list_routers(self): # Remove router from agent self.openstack( - 'network agent remove router --l3 {} {}'.format( - agent_id, router_id - ) + f'network agent remove router --l3 {agent_id} {router_id}' ) cmd_output = self.openstack( - 'network agent list --router %s' % router_id, + f'network agent list --router {router_id}', parse_output=True, ) agent_ids = [x['ID'] for x in cmd_output] diff --git a/openstackclient/tests/functional/network/v2/test_network_flavor.py b/openstackclient/tests/functional/network/v2/test_network_flavor.py index e04d10e805..2b88670378 100644 --- a/openstackclient/tests/functional/network/v2/test_network_flavor.py +++ b/openstackclient/tests/functional/network/v2/test_network_flavor.py @@ -37,10 +37,10 @@ def test_network_flavor_add_remove_profile(self): ) service_profile_id = cmd_output2.get('id') - self.addCleanup(self.openstack, 'network flavor delete %s' % flavor_id) + self.addCleanup(self.openstack, f'network flavor delete {flavor_id}') self.addCleanup( self.openstack, - 'network flavor profile delete %s' % service_profile_id, + f'network flavor profile delete {service_profile_id}', ) # Add flavor to service profile self.openstack( diff --git a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py index 8258d58318..c80643e31d 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py @@ -22,8 +22,8 @@ class TestMeterRule(common.NetworkTests): """Functional tests for meter rule""" - METER_ID = None - METER_RULE_ID = None + METER_ID: str + METER_RULE_ID: str @classmethod def setUpClass(cls): diff --git a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py index d22f297470..120f53d28d 100644 --- a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py +++ b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py @@ -31,20 +31,15 @@ def setUp(self): self.created_ndp_proxies = [] json_output = self.openstack( - 'address scope create --ip-version 6 ' - '%(address_s_name)s' % {'address_s_name': self.ADDR_SCOPE_NAME}, + f'address scope create --ip-version 6 {self.ADDR_SCOPE_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) self.ADDRESS_SCOPE_ID = json_output['id'] json_output = self.openstack( - 'subnet pool create %(subnet_p_name)s ' - '--address-scope %(address_scope)s ' - '--pool-prefix 2001:db8::/96 --default-prefix-length 112' - % { - 'subnet_p_name': self.SUBNET_P_NAME, - 'address_scope': self.ADDRESS_SCOPE_ID, - }, + f'subnet pool create {self.SUBNET_P_NAME} ' + f'--address-scope {self.ADDRESS_SCOPE_ID} ' + '--pool-prefix 2001:db8::/96 --default-prefix-length 112', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -57,12 +52,7 @@ def setUp(self): self.EXT_NET_ID = json_output['id'] json_output = self.openstack( 'subnet create --ip-version 6 --subnet-pool ' - '%(subnet_pool)s --network %(net_id)s %(sub_name)s' - % { - 'subnet_pool': self.SUBNET_POOL_ID, - 'net_id': self.EXT_NET_ID, - 'sub_name': self.EXT_SUB_NAME, - }, + f'{self.SUBNET_POOL_ID} --network {self.EXT_NET_ID} {self.EXT_SUB_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -74,8 +64,7 @@ def setUp(self): self.assertIsNotNone(json_output['id']) self.ROT_ID = json_output['id'] output = self.openstack( - 'router set %(router_id)s --external-gateway %(net_id)s' - % {'router_id': self.ROT_ID, 'net_id': self.EXT_NET_ID} + f'router set {self.ROT_ID} --external-gateway {self.EXT_NET_ID}' ) self.assertEqual('', output) output = self.openstack('router set --enable-ndp-proxy ' + self.ROT_ID) @@ -93,23 +82,13 @@ def setUp(self): self.INT_NET_ID = json_output['id'] json_output = self.openstack( 'subnet create --ip-version 6 --subnet-pool ' - '%(subnet_pool)s --network %(net_id)s %(sub_name)s' - % { - 'subnet_pool': self.SUBNET_POOL_ID, - 'net_id': self.INT_NET_ID, - 'sub_name': self.INT_SUB_NAME, - }, + f'{self.SUBNET_POOL_ID} --network {self.INT_NET_ID} {self.INT_SUB_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) self.INT_SUB_ID = json_output['id'] json_output = self.openstack( - 'port create --network %(net_id)s ' - '%(port_name)s' - % { - 'net_id': self.INT_NET_ID, - 'port_name': self.INT_PORT_NAME, - }, + f'port create --network {self.INT_NET_ID} {self.INT_PORT_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -161,14 +140,13 @@ def tearDown(self): def _create_ndp_proxies(self, ndp_proxies): for ndp_proxy in ndp_proxies: output = self.openstack( - 'router ndp proxy create %(router)s --name %(name)s ' - '--port %(port)s --ip-address %(address)s' - % { - 'router': ndp_proxy['router_id'], - 'name': ndp_proxy['name'], - 'port': ndp_proxy['port_id'], - 'address': ndp_proxy['address'], - }, + 'router ndp proxy create {router} --name {name} ' + '--port {port} --ip-address {address}'.format( + router=ndp_proxy['router_id'], + name=ndp_proxy['name'], + port=ndp_proxy['port_id'], + address=ndp_proxy['address'], + ), parse_output=True, ) self.assertEqual(ndp_proxy['router_id'], output['router_id']) @@ -214,8 +192,7 @@ def test_ndp_proxy_set_and_show(self): self._create_ndp_proxies([ndp_proxies]) ndp_proxy_id = self.created_ndp_proxies[0]['id'] output = self.openstack( - 'router ndp proxy set --description %s %s' - % (description, ndp_proxy_id) + f'router ndp proxy set --description {description} {ndp_proxy_id}' ) self.assertEqual('', output) json_output = self.openstack( 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 8db484e2e2..28c38e5a45 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py @@ -32,40 +32,39 @@ class NetworkQosRuleTestsMinimumBandwidth(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type minimum-bandwidth ' '--min-kbps 2800 ' - '--egress %s' % self.QOS_POLICY_NAME, + f'--egress {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {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 %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type minimum-bandwidth ' '--min-kbps 2800 ' - '--egress %s' % policy_name, + f'--egress {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -75,27 +74,24 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) def test_qos_rule_set(self): self.openstack( - 'network qos rule set --min-kbps 7500 %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID) + f'network qos rule set --min-kbps 7500 {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(7500, cmd_output['min_kbps']) @@ -107,40 +103,39 @@ class NetworkQosRuleTestsMinimumPacketRate(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type minimum-packet-rate ' '--min-kpps 2800 ' - '--egress %s' % self.QOS_POLICY_NAME, + f'--egress {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {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 %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type minimum-packet-rate ' '--min-kpps 2800 ' - '--egress %s' % policy_name, + f'--egress {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -150,27 +145,24 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) 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) + f'network qos rule set --min-kpps 7500 --any {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(7500, cmd_output['min_kpps']) @@ -183,37 +175,36 @@ class NetworkQosRuleTestsDSCPMarking(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type dscp-marking ' - '--dscp-mark 8 %s' % self.QOS_POLICY_NAME, + f'--dscp-mark 8 {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {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 %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type dscp-marking ' - '--dscp-mark 8 %s' % policy_name, + f'--dscp-mark 8 {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -223,27 +214,24 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) def test_qos_rule_set(self): self.openstack( - 'network qos rule set --dscp-mark 32 %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID) + f'network qos rule set --dscp-mark 32 {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(32, cmd_output['dscp_mark']) @@ -255,40 +243,39 @@ class NetworkQosRuleTestsBandwidthLimit(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type bandwidth-limit ' '--max-kbps 10000 ' - '--egress %s' % self.QOS_POLICY_NAME, + f'--egress {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {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 %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type bandwidth-limit ' '--max-kbps 10000 ' '--max-burst-kbits 1400 ' - '--egress %s' % policy_name, + f'--egress {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -298,15 +285,14 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) @@ -315,11 +301,10 @@ def test_qos_rule_set(self): self.openstack( 'network qos rule set --max-kbps 15000 ' '--max-burst-kbits 1800 ' - '--ingress %s %s' % (self.QOS_POLICY_NAME, self.RULE_ID) + f'--ingress {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(15000, cmd_output['max_kbps']) diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py index 77ce35716b..745191b8cb 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py @@ -67,7 +67,7 @@ def test_qos_rule_type_list_all_rules(self): def test_qos_rule_type_details(self): for rule_type in self.AVAILABLE_RULE_TYPES: cmd_output = self.openstack( - 'network qos rule type show %s -f json' % rule_type, + f'network qos rule type show {rule_type} -f json', parse_output=True, ) self.assertEqual(rule_type, cmd_output['rule_type_name']) diff --git a/openstackclient/tests/functional/network/v2/test_network_rbac.py b/openstackclient/tests/functional/network/v2/test_network_rbac.py index 657175a284..d09b6e9b4e 100644 --- a/openstackclient/tests/functional/network/v2/test_network_rbac.py +++ b/openstackclient/tests/functional/network/v2/test_network_rbac.py @@ -18,8 +18,8 @@ class NetworkRBACTests(common.NetworkTests): """Functional tests for network rbac""" - OBJECT_ID = None - ID = None + OBJECT_ID: str + ID: str HEADERS = ['ID'] FIELDS = ['id'] diff --git a/openstackclient/tests/functional/network/v2/test_network_segment_range.py b/openstackclient/tests/functional/network/v2/test_network_segment_range.py index 604ee3cfab..3f07948b5c 100644 --- a/openstackclient/tests/functional/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/functional/network/v2/test_network_segment_range.py @@ -128,8 +128,8 @@ def test_network_segment_range_set_show(self): new_minimum = 2020 new_maximum = 2029 cmd_output = self.openstack( - 'network segment range set --minimum {min} --maximum {max} ' - '{name}'.format(min=new_minimum, max=new_maximum, name=name) + f'network segment range set --minimum {new_minimum} --maximum {new_maximum} ' + f'{name}' ) self.assertOutput('', cmd_output) diff --git a/openstackclient/tests/functional/network/v2/test_network_trunk.py b/openstackclient/tests/functional/network/v2/test_network_trunk.py index 7c504286d7..1621973596 100644 --- a/openstackclient/tests/functional/network/v2/test_network_trunk.py +++ b/openstackclient/tests/functional/network/v2/test_network_trunk.py @@ -32,33 +32,27 @@ def setUp(self): self.parent_port_name = uuid.uuid4().hex self.sub_port_name = uuid.uuid4().hex - self.openstack('network create %s' % network_name) - self.addCleanup(self.openstack, 'network delete %s' % network_name) + self.openstack(f'network create {network_name}') + self.addCleanup(self.openstack, f'network delete {network_name}') self.openstack( - 'subnet create %s ' - '--network %s --subnet-range 10.0.0.0/24' - % (subnet_name, network_name) + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' ) self.openstack( - 'port create %s --network %s' - % (self.parent_port_name, network_name) - ) - self.addCleanup( - self.openstack, 'port delete %s' % self.parent_port_name + f'port create {self.parent_port_name} --network {network_name}' ) + self.addCleanup(self.openstack, f'port delete {self.parent_port_name}') json_out = self.openstack( - 'port create %s --network %s -f json' - % (self.sub_port_name, network_name) + f'port create {self.sub_port_name} --network {network_name} -f json' ) self.sub_port_id = json.loads(json_out)['id'] - self.addCleanup(self.openstack, 'port delete %s' % self.sub_port_name) + self.addCleanup(self.openstack, f'port delete {self.sub_port_name}') def test_network_trunk_create_delete(self): trunk_name = uuid.uuid4().hex self.openstack( - 'network trunk create %s --parent-port %s -f json ' - % (trunk_name, self.parent_port_name) + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} -f json ' ) raw_output = self.openstack('network trunk delete ' + trunk_name) self.assertEqual('', raw_output) @@ -67,8 +61,7 @@ def test_network_trunk_list(self): trunk_name = uuid.uuid4().hex json_output = json.loads( self.openstack( - 'network trunk create %s --parent-port %s -f json ' - % (trunk_name, self.parent_port_name) + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} -f json ' ) ) self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) @@ -81,14 +74,13 @@ def test_network_trunk_set_unset(self): trunk_name = uuid.uuid4().hex json_output = json.loads( self.openstack( - 'network trunk create %s --parent-port %s -f json ' - % (trunk_name, self.parent_port_name) + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} -f json ' ) ) self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) self.assertEqual(trunk_name, json_output['name']) - self.openstack('network trunk set ' '--enable ' + trunk_name) + self.openstack('network trunk set --enable ' + trunk_name) json_output = json.loads( self.openstack('network trunk show -f json ' + trunk_name) @@ -98,8 +90,7 @@ def test_network_trunk_set_unset(self): # Add subport to trunk self.openstack( 'network trunk set ' - + '--subport port=%s,segmentation-type=vlan,segmentation-id=42 ' - % (self.sub_port_name) + + f'--subport port={self.sub_port_name},segmentation-type=vlan,segmentation-id=42 ' + trunk_name ) json_output = json.loads( @@ -132,10 +123,9 @@ def test_network_trunk_list_subports(self): trunk_name = uuid.uuid4().hex json_output = json.loads( self.openstack( - 'network trunk create %s --parent-port %s ' - '--subport port=%s,segmentation-type=vlan,segmentation-id=42 ' + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} ' + f'--subport port={self.sub_port_name},segmentation-type=vlan,segmentation-id=42 ' '-f json ' - % (trunk_name, self.parent_port_name, self.sub_port_name) ) ) self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) @@ -143,7 +133,7 @@ def test_network_trunk_list_subports(self): json_output = json.loads( self.openstack( - 'network subport list --trunk %s -f json' % trunk_name + f'network subport list --trunk {trunk_name} -f json' ) ) self.assertEqual( diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index 0ba3773417..7f1336801f 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -12,6 +12,8 @@ import uuid +from tempest.lib import exceptions as tempest_exc + from openstackclient.tests.functional.network.v2 import common @@ -31,14 +33,14 @@ def setUpClass(cls): cls.NETWORK_NAME = uuid.uuid4().hex # Create a network for the port tests - cls.openstack('network create %s' % cls.NETWORK_NAME) + cls.openstack(f'network create {cls.NETWORK_NAME}') @classmethod def tearDownClass(cls): try: if cls.haz_network: raw_output = cls.openstack( - 'network delete %s' % cls.NETWORK_NAME + f'network delete {cls.NETWORK_NAME}' ) cls.assertOutput('', raw_output) finally: @@ -56,9 +58,7 @@ def test_port_delete(self): self.assertEqual(self.NAME, json_output.get('name')) json_output = self.openstack( - 'port create --network {} {}x'.format( - self.NETWORK_NAME, self.NAME - ), + f'port create --network {self.NETWORK_NAME} {self.NAME}x', parse_output=True, ) id2 = json_output.get('id') @@ -80,20 +80,28 @@ def test_port_list(self): self.assertIsNotNone(id1) mac1 = json_output.get('mac_address') self.assertIsNotNone(mac1) - self.addCleanup(self.openstack, 'port delete %s' % id1) + self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(self.NAME, json_output.get('name')) + # sg for port2 + sg_name1 = uuid.uuid4().hex + json_output = self.openstack( + f'security group create {sg_name1}', + parse_output=True, + ) + sg_id1 = json_output.get('id') + self.addCleanup(self.openstack, f'security group delete {sg_id1}') json_output = self.openstack( - 'port create --network {} {}x'.format( - self.NETWORK_NAME, self.NAME - ), + f'port create --network {self.NETWORK_NAME} ' + f'--security-group {sg_name1} {self.NAME}x', parse_output=True, ) + id2 = json_output.get('id') self.assertIsNotNone(id2) mac2 = json_output.get('mac_address') self.assertIsNotNone(mac2) - self.addCleanup(self.openstack, 'port delete %s' % id2) + self.addCleanup(self.openstack, f'port delete {id2}') self.assertEqual(self.NAME + 'x', json_output.get('name')) # Test list @@ -117,10 +125,16 @@ def test_port_list(self): id_list = [item.get('ID') for item in json_output] self.assertIn(id1, id_list) self.assertIn(id2, id_list) + item_sg_map = { + item.get('ID'): item.get('Security Groups') for item in json_output + } + self.assertIn(id1, item_sg_map.keys()) + self.assertIn(id2, item_sg_map.keys()) + self.assertIn([sg_id1], item_sg_map.values()) # Test list --mac-address json_output = self.openstack( - 'port list --mac-address %s' % mac2, + f'port list --mac-address {mac2}', parse_output=True, ) item_map = { @@ -131,6 +145,17 @@ def test_port_list(self): self.assertNotIn(mac1, item_map.values()) self.assertIn(mac2, item_map.values()) + # Test list --security-group + json_output = self.openstack( + f'port list --security-group {sg_id1}', + parse_output=True, + ) + item_map = { + item.get('ID'): item.get('Security Groups') for item in json_output + } + self.assertNotIn(id1, item_map.keys()) + self.assertIn(id2, item_map.keys()) + # Test list with unknown fields json_output = self.openstack( 'port list -c ID -c Name -c device_id', @@ -139,30 +164,38 @@ def test_port_list(self): id_list = [p['ID'] for p in json_output] self.assertIn(id1, id_list) self.assertIn(id2, id_list) - # Check an unknown field exists - self.assertIn('device_id', json_output[0]) + # Check an unknown field does not exist + self.assertNotIn('device_id', json_output[0]) + + # Test list with only unknown fields + exc = self.assertRaises( + tempest_exc.CommandFailed, + self.openstack, + 'port list -c device_id', + ) + self.assertIn("No recognized column names in ['device_id']", str(exc)) def test_port_set(self): """Test create, set, show, delete""" name = uuid.uuid4().hex json_output = self.openstack( 'port create ' - '--network %s ' + f'--network {self.NETWORK_NAME} ' '--description xyzpdq ' - '--disable %s' % (self.NETWORK_NAME, name), + f'--disable {name}', parse_output=True, ) id1 = json_output.get('id') - self.addCleanup(self.openstack, 'port delete %s' % id1) + self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(name, json_output.get('name')) self.assertEqual('xyzpdq', json_output.get('description')) self.assertEqual(False, json_output.get('admin_state_up')) - raw_output = self.openstack('port set --enable %s' % name) + raw_output = self.openstack(f'port set --enable {name}') self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) sg_id = json_output.get('security_group_ids')[0] @@ -178,7 +211,7 @@ def test_port_set(self): self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) self.assertEqual([], json_output.get('security_group_ids')) @@ -186,19 +219,19 @@ def test_port_set(self): def test_port_admin_set(self): """Test create, set (as admin), show, delete""" json_output = self.openstack( - 'port create ' '--network %s %s' % (self.NETWORK_NAME, self.NAME), + f'port create --network {self.NETWORK_NAME} {self.NAME}', parse_output=True, ) id_ = json_output.get('id') - self.addCleanup(self.openstack, 'port delete %s' % id_) + self.addCleanup(self.openstack, f'port delete {id_}') raw_output = self.openstack( '--os-username admin ' - 'port set --mac-address 11:22:33:44:55:66 %s' % self.NAME + f'port set --mac-address 11:22:33:44:55:66 {self.NAME}' ) self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % self.NAME, + f'port show {self.NAME}', parse_output=True, ) self.assertEqual(json_output.get('mac_address'), '11:22:33:44:55:66') @@ -207,39 +240,39 @@ def test_port_set_sg(self): """Test create, set, show, delete""" sg_name1 = uuid.uuid4().hex json_output = self.openstack( - 'security group create %s' % sg_name1, + f'security group create {sg_name1}', parse_output=True, ) sg_id1 = json_output.get('id') - self.addCleanup(self.openstack, 'security group delete %s' % sg_id1) + self.addCleanup(self.openstack, f'security group delete {sg_id1}') sg_name2 = uuid.uuid4().hex json_output = self.openstack( - 'security group create %s' % sg_name2, + f'security group create {sg_name2}', parse_output=True, ) sg_id2 = json_output.get('id') - self.addCleanup(self.openstack, 'security group delete %s' % sg_id2) + self.addCleanup(self.openstack, f'security group delete {sg_id2}') name = uuid.uuid4().hex json_output = self.openstack( 'port create ' - '--network %s ' - '--security-group %s %s' % (self.NETWORK_NAME, sg_name1, name), + f'--network {self.NETWORK_NAME} ' + f'--security-group {sg_name1} {name}', parse_output=True, ) id1 = json_output.get('id') - self.addCleanup(self.openstack, 'port delete %s' % id1) + self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(name, json_output.get('name')) self.assertEqual([sg_id1], json_output.get('security_group_ids')) raw_output = self.openstack( - 'port set ' '--security-group %s %s' % (sg_name2, name) + f'port set --security-group {sg_name2} {name}' ) self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) self.assertEqual(name, json_output.get('name')) @@ -256,15 +289,92 @@ def test_port_set_sg(self): self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) self.assertEqual([sg_id2], json_output.get('security_group_ids')) def _create_resource_for_tag_test(self, name, args): return self.openstack( - '{} create --network {} {} {}'.format( - self.base_command, self.NETWORK_NAME, args, name - ), + f'{self.base_command} create --network {self.NETWORK_NAME} {args} {name}', + parse_output=True, + ) + + def _trunk_creation(self): + pport = uuid.uuid4().hex + sport1 = uuid.uuid4().hex + sport2 = uuid.uuid4().hex + trunk = uuid.uuid4().hex + json_output = self.openstack( + f'port create --network {self.NETWORK_NAME} {pport}', + parse_output=True, + ) + pport_id = json_output.get('id') + json_output = self.openstack( + f'port create --network {self.NETWORK_NAME} {sport1}', + parse_output=True, + ) + sport1_id = json_output.get('id') + json_output = self.openstack( + f'port create --network {self.NETWORK_NAME} {sport2}', + parse_output=True, + ) + sport2_id = json_output.get('id') + + self.openstack( + f'network trunk create --parent-port {pport} {trunk}', + ) + self.openstack( + f'network trunk set --subport port={sport1},' + f'segmentation-type=vlan,segmentation-id=100 {trunk}', + ) + self.openstack( + f'network trunk set --subport port={sport2},' + f'segmentation-type=vlan,segmentation-id=101 {trunk}', + ) + + # NOTE(ralonsoh): keep this order to first delete the trunk and then + # the ports. + self.addCleanup(self.openstack, f'port delete {pport_id}') + self.addCleanup(self.openstack, f'port delete {sport1_id}') + self.addCleanup(self.openstack, f'port delete {sport2_id}') + self.addCleanup(self.openstack, f'network trunk delete {trunk}') + + return pport_id, sport1_id, sport2_id + + def check_subports(self, subports, pport_id, sport1_id, sport2_id): + self.assertEqual(2, len(subports)) + for subport in subports: + if subport['port_id'] == sport1_id: + self.assertEqual(100, subport['segmentation_id']) + elif subport['port_id'] == sport2_id: + self.assertEqual(101, subport['segmentation_id']) + else: + self.fail( + f'Port {pport_id} does not have subport ' + f'{subport["port_id"]}' + ) + self.assertEqual('vlan', subport['segmentation_type']) + + def test_port_list_with_trunk(self): + pport_id, sport1_id, sport2_id = self._trunk_creation() + + # List all ports with "--long" flag to retrieve the trunk details + json_output = self.openstack( + 'port list --long', + parse_output=True, + ) + port = next(port for port in json_output if port['ID'] == pport_id) + subports = port['Trunk subports'] + self.check_subports(subports, pport_id, sport1_id, sport2_id) + + def test_port_show_with_trunk(self): + pport_id, sport1_id, sport2_id = self._trunk_creation() + + # List all ports with "--long" flag to retrieve the trunk details + port = self.openstack( + f'port show {pport_id}', parse_output=True, ) + subports = port['trunk_details']['sub_ports'] + self.check_subports(subports, pport_id, sport1_id, sport2_id) diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index bd4261ac72..cc7c8b1b70 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -44,6 +44,44 @@ def test_router_create_and_delete(self): del_output = self.openstack('router delete ' + name1 + ' ' + name2) self.assertOutput('', del_output) + def test_router_create_with_external_gateway(self): + network_name = uuid.uuid4().hex + subnet_name = uuid.uuid4().hex + qos_policy = uuid.uuid4().hex + router_name = uuid.uuid4().hex + + cmd_net = self.openstack( + f'network create --external {network_name}', parse_output=True + ) + self.addCleanup(self.openstack, f'network delete {network_name}') + network_id = cmd_net['id'] + + self.openstack( + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' + ) + + cmd_qos = self.openstack( + f'network qos policy create {qos_policy}', parse_output=True + ) + self.addCleanup( + self.openstack, f'network qos policy delete {qos_policy}' + ) + qos_id = cmd_qos['id'] + + self.openstack( + f'router create --external-gateway {network_name} ' + f'--qos-policy {qos_policy} {router_name}' + ) + self.addCleanup(self.openstack, f'router delete {router_name}') + + cmd_output = self.openstack( + f'router show {router_name}', parse_output=True + ) + gw_info = cmd_output['external_gateway_info'] + self.assertEqual(network_id, gw_info['network_id']) + self.assertEqual(qos_id, gw_info['qos_policy_id']) + def test_router_list(self): """Test create, list filter""" # Get project IDs @@ -68,6 +106,10 @@ def test_router_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex name2 = uuid.uuid4().hex cmd_output = self.openstack( @@ -275,17 +317,16 @@ def test_router_add_remove_route(self): subnet_name = uuid.uuid4().hex router_name = uuid.uuid4().hex - self.openstack('network create %s' % network_name) - self.addCleanup(self.openstack, 'network delete %s' % network_name) + self.openstack(f'network create {network_name}') + self.addCleanup(self.openstack, f'network delete {network_name}') self.openstack( - 'subnet create %s ' - '--network %s --subnet-range 10.0.0.0/24' - % (subnet_name, network_name) + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' ) - self.openstack('router create %s' % router_name) - self.addCleanup(self.openstack, 'router delete %s' % router_name) + self.openstack(f'router create {router_name}') + self.addCleanup(self.openstack, f'router delete {router_name}') self.openstack(f'router add subnet {router_name} {subnet_name}') self.addCleanup( @@ -295,24 +336,20 @@ def test_router_add_remove_route(self): out1 = ( self.openstack( - 'router add route %s ' - '--route destination=10.0.10.0/24,gateway=10.0.0.10' - % router_name, + f'router add route {router_name} ' + '--route destination=10.0.10.0/24,gateway=10.0.0.10', parse_output=True, ), ) self.assertEqual(1, len(out1[0]['routes'])) - self.addCleanup( - self.openstack, 'router set %s --no-route' % router_name - ) + self.addCleanup(self.openstack, f'router set {router_name} --no-route') out2 = ( self.openstack( - 'router add route %s ' + f'router add route {router_name} ' '--route destination=10.0.10.0/24,gateway=10.0.0.10 ' - '--route destination=10.0.11.0/24,gateway=10.0.0.11' - % router_name, + '--route destination=10.0.11.0/24,gateway=10.0.0.11', parse_output=True, ), ) @@ -320,10 +357,9 @@ def test_router_add_remove_route(self): out3 = ( self.openstack( - 'router remove route %s ' + f'router remove route {router_name} ' '--route destination=10.0.11.0/24,gateway=10.0.0.11 ' - '--route destination=10.0.12.0/24,gateway=10.0.0.12' - % router_name, + '--route destination=10.0.12.0/24,gateway=10.0.0.12', parse_output=True, ), ) diff --git a/openstackclient/tests/functional/network/v2/test_subnet_pool.py b/openstackclient/tests/functional/network/v2/test_subnet_pool.py index 89eb6f0ed4..97d9f0b9d4 100644 --- a/openstackclient/tests/functional/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/functional/network/v2/test_subnet_pool.py @@ -63,6 +63,10 @@ def test_subnet_pool_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex name2 = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/object/v1/test_object.py b/openstackclient/tests/functional/object/v1/test_object.py index 73044de9d1..68ca204337 100644 --- a/openstackclient/tests/functional/object/v1/test_object.py +++ b/openstackclient/tests/functional/object/v1/test_object.py @@ -59,7 +59,7 @@ def _test_object(self, object_file): items = self.parse_listing(raw_output) self.assert_show_fields(items, OBJECT_FIELDS) - raw_output = self.openstack('object list %s' % self.CONTAINER_NAME) + raw_output = self.openstack(f'object list {self.CONTAINER_NAME}') items = self.parse_listing(raw_output) self.assert_table_structure(items, BASIC_LIST_HEADERS) @@ -69,15 +69,12 @@ def _test_object(self, object_file): tmp_file = 'tmp.txt' self.addCleanup(os.remove, tmp_file) self.openstack( - 'object save %s %s --file %s' - % (self.CONTAINER_NAME, object_file, tmp_file) + f'object save {self.CONTAINER_NAME} {object_file} --file {tmp_file}' ) # TODO(stevemar): Assert returned fields raw_output = self.openstack( - 'object save {} {} --file -'.format( - self.CONTAINER_NAME, object_file - ) + f'object save {self.CONTAINER_NAME} {object_file} --file -' ) self.assertEqual(raw_output, 'test content') @@ -91,6 +88,6 @@ def _test_object(self, object_file): self.openstack(f'object create {self.CONTAINER_NAME} {object_file}') raw_output = self.openstack( - 'container delete -r %s' % self.CONTAINER_NAME + f'container delete -r {self.CONTAINER_NAME}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/volume/base.py b/openstackclient/tests/functional/volume/base.py index ab05642eb1..d1d4ffe00e 100644 --- a/openstackclient/tests/functional/volume/base.py +++ b/openstackclient/tests/functional/volume/base.py @@ -40,21 +40,15 @@ def wait_for_status( current_status = output['status'] if current_status == desired_status: print( - '{} {} now has status {}'.format( - check_type, check_name, current_status - ) + f'{check_type} {check_name} now has status {current_status}' ) return print( - 'Checking {} {} Waiting for {} current status: {}'.format( - check_type, check_name, desired_status, current_status - ) + f'Checking {check_type} {check_name} Waiting for {desired_status} current status: {current_status}' ) if current_status in failures: raise Exception( - 'Current status {} of {} {} is one of failures {}'.format( - current_status, check_type, check_name, failures - ) + f'Current status {current_status} of {check_type} {check_name} is one of failures {failures}' ) time.sleep(interval) total_sleep += interval @@ -72,15 +66,9 @@ def wait_for_delete( if check_name not in names: print(f'{check_type} {check_name} is now deleted') return - print( - 'Checking {} {} Waiting for deleted'.format( - check_type, check_name - ) - ) + print(f'Checking {check_type} {check_name} Waiting for deleted') time.sleep(interval) total_sleep += interval raise Exception( - 'Timeout: {} {} was not deleted in {} seconds'.format( - check_type, check_name, wait - ) + f'Timeout: {check_type} {check_name} was not deleted in {wait} seconds' ) diff --git a/openstackclient/tests/functional/volume/v1/common.py b/openstackclient/tests/functional/volume/v1/common.py deleted file mode 100644 index 755874785d..0000000000 --- a/openstackclient/tests/functional/volume/v1/common.py +++ /dev/null @@ -1,35 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import fixtures - -from openstackclient.tests.functional.volume import base as volume_base - - -class BaseVolumeTests(volume_base.BaseVolumeTests): - """Base class for Volume functional tests""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.haz_volume_v1 = cls.is_service_enabled('block-storage', '1.0') - - def setUp(self): - super().setUp() - - if not self.haz_volume_v1: - self.skipTest("No Volume v1 service present") - - ver_fixture = fixtures.EnvironmentVariable( - 'OS_VOLUME_API_VERSION', '1' - ) - self.useFixture(ver_fixture) diff --git a/openstackclient/tests/functional/volume/v1/test_qos.py b/openstackclient/tests/functional/volume/v1/test_qos.py deleted file mode 100644 index 5d0aa41f67..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_qos.py +++ /dev/null @@ -1,100 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class QosTests(common.BaseVolumeTests): - """Functional tests for volume qos.""" - - def test_volume_qos_create_list(self): - """Test create, list, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' + name1, - parse_output=True, - ) - self.assertEqual(name1, cmd_output['name']) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' + name2, - parse_output=True, - ) - self.assertEqual(name2, cmd_output['name']) - - # Test list - cmd_output = self.openstack( - 'volume qos list', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test delete multiple - del_output = self.openstack('volume qos delete ' + name1 + ' ' + name2) - self.assertOutput('', del_output) - - def test_volume_qos_set_show_unset(self): - """Tests create volume qos, set, unset, show, delete""" - - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' - + '--consumer front-end ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume qos delete ' + name) - self.assertEqual(name, cmd_output['name']) - - self.assertEqual("front-end", cmd_output['consumer']) - - # Test volume qos set - raw_output = self.openstack( - 'volume qos set ' - + '--no-property ' - + '--property Beta=b ' - + '--property Charlie=c ' - + name, - ) - self.assertOutput('', raw_output) - - # Test volume qos show - cmd_output = self.openstack( - 'volume qos show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) - self.assertEqual( - {'Beta': 'b', 'Charlie': 'c'}, - cmd_output['properties'], - ) - - # Test volume qos unset - raw_output = self.openstack( - 'volume qos unset ' + '--property Charlie ' + name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume qos show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) - self.assertEqual({'Beta': 'b'}, cmd_output['properties']) - - # TODO(qiangjiahui): Add tests for associate and disassociate volume type diff --git a/openstackclient/tests/functional/volume/v1/test_service.py b/openstackclient/tests/functional/volume/v1/test_service.py deleted file mode 100644 index 8d058c26fe..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_service.py +++ /dev/null @@ -1,76 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeServiceTests(common.BaseVolumeTests): - """Functional tests for volume service.""" - - def test_volume_service_list(self): - cmd_output = self.openstack('volume service list', parse_output=True) - - # Get the nonredundant services and hosts - services = list({x['Binary'] for x in cmd_output}) - - # Test volume service list --service - cmd_output = self.openstack( - 'volume service list ' + '--service ' + services[0], - parse_output=True, - ) - for x in cmd_output: - self.assertEqual(services[0], x['Binary']) - - # TODO(zhiyong.dai): test volume service list --host after solving - # https://bugs.launchpad.net/python-openstackclient/+bug/1664451 - - def test_volume_service_set(self): - # Get a service and host - cmd_output = self.openstack( - 'volume service list', - parse_output=True, - ) - service_1 = cmd_output[0]['Binary'] - host_1 = cmd_output[0]['Host'] - - # Test volume service set --enable - raw_output = self.openstack( - 'volume service set --enable ' + host_1 + ' ' + service_1 - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume service list --long', - parse_output=True, - ) - self.assertEqual('enabled', cmd_output[0]['Status']) - self.assertIsNone(cmd_output[0]['Disabled Reason']) - - # Test volume service set --disable and --disable-reason - disable_reason = 'disable_reason' - raw_output = self.openstack( - 'volume service set --disable ' - + '--disable-reason ' - + disable_reason - + ' ' - + host_1 - + ' ' - + service_1 - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume service list --long', - parse_output=True, - ) - self.assertEqual('disabled', cmd_output[0]['Status']) - self.assertEqual(disable_reason, cmd_output[0]['Disabled Reason']) diff --git a/openstackclient/tests/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py deleted file mode 100644 index 1acd014a64..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_snapshot.py +++ /dev/null @@ -1,232 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeSnapshotTests(common.BaseVolumeTests): - """Functional tests for volume snapshot.""" - - VOLLY = uuid.uuid4().hex - - @classmethod - def setUpClass(cls): - super().setUpClass() - # create a volume for all tests to create snapshot - cmd_output = cls.openstack( - 'volume create ' + '--size 1 ' + cls.VOLLY, - parse_output=True, - ) - cls.wait_for_status('volume', cls.VOLLY, 'available') - cls.VOLUME_ID = cmd_output['id'] - - @classmethod - def tearDownClass(cls): - try: - cls.wait_for_status('volume', cls.VOLLY, 'available') - raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) - cls.assertOutput('', raw_output) - finally: - super().tearDownClass() - - def test_volume_snapshot_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name1, - cmd_output["display_name"], - ) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name2, - cmd_output["display_name"], - ) - - self.wait_for_status('volume snapshot', name1, 'available') - self.wait_for_status('volume snapshot', name2, 'available') - - del_output = self.openstack( - 'volume snapshot delete ' + name1 + ' ' + name2 - ) - self.assertOutput('', del_output) - self.wait_for_delete('volume snapshot', name1) - self.wait_for_delete('volume snapshot', name2) - - def test_volume_snapshot_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name1) - self.assertEqual( - name1, - cmd_output["display_name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name1, 'available') - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name2) - self.assertEqual( - name2, - cmd_output["display_name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name2, 'available') - - # Test list --long, --status - cmd_output = self.openstack( - 'volume snapshot list ' + '--long ' + '--status error', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertNotIn(name1, names) - self.assertNotIn(name2, names) - - # Test list --volume - cmd_output = self.openstack( - 'volume snapshot list ' + '--volume ' + self.VOLLY, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test list --name - cmd_output = self.openstack( - 'volume snapshot list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_snapshot_set(self): - """Test create, set, unset, show, delete volume snapshot""" - name = uuid.uuid4().hex - new_name = name + "_" - cmd_output = self.openstack( - 'volume snapshot create ' - + '--volume ' - + self.VOLLY - + ' --description aaaa ' - + name, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name) - self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name) - self.assertEqual( - name, - cmd_output["display_name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["display_description"], - ) - self.wait_for_status('volume snapshot', name, 'available') - - # Test volume snapshot set - raw_output = self.openstack( - 'volume snapshot set ' - + '--name ' - + new_name - + ' --description bbbb ' - + '--property Alpha=a ' - + '--property Beta=b ' - + name, - ) - self.assertOutput('', raw_output) - - # Show snapshot set result - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual( - new_name, - cmd_output["display_name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'bbbb', - cmd_output["display_description"], - ) - self.assertEqual( - {'Alpha': 'a', 'Beta': 'b'}, - cmd_output["properties"], - ) - - # Test volume unset - raw_output = self.openstack( - 'volume snapshot unset ' + '--property Alpha ' + new_name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual( - {'Beta': 'b'}, - cmd_output["properties"], - ) - - # Test volume snapshot set --no-property - raw_output = self.openstack( - 'volume snapshot set ' + '--no-property ' + new_name, - ) - self.assertOutput('', raw_output) - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual({}, cmd_output["properties"]) diff --git a/openstackclient/tests/functional/volume/v1/test_transfer_request.py b/openstackclient/tests/functional/volume/v1/test_transfer_request.py deleted file mode 100644 index 68fe6666ff..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_transfer_request.py +++ /dev/null @@ -1,111 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class TransferRequestTests(common.BaseVolumeTests): - """Functional tests for transfer request.""" - - NAME = uuid.uuid4().hex - VOLUME_NAME = uuid.uuid4().hex - - @classmethod - def setUpClass(cls): - super().setUpClass() - cmd_output = cls.openstack( - 'volume create --size 1 ' + cls.VOLUME_NAME, - parse_output=True, - ) - cls.assertOutput(cls.VOLUME_NAME, cmd_output['name']) - - cls.wait_for_status("volume", cls.VOLUME_NAME, "available") - - @classmethod - def tearDownClass(cls): - try: - raw_output_volume = cls.openstack( - 'volume delete ' + cls.VOLUME_NAME - ) - cls.assertOutput('', raw_output_volume) - finally: - super().tearDownClass() - - def test_volume_transfer_request_accept(self): - volume_name = uuid.uuid4().hex - name = uuid.uuid4().hex - - # create a volume - cmd_output = self.openstack( - 'volume create --size 1 ' + volume_name, - parse_output=True, - ) - self.assertEqual(volume_name, cmd_output['name']) - - # create volume transfer request for the volume - # and get the auth_key of the new transfer request - cmd_output = self.openstack( - 'volume transfer request create ' - + volume_name - + ' --name ' - + name, - parse_output=True, - ) - auth_key = cmd_output['auth_key'] - self.assertTrue(auth_key) - - # accept the volume transfer request - output = self.openstack( - 'volume transfer request accept ' - + name - + ' ' - + '--auth-key ' - + auth_key, - parse_output=True, - ) - self.assertEqual(name, output.get('name')) - - # the volume transfer will be removed by default after accepted - # so just need to delete the volume here - raw_output = self.openstack('volume delete ' + volume_name) - self.assertEqual('', raw_output) - - def test_volume_transfer_request_list_show(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume transfer request create ' - + ' --name ' - + name - + ' ' - + self.VOLUME_NAME, - parse_output=True, - ) - self.addCleanup( - self.openstack, 'volume transfer request delete ' + name - ) - self.assertOutput(name, cmd_output['name']) - auth_key = cmd_output['auth_key'] - self.assertTrue(auth_key) - - cmd_output = self.openstack( - 'volume transfer request list', - parse_output=True, - ) - self.assertIn(name, [req['Name'] for req in cmd_output]) - - cmd_output = self.openstack( - 'volume transfer request show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) diff --git a/openstackclient/tests/functional/volume/v1/test_volume.py b/openstackclient/tests/functional/volume/v1/test_volume.py deleted file mode 100644 index 7c6cc03569..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_volume.py +++ /dev/null @@ -1,228 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeTests(common.BaseVolumeTests): - """Functional tests for volume.""" - - def test_volume_create_and_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 1 ' + name1, - parse_output=True, - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 2 ' + name2, - parse_output=True, - ) - self.assertEqual( - 2, - cmd_output["size"], - ) - - self.wait_for_status("volume", name1, "available") - self.wait_for_status("volume", name2, "available") - del_output = self.openstack('volume delete ' + name1 + ' ' + name2) - self.assertOutput('', del_output) - - def test_volume_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 1 ' + name1, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume delete ' + name1) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status("volume", name1, "available") - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 2 ' + name2, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume delete ' + name2) - self.assertEqual( - 2, - cmd_output["size"], - ) - self.wait_for_status("volume", name2, "available") - - # Test list - cmd_output = self.openstack( - 'volume list ', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test list --long - cmd_output = self.openstack( - 'volume list --long', - parse_output=True, - ) - bootable = [x["Bootable"] for x in cmd_output] - self.assertIn('false', bootable) - - # Test list --name - cmd_output = self.openstack( - 'volume list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_volume_set_and_unset(self): - """Tests create volume, set, unset, show, delete""" - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' - + '--size 1 ' - + '--description aaaa ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.assertEqual( - name, - cmd_output["name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["display_description"], - ) - self.assertEqual( - {'Alpha': 'a'}, - cmd_output["properties"], - ) - self.assertEqual( - 'false', - cmd_output["bootable"], - ) - self.wait_for_status("volume", name, "available") - - # Test volume set - new_name = uuid.uuid4().hex - self.addCleanup(self.openstack, 'volume delete ' + new_name) - raw_output = self.openstack( - 'volume set ' - + '--name ' - + new_name - + ' --size 2 ' - + '--description bbbb ' - + '--no-property ' - + '--property Beta=b ' - + '--property Gamma=c ' - + '--bootable ' - + name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume show ' + new_name, - parse_output=True, - ) - self.assertEqual( - new_name, - cmd_output["name"], - ) - self.assertEqual( - 2, - cmd_output["size"], - ) - self.assertEqual( - 'bbbb', - cmd_output["display_description"], - ) - self.assertEqual( - {'Beta': 'b', 'Gamma': 'c'}, - cmd_output["properties"], - ) - self.assertEqual( - 'true', - cmd_output["bootable"], - ) - - # Test volume unset - raw_output = self.openstack( - 'volume unset ' + '--property Beta ' + new_name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume show ' + new_name, - parse_output=True, - ) - self.assertEqual( - {'Gamma': 'c'}, - cmd_output["properties"], - ) - - def test_volume_create_and_list_and_show_backward_compatibility(self): - """Test backward compatibility of create, list, show""" - name1 = uuid.uuid4().hex - output = self.openstack( - 'volume create ' + '-c display_name -c id ' + '--size 1 ' + name1, - parse_output=True, - ) - self.assertIn('display_name', output) - self.assertEqual(name1, output['display_name']) - self.assertIn('id', output) - volume_id = output['id'] - self.assertIsNotNone(volume_id) - self.assertNotIn('name', output) - self.addCleanup(self.openstack, 'volume delete ' + volume_id) - - self.wait_for_status("volume", name1, "available") - - output = self.openstack( - 'volume list ' + '-c "Display Name"', - parse_output=True, - ) - for each_volume in output: - self.assertIn('Display Name', each_volume) - - output = self.openstack( - 'volume list ' + '-c "Name"', - parse_output=True, - ) - for each_volume in output: - self.assertIn('Name', each_volume) - - output = self.openstack( - 'volume show ' + '-c display_name -c id ' + name1, - parse_output=True, - ) - self.assertIn('display_name', output) - self.assertEqual(name1, output['display_name']) - self.assertIn('id', output) - self.assertNotIn('name', output) diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py deleted file mode 100644 index 97c8b66c1c..0000000000 --- a/openstackclient/tests/functional/volume/v1/test_volume_type.py +++ /dev/null @@ -1,213 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import time -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeTypeTests(common.BaseVolumeTests): - """Functional tests for volume type.""" - - def test_volume_type_create_list(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup( - self.openstack, - 'volume type delete ' + name, - ) - self.assertEqual(name, cmd_output['name']) - - cmd_output = self.openstack( - 'volume type show %s' % name, - parse_output=True, - ) - self.assertEqual(self.NAME, cmd_output['name']) - - cmd_output = self.openstack('volume type list', parse_output=True) - self.assertIn(self.NAME, [t['Name'] for t in cmd_output]) - - cmd_output = self.openstack( - 'volume type list --default', - parse_output=True, - ) - self.assertEqual(1, len(cmd_output)) - self.assertEqual('lvmdriver-1', cmd_output[0]['Name']) - - def test_volume_type_set_unset_properties(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume type delete ' + name) - self.assertEqual(name, cmd_output['name']) - - raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - 'volume type show %s' % name, - parse_output=True, - ) - self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - - raw_output = self.openstack('volume type unset --property a %s' % name) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - 'volume type show %s' % name, - parse_output=True, - ) - self.assertEqual({'c': 'd'}, cmd_output['properties']) - - def test_volume_type_set_unset_multiple_properties(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume type delete ' + name) - self.assertEqual(name, cmd_output['name']) - - raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - 'volume type show %s' % name, - parse_output=True, - ) - self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - - raw_output = self.openstack( - 'volume type unset --property a --property c %s' % name - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - 'volume type show %s' % name, - parse_output=True, - ) - self.assertEqual({}, cmd_output['properties']) - - def test_multi_delete(self): - vol_type1 = uuid.uuid4().hex - vol_type2 = uuid.uuid4().hex - self.openstack('volume type create %s' % vol_type1) - time.sleep(5) - self.openstack('volume type create %s' % vol_type2) - time.sleep(5) - cmd = f'volume type delete {vol_type1} {vol_type2}' - raw_output = self.openstack(cmd) - self.assertOutput('', raw_output) - - # 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 type commands. - def test_encryption_type(self): - encryption_type = uuid.uuid4().hex - # test create new encryption type - cmd_output = self.openstack( - 'volume type create ' - '--encryption-provider LuksEncryptor ' - '--encryption-cipher aes-xts-plain64 ' - '--encryption-key-size 128 ' - '--encryption-control-location front-end ' + encryption_type - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test show encryption type - cmd_output = self.openstack( - 'volume type show --encryption-type ' + encryption_type, - parse_output=True, - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test list encryption type - cmd_output = self.openstack( - 'volume type list --encryption-type', - parse_output=True, - ) - encryption_output = [ - t['Encryption'] for t in cmd_output if t['Name'] == encryption_type - ][0] - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, encryption_output[attr]) - # test set new encryption type - raw_output = self.openstack( - 'volume type set ' - '--encryption-provider LuksEncryptor ' - '--encryption-cipher aes-xts-plain64 ' - '--encryption-key-size 128 ' - '--encryption-control-location front-end ' + self.NAME - ) - self.assertEqual('', raw_output) - - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup( - self.openstack, - 'volume type delete ' + name, - ) - self.assertEqual(name, cmd_output['name']) - - cmd_output = self.openstack( - 'volume type show --encryption-type ' + name, - parse_output=True, - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test unset encryption type - raw_output = self.openstack( - 'volume type unset --encryption-type ' + name - ) - self.assertEqual('', raw_output) - cmd_output = self.openstack( - 'volume type show --encryption-type ' + name, - parse_output=True, - ) - self.assertEqual({}, cmd_output['encryption']) - # test delete encryption type - raw_output = self.openstack('volume type delete ' + encryption_type) - self.assertEqual('', raw_output) diff --git a/openstackclient/tests/functional/volume/v2/test_volume.py b/openstackclient/tests/functional/volume/v2/test_volume.py index 4667858751..32b60dfaac 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume.py +++ b/openstackclient/tests/functional/volume/v2/test_volume.py @@ -171,7 +171,7 @@ def test_volume_set_and_unset(self): cmd_output["volume_image_metadata"], ) self.assertEqual( - 'true', + True, cmd_output["bootable"], ) diff --git a/openstackclient/tests/functional/volume/v2/test_volume_backup.py b/openstackclient/tests/functional/volume/v2/test_volume_backup.py index 6bd37f4dc0..7ace71901c 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_backup.py @@ -56,4 +56,4 @@ def test_volume_backup_restore(self): self.wait_for_status( "volume", backup_restored['volume_id'], "available" ) - self.addCleanup(self.openstack, 'volume delete %s' % vol_id) + self.addCleanup(self.openstack, f'volume delete {vol_id}') diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py index bdbced00f6..80bf85a5bb 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py @@ -32,7 +32,7 @@ def test_volume_type_create_list(self): self.assertEqual(name, cmd_output['name']) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual(name, cmd_output['name']) @@ -57,19 +57,19 @@ def test_volume_type_set_unset_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - raw_output = self.openstack('volume type unset --property a %s' % name) + raw_output = self.openstack(f'volume type unset --property a {name}') self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'c': 'd'}, cmd_output['properties']) @@ -84,21 +84,21 @@ def test_volume_type_set_unset_multiple_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) raw_output = self.openstack( - 'volume type unset --property a --property c %s' % name + f'volume type unset --property a --property c {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({}, cmd_output['properties']) @@ -112,22 +112,20 @@ def test_volume_type_set_unset_project(self): self.addCleanup(self.openstack, 'volume type delete ' + name) self.assertEqual(name, cmd_output['name']) - raw_output = self.openstack( - 'volume type set --project admin %s' % name - ) + raw_output = self.openstack(f'volume type set --project admin {name}') self.assertEqual("", raw_output) raw_output = self.openstack( - 'volume type unset --project admin %s' % name + f'volume type unset --project admin {name}' ) self.assertEqual("", raw_output) def test_multi_delete(self): vol_type1 = uuid.uuid4().hex vol_type2 = uuid.uuid4().hex - self.openstack('volume type create %s' % vol_type1) + self.openstack(f'volume type create {vol_type1}') time.sleep(5) - self.openstack('volume type create %s' % vol_type2) + self.openstack(f'volume type create {vol_type2}') time.sleep(5) cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) diff --git a/openstackclient/tests/functional/volume/v3/test_volume.py b/openstackclient/tests/functional/volume/v3/test_volume.py index 244fe91c18..07a7959167 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume.py +++ b/openstackclient/tests/functional/volume/v3/test_volume.py @@ -124,7 +124,7 @@ def test_volume_set_and_unset(self): cmd_output["properties"], ) self.assertEqual( - 'false', + False, cmd_output["bootable"], ) self.wait_for_status("volume", name, "available") @@ -172,7 +172,7 @@ def test_volume_set_and_unset(self): cmd_output["volume_image_metadata"], ) self.assertEqual( - 'true', + True, cmd_output["bootable"], ) diff --git a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py index 2c9b72ba14..b84bb0368b 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py @@ -23,7 +23,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): super().setUpClass() - # create a volume for all tests to create snapshot + # create a test volume used by all snapshot tests cmd_output = cls.openstack( 'volume create ' + '--size 1 ' + cls.VOLLY, parse_output=True, @@ -40,147 +40,57 @@ def tearDownClass(cls): finally: super().tearDownClass() - def test_volume_snapshot_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name1, - cmd_output["name"], - ) + def test_volume_snapshot(self): + # create volume snapshot + name = uuid.uuid4().hex - name2 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + '--volume ' + + self.VOLLY + + ' --description aaaa ' + + '--property Alpha=a ' + + name, parse_output=True, ) - self.assertEqual( - name2, - cmd_output["name"], - ) - - self.wait_for_status('volume snapshot', name1, 'available') - self.wait_for_status('volume snapshot', name2, 'available') + snap_id = cmd_output['id'] - del_output = self.openstack( - 'volume snapshot delete ' + name1 + ' ' + name2 + self.addCleanup(self.wait_for_delete, 'volume snapshot', snap_id) + # delete volume snapshot + self.addCleanup( + self.openstack, + 'volume snapshot delete ' + snap_id, ) - self.assertOutput('', del_output) - self.wait_for_delete('volume snapshot', name1) - self.wait_for_delete('volume snapshot', name2) + self.wait_for_status('volume snapshot', snap_id, 'available') - def test_volume_snapshot_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, + # show volume snapshot + snapshot_info = self.openstack( + 'volume snapshot show ' + name, parse_output=True, ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name1) - self.assertEqual( - name1, - cmd_output["name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name1, 'available') - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name2) - self.assertEqual( - name2, - cmd_output["name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name2, 'available') - raw_output = self.openstack( - 'volume snapshot set ' + '--state error ' + name2 - ) - self.assertOutput('', raw_output) + self.assertEqual(name, snapshot_info['name']) + self.assertEqual('aaaa', snapshot_info["description"]) + self.assertEqual({'Alpha': 'a'}, snapshot_info["properties"]) - # Test list --long, --status + # list volume snapshot --name cmd_output = self.openstack( - 'volume snapshot list ' + '--long ' + '--status error', + 'volume snapshot list --name ' + name, parse_output=True, ) - names = [x["Name"] for x in cmd_output] - self.assertNotIn(name1, names) - self.assertIn(name2, names) + names = [x['Name'] for x in cmd_output] + self.assertIn(name, names) - # Test list --volume + # list volume snapshot --volume cmd_output = self.openstack( 'volume snapshot list ' + '--volume ' + self.VOLLY, parse_output=True, ) names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) + self.assertIn(name, names) - # Test list --name - cmd_output = self.openstack( - 'volume snapshot list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_volume_snapshot_set(self): - """Test create, set, unset, show, delete volume snapshot""" - name = uuid.uuid4().hex + # set volume snapshot new_name = name + "_" - cmd_output = self.openstack( - 'volume snapshot create ' - + '--volume ' - + self.VOLLY - + ' --description aaaa ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name) - self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name) - self.assertEqual( - name, - cmd_output["name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["description"], - ) - self.assertEqual( - {'Alpha': 'a'}, - cmd_output["properties"], - ) - self.wait_for_status('volume snapshot', name, 'available') - - # Test volume snapshot set raw_output = self.openstack( 'volume snapshot set ' + '--name ' @@ -188,11 +98,10 @@ def test_volume_snapshot_set(self): + ' --description bbbb ' + '--property Alpha=c ' + '--property Beta=b ' - + name, + + snap_id, ) self.assertOutput('', raw_output) - # Show snapshot set result cmd_output = self.openstack( 'volume snapshot show ' + new_name, parse_output=True, @@ -201,10 +110,6 @@ def test_volume_snapshot_set(self): new_name, cmd_output["name"], ) - self.assertEqual( - 1, - cmd_output["size"], - ) self.assertEqual( 'bbbb', cmd_output["description"], @@ -214,7 +119,7 @@ def test_volume_snapshot_set(self): cmd_output["properties"], ) - # Test volume snapshot unset + # unset volume snapshot raw_output = self.openstack( 'volume snapshot unset ' + '--property Alpha ' + new_name, ) @@ -229,16 +134,25 @@ def test_volume_snapshot_set(self): cmd_output["properties"], ) - # Test volume snapshot set --no-property + # set volume snapshot --no-property, --state error raw_output = self.openstack( - 'volume snapshot set ' + '--no-property ' + new_name, + 'volume snapshot set ' + + '--no-property ' + + '--state error ' + + new_name, ) self.assertOutput('', raw_output) + cmd_output = self.openstack( 'volume snapshot show ' + new_name, parse_output=True, ) - self.assertNotIn( - {'Beta': 'b'}, - cmd_output["properties"], + self.assertEqual({}, cmd_output["properties"]) + + # list volume snapshot --long --status + cmd_output = self.openstack( + 'volume snapshot list ' + '--long ' + '--status error', + parse_output=True, ) + names = [x["Name"] for x in cmd_output] + self.assertIn(new_name, names) diff --git a/openstackclient/tests/functional/volume/v3/test_volume_type.py b/openstackclient/tests/functional/volume/v3/test_volume_type.py index 78f9e4063f..421b3224f6 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_type.py @@ -32,7 +32,7 @@ def test_volume_type_create_list(self): self.assertEqual(name, cmd_output['name']) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual(name, cmd_output['name']) @@ -57,19 +57,19 @@ def test_volume_type_set_unset_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - raw_output = self.openstack('volume type unset --property a %s' % name) + raw_output = self.openstack(f'volume type unset --property a {name}') self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'c': 'd'}, cmd_output['properties']) @@ -84,21 +84,21 @@ def test_volume_type_set_unset_multiple_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) raw_output = self.openstack( - 'volume type unset --property a --property c %s' % name + f'volume type unset --property a --property c {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({}, cmd_output['properties']) @@ -112,22 +112,20 @@ def test_volume_type_set_unset_project(self): self.addCleanup(self.openstack, 'volume type delete ' + name) self.assertEqual(name, cmd_output['name']) - raw_output = self.openstack( - 'volume type set --project admin %s' % name - ) + raw_output = self.openstack(f'volume type set --project admin {name}') self.assertEqual("", raw_output) raw_output = self.openstack( - 'volume type unset --project admin %s' % name + f'volume type unset --project admin {name}' ) self.assertEqual("", raw_output) def test_multi_delete(self): vol_type1 = uuid.uuid4().hex vol_type2 = uuid.uuid4().hex - self.openstack('volume type create %s' % vol_type1) + self.openstack(f'volume type create {vol_type1}') time.sleep(5) - self.openstack('volume type create %s' % vol_type2) + self.openstack(f'volume type create {vol_type2}') time.sleep(5) cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 01ebf7b929..a609025b22 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -26,11 +26,10 @@ class TestSecurityGroup(utils.TestCase): - def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_security_group(self): sg_name = 'name-' + uuid.uuid4().hex @@ -44,15 +43,13 @@ def test_create_security_group(self): 'rules': [], } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_security_group( - self.compute_sdk_client, sg_name, sg_description + self.compute_client, sg_name, sg_description ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-security-groups', data={'name': sg_name, 'description': sg_description}, microversion='2.1', @@ -71,13 +68,11 @@ def test_list_security_groups(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_security_groups(self.compute_sdk_client) + result = compute.list_security_groups(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-security-groups', microversion='2.1' ) self.assertEqual(data['security_groups'], result) @@ -94,13 +89,13 @@ def test_find_security_group_by_id(self): 'rules': [], } } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(data=data), ] - result = compute.find_security_group(self.compute_sdk_client, sg_id) + result = compute.find_security_group(self.compute_client, sg_id) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call(f'/os-security-groups/{sg_id}', microversion='2.1'), ] @@ -121,14 +116,14 @@ def test_find_security_group_by_name(self): } ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] - result = compute.find_security_group(self.compute_sdk_client, sg_name) + result = compute.find_security_group(self.compute_client, sg_name) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call( f'/os-security-groups/{sg_name}', microversion='2.1' @@ -140,14 +135,14 @@ def test_find_security_group_by_name(self): def test_find_security_group_not_found(self): data = {'security_groups': []} - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] self.assertRaises( osc_lib_exceptions.NotFound, compute.find_security_group, - self.compute_sdk_client, + self.compute_client, 'invalid-sg', ) @@ -171,7 +166,7 @@ def test_find_security_group_by_name_duplicate(self): }, ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] @@ -179,7 +174,7 @@ def test_find_security_group_by_name_duplicate(self): self.assertRaises( osc_lib_exceptions.NotFound, compute.find_security_group, - self.compute_sdk_client, + self.compute_client, sg_name, ) @@ -196,15 +191,13 @@ def test_update_security_group(self): 'rules': [], } } - self.compute_sdk_client.put.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.put.return_value = fakes.FakeResponse(data=data) result = compute.update_security_group( - self.compute_sdk_client, sg_id, sg_name, sg_description + self.compute_client, sg_id, sg_name, sg_description ) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-security-groups/{sg_id}', data={'name': sg_name, 'description': sg_description}, microversion='2.1', @@ -213,13 +206,13 @@ def test_update_security_group(self): def test_delete_security_group(self): sg_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_security_group(self.compute_sdk_client, sg_id) + result = compute.delete_security_group(self.compute_client, sg_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-security-groups/{sg_id}', microversion='2.1', ) @@ -227,11 +220,10 @@ def test_delete_security_group(self): class TestSecurityGroupRule(utils.TestCase): - def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_security_group_rule(self): sg_id = uuid.uuid4().hex @@ -244,12 +236,10 @@ def test_create_security_group_rule(self): 'cidr': '10.0.0.0/24', } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_security_group_rule( - self.compute_sdk_client, + self.compute_client, security_group_id=sg_id, ip_protocol='tcp', from_port=22, @@ -258,7 +248,7 @@ def test_create_security_group_rule(self): remote_group=None, ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-security-group-rules', data={ 'parent_group_id': sg_id, @@ -274,15 +264,13 @@ def test_create_security_group_rule(self): def test_delete_security_group_rule(self): sg_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_security_group_rule( - self.compute_sdk_client, sg_id - ) + result = compute.delete_security_group_rule(self.compute_client, sg_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-security-group-rules/{sg_id}', microversion='2.1', ) @@ -290,11 +278,10 @@ def test_delete_security_group_rule(self): class TestNetwork(utils.TestCase): - def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_network(self): net_name = 'name-' + uuid.uuid4().hex @@ -308,18 +295,16 @@ def test_create_network(self): # other fields omitted for brevity } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_network( - self.compute_sdk_client, + self.compute_client, name=net_name, subnet=net_subnet, share_subnet=True, ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-networks', data={ 'label': net_name, @@ -340,13 +325,11 @@ def test_list_networks(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_networks(self.compute_sdk_client) + result = compute.list_networks(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-networks', microversion='2.1' ) self.assertEqual(data['networks'], result) @@ -361,13 +344,13 @@ def test_find_network_by_id(self): # other fields omitted for brevity } } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(data=data), ] - result = compute.find_network(self.compute_sdk_client, net_id) + result = compute.find_network(self.compute_client, net_id) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call(f'/os-networks/{net_id}', microversion='2.1'), ] @@ -386,14 +369,14 @@ def test_find_network_by_name(self): } ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] - result = compute.find_network(self.compute_sdk_client, net_name) + result = compute.find_network(self.compute_client, net_name) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call(f'/os-networks/{net_name}', microversion='2.1'), mock.call('/os-networks', microversion='2.1'), @@ -403,14 +386,14 @@ def test_find_network_by_name(self): def test_find_network_not_found(self): data = {'networks': []} - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] self.assertRaises( osc_lib_exceptions.NotFound, compute.find_network, - self.compute_sdk_client, + self.compute_client, 'invalid-net', ) @@ -430,7 +413,7 @@ def test_find_network_by_name_duplicate(self): }, ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] @@ -438,30 +421,29 @@ def test_find_network_by_name_duplicate(self): self.assertRaises( osc_lib_exceptions.NotFound, compute.find_network, - self.compute_sdk_client, + self.compute_client, net_name, ) def test_delete_network(self): net_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_network(self.compute_sdk_client, net_id) + result = compute.delete_network(self.compute_client, net_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-networks/{net_id}', microversion='2.1' ) self.assertIsNone(result) class TestFloatingIP(utils.TestCase): - def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_floating_ip(self): network = 'network-' + uuid.uuid4().hex @@ -474,15 +456,13 @@ def test_create_floating_ip(self): 'pool': network, } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_floating_ip( - self.compute_sdk_client, network=network + self.compute_client, network=network ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-floating-ips', data={'pool': network}, microversion='2.1' ) self.assertEqual(data['floating_ip'], result) @@ -499,13 +479,11 @@ def test_list_floating_ips(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_floating_ips(self.compute_sdk_client) + result = compute.list_floating_ips(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-floating-ips', microversion='2.1' ) self.assertEqual(data['floating_ips'], result) @@ -521,37 +499,36 @@ def test_get_floating_ip(self): 'pool': f'network-{uuid.uuid4().hex}', } } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(data=data), ] - result = compute.get_floating_ip(self.compute_sdk_client, fip_id) + result = compute.get_floating_ip(self.compute_client, fip_id) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( f'/os-floating-ips/{fip_id}', microversion='2.1' ) self.assertEqual(data['floating_ip'], result) def test_delete_floating_ip(self): fip_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_floating_ip(self.compute_sdk_client, fip_id) + result = compute.delete_floating_ip(self.compute_client, fip_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-floating-ips/{fip_id}', microversion='2.1' ) self.assertIsNone(result) class TestFloatingIPPool(utils.TestCase): - def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_list_floating_ip_pools(self): data = { @@ -561,13 +538,11 @@ def test_list_floating_ip_pools(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_floating_ip_pools(self.compute_sdk_client) + result = compute.list_floating_ip_pools(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-floating-ip-pools', microversion='2.1' ) self.assertEqual(data['floating_ip_pools'], result) diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py index 39bc26a61d..3ff22f5090 100644 --- a/openstackclient/tests/unit/api/test_object_store_v1.py +++ b/openstackclient/tests/unit/api/test_object_store_v1.py @@ -135,16 +135,18 @@ def test_container_list_full_listing(self): self.requests_mock.register_uri( 'GET', FAKE_URL - + '?marker=%s&limit=1&format=json' - % LIST_CONTAINER_RESP[0]['name'], + + '?marker={}&limit=1&format=json'.format( + LIST_CONTAINER_RESP[0]['name'] + ), json=[LIST_CONTAINER_RESP[1]], status_code=200, ) self.requests_mock.register_uri( 'GET', FAKE_URL - + '?marker=%s&limit=1&format=json' - % LIST_CONTAINER_RESP[1]['name'], + + '?marker={}&limit=1&format=json'.format( + LIST_CONTAINER_RESP[1]['name'] + ), json=[], status_code=200, ) diff --git a/openstackclient/tests/unit/api/test_volume_v2.py b/openstackclient/tests/unit/api/test_volume_v2.py new file mode 100644 index 0000000000..046d1cb9ba --- /dev/null +++ b/openstackclient/tests/unit/api/test_volume_v2.py @@ -0,0 +1,124 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Volume v2 API Library Tests""" + +import http +from unittest import mock +import uuid + +from openstack.block_storage.v2 import _proxy +from osc_lib import exceptions as osc_lib_exceptions + +from openstackclient.api import volume_v2 as volume +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +class TestConsistencyGroup(utils.TestCase): + def setUp(self): + super().setUp() + + self.volume_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_consistency_group_by_id(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroup': { + 'id': cg_id, + 'name': cg_name, + 'status': 'available', + 'availability_zone': 'az1', + 'created_at': '2015-09-16T09:28:52.000000', + 'description': 'description-' + uuid.uuid4().hex, + 'volume_types': ['123456'], + } + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_id) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_id}'), + ] + ) + self.assertEqual(data['consistencygroup'], result) + + def test_find_consistency_group_by_name(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': cg_id, + 'name': cg_name, + } + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_name) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_name}'), + mock.call('/consistencygroups'), + ] + ) + self.assertEqual(data['consistencygroups'][0], result) + + def test_find_consistency_group_not_found(self): + data = {'consistencygroups': []} + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + 'invalid-cg', + ) + + def test_find_consistency_group_by_name_duplicate(self): + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + cg_name, + ) diff --git a/openstackclient/tests/unit/api/test_volume_v3.py b/openstackclient/tests/unit/api/test_volume_v3.py new file mode 100644 index 0000000000..d70f899334 --- /dev/null +++ b/openstackclient/tests/unit/api/test_volume_v3.py @@ -0,0 +1,124 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Volume v3 API Library Tests""" + +import http +from unittest import mock +import uuid + +from openstack.block_storage.v3 import _proxy +from osc_lib import exceptions as osc_lib_exceptions + +from openstackclient.api import volume_v3 as volume +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +class TestConsistencyGroup(utils.TestCase): + def setUp(self): + super().setUp() + + self.volume_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_consistency_group_by_id(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroup': { + 'id': cg_id, + 'name': cg_name, + 'status': 'available', + 'availability_zone': 'az1', + 'created_at': '2015-09-16T09:28:52.000000', + 'description': 'description-' + uuid.uuid4().hex, + 'volume_types': ['123456'], + } + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_id) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_id}'), + ] + ) + self.assertEqual(data['consistencygroup'], result) + + def test_find_consistency_group_by_name(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': cg_id, + 'name': cg_name, + } + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_name) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_name}'), + mock.call('/consistencygroups'), + ] + ) + self.assertEqual(data['consistencygroups'][0], result) + + def test_find_consistency_group_not_found(self): + data = {'consistencygroups': []} + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + 'invalid-cg', + ) + + def test_find_consistency_group_by_name_duplicate(self): + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + cg_name, + ) diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index 0965c01ce4..d1383b5409 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -99,9 +99,7 @@ class TestAvailabilityZoneList( def setUp(self): super().setUp() - self.compute_sdk_client.availability_zones.return_value = ( - self.compute_azs - ) + self.compute_client.availability_zones.return_value = self.compute_azs self.volume_sdk_client.availability_zones.return_value = ( self.volume_azs ) @@ -120,9 +118,7 @@ def test_availability_zone_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_called_with( - details=True - ) + self.compute_client.availability_zones.assert_called_with(details=True) self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_called_with() @@ -150,9 +146,7 @@ def test_availability_zone_list_long(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_called_with( - details=True - ) + self.compute_client.availability_zones.assert_called_with(details=True) self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_called_with() @@ -186,9 +180,7 @@ def test_availability_zone_list_compute(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_called_with( - details=True - ) + self.compute_client.availability_zones.assert_called_with(details=True) self.volume_sdk_client.availability_zones.assert_not_called() self.network_client.availability_zones.assert_not_called() @@ -212,7 +204,7 @@ def test_availability_zone_list_volume(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_not_called() + self.compute_client.availability_zones.assert_not_called() self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_not_called() @@ -236,7 +228,7 @@ def test_availability_zone_list_network(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_not_called() + self.compute_client.availability_zones.assert_not_called() self.volume_sdk_client.availability_zones.assert_not_called() self.network_client.availability_zones.assert_called_with() diff --git a/openstackclient/tests/unit/common/test_command.py b/openstackclient/tests/unit/common/test_command.py index f31c711729..1f1efceadf 100644 --- a/openstackclient/tests/unit/common/test_command.py +++ b/openstackclient/tests/unit/common/test_command.py @@ -14,9 +14,9 @@ from unittest import mock -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.tests.unit import fakes as test_fakes from openstackclient.tests.unit import utils as test_utils @@ -31,7 +31,7 @@ def test_command_has_logger(self): cmd = FakeCommand(mock.Mock(), mock.Mock()) self.assertTrue(hasattr(cmd, 'log')) self.assertEqual( - 'openstackclient.tests.unit.common.test_command.' 'FakeCommand', + 'openstackclient.tests.unit.common.test_command.FakeCommand', cmd.log.name, ) diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py index 765903b3b6..dd684312c1 100644 --- a/openstackclient/tests/unit/common/test_extension.py +++ b/openstackclient/tests/unit/common/test_extension.py @@ -10,11 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.common import extension from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit import utils @@ -22,23 +20,13 @@ from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -class TestExtension(network_fakes.FakeClientMixin, utils.TestCommand): - def setUp(self): - super().setUp() - - identity_client = identity_fakes.FakeIdentityv2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.app.client_manager.identity = identity_client - self.identity_extensions_mock = identity_client.extensions - self.identity_extensions_mock.reset_mock() - - sdk_connection = self.app.client_manager.sdk_connection - self.compute_extensions_mock = sdk_connection.compute.extensions - self.compute_extensions_mock.reset_mock() - self.volume_extensions_mock = sdk_connection.volume.extensions - self.volume_extensions_mock.reset_mock() +class TestExtension( + network_fakes.FakeClientMixin, + compute_fakes.FakeClientMixin, + volume_fakes.FakeClientMixin, + identity_fakes.FakeClientMixin, + utils.TestCommand, +): ... class TestExtensionList(TestExtension): @@ -60,11 +48,13 @@ class TestExtensionList(TestExtension): def setUp(self): super().setUp() - self.identity_extensions_mock.list.return_value = [ + self.identity_client.extensions.list.return_value = [ self.identity_extension ] - self.compute_extensions_mock.return_value = [self.compute_extension] - self.volume_extensions_mock.return_value = [self.volume_extension] + self.compute_client.extensions.return_value = [self.compute_extension] + self.volume_sdk_client.extensions.return_value = [ + self.volume_extension + ] self.network_client.extensions.return_value = [self.network_extension] # Get the command object to test @@ -112,9 +102,9 @@ def test_extension_list_no_options(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.identity_extensions_mock.list.assert_called_with() - self.compute_extensions_mock.assert_called_with() - self.volume_extensions_mock.assert_called_with() + self.identity_client.extensions.list.assert_called_with() + self.compute_client.extensions.assert_called_with() + self.volume_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_long(self): @@ -159,9 +149,9 @@ def test_extension_list_long(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist, True) - self.identity_extensions_mock.list.assert_called_with() - self.compute_extensions_mock.assert_called_with() - self.volume_extensions_mock.assert_called_with() + self.identity_client.extensions.list.assert_called_with() + self.compute_client.extensions.assert_called_with() + self.volume_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_identity(self): @@ -179,7 +169,7 @@ def test_extension_list_identity(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.identity_extensions_mock.list.assert_called_with() + self.identity_client.extensions.list.assert_called_with() def test_extension_list_network(self): arglist = [ @@ -237,7 +227,7 @@ def test_extension_list_compute(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.compute_extensions_mock.assert_called_with() + self.compute_client.extensions.assert_called_with() def test_extension_list_compute_and_network(self): arglist = [ @@ -261,7 +251,7 @@ def test_extension_list_compute_and_network(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.compute_extensions_mock.assert_called_with() + self.compute_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_volume(self): @@ -279,7 +269,7 @@ def test_extension_list_volume(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.volume_extensions_mock.assert_called_with() + self.volume_sdk_client.extensions.assert_called_with() class TestExtensionShow(TestExtension): @@ -304,8 +294,8 @@ def setUp(self): self.cmd = extension.ShowExtension(self.app, None) - self.app.client_manager.network.find_extension = mock.Mock( - return_value=self.extension_details + self.app.client_manager.network.find_extension.return_value = ( + self.extension_details ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/common/test_limits.py b/openstackclient/tests/unit/common/test_limits.py index b7309ab7c6..a375a2ae5a 100644 --- a/openstackclient/tests/unit/common/test_limits.py +++ b/openstackclient/tests/unit/common/test_limits.py @@ -68,7 +68,7 @@ def setUp(self): ('DELETE', '*', 100, 100, 'MINUTE', '2011-12-15T22:42:45Z'), ] - self.compute_sdk_client.get_limits.return_value = self.fake_limits + self.compute_client.get_limits.return_value = self.fake_limits def test_compute_show_absolute(self): arglist = ['--absolute'] diff --git a/openstackclient/tests/unit/common/test_logs.py b/openstackclient/tests/unit/common/test_logs.py deleted file mode 100644 index 234ba427e9..0000000000 --- a/openstackclient/tests/unit/common/test_logs.py +++ /dev/null @@ -1,221 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release -# or Jun 2017. - -import logging -from unittest import mock - -from osc_lib import logs - -from openstackclient.tests.unit import utils - - -class TestContext(utils.TestCase): - def test_log_level_from_options(self): - opts = mock.Mock() - opts.verbose_level = 0 - self.assertEqual(logging.ERROR, logs.log_level_from_options(opts)) - opts.verbose_level = 1 - self.assertEqual(logging.WARNING, logs.log_level_from_options(opts)) - opts.verbose_level = 2 - self.assertEqual(logging.INFO, logs.log_level_from_options(opts)) - opts.verbose_level = 3 - self.assertEqual(logging.DEBUG, logs.log_level_from_options(opts)) - - def test_log_level_from_config(self): - cfg = {'verbose_level': 0} - self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 2} - self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 3} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'critical'} - self.assertEqual(logging.CRITICAL, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'error'} - self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'warning'} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'info'} - self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'debug'} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'bogus'} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'info', 'debug': True} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - - @mock.patch('warnings.simplefilter') - def test_set_warning_filter(self, simplefilter): - logs.set_warning_filter(logging.ERROR) - simplefilter.assert_called_with("ignore") - logs.set_warning_filter(logging.WARNING) - simplefilter.assert_called_with("ignore") - logs.set_warning_filter(logging.INFO) - simplefilter.assert_called_with("once") - - -class TestFileFormatter(utils.TestCase): - def test_nothing(self): - formatter = logs._FileFormatter() - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s %(message)s' - ), - formatter.fmt, - ) - - def test_options(self): - class Opts: - cloud = 'cloudy' - os_project_name = 'projecty' - username = 'usernamey' - - options = Opts() - formatter = logs._FileFormatter(options=options) - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [cloudy usernamey projecty] %(message)s' - ), - formatter.fmt, - ) - - def test_config(self): - config = mock.Mock() - config.config = {'cloud': 'cloudy'} - config.auth = {'project_name': 'projecty', 'username': 'usernamey'} - formatter = logs._FileFormatter(config=config) - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [cloudy usernamey projecty] %(message)s' - ), - formatter.fmt, - ) - - -class TestLogConfigurator(utils.TestCase): - def setUp(self): - super().setUp() - self.options = mock.Mock() - self.options.verbose_level = 1 - self.options.log_file = None - self.options.debug = False - self.root_logger = mock.Mock() - self.root_logger.setLevel = mock.Mock() - self.root_logger.addHandler = mock.Mock() - self.requests_log = mock.Mock() - self.requests_log.setLevel = mock.Mock() - self.cliff_log = mock.Mock() - self.cliff_log.setLevel = mock.Mock() - self.stevedore_log = mock.Mock() - self.stevedore_log.setLevel = mock.Mock() - self.iso8601_log = mock.Mock() - self.iso8601_log.setLevel = mock.Mock() - self.loggers = [ - self.root_logger, - self.requests_log, - self.cliff_log, - self.stevedore_log, - self.iso8601_log, - ] - - @mock.patch('logging.StreamHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - def test_init(self, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - console_logger = mock.Mock() - console_logger.setFormatter = mock.Mock() - console_logger.setLevel = mock.Mock() - handle.return_value = console_logger - - configurator = logs.LogConfigurator(self.options) - - getLogger.assert_called_with('iso8601') # last call - warning_filter.assert_called_with(logging.WARNING) - self.root_logger.setLevel.assert_called_with(logging.DEBUG) - self.root_logger.addHandler.assert_called_with(console_logger) - self.requests_log.setLevel.assert_called_with(logging.ERROR) - self.cliff_log.setLevel.assert_called_with(logging.ERROR) - self.stevedore_log.setLevel.assert_called_with(logging.ERROR) - self.iso8601_log.setLevel.assert_called_with(logging.ERROR) - self.assertFalse(configurator.dump_trace) - - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - def test_init_no_debug(self, warning_filter, getLogger): - getLogger.side_effect = self.loggers - self.options.debug = True - - configurator = logs.LogConfigurator(self.options) - - warning_filter.assert_called_with(logging.DEBUG) - self.requests_log.setLevel.assert_called_with(logging.DEBUG) - self.assertTrue(configurator.dump_trace) - - @mock.patch('logging.FileHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - @mock.patch('osc_lib.logs._FileFormatter') - def test_init_log_file(self, formatter, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - self.options.log_file = '/tmp/log_file' - file_logger = mock.Mock() - file_logger.setFormatter = mock.Mock() - file_logger.setLevel = mock.Mock() - handle.return_value = file_logger - mock_formatter = mock.Mock() - formatter.return_value = mock_formatter - - logs.LogConfigurator(self.options) - - handle.assert_called_with(filename=self.options.log_file) - self.root_logger.addHandler.assert_called_with(file_logger) - file_logger.setFormatter.assert_called_with(mock_formatter) - file_logger.setLevel.assert_called_with(logging.WARNING) - - @mock.patch('logging.FileHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - @mock.patch('osc_lib.logs._FileFormatter') - def test_configure(self, formatter, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - configurator = logs.LogConfigurator(self.options) - cloud_config = mock.Mock() - config_log = '/tmp/config_log' - cloud_config.config = { - 'log_file': config_log, - 'verbose_level': 1, - 'log_level': 'info', - } - file_logger = mock.Mock() - file_logger.setFormatter = mock.Mock() - file_logger.setLevel = mock.Mock() - handle.return_value = file_logger - mock_formatter = mock.Mock() - formatter.return_value = mock_formatter - - configurator.configure(cloud_config) - - warning_filter.assert_called_with(logging.INFO) - handle.assert_called_with(filename=config_log) - self.root_logger.addHandler.assert_called_with(file_logger) - file_logger.setFormatter.assert_called_with(mock_formatter) - file_logger.setLevel.assert_called_with(logging.INFO) - self.assertFalse(configurator.dump_trace) diff --git a/openstackclient/tests/unit/common/test_module.py b/openstackclient/tests/unit/common/test_module.py index 476538a194..8396203681 100644 --- a/openstackclient/tests/unit/common/test_module.py +++ b/openstackclient/tests/unit/common/test_module.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Test module module""" @@ -19,10 +18,18 @@ from unittest import mock from openstackclient.common import module as osc_module -from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils +class FakeModule: + def __init__(self, name, version): + self.name = name + self.__version__ = version + # Workaround for openstacksdk case + self.version = mock.Mock() + self.version.__version__ = version + + # NOTE(dtroyer): module_1 must match the version list filter (not --all) # currently == '*client*' module_name_1 = 'fakeclient' @@ -45,11 +52,11 @@ MODULES = { 'sys': sys, - module_name_1: fakes.FakeModule(module_name_1, module_version_1), - module_name_2: fakes.FakeModule(module_name_2, module_version_2), - module_name_3: fakes.FakeModule(module_name_3, module_version_3), - module_name_4: fakes.FakeModule(module_name_4, module_version_4), - module_name_5: fakes.FakeModule(module_name_5, module_version_5), + module_name_1: FakeModule(module_name_1, module_version_1), + module_name_2: FakeModule(module_name_2, module_version_2), + module_name_3: FakeModule(module_name_3, module_version_3), + module_name_4: FakeModule(module_name_4, module_version_4), + module_name_5: FakeModule(module_name_5, module_version_5), } diff --git a/openstackclient/tests/unit/common/test_project_cleanup.py b/openstackclient/tests/unit/common/test_project_cleanup.py index 019d6cf157..42020646cf 100644 --- a/openstackclient/tests/unit/common/test_project_cleanup.py +++ b/openstackclient/tests/unit/common/test_project_cleanup.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from io import StringIO from unittest import mock from openstackclient.common import project_cleanup @@ -70,7 +69,7 @@ def test_project_cleanup_with_filters(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -83,7 +82,12 @@ def test_project_cleanup_with_filters(self): filters=filters, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters=filters), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters=filters, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -114,7 +118,12 @@ def test_project_cleanup_with_auto_approve(self): filters={}, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -133,7 +142,7 @@ def test_project_cleanup_with_project(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -144,7 +153,12 @@ def test_project_cleanup_with_project(self): filters={}, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -163,7 +177,7 @@ def test_project_cleanup_with_project_abort(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('n')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -219,7 +233,7 @@ def test_project_cleanup_with_auth_project(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_not_called() @@ -230,7 +244,12 @@ def test_project_cleanup_with_auth_project(self): filters={}, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -248,7 +267,7 @@ def test_project_cleanup_with_skip_resource(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -260,7 +279,12 @@ def test_project_cleanup_with_skip_resource(self): filters={}, skip_resources=[skip_resource], ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=[skip_resource], + ), ] self.project_cleanup_mock.assert_has_calls(calls) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index e18161d4e4..7bfb2e7f2f 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy from unittest import mock from openstack.block_storage.v3 import quota_set as _volume_quota_set @@ -98,7 +99,7 @@ def setUp(self): _compute_quota_set.QuotaSet ) # the defaults are global hence use of return_value here - self.compute_sdk_client.get_quota_set_defaults.return_value = ( + self.compute_client.get_quota_set_defaults.return_value = ( self.default_compute_quotas ) self.compute_reference_data = ( @@ -164,7 +165,7 @@ def setUp(self): def test_quota_list_compute(self): # Two projects with non-default quotas - self.compute_sdk_client.get_quota_set.side_effect = self.compute_quotas + self.compute_client.get_quota_set.side_effect = self.compute_quotas arglist = [ '--compute', @@ -183,7 +184,7 @@ def test_quota_list_compute(self): def test_quota_list_compute_default(self): # One of the projects is at defaults - self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_client.get_quota_set.side_effect = [ self.compute_quotas[0], self.default_compute_quotas, ] @@ -205,7 +206,7 @@ def test_quota_list_compute_default(self): def test_quota_list_compute_project_not_found(self): # Make one of the projects disappear - self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_client.get_quota_set.side_effect = [ self.compute_quotas[0], sdk_exceptions.NotFoundException("NotFound"), ] @@ -227,7 +228,7 @@ def test_quota_list_compute_project_not_found(self): def test_quota_list_compute_project_inaccessible(self): # Make one of the projects inaccessible - self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_client.get_quota_set.side_effect = [ self.compute_quotas[0], sdk_exceptions.ForbiddenException("Forbidden"), ] @@ -249,7 +250,7 @@ def test_quota_list_compute_project_inaccessible(self): def test_quota_list_compute_server_error(self): # Make the server "break" - self.compute_sdk_client.get_quota_set.side_effect = ( + self.compute_client.get_quota_set.side_effect = ( sdk_exceptions.HttpException("Not implemented?") ) @@ -470,7 +471,7 @@ def test_quota_set(self): 'server_group_members': servgroup_members_num, } - self.compute_sdk_client.update_quota_set.assert_called_once_with( + self.compute_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs ) self.assertIsNone(result) @@ -744,7 +745,7 @@ def test_quota_set_with_class(self): 'volumes': volumes, } - self.compute_sdk_client.update_quota_class_set.assert_called_with( + self.compute_client.update_quota_class_set.assert_called_with( self.projects[0].name, **kwargs_compute ) self.volume_sdk_client.update_quota_class_set.assert_called_with( @@ -842,7 +843,7 @@ def test_quota_set_default(self): 'volumes': volumes, } - self.compute_sdk_client.update_quota_class_set.assert_called_with( + self.compute_client.update_quota_class_set.assert_called_with( 'default', **kwargs_compute ) self.volume_sdk_client.update_quota_class_set.assert_called_with( @@ -899,7 +900,7 @@ def test_quota_set_with_force(self): 'subnet': subnet, 'force': True, } - self.compute_sdk_client.update_quota_set.assert_called_once_with( + self.compute_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_compute ) self.volume_sdk_client.update_quota_set.assert_called_once_with( @@ -942,7 +943,7 @@ def test_quota_set_with_no_force(self): 'subnet': 10, 'check_limit': True, } - self.compute_sdk_client.update_quota_set.assert_called_once_with( + self.compute_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_compute ) self.volume_sdk_client.update_quota_set.assert_called_once_with( @@ -955,18 +956,35 @@ def test_quota_set_with_no_force(self): class TestQuotaShow(TestQuota): + _network_quota_details = { + 'floating_ips': {'limit': 0, 'reserved': 0, 'used': 0}, + 'health_monitors': {'limit': 0, 'reserved': 0, 'used': 0}, + 'l7_policies': {'limit': 0, 'reserved': 0, 'used': 0}, + 'listeners': {'limit': 0, 'reserved': 0, 'used': 0}, + 'load_balancers': {'limit': 0, 'reserved': 0, 'used': 0}, + 'networks': {'limit': 0, 'reserved': 0, 'used': 0}, + 'pools': {'limit': 0, 'reserved': 0, 'used': 0}, + 'ports': {'limit': 0, 'reserved': 0, 'used': 0}, + 'rbac_policies': {'limit': 0, 'reserved': 0, 'used': 0}, + 'routers': {'limit': 0, 'reserved': 0, 'used': 0}, + 'security_group_rules': {'limit': 0, 'reserved': 0, 'used': 0}, + 'security_groups': {'limit': 0, 'reserved': 0, 'used': 0}, + 'subnet_pools': {'limit': 0, 'reserved': 0, 'used': 0}, + 'subnets': {'limit': 0, 'reserved': 0, 'used': 0}, + } + def setUp(self): super().setUp() self.identity_sdk_client.find_project.return_value = self.projects[0] - self.compute_sdk_client.get_quota_set.return_value = ( + self.compute_client.get_quota_set.return_value = ( sdk_fakes.generate_fake_resource(_compute_quota_set.QuotaSet) ) self.default_compute_quotas = sdk_fakes.generate_fake_resource( _compute_quota_set.QuotaSet ) - self.compute_sdk_client.get_quota_set_defaults.return_value = ( + self.compute_client.get_quota_set_defaults.return_value = ( self.default_compute_quotas ) @@ -980,9 +998,15 @@ def setUp(self): self.default_volume_quotas ) - self.network_client.get_quota.return_value = ( - sdk_fakes.generate_fake_resource(_network_quota_set.Quota) - ) + def get_network_quota_mock(*args, **kwargs): + if kwargs.get("details"): + return sdk_fakes.generate_fake_resource( + _network_quota_set.QuotaDetails, + **self._network_quota_details, + ) + return sdk_fakes.generate_fake_resource(_network_quota_set.Quota) + + self.network_client.get_quota.side_effect = get_network_quota_mock self.default_network_quotas = sdk_fakes.generate_fake_resource( _network_quota_set.QuotaDefault ) @@ -1004,7 +1028,7 @@ def test_quota_show(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, ) @@ -1018,6 +1042,26 @@ def test_quota_show(self): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__missing_services(self): + self.app.client_manager.compute_endpoint_enabled = False + self.app.client_manager.volume_endpoint_enabled = False + self.app.client_manager.network_endpoint_enabled = False + + arglist = [ + self.projects[0].name, + ] + verifylist = [ + ('service', 'all'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.compute_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_not_called() + def test_quota_show__with_compute(self): arglist = [ '--compute', @@ -1031,7 +1075,7 @@ def test_quota_show__with_compute(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, ) @@ -1051,7 +1095,7 @@ def test_quota_show__with_volume(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_not_called() + self.compute_client.get_quota_set.assert_not_called() self.volume_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, @@ -1071,7 +1115,7 @@ def test_quota_show__with_network(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_not_called() + self.compute_client.get_quota_set.assert_not_called() self.volume_sdk_client.get_quota_set.assert_not_called() self.network_client.get_quota.assert_called_once_with( self.projects[0].id, @@ -1079,6 +1123,64 @@ def test_quota_show__with_network(self): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__with_network_and_usage(self): + # ensure we do not interfere with other tests + self._network_quota_details = copy.deepcopy( + self._network_quota_details + ) + # set a couple of resources + self._network_quota_details["floating_ips"].update( + limit=30, reserved=20, used=7 + ) + self._network_quota_details["security_group_rules"].update( + limit=9, reserved=7, used=5 + ) + + arglist = [ + '--network', + '--usage', + self.projects[0].name, + ] + verifylist = [ + ('service', 'network'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, result_gen = self.cmd.take_action(parsed_args) + + self.assertEqual(('Resource', 'Limit', 'In Use', 'Reserved'), headers) + + result = sorted(result_gen) + + self.assertEqual( + [ + ('floating-ips', 30, 7, 20), + ('health_monitors', 0, 0, 0), + ('l7_policies', 0, 0, 0), + ('listeners', 0, 0, 0), + ('load_balancers', 0, 0, 0), + ('networks', 0, 0, 0), + ('pools', 0, 0, 0), + ('ports', 0, 0, 0), + ('rbac_policies', 0, 0, 0), + ('routers', 0, 0, 0), + ('secgroup-rules', 9, 5, 7), + ('secgroups', 0, 0, 0), + ('subnet_pools', 0, 0, 0), + ('subnets', 0, 0, 0), + ], + result, + ) + + self.compute_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_called_once_with( + self.projects[0].id, + details=True, + ) + self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__with_default(self): arglist = [ '--default', @@ -1092,7 +1194,7 @@ def test_quota_show__with_default(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set_defaults.assert_called_once_with( + self.compute_client.get_quota_set_defaults.assert_called_once_with( self.projects[0].id, ) self.volume_sdk_client.get_quota_set_defaults.assert_called_once_with( @@ -1116,7 +1218,7 @@ def test_quota_show__with_usage(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=True, ) @@ -1138,7 +1240,7 @@ def test_quota_show__no_project(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[1].id, usage=False ) self.volume_sdk_client.get_quota_set.assert_called_once_with( @@ -1158,7 +1260,7 @@ def setUp(self): self.identity_sdk_client.find_project.return_value = self.projects[0] - self.compute_sdk_client.revert_quota_set.return_value = None + self.compute_client.revert_quota_set.return_value = None self.volume_sdk_client.revert_quota_set.return_value = None self.network_client.delete_quota.return_value = None @@ -1182,7 +1284,7 @@ def test_delete(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_called_once_with( + self.compute_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) self.volume_sdk_client.revert_quota_set.assert_called_once_with( @@ -1211,7 +1313,7 @@ def test_delete__compute(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_called_once_with( + self.compute_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) self.volume_sdk_client.revert_quota_set.assert_not_called() @@ -1236,7 +1338,7 @@ def test_delete__volume(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_not_called() + self.compute_client.revert_quota_set.assert_not_called() self.volume_sdk_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) @@ -1261,7 +1363,7 @@ def test_delete__network(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_not_called() + self.compute_client.revert_quota_set.assert_not_called() self.volume_sdk_client.revert_quota_set.assert_not_called() self.network_client.delete_quota.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 d021c5d760..37f93b772b 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -13,7 +13,6 @@ # under the License. # -import copy import random import re from unittest import mock @@ -21,97 +20,30 @@ from keystoneauth1 import discover from openstack.compute.v2 import _proxy -from openstack.compute.v2 import aggregate as _aggregate from openstack.compute.v2 import availability_zone as _availability_zone from openstack.compute.v2 import extension as _extension from openstack.compute.v2 import flavor as _flavor -from openstack.compute.v2 import hypervisor as _hypervisor -from openstack.compute.v2 import keypair as _keypair from openstack.compute.v2 import limits as _limits from openstack.compute.v2 import migration as _migration from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_action as _server_action -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 server_migration as _server_migration -from openstack.compute.v2 import service as _service -from openstack.compute.v2 import usage as _usage from openstack.compute.v2 import volume_attachment as _volume_attachment -from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_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 from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -class FakeComputev2Client: - def __init__(self, **kwargs): - self.agents = mock.Mock() - self.agents.resource_class = fakes.FakeResource(None, {}) - - self.images = mock.Mock() - self.images.resource_class = fakes.FakeResource(None, {}) - - self.servers = mock.Mock() - self.servers.resource_class = fakes.FakeResource(None, {}) - - self.services = mock.Mock() - self.services.resource_class = fakes.FakeResource(None, {}) - - self.extensions = mock.Mock() - self.extensions.resource_class = fakes.FakeResource(None, {}) - - self.flavors = mock.Mock() - - self.flavor_access = mock.Mock() - self.flavor_access.resource_class = fakes.FakeResource(None, {}) - - self.usage = mock.Mock() - self.usage.resource_class = fakes.FakeResource(None, {}) - - self.volumes = mock.Mock() - self.volumes.resource_class = fakes.FakeResource(None, {}) - - self.hypervisors = mock.Mock() - self.hypervisors.resource_class = fakes.FakeResource(None, {}) - - self.hypervisors_stats = mock.Mock() - self.hypervisors_stats.resource_class = fakes.FakeResource(None, {}) - - self.keypairs = mock.Mock() - self.keypairs.resource_class = fakes.FakeResource(None, {}) - - self.server_groups = mock.Mock() - self.server_groups.resource_class = fakes.FakeResource(None, {}) - - self.server_migrations = mock.Mock() - self.server_migrations.resource_class = fakes.FakeResource(None, {}) - - self.instance_action = mock.Mock() - self.instance_action.resource_class = fakes.FakeResource(None, {}) - - self.migrations = mock.Mock() - self.migrations.resource_class = fakes.FakeResource(None, {}) - - self.auth_token = kwargs['token'] - - self.management_url = kwargs['endpoint'] - - class FakeClientMixin: def setUp(self): super().setUp() - # TODO(stephenfin): Rename to 'compute_client' once all commands are - # migrated to SDK - self.app.client_manager.sdk_connection.compute = mock.Mock( - _proxy.Proxy - ) - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) + self.app.client_manager.compute = mock.Mock(_proxy.Proxy) + self.compute_client = self.app.client_manager.compute self.set_compute_api_version() # default to the lowest def set_compute_api_version(self, version: str = '2.1'): @@ -123,8 +55,8 @@ def set_compute_api_version(self, version: str = '2.1'): """ assert re.match(r'2.\d+', version) - self.compute_sdk_client.default_microversion = version - self.compute_sdk_client.get_endpoint_data.return_value = ( + self.compute_client.default_microversion = version + self.compute_client.get_endpoint_data.return_value = ( discover.EndpointData( min_microversion='2.1', # nova has not bumped this yet max_microversion=version, @@ -133,85 +65,26 @@ def set_compute_api_version(self, version: str = '2.1'): class TestComputev2( + identity_fakes.FakeClientMixin, network_fakes.FakeClientMixin, image_fakes.FakeClientMixin, volume_fakes.FakeClientMixin, - identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, ): ... -def create_one_aggregate(attrs=None): - """Create a fake aggregate. - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.aggregate.Aggregate object - """ - attrs = attrs or {} - - # Set default attribute - aggregate_info = { - "name": "aggregate-name-" + uuid.uuid4().hex, - "availability_zone": "ag_zone", - "hosts": [], - "id": "aggregate-id-" + uuid.uuid4().hex, - "metadata": { - "availability_zone": "ag_zone", - "key1": "value1", - }, - } - - # Overwrite default attributes. - aggregate_info.update(attrs) - - aggregate = _aggregate.Aggregate(**aggregate_info) - return aggregate - - -def create_aggregates(attrs=None, count=2): - """Create multiple fake aggregates. - - :param dict attrs: A dictionary with all attributes - :param int count: The number of aggregates to fake - :return: A list of fake openstack.compute.v2.aggregate.Aggregate objects - """ - aggregates = [] - for i in range(0, count): - aggregates.append(create_one_aggregate(attrs)) - - return aggregates - - -def get_aggregates(aggregates=None, count=2): - """Get an iterable MagicMock object with a list of faked aggregates. - - If aggregates list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :return: A list of fake openstack.compute.v2.aggregate.Aggregate objects - :param int count: The number of aggregates to fake - :return: An iterable Mock object with side_effect set to a list of faked - aggregates - """ - if aggregates is None: - aggregates = create_aggregates(count) - return mock.Mock(side_effect=aggregates) - - def create_one_agent(attrs=None): """Create a fake agent. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with agent_id, os, and so on + :param dict attrs: A dictionary with all attributes + :return: A dicionarty faking the agent """ attrs = attrs or {} # set default attributes. - agent_info = { + agent_attrs = { 'agent_id': 'agent-id-' + uuid.uuid4().hex, 'os': 'agent-os-' + uuid.uuid4().hex, 'architecture': 'agent-architecture', @@ -221,22 +94,20 @@ def create_one_agent(attrs=None): 'hypervisor': 'hypervisor', } + assert not set(attrs) - set(agent_attrs), 'unknown keys' + # Overwrite default attributes. - agent_info.update(attrs) + agent_attrs.update(attrs) - agent = fakes.FakeResource(info=copy.deepcopy(agent_info), loaded=True) - return agent + return agent_attrs def create_agents(attrs=None, count=2): """Create multiple fake agents. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of agents to fake - :return: - A list of FakeResource objects faking the agents + :param dict attrs: A dictionary with all attributes + :param int count: The number of agents to fake + :return: A list of dictionaries faking the agents """ agents = [] for i in range(0, count): @@ -249,7 +120,7 @@ def create_one_extension(attrs=None): """Create a fake extension. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.extension.Extension object + :return: A fake :class:`~openstack.compute.v2.extension.Extension` object """ attrs = attrs or {} @@ -281,10 +152,8 @@ def create_one_extension(attrs=None): def create_one_security_group(attrs=None): """Create a fake security group. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, name, etc. + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the security group """ attrs = attrs or {} @@ -297,6 +166,8 @@ def create_one_security_group(attrs=None): 'rules': [], } + assert not set(attrs) - set(security_group_attrs), 'unknown keys' + # Overwrite default attributes. security_group_attrs.update(attrs) return security_group_attrs @@ -305,12 +176,9 @@ def create_one_security_group(attrs=None): def create_security_groups(attrs=None, count=2): """Create multiple fake security groups. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of security groups to fake - :return: - A list of FakeResource objects faking the security groups + :param dict attrs: A dictionary with all attributes + :param int count: The number of security groups to fake + :return: A list of dictionaries faking the security groups """ security_groups = [] for i in range(0, count): @@ -319,32 +187,11 @@ def create_security_groups(attrs=None, count=2): return security_groups -def get_security_groups(security_groups=None, count=2): - """Get an iterable MagicMock with a list of faked security groups. - - If security groups list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List security_groups: - A list of FakeResource objects faking security groups - :param int count: - The number of security groups to fake - :return: - An iterable Mock object with side_effect set to a list of faked - security groups - """ - if security_groups is None: - security_groups = create_security_groups(count) - return mock.Mock(side_effect=security_groups) - - def create_one_security_group_rule(attrs=None): """Create a fake security group rule. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, etc. + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the security group rule """ attrs = attrs or {} @@ -359,6 +206,8 @@ def create_one_security_group_rule(attrs=None): 'to_port': 0, } + assert not set(attrs) - set(security_group_rule_attrs), 'unknown keys' + # Overwrite default attributes. security_group_rule_attrs.update(attrs) @@ -368,12 +217,9 @@ def create_one_security_group_rule(attrs=None): def create_security_group_rules(attrs=None, count=2): """Create multiple fake security group rules. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of security group rules to fake - :return: - A list of FakeResource objects faking the security group rules + :param dict attrs: A dictionary with all attributes + :param int count: The number of security group rules to fake + :return: A list of dictionaries faking the security group rules """ security_group_rules = [] for i in range(0, count): @@ -382,66 +228,11 @@ def create_security_group_rules(attrs=None, count=2): return security_group_rules -def create_one_server(attrs=None, methods=None): - """Create a fake server. - - :param dict attrs: - A dictionary with all attributes - :param dict methods: - A dictionary with all methods - :return: - A FakeResource object, with id, name, metadata, and so on - """ - attrs = attrs or {} - methods = methods or {} - - # Set default attributes. - server_info = { - 'id': 'server-id-' + uuid.uuid4().hex, - 'name': 'server-name-' + uuid.uuid4().hex, - 'metadata': {}, - 'image': { - 'id': 'image-id-' + uuid.uuid4().hex, - }, - 'flavor': { - 'id': 'flavor-id-' + uuid.uuid4().hex, - }, - 'OS-EXT-STS:power_state': 1, - } - - # Overwrite default attributes. - server_info.update(attrs) - - server = fakes.FakeResource( - info=copy.deepcopy(server_info), methods=methods, loaded=True - ) - return server - - -def create_servers(attrs=None, methods=None, count=2): - """Create multiple fake servers. - - :param dict attrs: - A dictionary with all attributes - :param dict methods: - A dictionary with all methods - :param int count: - The number of servers to fake - :return: - A list of FakeResource objects faking the servers - """ - servers = [] - for i in range(0, count): - servers.append(create_one_server(attrs, methods)) - - return servers - - -def create_one_sdk_server(attrs=None): - """Create a fake server for testing migration to sdk +def create_one_server(attrs=None): + """Create a fake server :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.server.Server object, + :return: A fake :class:`~openstack.compute.v2.server.Server` object, """ attrs = attrs or {} @@ -469,43 +260,26 @@ def create_one_sdk_server(attrs=None): return server -def create_sdk_servers(attrs=None, count=2): - """Create multiple fake servers for testing migration to sdk +def create_servers(attrs=None, count=2): + """Create multiple fake servers :param dict attrs: A dictionary with all attributes :param int count: The number of servers to fake - :return: A list of fake openstack.compute.v2.server.Server objects + :return: A list of fake :class:`openstack.compute.v2.server.Server` objects """ servers = [] for i in range(0, count): - servers.append(create_one_sdk_server(attrs)) + servers.append(create_one_server(attrs)) return servers -def get_servers(servers=None, count=2): - """Get an iterable MagicMock object with a list of faked servers. - - If servers list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param list servers: A list of fake openstack.compute.v2.server.Server - objects - :param int count: - The number of servers to fake - :return: An iterable Mock object with side_effect set to a list of faked - servers - """ - if servers is None: - servers = create_servers(count) - return mock.Mock(side_effect=servers) - - def create_one_server_action(attrs=None): """Create a fake server action. :param attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.server_action.ServerAction object + :return: A fake :class:`~openstack.compute.v2.server_action.ServerAction` + object """ attrs = attrs or {} @@ -544,56 +318,11 @@ def create_one_server_action(attrs=None): return server_action -def create_one_service(attrs=None): - """Create a fake service. - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.service.Service object - """ - attrs = attrs or {} - - # Set default attributes. - service_info = { - 'id': 'id-' + uuid.uuid4().hex, - 'host': 'host-' + uuid.uuid4().hex, - 'binary': 'binary-' + uuid.uuid4().hex, - 'status': 'enabled', - 'availability_zone': 'zone-' + uuid.uuid4().hex, - 'state': 'state-' + uuid.uuid4().hex, - 'updated_at': 'time-' + uuid.uuid4().hex, - 'disabled_reason': 'earthquake', - # Introduced in API microversion 2.11 - 'is_forced_down': False, - } - - # Overwrite default attributes. - service_info.update(attrs) - - return _service.Service(**service_info) - - -def create_services(attrs=None, count=2): - """Create multiple fake services. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of services to fake - :return: - A list of FakeResource objects faking the services - """ - services = [] - for i in range(0, count): - services.append(create_one_service(attrs)) - - return services - - def create_one_flavor(attrs=None): """Create a fake flavor. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.flavor.Flavor object + :return: A fake :class:`~openstack.compute.v2.flavor.Flavor` object """ attrs = attrs or {} @@ -626,7 +355,7 @@ def create_flavors(attrs=None, count=2): :param dict attrs: A dictionary with all attributes :param int count: The number of flavors to fake - :return: A list of fake openstack.compute.v2.flavor.Flavor objects + :return: A list of fake :class:`openstack.compute.v2.flavor.Flavor` objects """ flavors = [] for i in range(0, count): @@ -635,30 +364,11 @@ def create_flavors(attrs=None, count=2): return flavors -def get_flavors(flavors=None, count=2): - """Get an iterable MagicMock object with a list of faked flavors. - - If flavors list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param list flavors: A list of fake openstack.compute.v2.flavor.Flavor - objects - :param int count: The number of flavors to fake - :return: An iterable Mock object with side_effect set to a list of faked - flavors - """ - if flavors is None: - flavors = create_flavors(count) - return mock.Mock(side_effect=flavors) - - def create_one_flavor_access(attrs=None): """Create a fake flavor access. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with flavor_id, tenat_id + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the flavor access """ attrs = attrs or {} @@ -668,77 +378,20 @@ def create_one_flavor_access(attrs=None): 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, } - # Overwrite default attributes. - flavor_access_info.update(attrs) - - flavor_access = fakes.FakeResource( - info=copy.deepcopy(flavor_access_info), loaded=True - ) - - return flavor_access - - -def create_one_keypair(attrs=None): - """Create a fake keypair - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.keypair.Keypair object - """ - attrs = attrs or {} - - # Set default attributes. - keypair_info = { - 'name': 'keypair-name-' + uuid.uuid4().hex, - 'type': 'ssh', - 'fingerprint': 'dummy', - 'public_key': 'dummy', - 'user_id': 'user', - } + assert not set(attrs) - set(flavor_access_info), 'unknown keys' # Overwrite default attributes. - keypair_info.update(attrs) - - return _keypair.Keypair(**keypair_info) - - -def create_keypairs(attrs=None, count=2): - """Create multiple fake keypairs. - - :param dict attrs: A dictionary with all attributes - :param int count: The number of keypairs to fake - :return: A list of fake openstack.compute.v2.keypair.Keypair objects - """ - - keypairs = [] - for i in range(0, count): - keypairs.append(create_one_keypair(attrs)) - - return keypairs - - -def get_keypairs(keypairs=None, count=2): - """Get an iterable MagicMock object with a list of faked keypairs. - - If keypairs list is provided, then initialize the Mock object with the - list. Otherwise create one. + flavor_access_info.update(attrs) - :param list keypairs: A list of fake openstack.compute.v2.keypair.Keypair - objects - :param int count: The number of keypairs to fake - :return: An iterable Mock object with side_effect set to a list of faked - keypairs - """ - if keypairs is None: - keypairs = create_keypairs(count) - return mock.Mock(side_effect=keypairs) + return flavor_access_info def create_one_availability_zone(attrs=None): """Create a fake AZ. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.availability_zone.AvailabilityZone - object + :return: A fake + :class:`~openstack.compute.v2.availability_zone.AvailabilityZone` object """ attrs = attrs or {} @@ -785,12 +438,10 @@ def create_availability_zones(attrs=None, count=2): def create_one_floating_ip(attrs=None): - """Create a fake floating ip. + """Create a fake floating IP. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, ip, and so on + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the floating IP """ attrs = attrs or {} @@ -803,6 +454,8 @@ def create_one_floating_ip(attrs=None): 'pool': 'public', } + assert not set(attrs) - set(floating_ip_attrs), 'unknown keys' + # Overwrite default attributes. floating_ip_attrs.update(attrs) @@ -810,14 +463,11 @@ def create_one_floating_ip(attrs=None): def create_floating_ips(attrs=None, count=2): - """Create multiple fake floating ips. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of floating ips to fake - :return: - A list of FakeResource objects faking the floating ips + """Create multiple fake floating IPs. + + :param dict attrs: A dictionary with all attributes + :param int count: The number of floating IPs to fake + :return: A list of dictionaries faking the floating IPs """ floating_ips = [] for i in range(0, count): @@ -825,32 +475,11 @@ def create_floating_ips(attrs=None, count=2): return floating_ips -def get_floating_ips(floating_ips=None, count=2): - """Get an iterable MagicMock object with a list of faked floating ips. - - If floating_ips list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List floating_ips: - A list of FakeResource objects faking floating ips - :param int count: - The number of floating ips to fake - :return: - An iterable Mock object with side_effect set to a list of faked - floating ips - """ - if floating_ips is None: - floating_ips = create_floating_ips(count) - return mock.Mock(side_effect=floating_ips) - - def create_one_floating_ip_pool(attrs=None): - """Create a fake floating ip pool. + """Create a fake floating IP pool. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with name, etc + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the floating IP pool """ if attrs is None: attrs = {} @@ -860,6 +489,8 @@ def create_one_floating_ip_pool(attrs=None): 'name': 'floating-ip-pool-name-' + uuid.uuid4().hex, } + assert not set(attrs) - set(floating_ip_pool_attrs), 'unknown keys' + # Overwrite default attributes. floating_ip_pool_attrs.update(attrs) @@ -867,14 +498,11 @@ def create_one_floating_ip_pool(attrs=None): def create_floating_ip_pools(attrs=None, count=2): - """Create multiple fake floating ip pools. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of floating ip pools to fake - :return: - A list of FakeResource objects faking the floating ip pools + """Create multiple fake floating IP pools. + + :param dict attrs: A dictionary with all attributes + :param int count: The number of floating IP pools to fake + :return: A list of dictionaries faking the floating IP pools """ floating_ip_pools = [] for i in range(0, count): @@ -885,10 +513,8 @@ def create_floating_ip_pools(attrs=None, count=2): def create_one_network(attrs=None): """Create a fake network. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, label, cidr and so on + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the network """ attrs = attrs or {} @@ -928,6 +554,8 @@ def create_one_network(attrs=None): 'vpn_public_port': None, } + assert not set(attrs) - set(network_attrs), 'unknown keys' + # Overwrite default attributes. network_attrs.update(attrs) @@ -937,12 +565,9 @@ def create_one_network(attrs=None): def create_networks(attrs=None, count=2): """Create multiple fake networks. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of networks to fake - :return: - A list of FakeResource objects faking the networks + :param dict attrs: A dictionary with all attributes + :param int count: The number of networks to fake + :return: A list of dictionaries faking the networks """ networks = [] for i in range(0, count): @@ -951,81 +576,6 @@ def create_networks(attrs=None, count=2): return networks -def get_networks(networks=None, count=2): - """Get an iterable MagicMock 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 = create_networks(count=count) - return mock.Mock(side_effect=networks) - - -def create_one_usage(attrs=None): - """Create a fake usage. - - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with tenant_id and other attributes - """ - if attrs is None: - attrs = {} - - # Set default attributes. - usage_info = { - 'project_id': 'usage-tenant-id-' + uuid.uuid4().hex, - 'total_memory_mb_usage': 512.0, - 'total_vcpus_usage': 1.0, - 'total_local_gb_usage': 1.0, - 'server_usages': [ - { - 'ended_at': None, - 'flavor': 'usage-flavor-' + uuid.uuid4().hex, - 'hours': 1.0, - 'local_gb': 1, - 'memory_mb': 512, - 'name': 'usage-name-' + uuid.uuid4().hex, - 'instance_id': uuid.uuid4().hex, - 'state': 'active', - 'uptime': 3600, - 'vcpus': 1, - } - ], - } - - # Overwrite default attributes. - usage_info.update(attrs) - - return _usage.Usage(**usage_info) - - -def create_usages(attrs=None, count=2): - """Create multiple fake services. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of services to fake - :return: - A list of FakeResource objects faking the services - """ - usages = [] - for i in range(0, count): - usages.append(create_one_usage(attrs)) - - return usages - - def create_limits(attrs=None): """Create a fake limits object.""" attrs = attrs or {} @@ -1091,7 +641,7 @@ def create_one_migration(attrs=None): """Create a fake migration. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.migration.Migration object + :return: A fake :class:`~openstack.compute.v2.migration.Migration` object """ attrs = attrs or {} @@ -1127,7 +677,7 @@ def create_migrations(attrs=None, count=2): :param dict attrs: A dictionary with all attributes :param int count: The number of migrations to fake - :return: A list of fake openstack.compute.v2.migration.Migration objects + :return: A list of fake :class:`openstack.compute.v2.migration.Migration` objects """ migrations = [] for i in range(0, count): @@ -1140,7 +690,8 @@ def create_one_server_migration(attrs=None): """Create a fake server migration. :param dict attrs: A dictionary with all attributes - :return A fake openstack.compute.v2.server_migration.ServerMigration object + :return: A fake + :class:`~openstack.compute.v2.server_migration.ServerMigration` object """ attrs = attrs or {} @@ -1196,8 +747,8 @@ def create_one_volume_attachment(attrs=None): """Create a fake volume attachment. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.volume_attachment.VolumeAttachment - object + :return: A fake + :class:`~openstack.compute.v2.volume_attachment.VolumeAttachment` object """ attrs = attrs or {} @@ -1237,98 +788,13 @@ def create_volume_attachments(attrs=None, count=2): return volume_attachments -def create_one_hypervisor(attrs=None): - """Create a fake hypervisor. - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.hypervisor.Hypervisor object - """ - attrs = attrs or {} - - # Set default attributes. - hypervisor_info = { - 'id': 'hypervisor-id-' + uuid.uuid4().hex, - 'hypervisor_hostname': 'hypervisor-hostname-' + uuid.uuid4().hex, - 'status': 'enabled', - 'host_ip': '192.168.0.10', - 'cpu_info': { - 'aaa': 'aaa', - }, - 'free_disk_gb': 50, - 'hypervisor_version': 2004001, - 'disk_available_least': 50, - 'local_gb': 50, - 'free_ram_mb': 1024, - 'service': { - 'host': 'aaa', - 'disabled_reason': None, - 'id': 1, - }, - 'vcpus_used': 0, - 'hypervisor_type': 'QEMU', - 'local_gb_used': 0, - 'vcpus': 4, - 'memory_mb_used': 512, - 'memory_mb': 1024, - 'current_workload': 0, - 'state': 'up', - 'running_vms': 0, - } - - # Overwrite default attributes. - hypervisor_info.update(attrs) - - hypervisor = _hypervisor.Hypervisor(**hypervisor_info, loaded=True) - return hypervisor - - -def create_hypervisors(attrs=None, count=2): - """Create multiple fake hypervisors. - - :param dict attrs: A dictionary with all attributes - :param int count: The number of hypervisors to fake - :return: A list of fake openstack.compute.v2.hypervisor.Hypervisor objects - """ - hypervisors = [] - for i in range(0, count): - hypervisors.append(create_one_hypervisor(attrs)) - - return hypervisors - - -def create_one_server_group(attrs=None): - """Create a fake server group - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.server_group.ServerGroup object - """ - 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. + """Create a fake ServerInterface. :param dict attrs: A dictionary with all attributes :param dict methods: A dictionary with all methods - :return: A fake openstack.compute.v2.server_interface.ServerInterface - object + :return: A fake + :class:`~openstack.compute.v2.server_interface.ServerInterface` object """ attrs = attrs or {} diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py index eb8c0774aa..f04ba1066e 100644 --- a/openstackclient/tests/unit/compute/v2/test_agent.py +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -61,7 +61,7 @@ def setUp(self): self._agent['version'], ) - self.compute_sdk_client.post.return_value = fakes.FakeResponse( + self.compute_client.post.return_value = fakes.FakeResponse( data={'agent': self._agent} ) self.cmd = agent.CreateAgent(self.app, None) @@ -87,7 +87,7 @@ def test_agent_create(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.post.assert_called_with( + self.compute_client.post.assert_called_with( '/os-agents', json={ 'agent': { @@ -110,7 +110,7 @@ class TestAgentDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) @@ -125,7 +125,7 @@ def test_delete_one_agent(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( '/os-agents/123', microversion='2.1', ) @@ -143,7 +143,7 @@ def test_delete_multiple_agents(self): calls = [ mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist ] - self.compute_sdk_client.delete.assert_has_calls(calls) + self.compute_client.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_agents_exception(self): @@ -154,7 +154,7 @@ def test_delete_multiple_agents_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.delete.side_effect = [ + self.compute_client.delete.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NO_CONTENT), fakes.FakeResponse(status_code=http.HTTPStatus.NO_CONTENT), fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), @@ -166,7 +166,7 @@ def test_delete_multiple_agents_exception(self): calls = [ mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist ] - self.compute_sdk_client.delete.assert_has_calls(calls) + self.compute_client.delete.assert_has_calls(calls) def test_agent_delete_no_input(self): arglist = [] @@ -208,7 +208,7 @@ def setUp(self): for _agent in _agents ] - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'agents': _agents}, ) self.cmd = agent.ListAgent(self.app, None) @@ -222,7 +222,7 @@ def test_agent_list(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-agents', microversion='2.1', ) @@ -241,7 +241,7 @@ def test_agent_list_with_hypervisor(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-agents?hypervisor=hypervisor', microversion='2.1', ) @@ -252,10 +252,10 @@ def setUp(self): super().setUp() self.agent = _generate_fake_agent() - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'agents': [self.agent]}, ) - self.compute_sdk_client.put.return_value = fakes.FakeResponse() + self.compute_client.put.return_value = fakes.FakeResponse() self.cmd = agent.SetAgent(self.app, None) @@ -269,7 +269,7 @@ def test_agent_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { @@ -297,7 +297,7 @@ def test_agent_set_version(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { @@ -325,7 +325,7 @@ def test_agent_set_url(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { @@ -353,7 +353,7 @@ def test_agent_set_md5hash(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 6cfe5bc79a..b68e76edc5 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -16,7 +16,9 @@ from unittest import mock from unittest.mock import call +from openstack.compute.v2 import aggregate as _aggregate from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -26,8 +28,6 @@ class TestAggregate(compute_fakes.TestComputev2): - fake_ag = compute_fakes.create_one_aggregate() - columns = ( 'availability_zone', 'created_at', @@ -41,28 +41,33 @@ class TestAggregate(compute_fakes.TestComputev2): 'uuid', ) - data = ( - fake_ag.availability_zone, - fake_ag.created_at, - fake_ag.deleted_at, - format_columns.ListColumn(fake_ag.hosts), - fake_ag.id, - fake_ag.is_deleted, - fake_ag.name, - format_columns.DictColumn(fake_ag.metadata), - fake_ag.updated_at, - fake_ag.uuid, - ) + def setUp(self): + super().setUp() + + self.fake_ag = sdk_fakes.generate_fake_resource( + _aggregate.Aggregate, + metadata={'availability_zone': 'ag_zone', 'key1': 'value1'}, + ) + self.data = ( + self.fake_ag.availability_zone, + self.fake_ag.created_at, + self.fake_ag.deleted_at, + format_columns.ListColumn(self.fake_ag.hosts), + self.fake_ag.id, + self.fake_ag.is_deleted, + self.fake_ag.name, + format_columns.DictColumn(self.fake_ag.metadata), + self.fake_ag.updated_at, + self.fake_ag.uuid, + ) class TestAggregateAddHost(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag - self.compute_sdk_client.add_host_to_aggregate.return_value = ( - self.fake_ag - ) + self.compute_client.find_aggregate.return_value = self.fake_ag + self.compute_client.add_host_to_aggregate.return_value = self.fake_ag self.cmd = aggregate.AddAggregateHost(self.app, None) def test_aggregate_add_host(self): @@ -76,10 +81,10 @@ def test_aggregate_add_host(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.add_host_to_aggregate.assert_called_once_with( + self.compute_client.add_host_to_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host ) self.assertEqual(self.columns, columns) @@ -90,10 +95,8 @@ class TestAggregateCreate(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.create_aggregate.return_value = self.fake_ag - self.compute_sdk_client.set_aggregate_metadata.return_value = ( - self.fake_ag - ) + self.compute_client.create_aggregate.return_value = self.fake_ag + self.compute_client.set_aggregate_metadata.return_value = self.fake_ag self.cmd = aggregate.CreateAggregate(self.app, None) def test_aggregate_create(self): @@ -105,7 +108,7 @@ def test_aggregate_create(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_aggregate.assert_called_once_with( + self.compute_client.create_aggregate.assert_called_once_with( name=parsed_args.name ) self.assertEqual(self.columns, columns) @@ -124,7 +127,7 @@ def test_aggregate_create_with_zone(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_aggregate.assert_called_once_with( + self.compute_client.create_aggregate.assert_called_once_with( name=parsed_args.name, availability_zone=parsed_args.zone ) self.assertEqual(self.columns, columns) @@ -144,10 +147,10 @@ def test_aggregate_create_with_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_aggregate.assert_called_once_with( + self.compute_client.create_aggregate.assert_called_once_with( name=parsed_args.name ) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties ) self.assertEqual(self.columns, columns) @@ -155,14 +158,18 @@ def test_aggregate_create_with_property(self): class TestAggregateDelete(TestAggregate): - fake_ags = compute_fakes.create_aggregates(count=2) - def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate = compute_fakes.get_aggregates( - self.fake_ags + self.fake_ags = list( + sdk_fakes.generate_fake_resources(_aggregate.Aggregate, 2) ) + + self.compute_client.find_aggregate.side_effect = [ + self.fake_ags[0], + self.fake_ags[1], + ] + self.cmd = aggregate.DeleteAggregate(self.app, None) def test_aggregate_delete(self): @@ -172,10 +179,10 @@ def test_aggregate_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( self.fake_ags[0].id, ignore_missing=False ) - self.compute_sdk_client.delete_aggregate.assert_called_once_with( + self.compute_client.delete_aggregate.assert_called_once_with( self.fake_ags[0].id, ignore_missing=False ) @@ -193,8 +200,8 @@ def test_delete_multiple_aggregates(self): calls = [] for a in self.fake_ags: calls.append(call(a.id, ignore_missing=False)) - self.compute_sdk_client.find_aggregate.assert_has_calls(calls) - self.compute_sdk_client.delete_aggregate.assert_has_calls(calls) + self.compute_client.find_aggregate.assert_has_calls(calls) + self.compute_client.delete_aggregate.assert_has_calls(calls) def test_delete_multiple_agggregates_with_exception(self): arglist = [ @@ -207,7 +214,7 @@ def test_delete_multiple_agggregates_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_aggregate.side_effect = [ + self.compute_client.find_aggregate.side_effect = [ self.fake_ags[0], sdk_exceptions.NotFoundException, ] @@ -220,65 +227,68 @@ def test_delete_multiple_agggregates_with_exception(self): calls = [] for a in arglist: calls.append(call(a, ignore_missing=False)) - self.compute_sdk_client.find_aggregate.assert_has_calls(calls) - self.compute_sdk_client.delete_aggregate.assert_called_with( + self.compute_client.find_aggregate.assert_has_calls(calls) + self.compute_client.delete_aggregate.assert_called_with( self.fake_ags[0].id, ignore_missing=False ) class TestAggregateList(TestAggregate): - list_columns = ( - "ID", - "Name", - "Availability Zone", - ) + def setUp(self): + super().setUp() - list_columns_long = ( - "ID", - "Name", - "Availability Zone", - "Properties", - "Hosts", - ) + self.compute_client.aggregates.return_value = [self.fake_ag] + self.cmd = aggregate.ListAggregate(self.app, None) - list_data = ( - ( - TestAggregate.fake_ag.id, - TestAggregate.fake_ag.name, - TestAggregate.fake_ag.availability_zone, - ), - ) + def test_aggregate_list(self): + self.set_compute_api_version('2.41') + + parsed_args = self.check_parser(self.cmd, [], []) + columns, data = self.cmd.take_action(parsed_args) - list_data_long = ( - ( - TestAggregate.fake_ag.id, - TestAggregate.fake_ag.name, - TestAggregate.fake_ag.availability_zone, - format_columns.DictColumn( - { - key: value - for key, value in TestAggregate.fake_ag.metadata.items() - if key != 'availability_zone' - } + expected_columns = ( + "ID", + "UUID", + "Name", + "Availability Zone", + ) + expected_data = ( + ( + self.fake_ag.id, + self.fake_ag.uuid, + self.fake_ag.name, + self.fake_ag.availability_zone, ), - format_columns.ListColumn(TestAggregate.fake_ag.hosts), - ), - ) + ) - def setUp(self): - super().setUp() + self.assertEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) - self.compute_sdk_client.aggregates.return_value = [self.fake_ag] - self.cmd = aggregate.ListAggregate(self.app, None) + def test_aggregate_list_pre_v241(self): + self.set_compute_api_version('2.40') - def test_aggregate_list(self): parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns, columns) - self.assertCountEqual(self.list_data, tuple(data)) + expected_columns = ( + "ID", + "Name", + "Availability Zone", + ) + expected_data = ( + ( + self.fake_ag.id, + self.fake_ag.name, + self.fake_ag.availability_zone, + ), + ) + + self.assertEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_aggregate_list_with_long(self): + self.set_compute_api_version('2.41') + arglist = [ '--long', ] @@ -288,16 +298,41 @@ def test_aggregate_list_with_long(self): parsed_args = self.check_parser(self.cmd, arglist, vertifylist) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns_long, columns) - self.assertCountEqual(self.list_data_long, tuple(data)) + expected_columns = ( + "ID", + "UUID", + "Name", + "Availability Zone", + "Properties", + "Hosts", + ) + expected_data = ( + ( + self.fake_ag.id, + self.fake_ag.uuid, + self.fake_ag.name, + self.fake_ag.availability_zone, + format_columns.DictColumn( + { + key: value + for key, value in self.fake_ag.metadata.items() + if key != 'availability_zone' + } + ), + format_columns.ListColumn(self.fake_ag.hosts), + ), + ) + + self.assertEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) class TestAggregateRemoveHost(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag - self.compute_sdk_client.remove_host_from_aggregate.return_value = ( + self.compute_client.find_aggregate.return_value = self.fake_ag + self.compute_client.remove_host_from_aggregate.return_value = ( self.fake_ag ) self.cmd = aggregate.RemoveAggregateHost(self.app, None) @@ -313,10 +348,10 @@ def test_aggregate_remove_host(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.remove_host_from_aggregate.assert_called_once_with( + self.compute_client.remove_host_from_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host ) self.assertEqual(self.columns, columns) @@ -327,7 +362,7 @@ class TestAggregateSet(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.SetAggregate(self.app, None) def test_aggregate_set_no_option(self): @@ -340,11 +375,11 @@ def test_aggregate_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.update_aggregate) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_name(self): @@ -360,13 +395,13 @@ def test_aggregate_set_with_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.update_aggregate.assert_called_once_with( + self.compute_client.update_aggregate.assert_called_once_with( self.fake_ag.id, name=parsed_args.name ) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_zone(self): @@ -382,13 +417,13 @@ def test_aggregate_set_with_zone(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.update_aggregate.assert_called_once_with( + self.compute_client.update_aggregate.assert_called_once_with( self.fake_ag.id, availability_zone=parsed_args.zone ) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_property(self): @@ -406,11 +441,11 @@ def test_aggregate_set_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_client.update_aggregate) + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties ) self.assertIsNone(result) @@ -429,11 +464,11 @@ def test_aggregate_set_with_no_property_and_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_client.update_aggregate) + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None, 'key2': 'value2'} ) self.assertIsNone(result) @@ -449,11 +484,11 @@ def test_aggregate_set_with_no_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_client.update_aggregate) + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None} ) self.assertIsNone(result) @@ -472,13 +507,13 @@ def test_aggregate_set_with_zone_and_no_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.update_aggregate.assert_called_once_with( + self.compute_client.update_aggregate.assert_called_once_with( self.fake_ag.id, availability_zone=parsed_args.zone ) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None} ) self.assertIsNone(result) @@ -498,25 +533,25 @@ class TestAggregateShow(TestAggregate): 'uuid', ) - data = ( - TestAggregate.fake_ag.availability_zone, - TestAggregate.fake_ag.created_at, - TestAggregate.fake_ag.deleted_at, - format_columns.ListColumn(TestAggregate.fake_ag.hosts), - TestAggregate.fake_ag.id, - TestAggregate.fake_ag.is_deleted, - TestAggregate.fake_ag.name, - format_columns.DictColumn(TestAggregate.fake_ag.metadata), - TestAggregate.fake_ag.updated_at, - TestAggregate.fake_ag.uuid, - ) - def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.ShowAggregate(self.app, None) + self.data = ( + self.fake_ag.availability_zone, + self.fake_ag.created_at, + self.fake_ag.deleted_at, + format_columns.ListColumn(self.fake_ag.hosts), + self.fake_ag.id, + self.fake_ag.is_deleted, + self.fake_ag.name, + format_columns.DictColumn(self.fake_ag.metadata), + self.fake_ag.updated_at, + self.fake_ag.uuid, + ) + def test_aggregate_show(self): arglist = [ 'ag1', @@ -526,7 +561,7 @@ def test_aggregate_show(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) @@ -538,7 +573,7 @@ class TestAggregateUnset(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.UnsetAggregate(self.app, None) def test_aggregate_unset(self): @@ -554,7 +589,7 @@ def test_aggregate_unset(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'unset_key': None} ) self.assertIsNone(result) @@ -574,7 +609,7 @@ def test_aggregate_unset_multiple_properties(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'unset_key1': None, 'unset_key2': None} ) self.assertIsNone(result) @@ -589,7 +624,7 @@ def test_aggregate_unset_no_option(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) @@ -599,7 +634,7 @@ class TestAggregateCacheImage(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.find_image_mock = mock.Mock(side_effect=self.images) self.app.client_manager.sdk_connection.image.find_image = ( self.find_image_mock @@ -630,10 +665,10 @@ def test_aggregate_cache_add_single_image(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.aggregate_precache_images.assert_called_once_with( + self.compute_client.aggregate_precache_images.assert_called_once_with( self.fake_ag.id, [self.images[0].id] ) @@ -651,9 +686,9 @@ def test_aggregate_cache_add_multiple_images(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.aggregate_precache_images.assert_called_once_with( + self.compute_client.aggregate_precache_images.assert_called_once_with( self.fake_ag.id, [self.images[0].id, self.images[1].id] ) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index a179db5a7b..8d9d36ccd2 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -13,7 +13,9 @@ # under the License. # -from unittest import mock + +from openstack.compute.v2 import server as _server +from openstack.test import fakes as sdk_fakes from openstackclient.compute.v2 import console from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -24,8 +26,8 @@ class TestConsoleLog(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self._server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self._server + self._server = sdk_fakes.generate_fake_resource(_server.Server) + self.compute_client.find_server.return_value = self._server self.cmd = console.ShowConsoleLog(self.app, None) @@ -46,13 +48,13 @@ def test_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) output = {'output': '1st line\n2nd line\n'} - self.compute_sdk_client.get_server_console_output.return_value = output + self.compute_client.get_server_console_output.return_value = output self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( name_or_id='fake_server', ignore_missing=False ) - self.compute_sdk_client.get_server_console_output.assert_called_with( + self.compute_client.get_server_console_output.assert_called_with( self._server.id, length=None ) stdout = self.app.stdout.content @@ -64,31 +66,30 @@ def test_show_lines(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) output = {'output': '1st line\n2nd line'} - self.compute_sdk_client.get_server_console_output.return_value = output + self.compute_client.get_server_console_output.return_value = output self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( name_or_id='fake_server', ignore_missing=False ) - self.compute_sdk_client.get_server_console_output.assert_called_with( + self.compute_client.get_server_console_output.assert_called_with( self._server.id, length=15 ) class TestConsoleUrlShow(compute_fakes.TestComputev2): - _server = compute_fakes.create_one_sdk_server() - def setUp(self): super().setUp() - self.compute_sdk_client.find_server.return_value = self._server + + self._server = sdk_fakes.generate_fake_resource(_server.Server) + self.compute_client.find_server.return_value = self._server + fake_console_data = { 'url': 'http://localhost', 'protocol': 'fake_protocol', 'type': 'fake_type', } - self.compute_sdk_client.create_console = mock.Mock( - return_value=fake_console_data - ) + self.compute_client.create_console.return_value = fake_console_data self.columns = ( 'protocol', @@ -113,7 +114,7 @@ def test_console_url_show_by_default(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='novnc' ) self.assertEqual(self.columns, columns) @@ -130,7 +131,7 @@ def test_console_url_show_with_novnc(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='novnc' ) self.assertEqual(self.columns, columns) @@ -147,13 +148,13 @@ def test_console_url_show_with_xvpvnc(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='xvpvnc' ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - def test_console_url_show_with_spice(self): + def test_console_url_show_with_spice_html5(self): arglist = [ '--spice', 'foo_vm', @@ -164,12 +165,29 @@ def test_console_url_show_with_spice(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='spice-html5' ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) + def test_console_url_show_with_spice_direct(self): + arglist = [ + '--spice-direct', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'spice-direct'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.create_console.assert_called_once_with( + self._server.id, console_type='spice-direct' + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + def test_console_url_show_with_rdp(self): arglist = [ '--rdp', @@ -181,7 +199,7 @@ def test_console_url_show_with_rdp(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='rdp-html5' ) self.assertEqual(self.columns, columns) @@ -198,7 +216,7 @@ def test_console_url_show_with_serial(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='serial' ) self.assertEqual(self.columns, columns) @@ -215,7 +233,7 @@ def test_console_url_show_with_mks(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='webmks' ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/compute/v2/test_console_connection.py b/openstackclient/tests/unit/compute/v2/test_console_connection.py new file mode 100644 index 0000000000..ab9cb0c05c --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_console_connection.py @@ -0,0 +1,72 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from openstack.compute.v2 import console_auth_token as _console_auth_token +from openstack.test import fakes as sdk_fakes + +from openstackclient.compute.v2 import console_connection +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes + + +class TestConsoleTokens(compute_fakes.TestComputev2): + def setUp(self): + super().setUp() + + self._console_auth_token = sdk_fakes.generate_fake_resource( + _console_auth_token.ConsoleAuthToken, + host='127.0.0.1', + instance_uuid=uuid.uuid4().hex, + internal_access_path=None, + port=5900, + tls_port=5901, + ) + self.compute_client.validate_console_auth_token.return_value = ( + self._console_auth_token + ) + + self.columns = ( + 'host', + 'instance_uuid', + 'internal_access_path', + 'port', + 'tls_port', + ) + self.data = ( + self._console_auth_token.host, + self._console_auth_token.instance_uuid, + self._console_auth_token.internal_access_path, + self._console_auth_token.port, + self._console_auth_token.tls_port, + ) + + self.cmd = console_connection.ShowConsoleConnectionInformation( + self.app, None + ) + + def test_console_connection_show(self): + arglist = [ + 'token', + ] + verifylist = [ + ('token', 'token'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.validate_console_auth_token.assert_called_once_with( + 'token' + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 1a93b5fc83..25bc8eaa76 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -16,12 +16,13 @@ from openstack.compute.v2 import flavor as _flavor from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from openstackclient.compute.v2 import flavor 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 import utils as tests_utils @@ -34,59 +35,60 @@ def setUp(self): class TestFlavorCreate(TestFlavor): - flavor = compute_fakes.create_one_flavor(attrs={'links': 'flavor-links'}) - project = identity_fakes.FakeProject.create_one_project() - - columns = ( - 'OS-FLV-DISABLED:disabled', - 'OS-FLV-EXT-DATA:ephemeral', - 'description', - 'disk', - 'id', - 'name', - 'os-flavor-access:is_public', - 'properties', - 'ram', - 'rxtx_factor', - 'swap', - 'vcpus', - ) - - data = ( - flavor.is_disabled, - flavor.ephemeral, - flavor.description, - flavor.disk, - flavor.id, - flavor.name, - flavor.is_public, - format_columns.DictColumn(flavor.extra_specs), - flavor.ram, - flavor.rxtx_factor, - flavor.swap, - flavor.vcpus, - ) - data_private = ( - flavor.is_disabled, - flavor.ephemeral, - flavor.description, - flavor.disk, - flavor.id, - flavor.name, - False, - format_columns.DictColumn(flavor.extra_specs), - flavor.ram, - flavor.rxtx_factor, - flavor.swap, - flavor.vcpus, - ) - def setUp(self): super().setUp() - # Return a project + self.flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, links='flavor-links' + ) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + + self.columns = ( + 'OS-FLV-DISABLED:disabled', + 'OS-FLV-EXT-DATA:ephemeral', + 'description', + 'disk', + 'id', + 'name', + 'os-flavor-access:is_public', + 'properties', + 'ram', + 'rxtx_factor', + 'swap', + 'vcpus', + ) + self.data = ( + self.flavor.is_disabled, + self.flavor.ephemeral, + self.flavor.description, + self.flavor.disk, + self.flavor.id, + self.flavor.name, + self.flavor.is_public, + format_columns.DictColumn(self.flavor.extra_specs), + self.flavor.ram, + self.flavor.rxtx_factor, + self.flavor.swap, + self.flavor.vcpus, + ) + self.data_private = ( + self.flavor.is_disabled, + self.flavor.ephemeral, + self.flavor.description, + self.flavor.disk, + self.flavor.id, + self.flavor.name, + False, + format_columns.DictColumn(self.flavor.extra_specs), + self.flavor.ram, + self.flavor.rxtx_factor, + self.flavor.swap, + self.flavor.vcpus, + ) + self.projects_mock.get.return_value = self.project - self.compute_sdk_client.create_flavor.return_value = self.flavor + self.compute_client.create_flavor.return_value = self.flavor + self.cmd = flavor.CreateFlavor(self.app, None) def test_flavor_create_default_options(self): @@ -109,7 +111,7 @@ def test_flavor_create_default_options(self): } columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_flavor.assert_called_once_with( + self.compute_client.create_flavor.assert_called_once_with( **default_args ) @@ -180,17 +182,17 @@ def test_flavor_create_all_options(self): # convert expected data tuple to list to be able to modify it cmp_data = list(self.data) cmp_data[7] = format_columns.DictColumn(props) - self.compute_sdk_client.create_flavor.return_value = create_flavor - self.compute_sdk_client.create_flavor_extra_specs.return_value = ( + self.compute_client.create_flavor.return_value = create_flavor + self.compute_client.create_flavor_extra_specs.return_value = ( expected_flavor ) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_flavor.assert_called_once_with(**args) - self.compute_sdk_client.create_flavor_extra_specs.assert_called_once_with( + self.compute_client.create_flavor.assert_called_once_with(**args) + self.compute_client.create_flavor_extra_specs.assert_called_once_with( create_flavor, props ) - self.compute_sdk_client.get_flavor_access.assert_not_called() + self.compute_client.get_flavor_access.assert_not_called() self.assertEqual(self.columns, columns) self.assertCountEqual(tuple(cmp_data), data) @@ -233,7 +235,7 @@ def test_flavor_create_other_options(self): ('vcpus', self.flavor.vcpus), ('rxtx_factor', self.flavor.rxtx_factor), ('public', False), - ('description', 'description'), + ('description', self.flavor.description), ('project', self.project.id), ('properties', {'key1': 'value1', 'key2': 'value2'}), ('name', self.flavor.name), @@ -245,7 +247,7 @@ def test_flavor_create_other_options(self): 'ram': self.flavor.ram, 'vcpus': self.flavor.vcpus, 'disk': self.flavor.disk, - 'id': 'auto', + 'id': None, 'ephemeral': self.flavor.ephemeral, 'swap': self.flavor.swap, 'rxtx_factor': self.flavor.rxtx_factor, @@ -265,19 +267,19 @@ def test_flavor_create_other_options(self): # convert expected data tuple to list to be able to modify it cmp_data = list(self.data_private) cmp_data[7] = format_columns.DictColumn(props) - self.compute_sdk_client.create_flavor.return_value = create_flavor - self.compute_sdk_client.create_flavor_extra_specs.return_value = ( + self.compute_client.create_flavor.return_value = create_flavor + self.compute_client.create_flavor_extra_specs.return_value = ( expected_flavor ) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_flavor.assert_called_once_with(**args) - self.compute_sdk_client.flavor_add_tenant_access.assert_called_with( + self.compute_client.create_flavor.assert_called_once_with(**args) + self.compute_client.flavor_add_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.compute_sdk_client.create_flavor_extra_specs.assert_called_with( + self.compute_client.create_flavor_extra_specs.assert_called_with( create_flavor, props ) self.assertEqual(self.columns, columns) @@ -328,7 +330,7 @@ def test_flavor_create_with_description(self): str(self.flavor.vcpus), '--rxtx-factor', str(self.flavor.rxtx_factor), - '--private', + '--public', '--description', 'fake description', self.flavor.name, @@ -341,7 +343,7 @@ def test_flavor_create_with_description(self): ('swap', self.flavor.swap), ('vcpus', self.flavor.vcpus), ('rxtx_factor', self.flavor.rxtx_factor), - ('public', False), + ('public', True), ('description', 'fake description'), ('name', self.flavor.name), ] @@ -358,14 +360,14 @@ def test_flavor_create_with_description(self): 'ephemeral': self.flavor.ephemeral, 'swap': self.flavor.swap, 'rxtx_factor': self.flavor.rxtx_factor, - 'is_public': self.flavor.is_public, + 'is_public': True, 'description': 'fake description', } - self.compute_sdk_client.create_flavor.assert_called_once_with(**args) + self.compute_client.create_flavor.assert_called_once_with(**args) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data_private, data) + self.assertCountEqual(self.data, data) def test_flavor_create_with_description_pre_v255(self): self.set_compute_api_version('2.54') @@ -395,12 +397,14 @@ def test_flavor_create_with_description_pre_v255(self): class TestFlavorDelete(TestFlavor): - flavors = compute_fakes.create_flavors(count=2) - def setUp(self): super().setUp() - self.compute_sdk_client.delete_flavor.return_value = None + self.flavors = list( + sdk_fakes.generate_fake_resources(_flavor.Flavor, 2) + ) + + self.compute_client.delete_flavor.return_value = None self.cmd = flavor.DeleteFlavor(self.app, None) @@ -411,14 +415,14 @@ def test_flavor_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_flavor.return_value = self.flavors[0] + self.compute_client.find_flavor.return_value = self.flavors[0] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( self.flavors[0].id, ignore_missing=False ) - self.compute_sdk_client.delete_flavor.assert_called_with( + self.compute_client.delete_flavor.assert_called_with( self.flavors[0].id ) self.assertIsNone(result) @@ -433,7 +437,7 @@ def test_delete_multiple_flavors(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_flavor.side_effect = self.flavors + self.compute_client.find_flavor.side_effect = self.flavors result = self.cmd.take_action(parsed_args) @@ -441,8 +445,8 @@ def test_delete_multiple_flavors(self): mock.call(i.id, ignore_missing=False) for i in self.flavors ] delete_calls = [mock.call(i.id) for i in self.flavors] - self.compute_sdk_client.find_flavor.assert_has_calls(find_calls) - self.compute_sdk_client.delete_flavor.assert_has_calls(delete_calls) + self.compute_client.find_flavor.assert_has_calls(find_calls) + self.compute_client.delete_flavor.assert_has_calls(delete_calls) self.assertIsNone(result) def test_multi_flavors_delete_with_exception(self): @@ -453,7 +457,7 @@ def test_multi_flavors_delete_with_exception(self): verifylist = [('flavor', [self.flavors[0].id, 'unexist_flavor'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_flavor.side_effect = [ + self.compute_client.find_flavor.side_effect = [ self.flavors[0], sdk_exceptions.ResourceNotFound, ] @@ -469,56 +473,55 @@ def test_multi_flavors_delete_with_exception(self): mock.call('unexist_flavor', ignore_missing=False), ] delete_calls = [mock.call(self.flavors[0].id)] - self.compute_sdk_client.find_flavor.assert_has_calls(find_calls) - self.compute_sdk_client.delete_flavor.assert_has_calls(delete_calls) + self.compute_client.find_flavor.assert_has_calls(find_calls) + self.compute_client.delete_flavor.assert_has_calls(delete_calls) class TestFlavorList(TestFlavor): - _flavor = compute_fakes.create_one_flavor() - - columns = ( - 'ID', - 'Name', - 'RAM', - 'Disk', - 'Ephemeral', - 'VCPUs', - 'Is Public', - ) - columns_long = columns + ('Swap', 'RXTX Factor', 'Properties') - - data = ( - ( - _flavor.id, - _flavor.name, - _flavor.ram, - _flavor.disk, - _flavor.ephemeral, - _flavor.vcpus, - _flavor.is_public, - ), - ) - data_long = ( - data[0] - + ( - _flavor.swap, - _flavor.rxtx_factor, - format_columns.DictColumn(_flavor.extra_specs), - ), - ) - def setUp(self): super().setUp() - self.api_mock = mock.Mock() - self.api_mock.side_effect = [ - [self._flavor], - [], - ] + self._flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, extra_specs={'property': 'value'} + ) + + self.columns = ( + 'ID', + 'Name', + 'RAM', + 'Disk', + 'Ephemeral', + 'VCPUs', + 'Is Public', + ) + self.columns_long = self.columns + ( + 'Swap', + 'RXTX Factor', + 'Properties', + ) + + self.data = ( + ( + self._flavor.id, + self._flavor.name, + self._flavor.ram, + self._flavor.disk, + self._flavor.ephemeral, + self._flavor.vcpus, + self._flavor.is_public, + ), + ) + self.data_long = ( + self.data[0] + + ( + self._flavor.swap, + self._flavor.rxtx_factor, + format_columns.DictColumn(self._flavor.extra_specs), + ), + ) - self.compute_sdk_client.flavors = self.api_mock + self.compute_client.flavors.side_effect = [[self._flavor], []] - # Get the command object to test self.cmd = flavor.ListFlavor(self.app, None) def test_flavor_list_no_options(self): @@ -541,8 +544,8 @@ def test_flavor_list_no_options(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -567,8 +570,8 @@ def test_flavor_list_all_flavors(self): 'is_public': None, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -593,8 +596,8 @@ def test_flavor_list_private_flavors(self): 'is_public': False, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -619,8 +622,8 @@ def test_flavor_list_public_flavors(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -645,15 +648,17 @@ def test_flavor_list_long(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, tuple(data)) def test_flavor_list_long_no_extra_specs(self): # use flavor with no extra specs for this test - flavor = compute_fakes.create_one_flavor(attrs={"extra_specs": {}}) + flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, extra_specs={} + ) self.data = ( ( flavor.id, @@ -673,15 +678,9 @@ def test_flavor_list_long_no_extra_specs(self): format_columns.DictColumn(flavor.extra_specs), ), ) - self.api_mock.side_effect = [ - [flavor], - [], - ] - self.compute_sdk_client.flavors = self.api_mock - self.compute_sdk_client.fetch_flavor_extra_specs = mock.Mock( - return_value=None - ) + self.compute_client.flavors.side_effect = [[flavor], []] + self.compute_client.fetch_flavor_extra_specs.return_value = None arglist = [ '--long', @@ -702,8 +701,8 @@ def test_flavor_list_long_no_extra_specs(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_called_once_with( + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_called_once_with( flavor ) @@ -736,25 +735,23 @@ def test_flavor_list_min_disk_min_ram(self): 'min_ram': 2048, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) class TestFlavorSet(TestFlavor): - # Return value of self.compute_sdk_client.find_flavor(). - flavor = compute_fakes.create_one_flavor( - attrs={'os-flavor-access:is_public': False} - ) - project = identity_fakes.FakeProject.create_one_project() - def setUp(self): super().setUp() - self.compute_sdk_client.find_flavor.return_value = self.flavor - # Return a project + self.flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, is_public=False, extra_specs={'property': 'value'} + ) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + + self.compute_client.find_flavor.return_value = self.flavor self.projects_mock.get.return_value = self.project self.cmd = flavor.SetFlavor(self.app, None) @@ -767,10 +764,10 @@ def test_flavor_set_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.create_flavor_extra_specs.assert_called_with( + self.compute_client.create_flavor_extra_specs.assert_called_with( self.flavor.id, {'FOO': '"B A R"'} ) self.assertIsNone(result) @@ -781,10 +778,10 @@ def test_flavor_set_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.delete_flavor_extra_specs_property.assert_called_with( + self.compute_client.delete_flavor_extra_specs_property.assert_called_with( self.flavor.id, 'property' ) self.assertIsNone(result) @@ -803,14 +800,14 @@ def test_flavor_set_project(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.flavor_add_tenant_access.assert_called_with( + self.compute_client.flavor_add_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.compute_sdk_client.create_flavor_extra_specs.assert_not_called() + self.compute_client.create_flavor_extra_specs.assert_not_called() self.assertIsNone(result) def test_flavor_set_no_project(self): @@ -847,7 +844,7 @@ def test_flavor_set_no_flavor(self): ) def test_flavor_set_with_unexist_flavor(self): - self.compute_sdk_client.find_flavor.side_effect = [ + self.compute_client.find_flavor.side_effect = [ sdk_exceptions.ResourceNotFound() ] @@ -876,10 +873,10 @@ def test_flavor_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.flavor_add_tenant_access.assert_not_called() + self.compute_client.flavor_add_tenant_access.assert_not_called() self.assertIsNone(result) def test_flavor_set_description(self): @@ -897,7 +894,7 @@ def test_flavor_set_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_flavor.assert_called_with( + self.compute_client.update_flavor.assert_called_with( flavor=self.flavor.id, description='description' ) self.assertIsNone(result) @@ -935,7 +932,7 @@ def test_flavor_set_description_using_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_flavor.assert_called_with( + self.compute_client.update_flavor.assert_called_with( flavor=self.flavor.id, description='description' ) self.assertIsNone(result) @@ -960,48 +957,44 @@ def test_flavor_set_description_using_name_pre_v255(self): class TestFlavorShow(TestFlavor): - # Return value of self.compute_sdk_client.find_flavor(). - flavor_access = compute_fakes.create_one_flavor_access() - flavor = compute_fakes.create_one_flavor() - - columns = ( - 'OS-FLV-DISABLED:disabled', - 'OS-FLV-EXT-DATA:ephemeral', - 'access_project_ids', - 'description', - 'disk', - 'id', - 'name', - 'os-flavor-access:is_public', - 'properties', - 'ram', - 'rxtx_factor', - 'swap', - 'vcpus', - ) - - data = ( - flavor.is_disabled, - flavor.ephemeral, - None, - flavor.description, - flavor.disk, - flavor.id, - flavor.name, - flavor.is_public, - format_columns.DictColumn(flavor.extra_specs), - flavor.ram, - flavor.rxtx_factor, - flavor.swap, - flavor.vcpus, - ) - def setUp(self): super().setUp() - # Return value of _find_resource() - self.compute_sdk_client.find_flavor.return_value = self.flavor - self.compute_sdk_client.get_flavor_access.return_value = [ + self.flavor_access = compute_fakes.create_one_flavor_access() + self.flavor = sdk_fakes.generate_fake_resource(_flavor.Flavor) + + self.columns = ( + 'OS-FLV-DISABLED:disabled', + 'OS-FLV-EXT-DATA:ephemeral', + 'access_project_ids', + 'description', + 'disk', + 'id', + 'name', + 'os-flavor-access:is_public', + 'properties', + 'ram', + 'rxtx_factor', + 'swap', + 'vcpus', + ) + self.data = ( + self.flavor.is_disabled, + self.flavor.ephemeral, + None, + self.flavor.description, + self.flavor.disk, + self.flavor.id, + self.flavor.name, + self.flavor.is_public, + format_columns.DictColumn(self.flavor.extra_specs), + self.flavor.ram, + self.flavor.rxtx_factor, + self.flavor.swap, + self.flavor.vcpus, + ) + self.compute_client.find_flavor.return_value = self.flavor + self.compute_client.get_flavor_access.return_value = [ self.flavor_access ] self.cmd = flavor.ShowFlavor(self.app, None) @@ -1035,12 +1028,10 @@ def test_public_flavor_show(self): self.assertCountEqual(self.data, data) def test_private_flavor_show(self): - private_flavor = compute_fakes.create_one_flavor( - attrs={ - 'os-flavor-access:is_public': False, - } + private_flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, is_public=False ) - self.compute_sdk_client.find_flavor.return_value = private_flavor + self.compute_client.find_flavor.return_value = private_flavor arglist = [ private_flavor.name, @@ -1052,7 +1043,7 @@ def test_private_flavor_show(self): data_with_project = ( private_flavor.is_disabled, private_flavor.ephemeral, - [self.flavor_access.tenant_id], + [self.flavor_access['tenant_id']], private_flavor.description, private_flavor.disk, private_flavor.id, @@ -1069,7 +1060,7 @@ def test_private_flavor_show(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_flavor_access.assert_called_with( + self.compute_client.get_flavor_access.assert_called_with( flavor=private_flavor.id ) self.assertEqual(self.columns, columns) @@ -1077,23 +1068,18 @@ def test_private_flavor_show(self): class TestFlavorUnset(TestFlavor): - # Return value of self.compute_sdk_client.find_flavor(). - flavor = compute_fakes.create_one_flavor( - attrs={'os-flavor-access:is_public': False} - ) - project = identity_fakes.FakeProject.create_one_project() - def setUp(self): super().setUp() - self.compute_sdk_client.find_flavor.return_value = self.flavor - # Return a project + self.flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, is_public=False + ) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + + self.compute_client.find_flavor.return_value = self.flavor self.projects_mock.get.return_value = self.project - self.cmd = flavor.UnsetFlavor(self.app, None) - self.mock_shortcut = ( - self.compute_sdk_client.delete_flavor_extra_specs_property - ) + self.cmd = flavor.UnsetFlavor(self.app, None) def test_flavor_unset_property(self): arglist = ['--property', 'property', 'baremetal'] @@ -1104,11 +1090,13 @@ def test_flavor_unset_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.mock_shortcut.assert_called_with(self.flavor.id, 'property') - self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_client.delete_flavor_extra_specs_property.assert_called_with( + self.flavor.id, 'property' + ) + self.compute_client.flavor_remove_tenant_access.assert_not_called() self.assertIsNone(result) def test_flavor_unset_properties(self): @@ -1126,22 +1114,17 @@ def test_flavor_unset_properties(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - calls = [ - mock.call(self.flavor.id, 'property1'), - mock.call(self.flavor.id, 'property2'), - ] - self.mock_shortcut.assert_has_calls(calls) - - # A bit tricky way to ensure we do not unset other properties - calls.append(mock.call(self.flavor.id, 'property')) - self.assertRaises( - AssertionError, self.mock_shortcut.assert_has_calls, calls + self.compute_client.delete_flavor_extra_specs_property.assert_has_calls( + [ + mock.call(self.flavor.id, 'property1'), + mock.call(self.flavor.id, 'property2'), + ] ) - - self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_client.flavor_remove_tenant_access.assert_not_called() def test_flavor_unset_project(self): arglist = [ @@ -1158,14 +1141,14 @@ def test_flavor_unset_project(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.flavor_remove_tenant_access.assert_called_with( + self.compute_client.flavor_remove_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.compute_sdk_client.delete_flavor_extra_specs_property.assert_not_called() + self.compute_client.delete_flavor_extra_specs_property.assert_not_called() self.assertIsNone(result) def test_flavor_unset_no_project(self): @@ -1202,7 +1185,7 @@ def test_flavor_unset_no_flavor(self): ) def test_flavor_unset_with_unexist_flavor(self): - self.compute_sdk_client.find_flavor.side_effect = [ + self.compute_client.find_flavor.side_effect = [ sdk_exceptions.ResourceNotFound ] @@ -1232,4 +1215,4 @@ def test_flavor_unset_nothing(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_client.flavor_remove_tenant_access.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index afda8e9589..8d38f8353b 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -59,7 +59,6 @@ def _generate_fake_host(): class TestHostList(compute_fakes.TestComputev2): - def setUp(self): super().setUp() @@ -73,7 +72,7 @@ def setUp(self): ) ] - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'hosts': [self._host]} ) self.cmd = host.ListHost(self.app, None) @@ -86,7 +85,7 @@ def test_host_list_no_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get.assert_called_with( + self.compute_client.get.assert_called_with( '/os-hosts', microversion='2.1' ) self.assertEqual(self.columns, columns) @@ -105,7 +104,7 @@ def test_host_list_with_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get.assert_called_with( + self.compute_client.get.assert_called_with( '/os-hosts', microversion='2.1' ) self.assertEqual(self.columns, columns) @@ -117,7 +116,7 @@ def setUp(self): super().setUp() self._host = _generate_fake_host() - self.compute_sdk_client.put.return_value = fakes.FakeResponse() + self.compute_client.put.return_value = fakes.FakeResponse() self.cmd = host.SetHost(self.app, None) @@ -133,7 +132,7 @@ def test_host_set_no_option(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.put.assert_not_called() + self.compute_client.put.assert_not_called() def test_host_set(self): arglist = [ @@ -151,7 +150,7 @@ def test_host_set(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.put.assert_called_with( + self.compute_client.put.assert_called_with( f'/os-hosts/{self._host["host"]}', json={ 'maintenance_mode': 'disable', @@ -184,7 +183,7 @@ def setUp(self): ) ] - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={ 'host': [ { @@ -227,7 +226,7 @@ def test_host_show_with_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get.assert_called_with( + self.compute_client.get.assert_called_with( '/os-hosts/' + self._host['host_name'], microversion='2.1' ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 2c226f0046..4282982a27 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -14,7 +14,9 @@ import json +from openstack.compute.v2 import hypervisor as _hypervisor from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -27,8 +29,10 @@ def setUp(self): super().setUp() # Fake hypervisors to be listed up - self.hypervisors = compute_fakes.create_hypervisors() - self.compute_sdk_client.hypervisors.return_value = self.hypervisors + self.hypervisors = list( + sdk_fakes.generate_fake_resources(_hypervisor.Hypervisor, count=2) + ) + self.compute_client.hypervisors.return_value = iter(self.hypervisors) self.columns = ( "ID", @@ -102,7 +106,7 @@ def test_hypervisor_list_no_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with(details=True) + self.compute_client.hypervisors.assert_called_with(details=True) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -117,17 +121,15 @@ def test_hypervisor_list_matching_option_found(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake the return value of search() - self.compute_sdk_client.hypervisors.return_value = [ - self.hypervisors[0] - ] + self.compute_client.hypervisors.return_value = [self.hypervisors[0]] self.data = ( ( self.hypervisors[0].id, self.hypervisors[0].name, - self.hypervisors[1].hypervisor_type, - self.hypervisors[1].host_ip, - self.hypervisors[1].state, + self.hypervisors[0].hypervisor_type, + self.hypervisors[0].host_ip, + self.hypervisors[0].state, ), ) @@ -136,7 +138,7 @@ def test_hypervisor_list_matching_option_found(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with( + self.compute_client.hypervisors.assert_called_with( hypervisor_hostname_pattern=self.hypervisors[0].name, details=True ) self.assertEqual(self.columns, columns) @@ -153,9 +155,7 @@ def test_hypervisor_list_matching_option_not_found(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake exception raised from search() - self.compute_sdk_client.hypervisors.side_effect = exceptions.NotFound( - None - ) + self.compute_client.hypervisors.side_effect = exceptions.NotFound(None) self.assertRaises( exceptions.NotFound, self.cmd.take_action, parsed_args @@ -199,7 +199,7 @@ def test_hypervisor_list_long_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with(details=True) + self.compute_client.hypervisors.assert_called_with(details=True) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, tuple(data)) @@ -217,7 +217,7 @@ def test_hypervisor_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with( + self.compute_client.hypervisors.assert_called_with( limit=1, details=True ) @@ -255,7 +255,7 @@ def test_hypervisor_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with( + self.compute_client.hypervisors.assert_called_with( marker='test_hyp', details=True ) @@ -290,19 +290,18 @@ def setUp(self): ) # Fake hypervisors to be listed up - self.hypervisor = compute_fakes.create_one_hypervisor( - attrs={ - 'uptime': uptime_string, - } + self.hypervisor = sdk_fakes.generate_fake_resource( + _hypervisor.Hypervisor, + uptime=uptime_string, + service={"id": 1, "host": "aaa"}, + cpu_info={"aaa": "aaa"}, ) - # Return value of compute_client.find_hypervisor - self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor + self.compute_client.find_hypervisor.return_value = self.hypervisor + self.compute_client.get_hypervisor.return_value = self.hypervisor - # Return value of compute_client.aggregates() - self.compute_sdk_client.aggregates.return_value = [] + self.compute_client.aggregates.return_value = [] - # Return value of compute_client.get_hypervisor_uptime() uptime_info = { 'status': self.hypervisor.status, 'state': self.hypervisor.state, @@ -310,9 +309,7 @@ def setUp(self): 'hypervisor_hostname': self.hypervisor.name, 'uptime': uptime_string, } - self.compute_sdk_client.get_hypervisor_uptime.return_value = ( - uptime_info - ) + self.compute_client.get_hypervisor_uptime.return_value = uptime_info self.columns_v288 = ( 'aggregates', @@ -334,18 +331,18 @@ def setUp(self): self.data_v288 = ( [], - format_columns.DictColumn({'aaa': 'aaa'}), - '192.168.0.10', + format_columns.DictColumn(self.hypervisor.cpu_info), + self.hypervisor.host_ip, '01:28:24', self.hypervisor.name, - 'QEMU', - 2004001, + self.hypervisor.hypervisor_type, + self.hypervisor.hypervisor_version, self.hypervisor.id, '0.94, 0.62, 0.50', - 'aaa', - 1, - 'up', - 'enabled', + self.hypervisor.service_details["host"], + self.hypervisor.service_details["id"], + self.hypervisor.state, + self.hypervisor.status, '3 days, 11:15', '1', ) @@ -380,31 +377,31 @@ def setUp(self): ) self.data = ( [], - format_columns.DictColumn({'aaa': 'aaa'}), - 0, - 50, - 50, - 1024, - '192.168.0.10', + format_columns.DictColumn(self.hypervisor.cpu_info), + self.hypervisor.current_workload, + self.hypervisor.disk_available, + self.hypervisor.local_disk_free, + self.hypervisor.memory_free, + self.hypervisor.host_ip, '01:28:24', self.hypervisor.name, - 'QEMU', - 2004001, + self.hypervisor.hypervisor_type, + self.hypervisor.hypervisor_version, self.hypervisor.id, '0.94, 0.62, 0.50', - 50, - 0, - 1024, - 512, - 0, - 'aaa', + self.hypervisor.local_disk_size, + self.hypervisor.local_disk_used, + self.hypervisor.memory_size, + self.hypervisor.memory_used, + self.hypervisor.running_vms, + self.hypervisor.service_details["host"], 1, - 'up', - 'enabled', + self.hypervisor.state, + self.hypervisor.status, '3 days, 11:15', '1', - 4, - 0, + self.hypervisor.vcpus, + self.hypervisor.vcpus_used, ) # Get the command object to test @@ -429,6 +426,13 @@ def test_hypervisor_show(self): self.assertEqual(self.columns_v288, columns) self.assertCountEqual(self.data_v288, data) + self.compute_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) + def test_hypervisor_show_pre_v288(self): self.set_compute_api_version('2.87') @@ -448,13 +452,20 @@ def test_hypervisor_show_pre_v288(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + self.compute_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) + def test_hypervisor_show_pre_v228(self): self.set_compute_api_version('2.27') # before microversion 2.28, nova returned a stringified version of this # field self.hypervisor.cpu_info = json.dumps(self.hypervisor.cpu_info) - self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor + self.compute_client.find_hypervisor.return_value = self.hypervisor arglist = [ self.hypervisor.name, @@ -472,6 +483,13 @@ def test_hypervisor_show_pre_v228(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + self.compute_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) + def test_hypervisor_show_uptime_not_implemented(self): self.set_compute_api_version('2.87') @@ -483,7 +501,7 @@ def test_hypervisor_show_uptime_not_implemented(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.get_hypervisor_uptime.side_effect = ( + self.compute_client.get_hypervisor_uptime.side_effect = ( sdk_exceptions.HttpException(http_status=501) ) @@ -518,28 +536,35 @@ def test_hypervisor_show_uptime_not_implemented(self): ) expected_data = ( [], - format_columns.DictColumn({'aaa': 'aaa'}), - 0, - 50, - 50, - 1024, - '192.168.0.10', + format_columns.DictColumn(self.hypervisor.cpu_info), + self.hypervisor.current_workload, + self.hypervisor.disk_available, + self.hypervisor.local_disk_free, + self.hypervisor.memory_free, + self.hypervisor.host_ip, self.hypervisor.name, - 'QEMU', - 2004001, + self.hypervisor.hypervisor_type, + self.hypervisor.hypervisor_version, self.hypervisor.id, - 50, - 0, - 1024, - 512, - 0, - 'aaa', + self.hypervisor.local_disk_size, + self.hypervisor.local_disk_used, + self.hypervisor.memory_size, + self.hypervisor.memory_used, + self.hypervisor.running_vms, + self.hypervisor.service_details["host"], 1, - 'up', - 'enabled', - 4, - 0, + self.hypervisor.state, + self.hypervisor.status, + self.hypervisor.vcpus, + self.hypervisor.vcpus_used, ) self.assertEqual(expected_columns, columns) self.assertCountEqual(expected_data, data) + + self.compute_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index 5a4e3c52f1..89d4d459fe 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -12,20 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. # -from unittest import mock from openstackclient.compute.v2 import hypervisor_stats from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import fakes -class TestHypervisorStats(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - self.compute_sdk_client.get = mock.Mock() - - # Not in fakes.py because hypervisor stats has been deprecated @@ -61,13 +53,13 @@ def create_one_hypervisor_stats(attrs=None): return stats_info -class TestHypervisorStatsShow(TestHypervisorStats): +class TestHypervisorStatsShow(compute_fakes.TestComputev2): _stats = create_one_hypervisor_stats() def setUp(self): super().setUp() - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'hypervisor_statistics': self._stats} ) diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index d6be7d0d60..4eaaf4c9b0 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -12,17 +12,19 @@ # License for the specific language governing permissions and limitations # under the License. -import copy from unittest import mock from unittest.mock import call import uuid +from openstack.compute.v2 import keypair as _keypair +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role_assignment as _role_assignment +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.compute.v2 import keypair from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.unit import utils as tests_utils @@ -31,20 +33,17 @@ def setUp(self): super().setUp() # Initialize the user mock + self._user = sdk_fakes.generate_fake_resource(_user.User) self.users_mock = self.identity_client.users self.users_mock.reset_mock() - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.users_mock.get.return_value = self._user class TestKeypairCreate(TestKeypair): def setUp(self): super().setUp() - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) self.columns = ( 'created_at', @@ -68,7 +67,7 @@ def setUp(self): # Get the command object to test self.cmd = keypair.CreateKeypair(self.app, None) - self.compute_sdk_client.create_keypair.return_value = self.keypair + self.compute_client.create_keypair.return_value = self.keypair @mock.patch.object( keypair, @@ -86,7 +85,7 @@ def test_keypair_create_no_options(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=mock_generate.return_value.public_key, ) @@ -122,11 +121,11 @@ def test_keypair_create_public_key(self): ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value - m_file.read.return_value = 'dummy' + m_file.read.return_value = self.keypair.public_key columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=self.keypair.public_key, ) @@ -161,7 +160,7 @@ def test_keypair_create_private_key(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=mock_generate.return_value.public_key, ) @@ -178,7 +177,7 @@ def test_keypair_create_with_key_type(self): self.set_compute_api_version('2.2') for key_type in ['x509', 'ssh']: - self.compute_sdk_client.create_keypair.return_value = self.keypair + self.compute_client.create_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -208,10 +207,10 @@ def test_keypair_create_with_key_type(self): ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value - m_file.read.return_value = 'dummy' + m_file.read.return_value = self.keypair.public_key columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=self.keypair.public_key, key_type=key_type, @@ -263,20 +262,20 @@ def test_key_pair_create_with_user(self, mock_generate): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, - user_id=identity_fakes.user_id, + user_id=self._user.id, public_key=mock_generate.return_value.public_key, ) @@ -288,11 +287,11 @@ def test_key_pair_create_with_user_pre_v210(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -306,11 +305,13 @@ def test_key_pair_create_with_user_pre_v210(self): class TestKeypairDelete(TestKeypair): - keypairs = compute_fakes.create_keypairs(count=2) - def setUp(self): super().setUp() + self.keypairs = list( + sdk_fakes.generate_fake_resources(_keypair.Keypair, count=2) + ) + self.cmd = keypair.DeleteKeypair(self.app, None) def test_keypair_delete(self): @@ -324,7 +325,7 @@ def test_keypair_delete(self): ret = self.cmd.take_action(parsed_args) self.assertIsNone(ret) - self.compute_sdk_client.delete_keypair.assert_called_with( + self.compute_client.delete_keypair.assert_called_with( self.keypairs[0].name, ignore_missing=False ) @@ -342,7 +343,7 @@ def test_delete_multiple_keypairs(self): calls = [] for k in self.keypairs: calls.append(call(k.name, ignore_missing=False)) - self.compute_sdk_client.delete_keypair.assert_has_calls(calls) + self.compute_client.delete_keypair.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_keypairs_with_exception(self): @@ -356,7 +357,7 @@ def test_delete_multiple_keypairs_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.delete_keypair.side_effect = [ + self.compute_client.delete_keypair.side_effect = [ None, exceptions.CommandError, ] @@ -369,14 +370,14 @@ def test_delete_multiple_keypairs_with_exception(self): calls = [] for k in arglist: calls.append(call(k, ignore_missing=False)) - self.compute_sdk_client.delete_keypair.assert_has_calls(calls) + self.compute_client.delete_keypair.assert_has_calls(calls) def test_keypair_delete_with_user(self): self.set_compute_api_version('2.10') - arglist = ['--user', identity_fakes.user_name, self.keypairs[0].name] + arglist = ['--user', self._user.name, self.keypairs[0].name] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', [self.keypairs[0].name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -384,18 +385,18 @@ def test_keypair_delete_with_user(self): ret = self.cmd.take_action(parsed_args) self.assertIsNone(ret) - self.compute_sdk_client.delete_keypair.assert_called_with( + self.compute_client.delete_keypair.assert_called_with( self.keypairs[0].name, - user_id=identity_fakes.user_id, + user_id=self._user.id, ignore_missing=False, ) def test_keypair_delete_with_user_pre_v210(self): self.set_compute_api_version('2.9') - arglist = ['--user', identity_fakes.user_name, self.keypairs[0].name] + arglist = ['--user', self._user.name, self.keypairs[0].name] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', [self.keypairs[0].name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -409,17 +410,19 @@ def test_keypair_delete_with_user_pre_v210(self): class TestKeypairList(TestKeypair): - # Return value of self.compute_sdk_client.keypairs(). - keypairs = compute_fakes.create_keypairs(count=1) - def setUp(self): super().setUp() - self.compute_sdk_client.keypairs.return_value = self.keypairs + self.keypairs = list( + sdk_fakes.generate_fake_resources(_keypair.Keypair, count=1) + ) + self.compute_client.keypairs.return_value = iter(self.keypairs) # Get the command object to test self.cmd = keypair.ListKeypair(self.app, None) + self._project = sdk_fakes.generate_fake_resource(_project.Project) + def test_keypair_list_no_options(self): arglist = [] verifylist = [] @@ -433,7 +436,7 @@ def test_keypair_list_no_options(self): # Set expected values - self.compute_sdk_client.keypairs.assert_called_with() + self.compute_client.keypairs.assert_called_with() self.assertEqual(('Name', 'Fingerprint'), columns) self.assertEqual( @@ -456,7 +459,7 @@ def test_keypair_list_v22(self): # Set expected values - self.compute_sdk_client.keypairs.assert_called_with() + self.compute_client.keypairs.assert_called_with() self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) self.assertEqual( @@ -475,26 +478,22 @@ def test_keypair_list_with_user(self): users_mock = self.identity_client.users users_mock.reset_mock() - users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + users_mock.get.return_value = self._user arglist = [ '--user', - identity_fakes.user_name, + self._user.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - users_mock.get.assert_called_with(identity_fakes.user_name) - self.compute_sdk_client.keypairs.assert_called_with( - user_id=identity_fakes.user_id, + users_mock.get.assert_called_with(self._user.name) + self.compute_client.keypairs.assert_called_with( + user_id=self._user.id, ) self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) @@ -514,10 +513,10 @@ def test_keypair_list_with_user_pre_v210(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -531,34 +530,30 @@ def test_keypair_list_with_user_pre_v210(self): def test_keypair_list_with_project(self): self.set_compute_api_version('2.35') - projects_mock = self.identity_client.tenants + projects_mock = self.identity_client.projects projects_mock.reset_mock() - projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + projects_mock.get.return_value = self._project - users_mock = self.identity_client.users - users_mock.reset_mock() - users_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ), - ] + role_assignments_mock = self.identity_sdk_client.role_assignments + role_assignments_mock.reset_mock() + assignment = sdk_fakes.generate_fake_resource( + _role_assignment.RoleAssignment + ) + assignment.user = self._user + role_assignments_mock.return_value = [assignment] - arglist = ['--project', identity_fakes.project_name] - verifylist = [('project', identity_fakes.project_name)] + arglist = ['--project', self._project.name] + verifylist = [('project', self._project.name)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - projects_mock.get.assert_called_with(identity_fakes.project_name) - users_mock.list.assert_called_with(tenant_id=identity_fakes.project_id) - self.compute_sdk_client.keypairs.assert_called_with( - user_id=identity_fakes.user_id, + projects_mock.get.assert_called_with(self._project.name) + role_assignments_mock.assert_called_with( + scope_project_id=self._project.id + ) + self.compute_client.keypairs.assert_called_with( + user_id=self._user.id, ) self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) @@ -576,8 +571,8 @@ def test_keypair_list_with_project(self): def test_keypair_list_with_project_pre_v210(self): self.set_compute_api_version('2.9') - arglist = ['--project', identity_fakes.project_name] - verifylist = [('project', identity_fakes.project_name)] + arglist = ['--project', self._project.name] + verifylist = [('project', self._project.name)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( @@ -590,9 +585,9 @@ def test_keypair_list_with_project_pre_v210(self): def test_keypair_list_conflicting_user_options(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, '--project', - identity_fakes.project_name, + self._project.name, ] self.assertRaises( @@ -617,7 +612,7 @@ def test_keypair_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.keypairs.assert_called_with(limit=1) + self.compute_client.keypairs.assert_called_with(limit=1) def test_keypair_list_with_limit_pre_v235(self): self.set_compute_api_version('2.34') @@ -653,7 +648,7 @@ def test_keypair_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.keypairs.assert_called_with(marker='test_kp') + self.compute_client.keypairs.assert_called_with(marker='test_kp') def test_keypair_list_with_marker_pre_v235(self): self.set_compute_api_version('2.34') @@ -707,8 +702,8 @@ def test_keypair_show_no_options(self): ) def test_keypair_show(self): - self.keypair = compute_fakes.create_one_keypair() - self.compute_sdk_client.find_keypair.return_value = self.keypair + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) + self.compute_client.find_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -727,7 +722,7 @@ def test_keypair_show(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_keypair.assert_called_with( + self.compute_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False ) @@ -735,8 +730,8 @@ def test_keypair_show(self): self.assertEqual(self.data, data) def test_keypair_show_public(self): - self.keypair = compute_fakes.create_one_keypair() - self.compute_sdk_client.find_keypair.return_value = self.keypair + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) + self.compute_client.find_keypair.return_value = self.keypair arglist = ['--public-key', self.keypair.name] verifylist = [('public_key', True), ('name', self.keypair.name)] @@ -751,8 +746,8 @@ def test_keypair_show_public(self): def test_keypair_show_with_user(self): self.set_compute_api_version('2.10') - self.keypair = compute_fakes.create_one_keypair() - self.compute_sdk_client.find_keypair.return_value = self.keypair + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) + self.compute_client.find_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -767,22 +762,22 @@ def test_keypair_show_with_user(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.users_mock.get.assert_called_with(identity_fakes.user_name) - self.compute_sdk_client.find_keypair.assert_called_with( + self.users_mock.get.assert_called_with(self._user.name) + self.compute_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False, - user_id=identity_fakes.user_id, + user_id=self._user.id, ) self.assertEqual(self.columns, columns) @@ -791,14 +786,14 @@ def test_keypair_show_with_user(self): def test_keypair_show_with_user_pre_v210(self): self.set_compute_api_version('2.9') - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index a116ff3c35..0597312510 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -13,7 +13,6 @@ # under the License. import base64 -import collections import getpass import json import tempfile @@ -21,7 +20,12 @@ import uuid import iso8601 +from openstack.compute.v2 import flavor as _flavor +from openstack.compute.v2 import server as _server +from openstack.compute.v2 import server_group as _server_group from openstack import exceptions as sdk_exceptions +from openstack.image.v2 import image as _image +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils as common_utils @@ -68,40 +72,16 @@ def setUp(self): self.attrs = {} def setup_sdk_servers_mock(self, count): - servers = compute_fakes.create_sdk_servers( + servers = compute_fakes.create_servers( attrs=self.attrs, count=count, ) # This is the return value for compute_client.find_server() - self.compute_sdk_client.find_server.side_effect = servers + self.compute_client.find_server.side_effect = servers return servers - def setup_sdk_volumes_mock(self, count): - volumes = volume_fakes.create_sdk_volumes(count=count) - - # This is the return value for volume_client.find_volume() - self.volume_sdk_client.find_volume.side_effect = volumes - - return volumes - - def run_method_with_sdk_servers(self, method_name, server_count): - servers = self.setup_sdk_servers_mock(count=server_count) - - arglist = [s.id for s in servers] - verifylist = [ - ('server', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [mock.call(s.id) for s in servers] - method = getattr(self.compute_sdk_client, method_name) - method.assert_has_calls(calls) - self.assertIsNone(result) - class TestServerAddFixedIP(TestServer): def setUp(self): @@ -110,10 +90,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddFixedIP(self.app, None) - # Mock network methods - self.find_network = mock.Mock() - self.app.client_manager.network.find_network = self.find_network - def test_server_add_fixed_ip_pre_v249_with_tag(self): self.set_compute_api_version('2.48') @@ -154,9 +130,7 @@ def test_server_add_fixed_ip(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -191,7 +165,7 @@ def test_server_add_fixed_ip(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'] ) @@ -201,9 +175,7 @@ def test_server_add_fixed_ip_with_fixed_ip(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -244,7 +216,7 @@ def test_server_add_fixed_ip_with_fixed_ip(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -256,9 +228,7 @@ def test_server_add_fixed_ip_with_tag(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -304,7 +274,7 @@ def test_server_add_fixed_ip_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -317,9 +287,7 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -365,7 +333,7 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -378,8 +346,8 @@ def setUp(self): super().setUp() self.app.client_manager.network_endpoint_enabled = False - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.cmd = server.AddFloatingIP(self.app, None) @@ -396,10 +364,10 @@ def test_server_add_floating_ip_default(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) - self.compute_sdk_client.add_floating_ip_to_server.assert_called_once_with( + self.compute_client.add_floating_ip_to_server.assert_called_once_with( self.server, '1.2.3.4', fixed_address=None ) @@ -419,10 +387,10 @@ def test_server_add_floating_ip_fixed(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) - self.compute_sdk_client.add_floating_ip_to_server.assert_called_once_with( + self.compute_client.add_floating_ip_to_server.assert_called_once_with( self.server, '1.2.3.4', fixed_address='5.6.7.8' ) @@ -434,10 +402,10 @@ class TestServerAddFloatingIPNetwork( def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server - self.network_client.update_ip = mock.Mock(return_value=None) + self.network_client.update_ip.return_value = None # Get the command object to test self.cmd = server.AddFloatingIP(self.app, None) @@ -445,8 +413,8 @@ def setUp(self): def test_server_add_floating_ip(self): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) - self.network_client.ports = mock.Mock(return_value=[_port]) + self.network_client.find_ip.return_value = _floating_ip + self.network_client.ports.return_value = [_port] arglist = [ self.server.id, _floating_ip['floating_ip_address'], @@ -477,8 +445,8 @@ def test_server_add_floating_ip(self): def test_server_add_floating_ip_no_ports(self): floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=floating_ip) - self.network_client.ports = mock.Mock(return_value=[]) + self.network_client.find_ip.return_value = floating_ip + self.network_client.ports.return_value = [] arglist = [ self.server.id, @@ -508,17 +476,17 @@ def test_server_add_floating_ip_no_ports(self): def test_server_add_floating_ip_no_external_gateway(self, success=False): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) + self.network_client.find_ip.return_value = _floating_ip return_value = [_port] # In the success case, we'll have two ports, where the first port is # not attached to an external gateway but the second port is. if success: return_value.append(_port) - self.network_client.ports = mock.Mock(return_value=return_value) + self.network_client.ports.return_value = return_value side_effect = [sdk_exceptions.NotFoundException()] if success: side_effect.append(None) - self.network_client.update_ip = mock.Mock(side_effect=side_effect) + self.network_client.update_ip.side_effect = side_effect arglist = [ self.server.id, _floating_ip['floating_ip_address'], @@ -564,8 +532,8 @@ def test_server_add_floating_ip_one_external_gateway(self): def test_server_add_floating_ip_with_fixed_ip(self): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) - self.network_client.ports = mock.Mock(return_value=[_port]) + self.network_client.find_ip.return_value = _floating_ip + self.network_client.ports.return_value = [_port] # The user has specified a fixed ip that matches one of the ports # already attached to the instance. arglist = [ @@ -604,8 +572,8 @@ def test_server_add_floating_ip_with_fixed_ip(self): def test_server_add_floating_ip_with_fixed_ip_no_port_found(self): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) - self.network_client.ports = mock.Mock(return_value=[_port]) + self.network_client.find_ip.return_value = _floating_ip + self.network_client.ports.return_value = [_port] # The user has specified a fixed ip that does not match any of the # ports already attached to the instance. nonexistent_ip = '10.0.0.9' @@ -643,9 +611,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddPort(self.app, None) - self.find_port = mock.Mock() - self.app.client_manager.network.find_port = self.find_port - def _test_server_add_port(self, port_id): servers = self.setup_sdk_servers_mock(count=1) port = 'fake-port' @@ -659,27 +624,29 @@ def _test_server_add_port(self, port_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], port_id=port_id ) self.assertIsNone(result) def test_server_add_port(self): - self._test_server_add_port(self.find_port.return_value.id) - self.find_port.assert_called_once_with( + self._test_server_add_port( + self.network_client.find_port.return_value.id + ) + self.network_client.find_port.assert_called_once_with( 'fake-port', ignore_missing=False ) def test_server_add_port_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_add_port('fake-port') - self.find_port.assert_not_called() + self.network_client.find_port.assert_not_called() def test_server_add_port_with_tag(self): self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) - self.find_port.return_value.id = 'fake-port' + self.network_client.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, 'fake-port', @@ -696,7 +663,7 @@ def test_server_add_port_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], port_id='fake-port', tag='tag1' ) @@ -704,7 +671,7 @@ def test_server_add_port_with_tag_pre_v249(self): self.set_compute_api_version('2.48') servers = self.setup_sdk_servers_mock(count=1) - self.find_port.return_value.id = 'fake-port' + self.network_client.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, 'fake-port', @@ -730,18 +697,21 @@ class TestServerVolume(TestServer): def setUp(self): super().setUp() - self.servers = self.setup_sdk_servers_mock(count=1) - self.volumes = self.setup_sdk_volumes_mock(count=1) + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + + self.volume = volume_fakes.create_one_sdk_volume() + self.volume_sdk_client.find_volume.return_value = self.volume attrs = { - 'server_id': self.servers[0].id, - 'volume_id': self.volumes[0].id, + 'server_id': self.server.id, + 'volume_id': self.volume.id, } self.volume_attachment = compute_fakes.create_one_volume_attachment( attrs=attrs ) - self.compute_sdk_client.create_volume_attachment.return_value = ( + self.compute_client.create_volume_attachment.return_value = ( self.volume_attachment ) @@ -758,12 +728,12 @@ def test_server_add_volume(self): arglist = [ '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ] @@ -781,8 +751,8 @@ def test_server_add_volume(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb' + self.compute_client.create_volume_attachment.assert_called_once_with( + self.server, volumeId=self.volume.id, device='/dev/sdb' ) def test_server_add_volume_with_tag(self): @@ -793,12 +763,12 @@ def test_server_add_volume_with_tag(self): '/dev/sdb', '--tag', 'foo', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('tag', 'foo'), ] @@ -818,9 +788,9 @@ def test_server_add_volume_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], - volumeId=self.volumes[0].id, + self.compute_client.create_volume_attachment.assert_called_once_with( + self.server, + volumeId=self.volume.id, device='/dev/sdb', tag='foo', ) @@ -829,14 +799,14 @@ def test_server_add_volume_with_tag_pre_v249(self): self.set_compute_api_version('2.48') arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, '--tag', 'foo', ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('tag', 'foo'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -856,13 +826,13 @@ def test_server_add_volume_with_enable_delete_on_termination(self): '--enable-delete-on-termination', '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('enable_delete_on_termination', True), ] @@ -888,9 +858,9 @@ def test_server_add_volume_with_enable_delete_on_termination(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], - volumeId=self.volumes[0].id, + self.compute_client.create_volume_attachment.assert_called_once_with( + self.server, + volumeId=self.volume.id, device='/dev/sdb', delete_on_termination=True, ) @@ -904,13 +874,13 @@ def test_server_add_volume_with_disable_delete_on_termination(self): '--disable-delete-on-termination', '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('disable_delete_on_termination', True), ] @@ -937,9 +907,9 @@ def test_server_add_volume_with_disable_delete_on_termination(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], - volumeId=self.volumes[0].id, + self.compute_client.create_volume_attachment.assert_called_once_with( + self.server, + volumeId=self.volume.id, device='/dev/sdb', delete_on_termination=False, ) @@ -950,13 +920,13 @@ def test_server_add_volume_with_enable_delete_on_termination_pre_v279( self.set_compute_api_version('2.78') arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, '--enable-delete-on-termination', ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('enable_delete_on_termination', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -974,13 +944,13 @@ def test_server_add_volume_with_disable_delete_on_termination_pre_v279( self.set_compute_api_version('2.78') arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, '--disable-delete-on-termination', ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('disable_delete_on_termination', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1002,13 +972,13 @@ def test_server_add_volume_with_disable_and_enable_delete_on_termination( '--disable-delete-on-termination', '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('enable_delete_on_termination', True), ('disable_delete_on_termination', True), @@ -1036,13 +1006,13 @@ def setUp(self): def test_server_remove_volume(self): arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1050,9 +1020,9 @@ def test_server_remove_volume(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.delete_volume_attachment.assert_called_once_with( - self.volumes[0], - self.servers[0], + self.compute_client.delete_volume_attachment.assert_called_once_with( + self.volume, + self.server, ignore_missing=False, ) @@ -1064,9 +1034,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddNetwork(self.app, None) - self.find_network = mock.Mock() - self.app.client_manager.network.find_network = self.find_network - def _test_server_add_network(self, net_id): servers = self.setup_sdk_servers_mock(count=1) network = 'fake-network' @@ -1080,27 +1047,29 @@ def _test_server_add_network(self, net_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], net_id=net_id ) self.assertIsNone(result) def test_server_add_network(self): - self._test_server_add_network(self.find_network.return_value.id) - self.find_network.assert_called_once_with( + self._test_server_add_network( + self.network_client.find_network.return_value.id + ) + self.network_client.find_network.assert_called_once_with( 'fake-network', ignore_missing=False ) def test_server_add_network_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_add_network('fake-network') - self.find_network.assert_not_called() + self.network_client.find_network.assert_not_called() def test_server_add_network_with_tag(self): self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) - self.find_network.return_value.id = 'fake-network' + self.network_client.find_network.return_value.id = 'fake-network' arglist = [ servers[0].id, @@ -1118,7 +1087,7 @@ def test_server_add_network_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], net_id='fake-network', tag='tag1' ) @@ -1126,7 +1095,7 @@ def test_server_add_network_with_tag_pre_v249(self): self.set_compute_api_version('2.48') servers = self.setup_sdk_servers_mock(count=1) - self.find_network.return_value.id = 'fake-network' + self.network_client.find_network.return_value.id = 'fake-network' arglist = [ servers[0].id, @@ -1153,11 +1122,9 @@ class TestServerAddSecurityGroup(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.add_security_group_to_server.return_value = ( - None - ) + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.add_security_group_to_server.return_value = None # Get the command object to test self.cmd = server.AddServerSecurityGroup(self.app, None) @@ -1166,7 +1133,7 @@ def test_server_add_security_group__nova_network(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1182,14 +1149,14 @@ def test_server_add_security_group__nova_network(self): ) as mock_find_nova_net_sg: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( - self.server, 'fake_sg' + self.compute_client.add_security_group_to_server.assert_called_once_with( + self.server, {'name': 'fake_sg'} ) mock_find_nova_net_sg.assert_called_once_with( - self.compute_sdk_client, 'fake_sg' + self.compute_client, 'fake_sg' ) self.assertIsNone(result) @@ -1197,17 +1164,17 @@ def test_server_add_security_group(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( - self.server, 'fake_sg' + self.compute_client.add_security_group_to_server.assert_called_once_with( + self.server, {'name': 'fake_sg'} ) self.assertIsNone(result) @@ -1246,7 +1213,6 @@ class TestServerCreate(TestServer): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -1295,7 +1261,6 @@ def datalist(self): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress None, # project_id format_columns.DictColumn({}), # properties @@ -1316,7 +1281,7 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor attrs = { 'addresses': {}, @@ -1324,10 +1289,10 @@ def setUp(self): 'image': self.image, 'flavor': self.flavor, } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.server = compute_fakes.create_one_server(attrs=attrs) - self.compute_sdk_client.create_server.return_value = self.server - self.compute_sdk_client.get_server.return_value = self.server + self.compute_client.create_server.return_value = self.server + self.compute_client.get_server.return_value = self.server self.volume = volume_fakes.create_one_volume() self.snapshot = volume_fakes.create_one_snapshot() @@ -1369,13 +1334,13 @@ def test_server_create_minimal(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] * 2 ) self.image_client.find_image.assert_called_once_with( self.image.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1396,8 +1361,10 @@ def test_server_create_minimal(self): self.assertEqual(self.datalist(), data) def test_server_create_with_options(self): - server_group = compute_fakes.create_one_server_group() - self.compute_sdk_client.find_server_group.return_value = server_group + server_group = sdk_fakes.generate_fake_resource( + _server_group.ServerGroup + ) + self.compute_client.find_server_group.return_value = server_group security_group = network_fakes.create_one_security_group() self.network_client.find_security_group.return_value = security_group @@ -1429,7 +1396,7 @@ def test_server_create_with_options(self): ('flavor', self.flavor.id), ('key_name', 'keyname'), ('properties', {'Beta': 'b'}), - ('security_group', [security_group.id]), + ('security_groups', [security_group.id]), ('hints', {'a': ['b', 'c']}), ('server_group', server_group.id), ('config_drive', True), @@ -1440,7 +1407,7 @@ def test_server_create_with_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] * 2 ) self.network_client.find_security_group.assert_called_once_with( @@ -1449,10 +1416,10 @@ def test_server_create_with_options(self): self.image_client.find_image.assert_called_once_with( self.image.id, ignore_missing=False ) - self.compute_sdk_client.find_server_group.assert_called_once_with( + self.compute_client.find_server_group.assert_called_once_with( server_group.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1499,7 +1466,7 @@ def test_server_create_with_not_exist_security_group(self): ('image', self.image.id), ('flavor', self.flavor.id), ('key_name', 'keyname'), - ('security_group', ['not_exist_sg']), + ('security_groups', ['not_exist_sg']), ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1525,7 +1492,7 @@ def test_server_create_with_security_group_in_nova_network(self): verifylist = [ ('image', self.image.id), ('flavor', self.flavor.id), - ('security_group', [sg_name]), + ('security_groups', [sg_name]), ('server_name', self.server.name), ] @@ -1542,8 +1509,8 @@ def test_server_create_with_security_group_in_nova_network(self): ) as mock_find: columns, data = self.cmd.take_action(parsed_args) - mock_find.assert_called_once_with(self.compute_sdk_client, sg_name) - self.compute_sdk_client.create_server.assert_called_once_with( + mock_find.assert_called_once_with(self.compute_client, sg_name) + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1565,6 +1532,60 @@ def test_server_create_with_security_group_in_nova_network(self): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) + def test_server_create_with_no_security_group(self): + arglist = [ + '--image', + self.image.id, + '--flavor', + self.flavor.id, + '--no-security-group', + self.server.name, + ] + verifylist = [ + ('image', self.image.id), + ('flavor', self.flavor.id), + ('key_name', None), + ('properties', None), + ('security_groups', []), + ('hints', {}), + ('server_group', None), + ('config_drive', False), + ('password', None), + ('server_name', self.server.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.find_flavor.assert_has_calls( + [mock.call(self.flavor.id, ignore_missing=False)] * 2 + ) + self.network_client.find_security_group.assert_not_called() + self.image_client.find_image.assert_called_once_with( + self.image.id, ignore_missing=False + ) + self.compute_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + security_groups=[], + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + def test_server_create_with_network(self): network_net1 = network_fakes.create_one_network() network_net2 = network_fakes.create_one_network() @@ -1665,7 +1686,7 @@ def find_port(name_or_id, ignore_missing): mock.call(port_port2.id, ignore_missing=False), ] ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1677,7 +1698,7 @@ def find_port(name_or_id, ignore_missing): }, { 'uuid': network_net2.id, - 'fixed': '10.0.0.2', + 'fixed_ip': '10.0.0.2', }, { 'port': port_port1.id, @@ -1741,7 +1762,7 @@ def test_server_create_with_network_tag(self): self.network_client.find_network.assert_called_once_with( network.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1802,7 +1823,7 @@ def test_server_create_with_network_tag_pre_v243(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def _test_server_create_with_auto_network(self, arglist): # requires API microversion 2.37 or later @@ -1820,7 +1841,7 @@ def _test_server_create_with_auto_network(self, arglist): columns, data = self.cmd.take_action(parsed_args) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1900,7 +1921,7 @@ def test_server_create_with_auto_network_pre_v237(self): 'allocation', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_auto_network_default(self): """Tests creating a server without specifying --nic using 2.37.""" @@ -1926,7 +1947,7 @@ def test_server_create_with_auto_network_default(self): columns, data = self.cmd.take_action(parsed_args) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1963,7 +1984,7 @@ def _test_server_create_with_none_network(self, arglist): columns, data = self.cmd.take_action(parsed_args) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2043,7 +2064,7 @@ def test_server_create_with_none_network_pre_v237(self): 'allocation', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_conflicting_network_options(self): arglist = [ @@ -2088,7 +2109,7 @@ def test_server_create_with_conflicting_network_options(self): 'other --nic, --network or --port value.', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_invalid_network_options(self): arglist = [ @@ -2111,7 +2132,7 @@ def test_server_create_with_invalid_network_options(self): 'Invalid argument abcdefgh; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_invalid_network_key(self): arglist = [ @@ -2134,7 +2155,7 @@ def test_server_create_with_invalid_network_key(self): 'Invalid argument abcdefgh=12324; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_empty_network_key_value(self): arglist = [ @@ -2157,7 +2178,7 @@ def test_server_create_with_empty_network_key_value(self): 'Invalid argument net-id=; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_only_network_key(self): arglist = [ @@ -2180,7 +2201,7 @@ def test_server_create_with_only_network_key(self): 'Invalid argument net-id; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_network_in_nova_network(self): net_name = 'nova-net-net' @@ -2226,8 +2247,8 @@ def test_server_create_with_network_in_nova_network(self): ) as mock_find: columns, data = self.cmd.take_action(parsed_args) - mock_find.assert_called_once_with(self.compute_sdk_client, net_name) - self.compute_sdk_client.create_server.assert_called_once_with( + mock_find.assert_called_once_with(self.compute_client, net_name) + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2270,7 +2291,7 @@ def test_server_create_with_conflicting_net_port_filters(self): [], ) self.assertIn("either 'network' or 'port'", str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_conflicting_fixed_ip_filters(self): arglist = [ @@ -2290,7 +2311,7 @@ def test_server_create_with_conflicting_fixed_ip_filters(self): [], ) self.assertIn("either 'v4-fixed-ip' or 'v6-fixed-ip'", str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_create_with_wait_ok(self, mock_wait_for_status): @@ -2313,7 +2334,7 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2331,7 +2352,7 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status): ], ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, ) @@ -2362,7 +2383,7 @@ def test_server_create_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2380,7 +2401,7 @@ def test_server_create_with_wait_fails(self, mock_wait_for_status): ], ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, ) @@ -2412,7 +2433,7 @@ def test_server_create_userdata(self): columns, data = self.cmd.take_action(parsed_args) mock_file.assert_called_with('userdata.sh', 'rb') - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2456,7 +2477,7 @@ def test_server_create_with_volume(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id='', flavor_id=self.flavor.id, @@ -2498,7 +2519,7 @@ def test_server_create_with_snapshot(self): self.volume_client.volume_snapshots.get.assert_called_once_with( self.snapshot.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id='', flavor_id=self.flavor.id, @@ -2549,7 +2570,7 @@ def test_server_create_with_block_device(self): # we don't do any validation of IDs when using the legacy option self.volume_client.volumes.get.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id='', flavor_id=self.flavor.id, @@ -2627,7 +2648,7 @@ def test_server_create_with_block_device_full(self): # we don't do any validation of IDs when using the legacy option self.volume_client.volumes.get.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2709,7 +2730,7 @@ def test_server_create_with_block_device_from_file(self): # we don't do any validation of IDs when using the legacy option self.volume_client.volumes.get.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2762,7 +2783,7 @@ def test_server_create_with_block_device_invalid_boot_index(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The boot_index key of --block-device ', str(ex)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_source_type(self): block_device = f'uuid={self.volume.name},source_type=foo' @@ -2780,7 +2801,7 @@ def test_server_create_with_block_device_invalid_source_type(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The source_type key of --block-device ', str(ex)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_destination_type(self): block_device = f'uuid={self.volume.name},destination_type=foo' @@ -2798,7 +2819,7 @@ def test_server_create_with_block_device_invalid_destination_type(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The destination_type key of --block-device ', str(ex)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_shutdown(self): block_device = f'uuid={self.volume.name},delete_on_termination=foo' @@ -2818,7 +2839,7 @@ def test_server_create_with_block_device_invalid_shutdown(self): self.assertIn( 'The delete_on_termination key of --block-device ', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_tag_pre_v242(self): self.set_compute_api_version('2.41') @@ -2840,7 +2861,7 @@ def test_server_create_with_block_device_tag_pre_v242(self): self.assertIn( '--os-compute-api-version 2.42 or greater is required', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_volume_type_pre_v267(self): self.set_compute_api_version('2.66') @@ -2862,7 +2883,7 @@ def test_server_create_with_block_device_volume_type_pre_v267(self): self.assertIn( '--os-compute-api-version 2.67 or greater is required', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping(self): self.volume_client.volumes.get.return_value = self.volume @@ -2901,7 +2922,7 @@ def test_server_create_with_block_device_mapping(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2965,7 +2986,7 @@ def test_server_create_with_block_device_mapping_min_input(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3028,7 +3049,7 @@ def test_server_create_with_block_device_mapping_default_input(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3093,7 +3114,7 @@ def test_server_create_with_block_device_mapping_full_input(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3161,7 +3182,7 @@ def test_server_create_with_block_device_mapping_snapshot(self): self.volume_client.volume_snapshots.get.assert_called_once_with( self.snapshot.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3236,7 +3257,7 @@ def test_server_create_with_block_device_mapping_multiple(self): self.volume_client.volumes.get.assert_has_calls( [mock.call(self.volume.name)] * 2 ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3292,7 +3313,7 @@ def test_server_create_with_block_device_mapping_invalid_format(self): self.assertIn( 'argument --block-device-mapping: Invalid argument ', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() # block device mapping don't contain device name "=uuid:::true" arglist = [ @@ -3314,7 +3335,7 @@ def test_server_create_with_block_device_mapping_invalid_format(self): self.assertIn( 'argument --block-device-mapping: Invalid argument ', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping_no_uuid(self): arglist = [ @@ -3336,7 +3357,7 @@ def test_server_create_with_block_device_mapping_no_uuid(self): self.assertIn( 'argument --block-device-mapping: Invalid argument ', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_volume_boot_from_volume_conflict(self): # Tests that specifying --volume and --boot-from-volume results in @@ -3369,7 +3390,7 @@ def test_server_create_volume_boot_from_volume_conflict(self): self.assertIn( '--volume is not allowed with --boot-from-volume', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_boot_from_volume_no_image(self): # Test --boot-from-volume option without --image or @@ -3397,7 +3418,7 @@ def test_server_create_boot_from_volume_no_image(self): 'to support --boot-from-volume option', str(ex), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_image_property(self): image = image_fakes.create_one_image({'hypervisor_type': 'qemu'}) @@ -3421,7 +3442,7 @@ def test_server_create_image_property(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_once_with() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=image.id, flavor_id=self.flavor.id, @@ -3471,7 +3492,7 @@ def test_server_create_image_property_multi(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_once_with() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=image.id, flavor_id=self.flavor.id, @@ -3526,7 +3547,7 @@ def test_server_create_image_property_missed(self): 'No images match the property expected by --image-property', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_image_property_with_image_list(self): target_image = image_fakes.create_one_image( @@ -3559,7 +3580,7 @@ def test_server_create_image_property_with_image_list(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_once_with() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=target_image.id, flavor_id=self.flavor.id, @@ -3615,7 +3636,7 @@ def test_server_create_no_boot_device(self): '(--volume, --snapshot, --block-device) is required', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_swap(self): arglist = [ @@ -3637,7 +3658,7 @@ def test_server_create_with_swap(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3686,7 +3707,7 @@ def test_server_create_with_ephemeral(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3733,7 +3754,7 @@ def test_server_create_with_ephemeral_missing_key(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_ephemeral_invalid_key(self): arglist = [ @@ -3753,7 +3774,7 @@ def test_server_create_with_ephemeral_invalid_key(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_invalid_hint(self): # Not a key-value pair @@ -3774,7 +3795,7 @@ def test_server_create_invalid_hint(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() # Empty key arglist = [ @@ -3794,7 +3815,7 @@ def test_server_create_invalid_hint(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_description(self): # Description is supported for nova api version 2.19 or above @@ -3820,7 +3841,7 @@ def test_server_create_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3867,7 +3888,7 @@ def test_server_create_with_description_pre_v219(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_tag(self): self.set_compute_api_version('2.52') @@ -3894,7 +3915,7 @@ def test_server_create_with_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3945,7 +3966,7 @@ def test_server_create_with_tag_pre_v252(self): self.assertIn( '--os-compute-api-version 2.52 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_host(self): # Explicit host is supported for nova api version 2.74 or above @@ -3971,7 +3992,7 @@ def test_server_create_with_host(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4020,7 +4041,7 @@ def test_server_create_with_host_pre_v274(self): self.assertIn( '--os-compute-api-version 2.74 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_hypervisor_hostname(self): # Explicit hypervisor_hostname is supported for nova api version @@ -4047,7 +4068,7 @@ def test_server_create_with_hypervisor_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4096,7 +4117,7 @@ def test_server_create_with_hypervisor_hostname_pre_v274(self): self.assertIn( '--os-compute-api-version 2.74 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_hostname(self): self.set_compute_api_version('2.90') @@ -4121,7 +4142,7 @@ def test_server_create_with_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4169,7 +4190,7 @@ def test_server_create_with_hostname_pre_v290(self): self.assertIn( '--os-compute-api-version 2.90 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert(self): self.set_compute_api_version('2.63') @@ -4196,7 +4217,7 @@ def test_server_create_with_trusted_image_cert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4246,7 +4267,7 @@ def test_server_create_with_trusted_image_cert_pre_v263(self): self.assertIn( '--os-compute-api-version 2.63 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_volume(self): self.set_compute_api_version('2.63') @@ -4279,7 +4300,7 @@ def test_server_create_with_trusted_image_cert_from_volume(self): 'directly from images', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_snapshot(self): self.set_compute_api_version('2.63') @@ -4312,7 +4333,7 @@ def test_server_create_with_trusted_image_cert_from_snapshot(self): 'directly from images', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_boot_from_volume(self): self.set_compute_api_version('2.63') @@ -4348,16 +4369,16 @@ def test_server_create_with_trusted_image_cert_boot_from_volume(self): 'directly from images', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() class TestServerDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.delete_server.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.delete_server.return_value = None # Get the command object to test self.cmd = server.DeleteServer(self.app, None) @@ -4373,10 +4394,10 @@ def test_server_delete_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) self.assertIsNone(result) @@ -4393,18 +4414,18 @@ def test_server_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=True ) self.assertIsNone(result) def test_server_delete_multi_servers(self): - servers = compute_fakes.create_sdk_servers(count=3) - self.compute_sdk_client.find_server.return_value = None - self.compute_sdk_client.find_server.side_effect = servers + servers = compute_fakes.create_servers(count=3) + self.compute_client.find_server.return_value = None + self.compute_client.find_server.side_effect = servers arglist = [] verifylist = [] @@ -4417,17 +4438,66 @@ def test_server_delete_multi_servers(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_has_calls( + self.compute_client.find_server.assert_has_calls( [ mock.call(s.id, ignore_missing=False, all_projects=False) for s in servers ] ) - self.compute_sdk_client.delete_server.assert_has_calls( + self.compute_client.delete_server.assert_has_calls( [mock.call(s, force=False) for s in servers] ) self.assertIsNone(result) + def test_server_delete_multi_servers_with_exceptions(self): + servers = compute_fakes.create_servers(count=2) + self.compute_client.find_server.side_effect = [ + servers[0], + sdk_exceptions.ResourceNotFound(), + servers[1], + ] + + arglist = [servers[0].id, 'unexist_server', servers[1].id] + + verifylist = [ + ('force', False), + ('all_projects', False), + ('wait', False), + ( + 'server', + [servers[0].id, 'unexist_server', servers[1].id], + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 3 servers failed to delete.', str(exc)) + + self.compute_client.find_server.assert_has_calls( + [ + mock.call( + servers[0].id, ignore_missing=False, all_projects=False + ), + mock.call( + 'unexist_server', ignore_missing=False, all_projects=False + ), + mock.call( + servers[1].id, ignore_missing=False, all_projects=False + ), + ] + ) + + self.compute_client.delete_server.assert_has_calls( + [ + mock.call(servers[0], force=False), + mock.call(servers[1], force=False), + ] + ) + def test_server_delete_with_all_projects(self): arglist = [ self.server.id, @@ -4441,10 +4511,10 @@ def test_server_delete_with_all_projects(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=True ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) self.assertIsNone(result) @@ -4462,20 +4532,20 @@ def test_server_delete_wait_ok(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) - self.compute_sdk_client.wait_for_delete.assert_called_once_with( + self.compute_client.wait_for_delete.assert_called_once_with( self.server, callback=mock.ANY, ) self.assertIsNone(result) def test_server_delete_wait_fails(self): - self.compute_sdk_client.wait_for_delete.side_effect = ( + self.compute_client.wait_for_delete.side_effect = ( sdk_exceptions.ResourceTimeout() ) @@ -4493,13 +4563,13 @@ def test_server_delete_wait_fails(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) - self.compute_sdk_client.wait_for_delete.assert_called_once_with( + self.compute_client.wait_for_delete.assert_called_once_with( self.server, callback=mock.ANY, ) @@ -4530,9 +4600,7 @@ def run_test_server_dump(self, server_count): self.assertIsNone(result) for s in servers: - s.trigger_crash_dump.assert_called_once_with( - self.compute_sdk_client - ) + s.trigger_crash_dump.assert_called_once_with(self.compute_client) def test_server_dump_one_server(self): self.run_test_server_dump(1) @@ -4563,10 +4631,18 @@ class _TestServerList(TestServer): 'Flavor Name', 'Flavor ID', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', ) + columns_all_projects = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + 'Project ID', + ) def setUp(self): super().setUp() @@ -4581,7 +4657,7 @@ def setUp(self): 'status': None, 'flavor': None, 'image': None, - 'host': None, + 'compute_host': None, 'project_id': None, 'all_projects': False, 'user_id': None, @@ -4608,12 +4684,12 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor self.attrs['flavor'] = {'original_name': self.flavor.name} # The servers to be listed. self.servers = self.setup_sdk_servers_mock(3) - self.compute_sdk_client.servers.return_value = self.servers + self.compute_client.servers.return_value = self.servers # Get the command object to test self.cmd = server.ListServer(self.app, None) @@ -4623,17 +4699,19 @@ class TestServerList(_TestServerList): def setUp(self): super().setUp() - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image ] - Flavor = collections.namedtuple('Flavor', 'id name') - self.compute_sdk_client.flavors.return_value = [ - Flavor(id=s.flavor['id'], name=self.flavor.name) + self.compute_client.flavors.return_value = [ + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) for s in self.servers ] @@ -4662,9 +4740,9 @@ def test_server_list_no_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called() - self.compute_sdk_client.flavors.assert_called() + self.compute_client.flavors.assert_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4676,14 +4754,14 @@ def test_server_list_no_servers(self): ('deleted', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.servers.return_value = [] + self.compute_client.servers.return_value = [] self.data = () columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4702,7 +4780,6 @@ def test_server_list_long_option(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), ) @@ -4718,15 +4795,45 @@ def test_server_list_long_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) image_ids = {s.image['id'] for s in self.servers if s.image} self.image_client.images.assert_called_once_with( id=f'in:{",".join(image_ids)}', ) - self.compute_sdk_client.flavors.assert_called_once_with(is_public=None) + self.compute_client.flavors.assert_called_once_with(is_public=None) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data, tuple(data)) + def test_server_list_all_projects_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + s.project_id, + ) + for s in self.servers + ) + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ('long', False), + ('deleted', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.image_client.images.assert_called() + self.compute_client.flavors.assert_called() + self.assertEqual(self.columns_all_projects, columns) + self.assertEqual(self.data, tuple(data)) + def test_server_list_column_option(self): arglist = [ '-c', @@ -4748,8 +4855,6 @@ def test_server_list_column_option(self): '-c', 'Availability Zone', '-c', - 'Pinned Availability Zone', - '-c', 'Host', '-c', 'Properties', @@ -4762,7 +4867,7 @@ def test_server_list_column_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertIn('Project ID', columns) self.assertIn('User ID', columns) self.assertIn('Created At', columns) @@ -4772,7 +4877,6 @@ def test_server_list_column_option(self): self.assertIn('Image ID', columns) self.assertIn('Flavor ID', columns) self.assertIn('Availability Zone', columns) - self.assertIn('Pinned Availability Zone', columns) self.assertIn('Host', columns) self.assertIn('Properties', columns) self.assertCountEqual(columns, set(columns)) @@ -4802,9 +4906,9 @@ def test_server_list_no_name_lookup_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4833,9 +4937,9 @@ def test_server_list_n_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4850,11 +4954,11 @@ def test_server_list_name_lookup_one_by_one(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.image_client.get_image.assert_called() - self.compute_sdk_client.find_flavor.assert_called() + self.compute_client.find_flavor.assert_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4871,9 +4975,9 @@ def test_server_list_with_image(self): ) self.kwargs['image'] = self.image.id - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_called_once() + self.compute_client.flavors.assert_called_once() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4885,14 +4989,14 @@ def test_server_list_with_flavor(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] ) self.kwargs['flavor'] = self.flavor.id - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called_once() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4909,7 +5013,7 @@ def test_server_list_with_changes_since(self): self.kwargs['changes-since'] = '2016-03-04T06:27:59Z' self.kwargs['deleted'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4930,7 +5034,7 @@ def test_server_list_with_invalid_changes_since(self, mock_parse_isotime): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - 'Invalid changes-since value: Invalid time ' 'value', str(e) + 'Invalid changes-since value: Invalid time value', str(e) ) mock_parse_isotime.assert_called_once_with('Invalid time value') @@ -4952,7 +5056,7 @@ def test_server_list_with_tag(self): self.kwargs['tags'] = 'tag1,tag2' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4995,7 +5099,7 @@ def test_server_list_with_not_tag(self): self.kwargs['not-tags'] = 'tag1,tag2' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5034,7 +5138,7 @@ def test_server_list_with_availability_zone(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['availability_zone'] = 'test-az' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5051,7 +5155,7 @@ def test_server_list_with_key_name(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['key_name'] = 'test-key' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5067,7 +5171,7 @@ def test_server_list_with_config_drive(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['config_drive'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5083,7 +5187,7 @@ def test_server_list_with_no_config_drive(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['config_drive'] = False - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5100,7 +5204,7 @@ def test_server_list_with_progress(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['progress'] = '100' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5131,7 +5235,7 @@ def test_server_list_with_vm_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['vm_state'] = 'active' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5148,7 +5252,7 @@ def test_server_list_with_task_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['task_state'] = 'deleting' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5165,7 +5269,7 @@ def test_server_list_with_power_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['power_state'] = 1 - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5185,7 +5289,6 @@ def test_server_list_long_with_host_status_v216(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), ) @@ -5202,23 +5305,24 @@ def test_server_list_long_with_host_status_v216(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.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.compute_sdk_client.servers.reset_mock() + self.compute_client.servers.reset_mock() self.attrs['host_status'] = 'UP' servers = self.setup_sdk_servers_mock(3) - self.compute_sdk_client.servers.return_value = servers + self.compute_client.servers.return_value = servers # Make sure the returned image and flavor IDs match the servers. - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.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 @@ -5240,7 +5344,6 @@ def test_server_list_long_with_host_status_v216(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), s.host_status, @@ -5250,7 +5353,7 @@ def test_server_list_long_with_host_status_v216(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(columns_long, columns) self.assertEqual(tuple(self.data2), tuple(data)) @@ -5277,9 +5380,9 @@ class TestServerListV273(_TestServerList): 'Image ID', 'Flavor', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', + 'Scheduler Hints', ) def setUp(self): @@ -5299,11 +5402,12 @@ def setUp(self): # The servers to be listed. self.servers = self.setup_sdk_servers_mock(3) - self.compute_sdk_client.servers.return_value = self.servers + self.compute_client.servers.return_value = self.servers - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image @@ -5311,7 +5415,7 @@ def setUp(self): # The flavor information is embedded, so now reason for this to be # called - self.compute_sdk_client.flavors = mock.NonCallableMock() + self.compute_client.flavors = mock.NonCallableMock() self.data = tuple( ( @@ -5347,7 +5451,7 @@ def test_server_list_with_locked(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['locked'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5362,7 +5466,7 @@ def test_server_list_with_unlocked_v273(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['locked'] = False - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5395,7 +5499,7 @@ def test_server_list_with_changes_before(self): self.kwargs['changes-before'] = '2016-03-05T06:27:59Z' self.kwargs['deleted'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5417,7 +5521,7 @@ def test_server_list_with_invalid_changes_before(self, mock_parse_isotime): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - 'Invalid changes-before value: Invalid time ' 'value', str(e) + 'Invalid changes-before value: Invalid time value', str(e) ) mock_parse_isotime.assert_called_once_with('Invalid time value') @@ -5454,9 +5558,7 @@ def test_server_list_v269_with_partial_constructs(self): ], "networks": {}, } - fake_server = compute_fakes.fakes.FakeResource( - info=server_dict, - ) + fake_server = _server.Server(**server_dict) self.servers.append(fake_server) columns, data = self.cmd.take_action(parsed_args) # get the first three servers out since our interest is in the partial @@ -5467,52 +5569,373 @@ def test_server_list_v269_with_partial_constructs(self): partial_server = next(data) expected_row = ( 'server-id-95a56bfc4xxxxxx28d7e418bfd97813a', - '', + None, 'UNKNOWN', - server.AddressesColumn(''), + server.AddressesColumn(None), '', '', ) self.assertEqual(expected_row, partial_server) -class TestServerLock(TestServer): +class TestServerListV296(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Pinned Availability Zone', + ) + def setUp(self): super().setUp() + self.set_compute_api_version('2.96') - self.server = compute_fakes.create_one_sdk_server() - - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.lock_server.return_value = None - - # Get the command object to test - self.cmd = server.LockServer(self.app, None) - - def test_server_lock(self): - self.run_method_with_sdk_servers('lock_server', 1) + self.image_client.images.return_value = [ + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] - def test_server_lock_multi_servers(self): - self.run_method_with_sdk_servers('lock_server', 3) + self.compute_client.flavors.return_value = [ + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) + for s in self.servers + ] - def test_server_lock_with_reason(self): - self.set_compute_api_version('2.73') + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # 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, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + getattr(s, 'pinned_availability_zone', ''), + ) + for s in self.servers + ) arglist = [ - self.server.id, - '--reason', - 'blah', + '--long', ] verifylist = [ - ('server', [self.server.id]), - ('reason', 'blah'), + ('all_projects', False), + ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-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', + '-c', + 'Pinned Availability Zone', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.servers.assert_called_with(**self.kwargs) + 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('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertCountEqual(columns, set(columns)) + + +class TestServerListV2100(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Pinned Availability Zone', + 'Scheduler Hints', + ) + + def setUp(self): + super().setUp() + self.set_compute_api_version('2.100') + + self.image_client.images.return_value = [ + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] + + self.compute_client.flavors.return_value = [ + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) + for s in self.servers + ] + + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # 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, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + getattr(s, 'pinned_availability_zone', ''), + format_columns.DictListColumn(None), + ) + for s in self.servers + ) + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-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', + '-c', + 'Pinned Availability Zone', + '-c', + 'Scheduler Hints', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.servers.assert_called_with(**self.kwargs) + 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('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertIn('Scheduler Hints', columns) + self.assertCountEqual(columns, set(columns)) + + +class TestServerAction(compute_fakes.TestComputev2): + def run_method_with_sdk_servers(self, method_name, server_count): + servers = compute_fakes.create_servers(count=server_count) + self.compute_client.find_server.side_effect = servers + + arglist = [s.id for s in servers] + verifylist = [ + ('server', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [mock.call(s.id) for s in servers] + method = getattr(self.compute_client, method_name) + method.assert_has_calls(calls) + self.assertIsNone(result) + + +class TestServerLock(TestServerAction): + def setUp(self): + super().setUp() + + # Get the command object to test + self.cmd = server.LockServer(self.app, None) + + def test_server_lock(self): + self.run_method_with_sdk_servers('lock_server', 1) + + def test_server_lock_multi_servers(self): + self.run_method_with_sdk_servers('lock_server', 3) + + def test_server_lock_with_reason(self): + self.set_compute_api_version('2.73') + + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.lock_server.return_value = None + + arglist = [ + self.server.id, + '--reason', + 'blah', + ] + verifylist = [ + ('server', [self.server.id]), + ('reason', 'blah'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.lock_server.assert_called_with( + self.compute_client.lock_server.assert_called_with( self.server.id, locked_reason="blah", ) @@ -5520,30 +5943,38 @@ def test_server_lock_with_reason(self): def test_server_lock_with_reason_multi_servers(self): self.set_compute_api_version('2.73') - server2 = compute_fakes.create_one_sdk_server() + server_a = compute_fakes.create_one_server() + server_b = compute_fakes.create_one_server() + + self.compute_client.find_server.side_effect = [server_a, server_b] + self.compute_client.lock_server.return_value = None arglist = [ - self.server.id, - server2.id, + server_a.id, + server_b.id, '--reason', 'choo..choo', ] verifylist = [ - ('server', [self.server.id, server2.id]), + ('server', [server_a.id, server_b.id]), ('reason', 'choo..choo'), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.assertEqual(2, self.compute_sdk_client.find_server.call_count) - self.compute_sdk_client.lock_server.assert_called_with( - self.server.id, - locked_reason="choo..choo", + + self.assertEqual(2, self.compute_client.find_server.call_count) + self.compute_client.lock_server.assert_has_calls( + [ + mock.call(server_a.id, locked_reason="choo..choo"), + mock.call(server_b.id, locked_reason="choo..choo"), + ] ) - self.assertEqual(2, self.compute_sdk_client.lock_server.call_count) def test_server_lock_with_reason_pre_v273(self): self.set_compute_api_version('2.72') - server = compute_fakes.create_one_sdk_server() + server = compute_fakes.create_one_server() + arglist = [ server.id, '--reason', @@ -5553,6 +5984,7 @@ def test_server_lock_with_reason_pre_v273(self): ('server', [server.id]), ('reason', "blah"), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( exceptions.CommandError, @@ -5569,10 +6001,10 @@ class TestServerMigrate(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.migrate_server.return_value = None - self.compute_sdk_client.live_migrate_server.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.migrate_server.return_value = None + self.compute_client.live_migrate_server.return_value = None # Get the command object to test self.cmd = server.MigrateServer(self.app, None) @@ -5591,13 +6023,13 @@ def test_server_migrate_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() self.assertIsNone(result) def test_server_migrate_with_host(self): @@ -5621,13 +6053,13 @@ def test_server_migrate_with_host(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, host='fakehost' ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() self.assertIsNone(result) def test_server_migrate_with_block_migration(self): @@ -5647,11 +6079,11 @@ def test_server_migrate_with_block_migration(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_migrate_with_disk_overcommit(self): arglist = [ @@ -5670,11 +6102,11 @@ def test_server_migrate_with_disk_overcommit(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_migrate_with_host_pre_v256(self): # Tests that --host is not allowed for a cold migration @@ -5706,11 +6138,11 @@ def test_server_migrate_with_host_pre_v256(self): str(ex), ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_live_migrate(self): # Tests the --live-migration option without --host or --live. @@ -5729,16 +6161,16 @@ def test_server_live_migrate(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration=False, host=None, disk_overcommit=False, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_host(self): @@ -5762,17 +6194,17 @@ def test_server_live_migrate_with_host(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) # No disk_overcommit and block_migration defaults to auto with # microversion >= 2.25 - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration='auto', host='fakehost', ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_host_pre_v230(self): @@ -5805,11 +6237,11 @@ def test_server_live_migrate_with_host_pre_v230(self): str(ex), ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_block_live_migrate(self): self.set_compute_api_version('2.24') @@ -5829,18 +6261,18 @@ def test_server_block_live_migrate(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) # No disk_overcommit and block_migration defaults to auto with # microversion >= 2.25 - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration=True, disk_overcommit=False, host=None, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_disk_overcommit(self): @@ -5861,16 +6293,16 @@ def test_server_live_migrate_with_disk_overcommit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration=False, disk_overcommit=True, host=None, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_disk_overcommit_post_v224(self): @@ -5892,16 +6324,16 @@ def test_server_live_migrate_with_disk_overcommit_post_v224(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) # There should be no 'disk_over_commit' value present - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration='auto', host=None, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) # A warning should have been logged for using --disk-overcommit. @@ -5927,15 +6359,15 @@ def test_server_migrate_with_wait(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -5960,15 +6392,15 @@ def test_server_migrate_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -5979,7 +6411,7 @@ class TestServerReboot(TestServer): def setUp(self): super().setUp() - self.compute_sdk_client.reboot_server.return_value = None + self.compute_client.reboot_server.return_value = None self.cmd = server.RebootServer(self.app, None) @@ -5998,7 +6430,7 @@ def test_server_reboot(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) @@ -6020,7 +6452,7 @@ def test_server_reboot_with_hard(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'HARD', ) @@ -6044,12 +6476,12 @@ def test_server_reboot_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, servers[0].id, callback=mock.ANY, ) @@ -6078,18 +6510,18 @@ def test_server_reboot_with_wait_fails( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, servers[0].id, callback=mock.ANY, ) -class TestServerPause(TestServer): +class TestServerPause(TestServerAction): def setUp(self): super().setUp() @@ -6114,9 +6546,9 @@ def setUp(self): 'status': 'ACTIVE', 'image': {'id': self.image.id}, } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.rebuild_server.return_value = self.server + self.server = compute_fakes.create_one_server(attrs=attrs) + self.compute_client.find_server.return_value = self.server + self.compute_client.rebuild_server.return_value = self.server self.cmd = server.RebuildServer(self.app, None) @@ -6138,15 +6570,15 @@ def test_rebuild_with_image_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_called_with( image_name, ignore_missing=False ) self.image_client.get_image.assert_called_with(self.image.id) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, image, admin_password=None + self.compute_client.rebuild_server.assert_called_once_with( + self.server, image ) def test_rebuild_with_current_image(self): @@ -6159,15 +6591,15 @@ def test_rebuild_with_current_image(self): # Get the command object to test. self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, self.image, admin_password=None + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image ) def test_rebuild_with_volume_backed_server_no_image(self): @@ -6203,15 +6635,15 @@ def test_rebuild_with_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, self.image, admin_password=None, name=name + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, name=name ) def test_rebuild_with_preserve_ephemeral(self): @@ -6227,18 +6659,15 @@ def test_rebuild_with_preserve_ephemeral(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - preserve_ephemeral=True, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, preserve_ephemeral=True ) def test_rebuild_with_no_preserve_ephemeral(self): @@ -6255,18 +6684,15 @@ def test_rebuild_with_no_preserve_ephemeral(self): # Get the command object to test self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - preserve_ephemeral=False, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, preserve_ephemeral=False ) def test_rebuild_with_password(self): @@ -6277,14 +6703,14 @@ def test_rebuild_with_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, admin_password=password, @@ -6300,18 +6726,15 @@ def test_rebuild_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - description=description, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, description=description ) def test_rebuild_with_description_pre_v219(self): @@ -6340,21 +6763,19 @@ def test_rebuild_with_wait_ok(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], @@ -6376,19 +6797,17 @@ def test_rebuild_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_called_once_with(self.image.id) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], @@ -6409,21 +6828,19 @@ def test_rebuild_with_wait_shutoff_status(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['shutoff'], @@ -6444,21 +6861,19 @@ def test_rebuild_with_wait_error_status(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], @@ -6478,12 +6893,12 @@ def test_rebuild_wrong_status_fails(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_called_once_with(self.image.id) - self.compute_sdk_client.rebuild_server.assert_not_called() + self.compute_client.rebuild_server.assert_not_called() def test_rebuild_with_property(self): arglist = [ @@ -6502,18 +6917,15 @@ def test_rebuild_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - metadata=expected_properties, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, metadata=expected_properties ) def test_rebuild_with_keypair_name(self): @@ -6533,18 +6945,15 @@ def test_rebuild_with_keypair_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - key_name=self.server.key_name, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, key_name=self.server.key_name ) def test_rebuild_with_keypair_name_pre_v254(self): @@ -6581,18 +6990,15 @@ def test_rebuild_with_no_keypair_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - key_name=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, key_name=None ) def test_rebuild_with_keypair_name_and_unset(self): @@ -6639,17 +7045,16 @@ def test_rebuild_with_user_data(self): # Ensure the userdata file is opened mock_file.assert_called_with('userdata.sh', 'rb') - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, - admin_password=None, user_data=base64.b64encode(user_data).decode('utf-8'), ) @@ -6687,18 +7092,15 @@ def test_rebuild_with_no_user_data(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - user_data=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, user_data=None ) def test_rebuild_with_no_user_data_pre_v254(self): @@ -6751,18 +7153,15 @@ def test_rebuild_with_trusted_image_cert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - trusted_image_certificates=['foo', 'bar'], + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, trusted_image_certificates=['foo', 'bar'] ) def test_rebuild_with_trusted_image_cert_pre_v263(self): @@ -6800,18 +7199,15 @@ def test_rebuild_with_no_trusted_image_cert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - trusted_image_certificates=None, + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, trusted_image_certificates=None ) def test_rebuild_with_no_trusted_image_cert_pre_v263(self): @@ -6847,18 +7243,15 @@ def test_rebuild_with_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - hostname='new-hostname', + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.image, hostname='new-hostname' ) def test_rebuild_with_hostname_pre_v290(self): @@ -6888,9 +7281,9 @@ def setUp(self): 'status': 'ACTIVE', 'image': '', } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.rebuild_server.return_value = self.server + self.server = compute_fakes.create_one_server(attrs=attrs) + self.compute_client.find_server.return_value = self.server + self.compute_client.rebuild_server.return_value = self.server self.cmd = server.RebuildServer(self.app, None) @@ -6912,15 +7305,15 @@ def test_rebuild_with_reimage_boot_volume(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_called_with( self.new_image.id, ignore_missing=False ) self.image_client.get_image.assert_not_called() - self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, self.new_image, admin_password=None + self.compute_client.rebuild_server.assert_called_once_with( + self.server, self.new_image ) def test_rebuild_with_no_reimage_boot_volume(self): @@ -6978,14 +7371,15 @@ def setUp(self): 'image': self.image, 'networks': {}, 'adminPass': 'passw0rd', + 'status': 'ACTIVE', } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.server = compute_fakes.create_one_server(attrs=attrs) attrs['id'] = self.server.id - self.new_server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.new_server = compute_fakes.create_one_server(attrs=attrs) # Return value for utils.find_resource for server. - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.get_server.return_value = self.server + self.compute_client.find_server.return_value = self.server + self.compute_client.get_server.return_value = self.server self.cmd = server.EvacuateServer(self.app, None) @@ -6993,15 +7387,13 @@ def _test_evacuate(self, args, verify_args, evac_args): parsed_args = self.check_parser(self.cmd, args, verify_args) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.evacuate_server.assert_called_once_with( + self.compute_client.evacuate_server.assert_called_once_with( self.server, **evac_args ) - self.compute_sdk_client.get_server.assert_called_once_with( - self.server.id - ) + self.compute_client.get_server.assert_called_once_with(self.server.id) def test_evacuate(self): args = [ @@ -7013,7 +7405,7 @@ def test_evacuate(self): evac_args = { 'host': None, 'on_shared_storage': False, - 'password': None, + 'admin_pass': None, } self._test_evacuate(args, verify_args, evac_args) @@ -7030,7 +7422,7 @@ def test_evacuate_with_password(self): evac_args = { 'host': None, 'on_shared_storage': False, - 'password': 'password', + 'admin_pass': 'password', } self._test_evacuate(args, verify_args, evac_args) @@ -7047,7 +7439,7 @@ def test_evacuate_with_host(self): ('server', self.server.id), ('host', 'target-host'), ] - evac_args = {'host': host, 'password': None} + evac_args = {'host': host, 'admin_pass': None} self._test_evacuate(args, verify_args, evac_args) @@ -7080,7 +7472,7 @@ def test_evacuate_without_share_storage(self): evac_args = { 'host': None, 'on_shared_storage': True, - 'password': None, + 'admin_pass': None, } self._test_evacuate(args, verify_args, evac_args) @@ -7111,12 +7503,39 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): evac_args = { 'host': None, 'on_shared_storage': False, - 'password': None, + 'admin_pass': None, + } + self._test_evacuate(args, verify_args, evac_args) + mock_wait_for_status.assert_called_once_with( + self.compute_client.get_server, + self.server.id, + success_status=['ACTIVE'], + callback=mock.ANY, + ) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_evacuate_with_wait_ok_shutoff(self, mock_wait_for_status): + self.server.status = 'SHUTOFF' + self.compute_client.get_server.return_value = self.server + + args = [ + self.server.id, + '--wait', + ] + verify_args = [ + ('server', self.server.id), + ('wait', True), + ] + evac_args = { + 'host': None, + 'on_shared_storage': False, + 'admin_pass': None, } self._test_evacuate(args, verify_args, evac_args) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, + success_status=['ACTIVE', 'SHUTOFF'], callback=mock.ANY, ) @@ -7125,7 +7544,7 @@ class TestServerRemoveFixedIP(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() # Get the command object to test self.cmd = server.RemoveFixedIP(self.app, None) @@ -7143,12 +7562,10 @@ def test_server_remove_fixed_ip(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.remove_fixed_ip_from_server( - self.server, '1.2.3.4' - ) + self.compute_client.remove_fixed_ip_from_server(self.server, '1.2.3.4') self.assertIsNone(result) @@ -7156,8 +7573,8 @@ class TestServerRescue(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.cmd = server.RescueServer(self.app, None) @@ -7172,10 +7589,10 @@ def test_rescue(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.rescue_server.assert_called_once_with( + self.compute_client.rescue_server.assert_called_once_with( self.server, admin_pass=None, image_ref=None ) self.assertIsNone(result) @@ -7199,10 +7616,10 @@ def test_rescue_with_image(self): self.image_client.find_image.assert_called_with( new_image.id, ignore_missing=False ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.rescue_server.assert_called_once_with( + self.compute_client.rescue_server.assert_called_once_with( self.server, admin_pass=None, image_ref=new_image.id ) self.assertIsNone(result) @@ -7222,10 +7639,10 @@ def test_rescue_with_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.rescue_server.assert_called_once_with( + self.compute_client.rescue_server.assert_called_once_with( self.server, admin_pass=password, image_ref=None ) self.assertIsNone(result) @@ -7236,8 +7653,8 @@ def setUp(self): super().setUp() self.app.client_manager.network_endpoint_enabled = False - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.cmd = server.RemoveFloatingIP(self.app, None) @@ -7254,10 +7671,10 @@ def test_server_remove_floating_ip(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) - self.compute_sdk_client.remove_floating_ip_from_server.assert_called_once_with( + self.compute_client.remove_floating_ip_from_server.assert_called_once_with( self.server, '1.2.3.4' ) @@ -7266,14 +7683,14 @@ class TestServerRemoveFloatingIPNetwork(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - self.network_client.update_ip = mock.Mock(return_value=None) + self.network_client.update_ip.return_value = None # Get the command object to test self.cmd = server.RemoveFloatingIP(self.app, None) def test_server_remove_floating_ip_default(self): _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) + self.network_client.find_ip.return_value = _floating_ip arglist = [ 'fake_server', _floating_ip['ip'], @@ -7306,9 +7723,6 @@ def setUp(self): # Get the command object to test self.cmd = server.RemovePort(self.app, None) - self.find_port = mock.Mock() - self.app.client_manager.network.find_port = self.find_port - def _test_server_remove_port(self, port_id): servers = self.setup_sdk_servers_mock(count=1) port = 'fake-port' @@ -7325,21 +7739,23 @@ def _test_server_remove_port(self, port_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.delete_server_interface.assert_called_with( + self.compute_client.delete_server_interface.assert_called_with( port_id, server=servers[0], ignore_missing=False ) self.assertIsNone(result) def test_server_remove_port(self): - self._test_server_remove_port(self.find_port.return_value.id) - self.find_port.assert_called_once_with( + self._test_server_remove_port( + self.network_client.find_port.return_value.id + ) + self.network_client.find_port.assert_called_once_with( 'fake-port', ignore_missing=False ) def test_server_remove_port_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_remove_port('fake-port') - self.find_port.assert_not_called() + self.network_client.find_port.assert_not_called() class TestServerRemoveNetwork(TestServer): @@ -7349,18 +7765,12 @@ def setUp(self): # Get the command object to test self.cmd = server.RemoveNetwork(self.app, None) - # Set method to be tested. - self.fake_inf = mock.Mock() - - self.find_network = mock.Mock() - self.app.client_manager.network.find_network = self.find_network - self.compute_sdk_client.server_interfaces.return_value = [ - self.fake_inf - ] + self.fake_if = mock.Mock() + self.compute_client.server_interfaces.return_value = [self.fake_if] def _test_server_remove_network(self, network_id): - self.fake_inf.net_id = network_id - self.fake_inf.port_id = 'fake-port' + self.fake_if.net_id = network_id + self.fake_if.port_id = 'fake-port' servers = self.setup_sdk_servers_mock(count=1) network = 'fake-network' @@ -7376,33 +7786,35 @@ def _test_server_remove_network(self, network_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_interfaces.assert_called_once_with( + self.compute_client.server_interfaces.assert_called_once_with( servers[0] ) - self.compute_sdk_client.delete_server_interface.assert_called_once_with( + self.compute_client.delete_server_interface.assert_called_once_with( 'fake-port', server=servers[0] ) self.assertIsNone(result) def test_server_remove_network(self): - self._test_server_remove_network(self.find_network.return_value.id) - self.find_network.assert_called_once_with( + self._test_server_remove_network( + self.network_client.find_network.return_value.id + ) + self.network_client.find_network.assert_called_once_with( 'fake-network', ignore_missing=False ) def test_server_remove_network_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_remove_network('fake-network') - self.find_network.assert_not_called() + self.network_client.find_network.assert_not_called() class TestServerRemoveSecurityGroup(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.remove_security_group_from_server.return_value = ( + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.remove_security_group_from_server.return_value = ( None ) @@ -7413,7 +7825,7 @@ def test_server_remove_security_group__nova_network(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -7429,14 +7841,14 @@ def test_server_remove_security_group__nova_network(self): ) as mock_find_nova_net_sg: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( - self.server, 'fake_sg' + self.compute_client.remove_security_group_from_server.assert_called_once_with( + self.server, {'name': 'fake_sg'} ) mock_find_nova_net_sg.assert_called_once_with( - self.compute_sdk_client, 'fake_sg' + self.compute_client, 'fake_sg' ) self.assertIsNone(result) @@ -7444,17 +7856,17 @@ def test_server_remove_security_group(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( - self.server, 'fake_sg' + self.compute_client.remove_security_group_from_server.assert_called_once_with( + self.server, {'name': 'fake_sg'} ) self.assertIsNone(result) @@ -7463,13 +7875,13 @@ class TestServerResize(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor - self.compute_sdk_client.resize_server.return_value = None - self.compute_sdk_client.revert_server_resize.return_value = None - self.compute_sdk_client.confirm_server_resize.return_value = None + self.compute_client.find_flavor.return_value = self.flavor + self.compute_client.resize_server.return_value = None + self.compute_client.revert_server_resize.return_value = None + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.ResizeServer(self.app, None) @@ -7487,11 +7899,11 @@ def test_server_resize_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_not_called() - self.compute_sdk_client.resize_server.assert_not_called() + self.compute_client.find_flavor.assert_not_called() + self.compute_client.resize_server.assert_not_called() self.assertIsNone(result) def test_server_resize(self): @@ -7510,17 +7922,17 @@ def test_server_resize(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( self.flavor.id, ignore_missing=False ) - self.compute_sdk_client.resize_server.assert_called_once_with( + self.compute_client.resize_server.assert_called_once_with( self.server, self.flavor ) - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() self.assertIsNone(result) def test_server_resize_confirm(self): @@ -7538,15 +7950,15 @@ def test_server_resize_confirm(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_not_called() - self.compute_sdk_client.resize_server.assert_not_called() - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.find_flavor.assert_not_called() + self.compute_client.resize_server.assert_not_called() + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() self.assertIsNone(result) # A warning should have been logged for using --confirm. @@ -7571,13 +7983,13 @@ def test_server_resize_revert(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_not_called() - self.compute_sdk_client.resize_server.assert_not_called() - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.find_flavor.assert_not_called() + self.compute_client.resize_server.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7607,20 +8019,20 @@ def test_server_resize_with_wait_ok(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( self.flavor.id, ignore_missing=False ) - self.compute_sdk_client.resize_server.assert_called_once_with( + self.compute_client.resize_server.assert_called_once_with( self.server, self.flavor ) - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -7647,20 +8059,20 @@ def test_server_resize_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( self.flavor.id, ignore_missing=False ) - self.compute_sdk_client.resize_server.assert_called_once_with( + self.compute_client.resize_server.assert_called_once_with( self.server, self.flavor ) - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -7671,9 +8083,9 @@ class TestServerResizeConfirm(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.confirm_server_resize.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.ResizeConfirm(self.app, None) @@ -7689,10 +8101,10 @@ def test_resize_confirm(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7703,9 +8115,9 @@ class TestServerMigrateConfirm(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.confirm_server_resize.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.MigrateConfirm(self.app, None) @@ -7722,10 +8134,10 @@ def test_migrate_confirm(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7741,9 +8153,9 @@ class TestServerConfirmMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.confirm_server_resize.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.ConfirmMigration(self.app, None) @@ -7759,10 +8171,10 @@ def test_migration_confirm(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7772,9 +8184,9 @@ class TestServerResizeRevert(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.revert_server_resize.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.revert_server_resize.return_value = None # Get the command object to test self.cmd = server.ResizeRevert(self.app, None) @@ -7790,10 +8202,10 @@ def test_resize_revert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7804,9 +8216,9 @@ class TestServerMigrateRevert(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.revert_server_resize.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.revert_server_resize.return_value = None # Get the command object to test self.cmd = server.MigrateRevert(self.app, None) @@ -7823,10 +8235,10 @@ def test_migrate_revert(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7842,9 +8254,9 @@ class TestServerRevertMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.revert_server_resize.return_value = None + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + self.compute_client.revert_server_resize.return_value = None # Get the command object to test self.cmd = server.RevertMigration(self.app, None) @@ -7860,16 +8272,16 @@ def test_migration_revert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) -class TestServerRestore(TestServer): +class TestServerRestore(TestServerAction): def setUp(self): super().setUp() @@ -7883,7 +8295,7 @@ def test_server_restore_multi_servers(self): self.run_method_with_sdk_servers('restore_server', 3) -class TestServerResume(TestServer): +class TestServerResume(TestServerAction): def setUp(self): super().setUp() @@ -7901,8 +8313,8 @@ class TestServerSet(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.SetServer(self.app, None) @@ -7914,18 +8326,19 @@ def test_server_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_state(self): arglist = [ '--state', 'active', + '--auto-approve', self.server.id, ] verifylist = [ @@ -7936,14 +8349,62 @@ def test_server_set_with_state(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.reset_server_state.assert_called_once_with( + self.compute_client.reset_server_state.assert_called_once_with( + self.server, state='active' + ) + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() + self.assertIsNone(result) + + def test_server_set_with_state_prompt_y(self): + arglist = [ + '--state', + 'active', + self.server.id, + ] + verifylist = [ + ('state', 'active'), + ('server', self.server.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch('getpass.getpass', return_value='y'): + result = self.cmd.take_action(parsed_args) + + self.compute_client.reset_server_state.assert_called_once_with( self.server, state='active' ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() + self.assertIsNone(result) + + def test_server_set_with_state_prompt_n(self): + arglist = [ + '--state', + 'active', + self.server.id, + ] + verifylist = [ + ('state', 'active'), + ('server', self.server.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch('getpass.getpass', return_value='n'): + result = self.cmd.take_action(parsed_args) + + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_invalid_state(self): @@ -7978,14 +8439,14 @@ def test_server_set_with_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.update_server.assert_called_once_with( self.server, name='foo_name' ) - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_property(self): @@ -8004,14 +8465,14 @@ def test_server_set_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.set_server_metadata.assert_called_once_with( + self.compute_client.set_server_metadata.assert_called_once_with( self.server, key1='value1', key2='value2' ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_password(self): @@ -8028,14 +8489,14 @@ def test_server_set_with_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.change_server_password.assert_called_once_with( + self.compute_client.change_server_password.assert_called_once_with( self.server, 'foo' ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_no_password(self): @@ -8051,14 +8512,14 @@ def test_server_set_with_no_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.clear_server_password.assert_called_once_with( + self.compute_client.clear_server_password.assert_called_once_with( self.server ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) # TODO(stephenfin): Remove this in a future major version @@ -8078,14 +8539,14 @@ def test_server_set_with_root_password(self, mock_getpass): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.change_server_password.assert_called_once_with( + self.compute_client.change_server_password.assert_called_once_with( self.server, mock.sentinel.fake_pass ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_description(self): @@ -8104,14 +8565,14 @@ def test_server_set_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.update_server.assert_called_once_with( self.server, description='foo_description' ) - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_description_pre_v219(self): @@ -8150,17 +8611,17 @@ def test_server_set_with_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.add_tag_to_server.assert_has_calls( + self.compute_client.add_tag_to_server.assert_has_calls( [ mock.call(self.server, tag='tag1'), mock.call(self.server, tag='tag2'), ] ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() self.assertIsNone(result) def test_server_set_with_tag_pre_v226(self): @@ -8202,14 +8663,14 @@ def test_server_set_with_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.update_server.assert_called_once_with( self.server, hostname='foo-hostname' ) - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_hostname_pre_v290(self): @@ -8234,12 +8695,12 @@ class TestServerShelve(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs={'status': 'ACTIVE'}, ) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.shelve_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.shelve_server.return_value = None # Get the command object to test self.cmd = server.ShelveServer(self.app, None) @@ -8256,14 +8717,12 @@ def test_shelve(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.shelve_server.assert_called_with( - self.server.id - ) - self.compute_sdk_client.shelve_offload_server.assert_not_called() + self.compute_client.shelve_server.assert_called_with(self.server.id) + self.compute_client.shelve_offload_server.assert_not_called() def test_shelve_already_shelved(self): self.server.status = 'SHELVED' @@ -8279,12 +8738,12 @@ def test_shelve_already_shelved(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.shelve_server.assert_not_called() - self.compute_sdk_client.shelve_offload_server.assert_not_called() + self.compute_client.shelve_server.assert_not_called() + self.compute_client.shelve_offload_server.assert_not_called() @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_shelve_with_wait(self, mock_wait_for_status): @@ -8299,16 +8758,14 @@ def test_shelve_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.shelve_server.assert_called_with( - self.server.id - ) - self.compute_sdk_client.shelve_offload_server.assert_not_called() + self.compute_client.shelve_server.assert_called_with(self.server.id) + self.compute_client.shelve_offload_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=('shelved', 'shelved_offloaded'), @@ -8328,25 +8785,21 @@ def test_shelve_offload(self, mock_wait_for_status): self.assertIsNone(result) # one call to retrieve to retrieve the server state before shelving - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, ) # one call to retrieve the server state before offloading - self.compute_sdk_client.get_server.assert_called_once_with( - self.server.id - ) + self.compute_client.get_server.assert_called_once_with(self.server.id) # one call to shelve the server - self.compute_sdk_client.shelve_server.assert_called_with( - self.server.id - ) + self.compute_client.shelve_server.assert_called_with(self.server.id) # one call to shelve offload the server - self.compute_sdk_client.shelve_offload_server.assert_called_once_with( + self.compute_client.shelve_offload_server.assert_called_once_with( self.server.id, ) # one call to wait for the shelve offload to complete mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=('shelved', 'shelved_offloaded'), @@ -8361,7 +8814,7 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor self.topology = { 'nodes': [{'vcpu_set': [0, 1]}, {'vcpu_set': [2, 3]}], @@ -8373,14 +8826,14 @@ def setUp(self): 'tenant_id': 'tenant-id-xxx', 'addresses': {'public': ['10.20.30.40', '2001:db8::f']}, } - self.compute_sdk_client.get_server_diagnostics.return_value = { + self.compute_client.get_server_diagnostics.return_value = { 'test': 'test' } - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs=server_info, ) self.server.fetch_topology = mock.MagicMock(return_value=self.topology) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.ShowServer(self.app, None) @@ -8418,7 +8871,6 @@ def setUp(self): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -8468,7 +8920,6 @@ def setUp(self): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress 'tenant-id-xxx', # project_id format_columns.DictColumn({}), # properties @@ -8509,10 +8960,10 @@ def test_show(self): self.assertTupleEqual(self.columns, columns) self.assertTupleEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() def test_show_embedded_flavor(self): # Tests using --os-compute-api-version >= 2.47 where the flavor @@ -8541,10 +8992,10 @@ def test_show_embedded_flavor(self): # Since the flavor details are in a dict we can't be sure of the # ordering so just assert that one of the keys is in the output. self.assertIn('original_name', data[columns.index('flavor')]._value) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() def test_show_diagnostics(self): arglist = [ @@ -8562,13 +9013,13 @@ def test_show_diagnostics(self): self.assertEqual(('test',), columns) self.assertEqual(('test',), data) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.compute_sdk_client.get_server_diagnostics.assert_called_once_with( + self.compute_client.get_server_diagnostics.assert_called_once_with( self.server ) - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() def test_show_topology(self): self.set_compute_api_version('2.78') @@ -8591,13 +9042,11 @@ def test_show_topology(self): self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.server.fetch_topology.assert_called_once_with( - self.compute_sdk_client - ) - self.compute_sdk_client.get_server.assert_not_called() + self.server.fetch_topology.assert_called_once_with(self.compute_client) + self.compute_client.get_server.assert_not_called() def test_show_topology_pre_v278(self): self.set_compute_api_version('2.77') @@ -8616,11 +9065,11 @@ def test_show_topology_pre_v278(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) self.server.fetch_topology.assert_not_called() - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() @mock.patch('openstackclient.compute.v2.server.os.system') @@ -8645,10 +9094,10 @@ def setUp(self): ], }, } - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs=self.attrs, ) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server def test_server_ssh_no_opts(self, mock_exec): arglist = [ @@ -8671,7 +9120,7 @@ def test_server_ssh_no_opts(self, mock_exec): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) self.assertIsNone(result) @@ -8704,7 +9153,7 @@ def test_server_ssh_passthrough_opts(self, mock_exec): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) self.assertIsNone(result) @@ -8738,7 +9187,7 @@ def test_server_ssh_deprecated_opts(self, mock_exec): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) self.assertIsNone(result) @@ -8752,7 +9201,7 @@ def test_server_ssh_deprecated_opts(self, mock_exec): ) -class TestServerStart(TestServer): +class TestServerStart(TestServerAction): def setUp(self): super().setUp() @@ -8766,28 +9215,29 @@ def test_server_start_multi_servers(self): self.run_method_with_sdk_servers('start_server', 3) def test_server_start_with_all_projects(self): - servers = self.setup_sdk_servers_mock(count=1) + server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = server arglist = [ - servers[0].id, + server.id, '--all-projects', ] verifylist = [ - ('server', [servers[0].id]), + ('server', [server.id]), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( - servers[0].id, + self.compute_client.find_server.assert_called_once_with( + server.id, ignore_missing=False, details=False, all_projects=True, ) -class TestServerStop(TestServer): +class TestServerStop(TestServerAction): def setUp(self): super().setUp() @@ -8801,28 +9251,29 @@ def test_server_stop_multi_servers(self): self.run_method_with_sdk_servers('stop_server', 3) def test_server_start_with_all_projects(self): - servers = self.setup_sdk_servers_mock(count=1) + server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = server arglist = [ - servers[0].id, + server.id, '--all-projects', ] verifylist = [ - ('server', [servers[0].id]), + ('server', [server.id]), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( - servers[0].id, + self.compute_client.find_server.assert_called_once_with( + server.id, ignore_missing=False, details=False, all_projects=True, ) -class TestServerSuspend(TestServer): +class TestServerSuspend(TestServerAction): def setUp(self): super().setUp() @@ -8836,7 +9287,7 @@ def test_server_suspend_multi_servers(self): self.run_method_with_sdk_servers('suspend_server', 3) -class TestServerUnlock(TestServer): +class TestServerUnlock(TestServerAction): def setUp(self): super().setUp() @@ -8850,7 +9301,7 @@ def test_server_unlock_multi_servers(self): self.run_method_with_sdk_servers('unlock_server', 3) -class TestServerUnpause(TestServer): +class TestServerUnpause(TestServerAction): def setUp(self): super().setUp() @@ -8868,8 +9319,8 @@ class TestServerUnrescue(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.cmd = server.UnrescueServer(self.app, None) @@ -8884,10 +9335,10 @@ def test_rescue(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.unrescue_server.assert_called_once_with( + self.compute_client.unrescue_server.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -8897,8 +9348,8 @@ class TestServerUnset(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.UnsetServer(self.app, None) @@ -8914,12 +9365,10 @@ def test_server_unset_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.delete_server_metadata.assert_not_called() - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.remove_tag_from_server.assert_not_called() + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.delete_server_metadata.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.remove_tag_from_server.assert_not_called() self.assertIsNone(result) def test_server_unset_with_property(self): @@ -8938,15 +9387,13 @@ def test_server_unset_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.delete_server_metadata.assert_called_once_with( + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.delete_server_metadata.assert_called_once_with( self.server, ['key1', 'key2'], ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.remove_tag_from_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.remove_tag_from_server.assert_not_called() self.assertIsNone(result) def test_server_unset_with_description(self): @@ -8965,14 +9412,12 @@ def test_server_unset_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.update_server.assert_called_once_with( self.server, description='' ) - self.compute_sdk_client.delete_server_metadata.assert_not_called() - self.compute_sdk_client.remove_tag_from_server.assert_not_called() + self.compute_client.delete_server_metadata.assert_not_called() + self.compute_client.remove_tag_from_server.assert_not_called() self.assertIsNone(result) def test_server_unset_with_description_pre_v219(self): @@ -9015,17 +9460,15 @@ def test_server_unset_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.remove_tag_from_server.assert_has_calls( + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.remove_tag_from_server.assert_has_calls( [ mock.call(self.server, 'tag1'), mock.call(self.server, 'tag2'), ] ) - self.compute_sdk_client.delete_server_metadata.assert_not_called() - self.compute_sdk_client.update_server.assert_not_called() + self.compute_client.delete_server_metadata.assert_not_called() + self.compute_client.update_server.assert_not_called() def test_server_unset_with_tag_pre_v226(self): self.set_compute_api_version('2.25') @@ -9055,12 +9498,12 @@ class TestServerUnshelve(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs={'status': 'SHELVED'}, ) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.unshelve_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.unshelve_server.return_value = None # Get the command object to test self.cmd = server.UnshelveServer(self.app, None) @@ -9076,11 +9519,11 @@ def test_unshelve(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id ) @@ -9100,11 +9543,11 @@ def test_unshelve_with_az(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id, availability_zone='foo-az', ) @@ -9146,11 +9589,11 @@ def test_unshelve_with_host(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id, host='server1', ) @@ -9192,11 +9635,11 @@ def test_unshelve_with_no_az(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id, availability_zone=None, ) @@ -9268,15 +9711,13 @@ def test_unshelve_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_with( - self.server.id - ) + self.compute_client.unshelve_server.assert_called_with(self.server.id) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=('active', 'shutoff'), @@ -9368,7 +9809,7 @@ def test_prep_server_detail(self): self.image_client.get_image.return_value = _image _flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = _flavor + self.compute_client.find_flavor.return_value = _flavor server_info = { 'image': {'id': _image.id}, @@ -9379,8 +9820,8 @@ def test_prep_server_detail(self): 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } - _server = compute_fakes.create_one_sdk_server(server_info) - self.compute_sdk_client.get_server.return_value = _server + _server = compute_fakes.create_one_server(server_info) + self.compute_client.get_server.return_value = _server expected = { 'OS-DCF:diskConfig': None, @@ -9417,7 +9858,6 @@ def test_prep_server_detail(self): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), @@ -9431,14 +9871,14 @@ def test_prep_server_detail(self): } actual = server._prep_server_detail( - self.compute_sdk_client, + self.compute_client, self.image_client, _server, ) self.assertCountEqual(expected, actual) # this should be called since we need the flavor (< 2.47) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( _flavor.id, ignore_missing=False ) @@ -9447,7 +9887,7 @@ def test_prep_server_detail_v247(self): self.image_client.get_image.return_value = _image _flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = _flavor + self.compute_client.find_flavor.return_value = _flavor server_info = { 'image': {'id': _image.id}, @@ -9466,8 +9906,8 @@ def test_prep_server_detail_v247(self): 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } - _server = compute_fakes.create_one_sdk_server(server_info) - self.compute_sdk_client.get_server.return_value = _server + _server = compute_fakes.create_one_server(server_info) + self.compute_client.get_server.return_value = _server expected = { 'OS-DCF:diskConfig': None, @@ -9504,7 +9944,6 @@ def test_prep_server_detail_v247(self): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), @@ -9518,11 +9957,11 @@ def test_prep_server_detail_v247(self): } actual = server._prep_server_detail( - self.compute_sdk_client, + self.compute_client, self.image_client, _server, ) self.assertCountEqual(expected, actual) # this shouldn't be called since we have a full flavor (>= 2.47) - self.compute_sdk_client.find_flavor.assert_not_called() + self.compute_client.find_flavor.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 9d5269ddbe..cc8acfb34f 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -22,24 +22,8 @@ from openstackclient.tests.unit.image.v2 import fakes as image_fakes -class TestServerBackup(compute_fakes.TestComputev2): - def setup_servers_mock(self, count): - servers = compute_fakes.create_sdk_servers( - count=count, - ) - - # This is the return value for compute_client.find_server() - self.compute_sdk_client.find_server = compute_fakes.get_servers( - servers, - 0, - ) - return servers - - -class TestServerBackupCreate(TestServerBackup): - # Just return whatever Image is testing with these days +class TestServerBackupCreate(compute_fakes.TestComputev2): def image_columns(self, image): - # columnlist = tuple(sorted(image.keys())) columnlist = ( 'id', 'name', @@ -66,42 +50,27 @@ def image_data(self, image): def setUp(self): super().setUp() + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server + + self.image = image_fakes.create_one_image( + {'name': self.server.name, 'status': 'active'} + ) + self.image_client.find_image.return_value = self.image + # Get the command object to test self.cmd = server_backup.CreateServerBackup(self.app, None) - def setup_images_mock(self, count, servers=None): - if servers: - images = image_fakes.create_images( - attrs={ - 'name': servers[0].name, - 'status': 'active', - }, - count=count, - ) - else: - images = image_fakes.create_images( - attrs={ - 'status': 'active', - }, - count=count, - ) - - self.image_client.find_image = mock.Mock(side_effect=images) - return images - def test_server_backup_defaults(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ - servers[0].id, + self.server.id, ] verifylist = [ ('name', None), ('type', None), ('rotate', None), ('wait', False), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -110,20 +79,17 @@ def test_server_backup_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.backup_server.assert_called_with( - servers[0].id, - servers[0].name, + self.compute_client.backup_server.assert_called_with( + self.server.id, + self.server.name, '', 1, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) def test_server_backup_create_options(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--name', 'image', @@ -131,13 +97,13 @@ def test_server_backup_create_options(self): 'daily', '--rotate', '2', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'image'), ('type', 'daily'), ('rotate', 2), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -146,23 +112,19 @@ def test_server_backup_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.backup_server.assert_called_with( - servers[0].id, + self.compute_client.backup_server.assert_called_with( + self.server.id, 'image', 'daily', 2, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_backup_wait_fail(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - self.image_client.get_image = mock.Mock( - side_effect=images[0], - ) + self.image_client.get_image.return_value = self.image arglist = [ '--name', @@ -170,13 +132,13 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): '--type', 'daily', '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'image'), ('type', 'daily'), ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -186,25 +148,20 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): parsed_args, ) - self.compute_sdk_client.backup_server.assert_called_with( - servers[0].id, + self.compute_client.backup_server.assert_called_with( + self.server.id, 'image', 'daily', 1, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_backup_wait_ok(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - - self.image_client.get_image = mock.Mock( - side_effect=images[0], - ) + self.image_client.get_image.side_effect = (self.image,) arglist = [ '--name', @@ -212,13 +169,13 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): '--type', 'daily', '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'image'), ('type', 'daily'), ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -227,16 +184,16 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.backup_server.assert_called_with( - servers[0].id, + self.compute_client.backup_server.assert_called_with( + self.server.id, 'image', 'daily', 1, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index 728d9f8424..710a82ea78 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -22,7 +22,7 @@ class TestListServerEvent(compute_fakes.TestComputev2): - fake_server = compute_fakes.create_one_sdk_server() + fake_server = compute_fakes.create_one_server() fake_event = compute_fakes.create_one_server_action() columns = ( @@ -64,8 +64,8 @@ class TestListServerEvent(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.find_server.return_value = self.fake_server - self.compute_sdk_client.server_actions.return_value = [ + self.compute_client.find_server.return_value = self.fake_server + self.compute_client.server_actions.return_value = [ self.fake_event, ] @@ -83,11 +83,11 @@ def test_server_event_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id ) @@ -107,11 +107,11 @@ def test_server_event_list_long(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id ) @@ -134,11 +134,11 @@ def test_server_event_list_with_changes_since(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, changes_since='2016-03-04T06:27:59Z', ) @@ -214,11 +214,11 @@ def test_server_event_list_with_changes_before(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, changes_before='2016-03-04T06:27:59Z', ) @@ -290,7 +290,7 @@ def test_server_event_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, limit=1, paginated=False, @@ -337,7 +337,7 @@ def test_server_event_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, marker='test_event', ) @@ -366,7 +366,7 @@ def test_server_event_list_with_marker_pre_v258(self): class TestShowServerEvent(compute_fakes.TestComputev2): - fake_server = compute_fakes.create_one_sdk_server() + fake_server = compute_fakes.create_one_server() fake_event = compute_fakes.create_one_server_action() columns = ( 'action', @@ -392,10 +392,8 @@ class TestShowServerEvent(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.find_server.return_value = self.fake_server - self.compute_sdk_client.get_server_action.return_value = ( - self.fake_event - ) + self.compute_client.find_server.return_value = self.fake_server + self.compute_client.get_server_action.return_value = self.fake_event self.cmd = server_event.ShowServerEvent(self.app, None) @@ -412,11 +410,11 @@ def test_server_event_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.get_server_action.assert_called_with( + self.compute_client.get_server_action.assert_called_with( self.fake_event.request_id, self.fake_server.id, ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index 2bef027960..a91824a9cb 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -13,6 +13,8 @@ # under the License. # +from openstack.compute.v2 import server_group as _server_group +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -22,34 +24,38 @@ class TestServerGroup(compute_fakes.TestComputev2): - fake_server_group = compute_fakes.create_one_server_group() - - columns = ( - 'id', - 'members', - 'name', - 'policy', - 'project_id', - 'rules', - 'user_id', - ) - - data = ( - fake_server_group.id, - 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().setUp() + + self.fake_server_group = sdk_fakes.generate_fake_resource( + _server_group.ServerGroup + ) + + self.columns = ( + 'id', + 'members', + 'name', + 'policy', + 'project_id', + 'rules', + 'user_id', + ) + self.data = ( + self.fake_server_group.id, + format_columns.ListColumn(self.fake_server_group.member_ids), + self.fake_server_group.name, + self.fake_server_group.policy, + self.fake_server_group.project_id, + format_columns.DictColumn(self.fake_server_group.rules), + self.fake_server_group.user_id, + ) class TestServerGroupCreate(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.create_server_group.return_value = ( + self.compute_client.create_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.CreateServerGroup(self.app, None) @@ -68,7 +74,7 @@ def test_server_group_create(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_group.assert_called_once_with( + self.compute_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, ) @@ -90,7 +96,7 @@ def test_server_group_create_with_soft_policies(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_group.assert_called_once_with( + self.compute_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, ) @@ -135,7 +141,7 @@ def test_server_group_create_with_rules(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_group.assert_called_once_with( + self.compute_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, rules=parsed_args.rules, @@ -173,7 +179,7 @@ class TestServerGroupDelete(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.find_server_group.return_value = ( + self.compute_client.find_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.DeleteServerGroup(self.app, None) @@ -187,10 +193,10 @@ def test_server_group_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server_group.assert_called_once_with( + self.compute_client.find_server_group.assert_called_once_with( 'affinity_group', ignore_missing=False ) - self.compute_sdk_client.delete_server_group.assert_called_once_with( + self.compute_client.delete_server_group.assert_called_once_with( self.fake_server_group.id ) self.assertIsNone(result) @@ -202,21 +208,17 @@ def test_server_group_multiple_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'affinity_group', ignore_missing=False ) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'anti_affinity_group', ignore_missing=False ) - self.compute_sdk_client.delete_server_group.assert_called_with( + self.compute_client.delete_server_group.assert_called_with( self.fake_server_group.id ) - self.assertEqual( - 2, self.compute_sdk_client.find_server_group.call_count - ) - self.assertEqual( - 2, self.compute_sdk_client.delete_server_group.call_count - ) + self.assertEqual(2, self.compute_client.find_server_group.call_count) + self.assertEqual(2, self.compute_client.delete_server_group.call_count) self.assertIsNone(result) def test_server_group_delete_no_input(self): @@ -237,7 +239,7 @@ def test_server_group_multiple_delete_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_server_group.side_effect = [ + self.compute_client.find_server_group.side_effect = [ self.fake_server_group, exceptions.CommandError, ] @@ -247,101 +249,23 @@ def test_server_group_multiple_delete_with_exception(self): except exceptions.CommandError as e: self.assertEqual('1 of 2 server groups failed to delete.', str(e)) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'affinity_group', ignore_missing=False ) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'anti_affinity_group', ignore_missing=False ) - self.assertEqual( - 2, self.compute_sdk_client.find_server_group.call_count - ) - self.compute_sdk_client.delete_server_group.assert_called_once_with( + self.assertEqual(2, self.compute_client.find_server_group.call_count) + self.compute_client.delete_server_group.assert_called_once_with( self.fake_server_group.id ) class TestServerGroupList(TestServerGroup): - list_columns = ( - 'ID', - 'Name', - 'Policies', - ) - - list_columns_long = ( - 'ID', - 'Name', - 'Policies', - 'Members', - 'Project Id', - '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 - ), - ), - ) - - 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.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().setUp() - self.compute_sdk_client.server_groups.return_value = [ + self.compute_client.server_groups.return_value = [ self.fake_server_group ] self.cmd = server_group.ListServerGroup(self.app, None) @@ -357,10 +281,23 @@ def test_server_group_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with() + self.compute_client.server_groups.assert_called_once_with() - self.assertCountEqual(self.list_columns, columns) - self.assertCountEqual(self.list_data, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policies', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + format_columns.ListColumn(self.fake_server_group.policies), + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_server_group_list_with_all_projects_and_long(self): arglist = [ @@ -375,12 +312,31 @@ def test_server_group_list_with_all_projects_and_long(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with( + self.compute_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)) + expected_columns = ( + 'ID', + 'Name', + 'Policies', + 'Members', + 'Project Id', + 'User Id', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + format_columns.ListColumn(self.fake_server_group.policies), + format_columns.ListColumn(self.fake_server_group.member_ids), + self.fake_server_group.project_id, + self.fake_server_group.user_id, + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_server_group_list_with_limit(self): arglist = [ @@ -397,7 +353,7 @@ def test_server_group_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with(limit=1) + self.compute_client.server_groups.assert_called_once_with(limit=1) def test_server_group_list_with_offset(self): arglist = [ @@ -414,7 +370,7 @@ def test_server_group_list_with_offset(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with(offset=5) + self.compute_client.server_groups.assert_called_once_with(offset=5) def test_server_group_list_v264(self): self.set_compute_api_version('2.64') @@ -426,10 +382,23 @@ def test_server_group_list_v264(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with() + self.compute_client.server_groups.assert_called_once_with() - self.assertCountEqual(self.list_columns_v264, columns) - self.assertCountEqual(self.list_data_v264, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policy', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + self.fake_server_group.policy, + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_server_group_list_with_all_projects_and_long_v264(self): self.set_compute_api_version('2.64') @@ -444,19 +413,38 @@ def test_server_group_list_with_all_projects_and_long_v264(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with( + self.compute_client.server_groups.assert_called_once_with( all_projects=True ) - self.assertCountEqual(self.list_columns_v264_long, columns) - self.assertCountEqual(self.list_data_v264_long, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policy', + 'Members', + 'Project Id', + 'User Id', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + self.fake_server_group.policy, + format_columns.ListColumn(self.fake_server_group.member_ids), + self.fake_server_group.project_id, + self.fake_server_group.user_id, + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) class TestServerGroupShow(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.find_server_group.return_value = ( + self.compute_client.find_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.ShowServerGroup(self.app, None) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 8ba6d12620..726bc81ecf 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -21,23 +21,8 @@ from openstackclient.tests.unit.image.v2 import fakes as image_fakes -class TestServerImage(compute_fakes.TestComputev2): - def setup_servers_mock(self, count): - servers = compute_fakes.create_sdk_servers( - count=count, - ) - - # This is the return value for compute_client.find_server() - self.compute_sdk_client.find_server = compute_fakes.get_servers( - servers, - 0, - ) - return servers - - -class TestServerImageCreate(TestServerImage): +class TestServerImageCreate(compute_fakes.TestComputev2): def image_columns(self, image): - # columnlist = tuple(sorted(image.keys())) columnlist = ( 'id', 'name', @@ -64,41 +49,24 @@ def image_data(self, image): def setUp(self): super().setUp() - # Get the command object to test - self.cmd = server_image.CreateServerImage(self.app, None) + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server - def setup_images_mock(self, count, servers=None): - if servers: - images = image_fakes.create_images( - attrs={ - 'name': servers[0].name, - 'status': 'active', - }, - count=count, - ) - else: - images = image_fakes.create_images( - attrs={ - 'status': 'active', - }, - count=count, - ) - - self.image_client.find_image = mock.Mock(side_effect=images) - self.compute_sdk_client.create_server_image = mock.Mock( - return_value=images[0], + self.image = image_fakes.create_one_image( + {'name': self.server.name, 'status': 'active'} ) - return images + self.image_client.find_image.return_value = self.image + self.compute_client.create_server_image.return_value = self.image - def test_server_image_create_defaults(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) + # Get the command object to test + self.cmd = server_image.CreateServerImage(self.app, None) + def test_server_image_create_defaults(self): arglist = [ - servers[0].id, + self.server.id, ] verifylist = [ - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -107,29 +75,26 @@ def test_server_image_create_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_image.assert_called_with( - servers[0].id, - servers[0].name, + self.compute_client.create_server_image.assert_called_with( + self.server.id, + self.server.name, None, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) def test_server_image_create_options(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--name', 'img-nam', '--property', 'key=value', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'img-nam'), - ('server', servers[0].id), + ('server', self.server.id), ('properties', {'key': 'value'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -139,27 +104,24 @@ def test_server_image_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_image.assert_called_with( - servers[0].id, + self.compute_client.create_server_image.assert_called_with( + self.server.id, 'img-nam', {'key': 'value'}, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_create_image_wait_fail(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -169,28 +131,25 @@ def test_server_create_image_wait_fail(self, mock_wait_for_status): parsed_args, ) - self.compute_sdk_client.create_server_image.assert_called_with( - servers[0].id, - servers[0].name, + self.compute_client.create_server_image.assert_called_with( + self.server.id, + self.server.name, None, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_create_image_wait_ok(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -199,15 +158,15 @@ def test_server_create_image_wait_ok(self, mock_wait_for_status): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_image.assert_called_with( - servers[0].id, - servers[0].name, + self.compute_client.create_server_image.assert_called_with( + self.server.id, + self.server.name, None, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 09ceae6fe9..5016c25720 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -52,11 +52,11 @@ class TestListMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.migrations = compute_fakes.create_migrations(count=3) - self.compute_sdk_client.migrations.return_value = self.migrations + self.compute_client.migrations.return_value = self.migrations self.data = ( common_utils.get_item_properties(s, self.MIGRATION_FIELDS) @@ -76,7 +76,7 @@ def test_server_migration_list_no_options(self): # Set expected values kwargs = {} - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -108,10 +108,10 @@ def test_server_migration_list(self): 'migration_type': 'migration', } - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( 'server1', ignore_missing=False ) - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -169,7 +169,7 @@ def test_server_migration_list(self): 'status': 'migrating', } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -247,7 +247,7 @@ def test_server_migration_list(self): 'changes_since': '2019-08-09T08:03:25Z', } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -376,7 +376,7 @@ def test_server_migration_list_with_changes_before(self): 'changes_before': '2019-08-09T08:03:25Z', } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -495,7 +495,7 @@ def test_server_migration_list_with_project(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project" @@ -572,7 +572,7 @@ def test_server_migration_list_with_user(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.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") @@ -644,7 +644,7 @@ def test_server_migration_list_with_project_and_user(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project" @@ -694,14 +694,14 @@ class TestServerMigrationShow(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = self.server self.server_migration = compute_fakes.create_one_server_migration() - self.compute_sdk_client.get_server_migration.return_value = ( + self.compute_client.get_server_migration.return_value = ( self.server_migration ) - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -759,10 +759,10 @@ def _test_server_migration_show(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.get_server_migration.assert_called_with( + self.compute_client.get_server_migration.assert_called_with( self.server.id, '2', ignore_missing=False ) @@ -811,7 +811,7 @@ def test_server_migration_show_pre_v224(self): def test_server_migration_show_by_uuid(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -830,18 +830,18 @@ def test_server_migration_show_by_uuid(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.server_migrations.assert_called_with( + self.compute_client.server_migrations.assert_called_with( self.server.id ) - self.compute_sdk_client.get_server_migration.assert_not_called() + self.compute_client.get_server_migration.assert_not_called() def test_server_migration_show_by_uuid_no_matches(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter([]) + self.compute_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, @@ -897,10 +897,10 @@ class TestServerMigrationAbort(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() # Return value for utils.find_resource for server. - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server_migration.AbortMigration(self.app, None) @@ -917,10 +917,10 @@ def test_migration_abort(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.abort_server_migration.assert_called_with( + self.compute_client.abort_server_migration.assert_called_with( '2', self.server.id, ignore_missing=False ) self.assertIsNone(result) @@ -946,7 +946,7 @@ def test_server_migration_abort_by_uuid(self): self.set_compute_api_version('2.59') self.server_migration = compute_fakes.create_one_server_migration() - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -959,13 +959,13 @@ def test_server_migration_abort_by_uuid(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.server_migrations.assert_called_with( + self.compute_client.server_migrations.assert_called_with( self.server.id ) - self.compute_sdk_client.abort_server_migration.assert_called_with( + self.compute_client.abort_server_migration.assert_called_with( self.server_migration.id, self.server.id, ignore_missing=False ) self.assertIsNone(result) @@ -973,7 +973,7 @@ def test_server_migration_abort_by_uuid(self): def test_server_migration_abort_by_uuid_no_matches(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter([]) + self.compute_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, @@ -1012,10 +1012,10 @@ class TestServerMigrationForceComplete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() # Return value for utils.find_resource for server. - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server_migration.ForceCompleteMigration(self.app, None) @@ -1032,10 +1032,10 @@ def test_migration_force_complete(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.force_complete_server_migration.assert_called_with( + self.compute_client.force_complete_server_migration.assert_called_with( '2', self.server.id ) self.assertIsNone(result) @@ -1061,7 +1061,7 @@ def test_server_migration_force_complete_by_uuid(self): self.set_compute_api_version('2.59') self.server_migration = compute_fakes.create_one_server_migration() - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -1074,13 +1074,13 @@ def test_server_migration_force_complete_by_uuid(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.server_migrations.assert_called_with( + self.compute_client.server_migrations.assert_called_with( self.server.id ) - self.compute_sdk_client.force_complete_server_migration.assert_called_with( + self.compute_client.force_complete_server_migration.assert_called_with( self.server_migration.id, self.server.id ) self.assertIsNone(result) @@ -1088,7 +1088,7 @@ def test_server_migration_force_complete_by_uuid(self): def test_server_migration_force_complete_by_uuid_no_matches(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter([]) + self.compute_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index e59853228b..9f26c6d5e3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -10,22 +10,29 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.block_storage.v3 import volume as _volume +from openstack.compute.v2 import server as _server +from openstack.compute.v2 import volume_attachment as _volume_attachment +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.compute.v2 import server_volume from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes class TestServerVolumeList(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.volume_attachments = compute_fakes.create_volume_attachments() + self.server = sdk_fakes.generate_fake_resource(_server.Server) + self.volume_attachments = list( + sdk_fakes.generate_fake_resources( + _volume_attachment.VolumeAttachment, count=2 + ) + ) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.volume_attachments.return_value = ( + self.compute_client.find_server.return_value = self.server + self.compute_client.volume_attachments.return_value = ( self.volume_attachments ) @@ -61,7 +68,7 @@ def test_server_volume_list(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -107,7 +114,7 @@ def test_server_volume_list_with_tags(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -156,7 +163,7 @@ def test_server_volume_list_with_delete_on_attachment(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -208,7 +215,7 @@ def test_server_volume_list_with_attachment_ids(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -217,10 +224,10 @@ class TestServerVolumeUpdate(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.server = sdk_fakes.generate_fake_resource(_server.Server) + self.compute_client.find_server.return_value = self.server - self.volume = volume_fakes.create_one_sdk_volume() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume # Get the command object to test @@ -241,7 +248,7 @@ def test_server_volume_update(self): result = self.cmd.take_action(parsed_args) # This is a no-op - self.compute_sdk_client.update_volume_attachment.assert_not_called() + self.compute_client.update_volume_attachment.assert_not_called() self.assertIsNone(result) def test_server_volume_update_with_delete_on_termination(self): @@ -261,7 +268,7 @@ def test_server_volume_update_with_delete_on_termination(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_volume_attachment.assert_called_once_with( + self.compute_client.update_volume_attachment.assert_called_once_with( self.server, self.volume, delete_on_termination=True, @@ -285,7 +292,7 @@ def test_server_volume_update_with_preserve_on_termination(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_volume_attachment.assert_called_once_with( + self.compute_client.update_volume_attachment.assert_called_once_with( self.server, self.volume, delete_on_termination=False ) self.assertIsNone(result) @@ -310,7 +317,7 @@ def test_server_volume_update_with_delete_on_termination_pre_v285(self): self.cmd.take_action, parsed_args, ) - self.compute_sdk_client.update_volume_attachment.assert_not_called() + self.compute_client.update_volume_attachment.assert_not_called() def test_server_volume_update_with_preserve_on_termination_pre_v285(self): self.set_compute_api_version('2.84') @@ -332,4 +339,4 @@ def test_server_volume_update_with_preserve_on_termination_pre_v285(self): self.cmd.take_action, parsed_args, ) - self.compute_sdk_client.update_volume_attachment.assert_not_called() + self.compute_client.update_volume_attachment.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index 2997fec79c..a47ea7298f 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -14,6 +14,8 @@ from unittest import mock +from openstack.compute.v2 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.compute.v2 import service @@ -21,12 +23,14 @@ class TestServiceDelete(compute_fakes.TestComputev2): - services = compute_fakes.create_services(count=2) - def setUp(self): super().setUp() - self.compute_sdk_client.delete_service.return_value = None + self.services = list( + sdk_fakes.generate_fake_resources(_service.Service, count=2) + ) + + self.compute_client.delete_service.return_value = None # Get the command object to test self.cmd = service.DeleteService(self.app, None) @@ -42,7 +46,7 @@ def test_service_delete(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.delete_service.assert_called_with( + self.compute_client.delete_service.assert_called_with( self.services[0].binary, ignore_missing=False ) self.assertIsNone(result) @@ -61,7 +65,7 @@ def test_multi_services_delete(self): calls = [] for s in self.services: calls.append(mock.call(s.binary, ignore_missing=False)) - self.compute_sdk_client.delete_service.assert_has_calls(calls) + self.compute_client.delete_service.assert_has_calls(calls) self.assertIsNone(result) def test_multi_services_delete_with_exception(self): @@ -73,9 +77,7 @@ def test_multi_services_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [None, exceptions.CommandError] - self.compute_sdk_client.delete_service = mock.Mock( - side_effect=delete_mock_result - ) + self.compute_client.delete_service.side_effect = delete_mock_result try: self.cmd.take_action(parsed_args) @@ -85,45 +87,21 @@ def test_multi_services_delete_with_exception(self): '1 of 2 compute services failed to delete.', str(e) ) - self.compute_sdk_client.delete_service.assert_any_call( + self.compute_client.delete_service.assert_any_call( self.services[0].binary, ignore_missing=False ) - self.compute_sdk_client.delete_service.assert_any_call( + self.compute_client.delete_service.assert_any_call( 'unexist_service', ignore_missing=False ) class TestServiceList(compute_fakes.TestComputev2): - service = compute_fakes.create_one_service() - - columns = ( - 'ID', - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - ) - columns_long = columns + ('Disabled Reason',) - - data = [ - ( - service.id, - service.binary, - service.host, - service.availability_zone, - service.status, - service.state, - service.updated_at, - ) - ] - data_long = [data[0] + (service.disabled_reason,)] - def setUp(self): super().setUp() - self.compute_sdk_client.services.return_value = [self.service] + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.compute_client.services.return_value = [self.service] # Get the command object to test self.cmd = service.ListService(self.app, None) @@ -146,13 +124,34 @@ def test_service_list(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.services.assert_called_with( + self.compute_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) + expected_columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + ) + expected_data = [ + ( + self.service.id, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + ) + ] + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, list(data)) def test_service_list_with_long_option(self): arglist = [ @@ -174,13 +173,36 @@ def test_service_list_with_long_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.services.assert_called_with( + self.compute_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) - self.assertEqual(self.columns_long, columns) - self.assertEqual(self.data_long, list(data)) + expected_columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + 'Disabled Reason', + ) + expected_data = [ + ( + self.service.id, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, + ) + ] + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, list(data)) def test_service_list_with_long_option_2_11(self): self.set_compute_api_version('2.11') @@ -204,27 +226,49 @@ def test_service_list_with_long_option_2_11(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.services.assert_called_with( + self.compute_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) # In 2.11 there is also a forced_down column. - columns_long = self.columns_long + ('Forced Down',) - data_long = [self.data_long[0] + (self.service.is_forced_down,)] + expected_columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + 'Disabled Reason', + 'Forced Down', + ) + expected_data = [ + ( + self.service.id, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, + self.service.is_forced_down, + ) + ] - self.assertEqual(columns_long, columns) - self.assertEqual(data_long, list(data)) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, list(data)) class TestServiceSet(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.service = compute_fakes.create_one_service() + self.service = sdk_fakes.generate_fake_resource(_service.Service) - self.compute_sdk_client.enable_service.return_value = self.service - self.compute_sdk_client.disable_service.return_value = self.service + self.compute_client.enable_service.return_value = self.service + self.compute_client.disable_service.return_value = self.service self.cmd = service.SetService(self.app, None) @@ -240,8 +284,8 @@ def test_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_not_called() - self.compute_sdk_client.disable_service.assert_not_called() + self.compute_client.enable_service.assert_not_called() + self.compute_client.disable_service.assert_not_called() self.assertIsNone(result) def test_service_set_enable(self): @@ -259,7 +303,7 @@ def test_service_set_enable(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_called_with( + self.compute_client.enable_service.assert_called_with( None, self.service.host, self.service.binary ) self.assertIsNone(result) @@ -279,7 +323,7 @@ def test_service_set_disable(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_with( + self.compute_client.disable_service.assert_called_with( None, self.service.host, self.service.binary, None ) self.assertIsNone(result) @@ -303,7 +347,7 @@ def test_service_set_disable_with_reason(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_with( + self.compute_client.disable_service.assert_called_with( None, self.service.host, self.service.binary, reason ) self.assertIsNone(result) @@ -373,11 +417,11 @@ def test_service_set_state_up(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, False ) - self.assertNotCalled(self.compute_sdk_client.enable_service) - self.assertNotCalled(self.compute_sdk_client.disable_service) + self.assertNotCalled(self.compute_client.enable_service) + self.assertNotCalled(self.compute_client.disable_service) self.assertIsNone(result) def test_service_set_state_down(self): @@ -395,11 +439,11 @@ def test_service_set_state_down(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) - self.assertNotCalled(self.compute_sdk_client.enable_service) - self.assertNotCalled(self.compute_sdk_client.disable_service) + self.assertNotCalled(self.compute_client.enable_service) + self.assertNotCalled(self.compute_client.disable_service) self.assertIsNone(result) def test_service_set_enable_and_state_down(self): @@ -419,10 +463,10 @@ def test_service_set_enable_and_state_down(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_called_once_with( + self.compute_client.enable_service.assert_called_once_with( None, self.service.host, self.service.binary ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) self.assertIsNone(result) @@ -445,12 +489,12 @@ def test_service_set_enable_and_state_down_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object( - self.compute_sdk_client, 'enable_service', side_effect=Exception() + self.compute_client, 'enable_service', side_effect=Exception() ): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) @@ -473,14 +517,12 @@ def test_service_set_disable_down(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.compute_sdk_client.services.return_value = [ - mock.Mock(id=service_id) - ] + self.compute_client.services.return_value = [mock.Mock(id=service_id)] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_once_with( + self.compute_client.disable_service.assert_called_once_with( service_id, self.service.host, self.service.binary, None ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( service_id, self.service.host, self.service.binary, True ) self.assertIsNone(result) @@ -506,11 +548,9 @@ def test_service_set_disable_reason(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.compute_sdk_client.services.return_value = [ - mock.Mock(id=service_id) - ] + self.compute_client.services.return_value = [mock.Mock(id=service_id)] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_once_with( + self.compute_client.disable_service.assert_called_once_with( service_id, self.service.host, self.service.binary, reason ) self.assertIsNone(result) @@ -534,25 +574,23 @@ def test_service_set_enable_up(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.compute_sdk_client.services.return_value = [ - mock.Mock(id=service_id) - ] + self.compute_client.services.return_value = [mock.Mock(id=service_id)] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_called_once_with( + self.compute_client.enable_service.assert_called_once_with( service_id, self.service.host, self.service.binary ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( service_id, self.service.host, self.service.binary, False ) self.assertIsNone(result) def test_service_set_find_service_by_host_and_binary_no_results(self): # Tests that no compute services are found by host and binary. - self.compute_sdk_client.services.return_value = [] + self.compute_client.services.return_value = [] ex = self.assertRaises( exceptions.CommandError, self.cmd._find_service_by_host_and_binary, - self.compute_sdk_client, + self.compute_client, 'fake-host', 'nova-compute', ) @@ -564,14 +602,14 @@ def test_service_set_find_service_by_host_and_binary_no_results(self): def test_service_set_find_service_by_host_and_binary_many_results(self): # Tests that more than one compute service is found by host and binary. - self.compute_sdk_client.services.return_value = [ + self.compute_client.services.return_value = [ mock.Mock(), mock.Mock(), ] ex = self.assertRaises( exceptions.CommandError, self.cmd._find_service_by_host_and_binary, - self.compute_sdk_client, + self.compute_client, 'fake-host', 'nova-compute', ) diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index 2efd25b54e..6169678185 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -14,9 +14,12 @@ import datetime from unittest import mock +from openstack.compute.v2 import usage as _usage +from openstack.identity.v3 import project as _project +from openstack.test import fakes as sdk_fakes + from openstackclient.compute.v2 import usage as usage_cmds from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes class TestUsage(compute_fakes.TestComputev2): @@ -28,11 +31,11 @@ def setUp(self): class TestUsageList(TestUsage): - project = identity_fakes.FakeProject.create_one_project() + project = sdk_fakes.generate_fake_resource(_project.Project) # Return value of self.usage_mock.list(). - usages = compute_fakes.create_usages( - attrs={'project_id': project.name}, count=1 - ) + usages = [ + sdk_fakes.generate_fake_resource(_usage.Usage, project_id=project.name) + ] columns = ( "Project", @@ -55,7 +58,7 @@ class TestUsageList(TestUsage): def setUp(self): super().setUp() - self.compute_sdk_client.usages.return_value = self.usages + self.compute_client.usages.return_value = self.usages self.projects_mock.list.return_value = [self.project] # Get the command object to test @@ -94,7 +97,7 @@ def test_usage_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) self.projects_mock.list.assert_called_with() - self.compute_sdk_client.usages.assert_called_with( + self.compute_client.usages.assert_called_with( start=datetime.datetime(2016, 11, 11, 0, 0), end=datetime.datetime(2016, 12, 20, 0, 0), detailed=True, @@ -115,7 +118,7 @@ def test_usage_list_with_pagination(self): columns, data = self.cmd.take_action(parsed_args) self.projects_mock.list.assert_called_with() - self.compute_sdk_client.usages.assert_has_calls( + self.compute_client.usages.assert_has_calls( [mock.call(start=mock.ANY, end=mock.ANY, detailed=True)] ) self.assertCountEqual(self.columns, columns) @@ -123,9 +126,11 @@ def test_usage_list_with_pagination(self): class TestUsageShow(TestUsage): - project = identity_fakes.FakeProject.create_one_project() + project = sdk_fakes.generate_fake_resource(_project.Project) # Return value of self.usage_mock.list(). - usage = compute_fakes.create_one_usage(attrs={'project_id': project.name}) + usage = sdk_fakes.generate_fake_resource( + _usage.Usage, project_id=project.name + ) columns = ( 'Project', @@ -146,7 +151,7 @@ class TestUsageShow(TestUsage): def setUp(self): super().setUp() - self.compute_sdk_client.get_usage.return_value = self.usage + self.compute_client.get_usage.return_value = self.usage self.projects_mock.get.return_value = self.project # Get the command object to test @@ -189,7 +194,7 @@ def test_usage_show_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_usage.assert_called_with( + self.compute_client.get_usage.assert_called_with( project=self.project.id, start=datetime.datetime(2016, 11, 11, 0, 0), end=datetime.datetime(2016, 12, 20, 0, 0), diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py index cc42b17506..be9d7f218d 100644 --- a/openstackclient/tests/unit/fakes.py +++ b/openstackclient/tests/unit/fakes.py @@ -12,13 +12,43 @@ # License for the specific language governing permissions and limitations # under the License. +# TODO(stephenfin): Remove the contents of this module in favour of the osc_lib +# version once our min version is bumped to 4.3.0 + import json -import sys from unittest import mock from keystoneauth1 import fixture +from osc_lib.tests.fakes import ( + FakeApp, + FakeClientManager as BaseFakeClientManager, + FakeLog, + FakeOptions, + FakeResource as BaseFakeResource, + FakeStdout, +) import requests +__all__ = [ + 'AUTH_TOKEN', + 'AUTH_URL', + 'INTERFACE', + 'PASSWORD', + 'PROJECT_NAME', + 'REGION_NAME', + 'TEST_RESPONSE_DICT', + 'TEST_RESPONSE_DICT_V3', + 'TEST_VERSIONS', + 'USERNAME', + 'VERSION', + 'FakeApp', + 'FakeClientManager', + 'FakeLog', + 'FakeOptions', + 'FakeResource', + 'FakeResponse', + 'FakeStdout', +] AUTH_TOKEN = "foobar" AUTH_URL = "http://0.0.0.0" @@ -47,79 +77,15 @@ TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) -class FakeStdout: - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result = result + line - return result - - -class FakeLog: - def __init__(self): - self.messages = {} - - def debug(self, msg): - self.messages['debug'] = msg - - def info(self, msg): - self.messages['info'] = msg - - def warning(self, msg): - self.messages['warning'] = msg - - def error(self, msg): - self.messages['error'] = msg - - def critical(self, msg): - self.messages['critical'] = msg - - -class FakeApp: - def __init__(self, _stdout, _log): - self.stdout = _stdout - self.client_manager = None - self.api_version = {} - self.stdin = sys.stdin - self.stdout = _stdout or sys.stdout - self.stderr = sys.stderr - self.log = _log - - -class FakeOptions: - def __init__(self, **kwargs): - self.os_beta_command = False - - -class FakeClient: - def __init__(self, **kwargs): - self.endpoint = kwargs['endpoint'] - self.token = kwargs['token'] - - -class FakeClientManager: +class FakeClientManager(BaseFakeClientManager): _api_version = { 'image': '2', } def __init__(self): - self.compute = None - self.identity = None - self.image = None - self.object_store = None - self.volume = None - self.network = None - self.sdk_connection = mock.Mock() + super().__init__() - self.session = None - self.auth_ref = None - self.auth_plugin_name = None + self.sdk_connection = mock.Mock() self.network_endpoint_enabled = True self.compute_endpoint_enabled = True @@ -154,68 +120,11 @@ def is_network_endpoint_enabled(self): def is_compute_endpoint_enabled(self): return self.compute_endpoint_enabled - def is_volume_endpoint_enabled(self, client): + def is_volume_endpoint_enabled(self, client=None): return self.volume_endpoint_enabled -class FakeModule: - def __init__(self, name, version): - self.name = name - self.__version__ = version - # Workaround for openstacksdk case - self.version = mock.Mock() - self.version.__version__ = version - - -class FakeResource: - def __init__(self, manager=None, info=None, loaded=False, methods=None): - """Set attributes and methods for a resource. - - :param manager: - The resource manager - :param Dictionary info: - A dictionary with all attributes - :param bool loaded: - True if the resource is loaded in memory - :param Dictionary methods: - A dictionary with all methods - """ - info = info or {} - methods = methods or {} - - self.__name__ = type(self).__name__ - self.manager = manager - self._info = info - self._add_details(info) - self._add_methods(methods) - self._loaded = loaded - - def _add_details(self, info): - for k, v in info.items(): - setattr(self, k, v) - - def _add_methods(self, methods): - """Fake methods with MagicMock objects. - - For each <@key, @value> pairs in methods, add an callable MagicMock - object named @key as an attribute, and set the mock's return_value to - @value. When users access the attribute with (), @value will be - returned, which looks like a function call. - """ - for name, ret in methods.items(): - method = mock.Mock(return_value=ret) - setattr(self, name, method) - - def __repr__(self): - reprkeys = sorted( - k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager' - ) - info = ", ".join(f"{k}={getattr(self, k)}" for k in reprkeys) - return f"<{self.__class__.__name__} {info}>" - - def keys(self): - return self._info.keys() - +class FakeResource(BaseFakeResource): def to_dict(self): return self._info @@ -247,11 +156,3 @@ def __init__( self._content = json.dumps(data) if not isinstance(self._content, bytes): self._content = self._content.encode() - - -class FakeModel(dict): - def __getattr__(self, key): - try: - return self[key] - except KeyError: - raise AttributeError(key) diff --git a/openstackclient/tests/unit/identity/test_common.py b/openstackclient/tests/unit/identity/test_common.py new file mode 100644 index 0000000000..ae85262dc0 --- /dev/null +++ b/openstackclient/tests/unit/identity/test_common.py @@ -0,0 +1,100 @@ +# 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 openstack import exceptions as sdk_exc +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions + +from openstackclient.identity import common +from openstackclient.tests.unit import utils as test_utils + + +class TestFindSDKId(test_utils.TestCase): + def setUp(self): + super().setUp() + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client = mock.Mock() + self.identity_sdk_client.find_user = mock.Mock() + + def test_find_sdk_id_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_not_found_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + self.assertRaises( + exceptions.CommandError, + common._find_sdk_id, + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + def test_find_sdk_id_not_found_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + + self.assertEqual(self.user.id, result) diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py index e07b026265..c5089d48b5 100644 --- a/openstackclient/tests/unit/identity/v2_0/fakes.py +++ b/openstackclient/tests/unit/identity/v2_0/fakes.py @@ -228,7 +228,7 @@ def create_one_extension(attrs=None): extension_info = { 'name': 'name-' + uuid.uuid4().hex, 'namespace': ( - 'http://docs.openstack.org/identity/' 'api/ext/OS-KSCRUD/v1.0' + 'http://docs.openstack.org/identity/api/ext/OS-KSCRUD/v1.0' ), 'description': 'description-' + uuid.uuid4().hex, 'updated': '2013-07-07T12:00:0-00:00', diff --git a/openstackclient/tests/unit/identity/v2_0/test_catalog.py b/openstackclient/tests/unit/identity/v2_0/test_catalog.py index 63b3d475d4..ef8b1cf9a1 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/unit/identity/v2_0/test_catalog.py @@ -53,8 +53,7 @@ def test_catalog_list(self): identity_fakes.TOKEN, fake_service=self.service_catalog, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -99,8 +98,7 @@ def test_catalog_list_with_endpoint_url(self): identity_fakes.TOKEN, fake_service=service_catalog, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -136,8 +134,7 @@ def test_catalog_show(self): identity_fakes.UNSCOPED_TOKEN, fake_service=self.service_catalog, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [ 'compute', diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py index 9b203b22b3..bb6a643743 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_project.py +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -195,7 +195,7 @@ def test_project_create_property(self): self.fake_project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('name', self.fake_project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -464,7 +464,7 @@ def test_project_set_unexist_project(self): ('description', None), ('enable', False), ('disable', False), - ('property', None), + ('properties', None), ] self.projects_mock.get.side_effect = exceptions.NotFound(None) self.projects_mock.find.side_effect = exceptions.NotFound(None) @@ -588,7 +588,7 @@ def test_project_set_property(self): self.fake_project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('project', self.fake_project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -683,7 +683,7 @@ def test_project_unset_key(self): self.fake_proj.name, ] verifylist = [ - ('property', ['fee', 'fo']), + ('properties', ['fee', 'fo']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py index 830653b0f4..117d2f0011 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role.py @@ -58,8 +58,7 @@ def setUp(self): identity_fakes.TOKEN, fake_service=self.fake_service, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref class TestRoleAdd(TestRole): diff --git a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py index a06c270f0d..741b3ee193 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py @@ -169,7 +169,8 @@ def test_role_assignment_list_project_and_user(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_def_creds(self): - auth_ref = self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref = mock.Mock() + auth_ref = self.app.client_manager.auth_ref auth_ref.project_id.return_value = identity_fakes.project_id auth_ref.user_id.return_value = identity_fakes.user_id diff --git a/openstackclient/tests/unit/identity/v2_0/test_token.py b/openstackclient/tests/unit/identity/v2_0/test_token.py index 1e90104d94..56a5a2c458 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_token.py +++ b/openstackclient/tests/unit/identity/v2_0/test_token.py @@ -11,29 +11,17 @@ # 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 openstackclient.identity.v2_0 import token from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -class TestToken(identity_fakes.TestIdentityv2): - fake_user = identity_fakes.FakeUser.create_one_user() - fake_project = identity_fakes.FakeProject.create_one_project() - +class TestTokenIssue(identity_fakes.TestIdentityv2): def setUp(self): super().setUp() - # Get a shortcut to the Auth Ref Mock - self.ar_mock = mock.PropertyMock() - type(self.app.client_manager).auth_ref = self.ar_mock - - -class TestTokenIssue(TestToken): - def setUp(self): - super().setUp() + self.fake_user = identity_fakes.FakeUser.create_one_user() + self.fake_project = identity_fakes.FakeProject.create_one_project() self.cmd = token.IssueToken(self.app, None) @@ -41,8 +29,7 @@ def test_token_issue(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -67,8 +54,7 @@ def test_token_issue_with_unscoped_token(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.UNSCOPED_TOKEN, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -91,7 +77,7 @@ def test_token_issue_with_unscoped_token(self): self.assertEqual(datalist, data) -class TestTokenRevoke(TestToken): +class TestTokenRevoke(identity_fakes.TestIdentityv2): TOKEN = 'fob' def setUp(self): diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index ad5ceb2840..c42ed47315 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -693,6 +693,14 @@ class TestIdentityv3( ): ... +class FakeModel(dict): + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + # We don't use FakeClientMixin since we want a different fake legacy client class TestFederatedIdentity(utils.TestCommand): def setUp(self): @@ -1075,7 +1083,7 @@ def create_one_endpoint_filter(attrs=None): # Overwrite default attributes if there are some attributes set endpoint_filter_info.update(attrs) - endpoint_filter = fakes.FakeModel(copy.deepcopy(endpoint_filter_info)) + endpoint_filter = FakeModel(copy.deepcopy(endpoint_filter_info)) return endpoint_filter @@ -1133,7 +1141,7 @@ def create_one_endpointgroup_filter(attrs=None): # Overwrite default attributes if there are some attributes set endpointgroup_filter_info.update(attrs) - endpointgroup_filter = fakes.FakeModel( + endpointgroup_filter = FakeModel( copy.deepcopy(endpointgroup_filter_info) ) diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py index 5367c1987f..0fc68dd366 100644 --- a/openstackclient/tests/unit/identity/v3/test_access_rule.py +++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py @@ -13,100 +13,93 @@ # under the License. # -import copy +from unittest.mock import call -from keystoneclient import exceptions as identity_exc +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import access_rule as _access_rule +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import access_rule -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestAccessRule(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - identity_manager = self.identity_client - self.access_rules_mock = identity_manager.access_rules - self.access_rules_mock.reset_mock() - self.roles_mock = identity_manager.roles - self.roles_mock.reset_mock() +class TestAccessRuleDelete(identity_fakes.TestIdentityv3): + access_rule = sdk_fakes.generate_fake_resource(_access_rule.AccessRule) - -class TestAccessRuleDelete(TestAccessRule): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.access_rules_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ACCESS_RULE), - loaded=True, + self.identity_sdk_client.get_access_rule.return_value = ( + self.access_rule ) - self.access_rules_mock.delete.return_value = None + self.identity_sdk_client.delete_access_rule.return_value = None # Get the command object to test self.cmd = access_rule.DeleteAccessRule(self.app, None) def test_access_rule_delete(self): - arglist = [ - identity_fakes.access_rule_id, - ] - verifylist = [('access_rule', [identity_fakes.access_rule_id])] + arglist = [self.access_rule.id] + verifylist = [('access_rule', [self.access_rule.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + result = self.cmd.take_action(parsed_args) - self.access_rules_mock.delete.assert_called_with( - identity_fakes.access_rule_id, + self.identity_sdk_client.delete_access_rule.assert_called_with( + user_id, + self.access_rule.id, ) self.assertIsNone(result) def test_delete_multi_access_rules_with_exception(self): - # mock returns for common.get_resource_by_id - mock_get = self.access_rules_mock.get - mock_get.side_effect = [ - mock_get.return_value, - identity_exc.NotFound, + self.identity_sdk_client.get_access_rule.side_effect = [ + self.access_rule, + sdk_exceptions.NotFoundException, ] + arglist = [ - identity_fakes.access_rule_id, + self.access_rule.id, 'nonexistent_access_rule', ] verifylist = [ ('access_rule', arglist), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + try: self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 access rules failed to' ' delete.', str(e) - ) + self.assertEqual('1 of 2 access rules failed to delete.', str(e)) + + calls = [] + for a in arglist: + calls.append(call(user_id, a)) - mock_get.assert_any_call(identity_fakes.access_rule_id) - mock_get.assert_any_call('nonexistent_access_rule') + self.identity_sdk_client.get_access_rule.assert_has_calls(calls) - self.assertEqual(2, mock_get.call_count) - self.access_rules_mock.delete.assert_called_once_with( - identity_fakes.access_rule_id + self.assertEqual( + 2, self.identity_sdk_client.get_access_rule.call_count + ) + self.identity_sdk_client.delete_access_rule.assert_called_once_with( + user_id, self.access_rule.id ) -class TestAccessRuleList(TestAccessRule): +class TestAccessRuleList(identity_fakes.TestIdentityv3): + access_rule = sdk_fakes.generate_fake_resource(_access_rule.AccessRule) + def setUp(self): super().setUp() - self.access_rules_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ACCESS_RULE), - loaded=True, - ), - ] + self.identity_sdk_client.access_rules.return_value = [self.access_rule] # Get the command object to test self.cmd = access_rule.ListAccessRule(self.app, None) @@ -116,31 +109,34 @@ def test_access_rule_list(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + columns, data = self.cmd.take_action(parsed_args) - self.access_rules_mock.list.assert_called_with(user=None) + self.identity_sdk_client.access_rules.assert_called_with(user=user_id) collist = ('ID', 'Service', 'Method', 'Path') self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.access_rule_id, - identity_fakes.access_rule_service, - identity_fakes.access_rule_method, - identity_fakes.access_rule_path, + self.access_rule.id, + self.access_rule.service, + self.access_rule.method, + self.access_rule.path, ), ) self.assertEqual(datalist, tuple(data)) -class TestAccessRuleShow(TestAccessRule): +class TestAccessRuleShow(identity_fakes.TestIdentityv3): + access_rule = sdk_fakes.generate_fake_resource(_access_rule.AccessRule) + def setUp(self): super().setUp() - self.access_rules_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ACCESS_RULE), - loaded=True, + self.identity_sdk_client.get_access_rule.return_value = ( + self.access_rule ) # Get the command object to test @@ -148,25 +144,28 @@ def setUp(self): def test_access_rule_show(self): arglist = [ - identity_fakes.access_rule_id, + self.access_rule.id, ] verifylist = [ - ('access_rule', identity_fakes.access_rule_id), + ('access_rule', self.access_rule.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + columns, data = self.cmd.take_action(parsed_args) - self.access_rules_mock.get.assert_called_with( - identity_fakes.access_rule_id + self.identity_sdk_client.get_access_rule.assert_called_with( + user_id, self.access_rule.id ) - collist = ('id', 'method', 'path', 'service') + collist = ('ID', 'Method', 'Path', 'Service') self.assertEqual(collist, columns) datalist = ( - identity_fakes.access_rule_id, - identity_fakes.access_rule_method, - identity_fakes.access_rule_path, - identity_fakes.access_rule_service, + self.access_rule.id, + self.access_rule.method, + self.access_rule.path, + self.access_rule.service, ) self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index 277204dd45..3a3a80e4a5 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -24,24 +24,13 @@ application_credential as _application_credential, ) from openstack.identity.v3 import role as _role +from openstack.identity.v3 import user as _user from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import application_credential from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3): - columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - 'Secret', - ) - def setUp(self): super().setUp() @@ -51,12 +40,25 @@ def setUp(self): roles=[], ) - self.datalist = ( + self.columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + 'Secret', + ) + self.data = ( self.application_credential.id, self.application_credential.name, self.application_credential.description, self.application_credential.project_id, - self.application_credential.roles, + application_credential.RolesColumn( + self.application_credential.roles + ), self.application_credential.unrestricted, self.application_credential.access_rules, self.application_credential.expires_at, @@ -100,7 +102,7 @@ def test_application_credential_create_basic(self): ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_application_credential_create_with_options(self): name = self.application_credential.name @@ -118,7 +120,7 @@ def test_application_credential_create_with_options(self): verifylist = [ ('name', self.application_credential.name), ('secret', 'moresecuresecret'), - ('role', [self.roles.id]), + ('roles', [self.roles.id]), ('expiration', '2024-01-01T00:00:00'), ('description', 'credential for testing'), ] @@ -146,7 +148,7 @@ def test_application_credential_create_with_options(self): ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_application_credential_create_with_access_rules_string(self): name = self.application_credential.name @@ -190,7 +192,7 @@ def test_application_credential_create_with_access_rules_string(self): ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) @mock.patch('openstackclient.identity.v3.application_credential.json.load') @mock.patch('openstackclient.identity.v3.application_credential.open') @@ -230,7 +232,7 @@ def test_application_credential_create_with_access_rules_file( ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3): @@ -295,12 +297,12 @@ def test_delete_multi_app_creds_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 application credentials failed to' ' delete.', str(e) + '1 of 2 application credentials failed to delete.', str(e) ) calls = [] for a in arglist: - calls.append(call(user_id, a)) + calls.append(call(user_id, a, ignore_missing=False)) self.identity_sdk_client.find_application_credential.assert_has_calls( calls @@ -325,6 +327,33 @@ def setUp(self): self.identity_sdk_client.application_credentials.return_value = [ self.application_credential ] + self.user = sdk_fakes.generate_fake_resource(resource_type=_user.User) + self.identity_sdk_client.find_user.return_value = self.user + + self.columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + self.data = ( + ( + self.application_credential.id, + self.application_credential.name, + self.application_credential.description, + self.application_credential.project_id, + application_credential.RolesColumn( + self.application_credential.roles + ), + self.application_credential.unrestricted, + self.application_credential.access_rules, + self.application_credential.expires_at, + ), + ) # Get the command object to test self.cmd = application_credential.ListApplicationCredential( @@ -339,39 +368,35 @@ def test_application_credential_list(self): conn = self.app.client_manager.sdk_connection user_id = conn.config.get_auth().get_user_id(conn.identity) - # 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.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + self.identity_sdk_client.find_user.assert_not_called() self.identity_sdk_client.application_credentials.assert_called_with( user=user_id ) - collist = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', + def test_application_credential_list_user(self): + arglist = ['--user', self.user.name] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + conn = self.app.client_manager.sdk_connection + conn.config.get_auth().get_user_id(conn.identity) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + self.identity_sdk_client.find_user.assert_called_once_with( + name_or_id=self.user.name, ignore_missing=False ) - self.assertEqual(collist, columns) - datalist = ( - ( - self.application_credential.id, - self.application_credential.name, - self.application_credential.description, - self.application_credential.project_id, - self.application_credential.roles, - self.application_credential.unrestricted, - self.application_credential.access_rules, - self.application_credential.expires_at, - ), + self.identity_sdk_client.application_credentials.assert_called_with( + user=self.user.id ) - self.assertEqual(datalist, tuple(data)) class TestApplicationCredentialShow(identity_fakes.TestIdentityv3): @@ -386,6 +411,29 @@ def setUp(self): self.application_credential ) + self.columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + self.data = ( + self.application_credential.id, + self.application_credential.name, + self.application_credential.description, + self.application_credential.project_id, + application_credential.RolesColumn( + self.application_credential.roles + ), + self.application_credential.unrestricted, + self.application_credential.access_rules, + self.application_credential.expires_at, + ) + # Get the command object to test self.cmd = application_credential.ShowApplicationCredential( self.app, None @@ -409,28 +457,8 @@ def test_application_credential_show(self): columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_application_credential.assert_called_with( - user_id, self.application_credential.id + user_id, self.application_credential.id, ignore_missing=False ) - collist = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - ) - self.assertEqual(collist, columns) - datalist = ( - self.application_credential.id, - self.application_credential.name, - self.application_credential.description, - self.application_credential.project_id, - self.application_credential.roles, - self.application_credential.unrestricted, - self.application_credential.access_rules, - self.application_credential.expires_at, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/identity/v3/test_catalog.py b/openstackclient/tests/unit/identity/v3/test_catalog.py index ed2db3b4d2..df292b1689 100644 --- a/openstackclient/tests/unit/identity/v3/test_catalog.py +++ b/openstackclient/tests/unit/identity/v3/test_catalog.py @@ -9,7 +9,6 @@ # 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 @@ -72,8 +71,7 @@ def test_catalog_list(self): identity_fakes.TOKEN_WITH_PROJECT_ID, fake_service=self.fake_service, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -110,8 +108,7 @@ def test_catalog_show(self): identity_fakes.TOKEN_WITH_PROJECT_ID, fake_service=self.fake_service, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [ 'compute', diff --git a/openstackclient/tests/unit/identity/v3/test_credential.py b/openstackclient/tests/unit/identity/v3/test_credential.py index 794f40b052..f21fd71a0f 100644 --- a/openstackclient/tests/unit/identity/v3/test_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_credential.py @@ -10,9 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import credential as _credential +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import credential @@ -20,26 +24,9 @@ from openstackclient.tests.unit import utils -class TestCredential(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the CredentialManager Mock - self.credentials_mock = self.identity_client.credentials - self.credentials_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - - -class TestCredentialCreate(TestCredential): - user = identity_fakes.FakeUser.create_one_user() - project = identity_fakes.FakeProject.create_one_project() +class TestCredentialCreate(identity_fakes.TestIdentityv3): + user = sdk_fakes.generate_fake_resource(_user.User) + project = sdk_fakes.generate_fake_resource(_project.Project) columns = ( 'blob', 'id', @@ -51,12 +38,17 @@ class TestCredentialCreate(TestCredential): def setUp(self): super().setUp() - self.credential = identity_fakes.FakeCredential.create_one_credential( - attrs={'user_id': self.user.id, 'project_id': self.project.id} + self.credential = sdk_fakes.generate_fake_resource( + resource_type=_credential.Credential, + user_id=self.user.id, + project_id=self.project.id, + type='cert', ) - self.credentials_mock.create.return_value = self.credential - self.users_mock.get.return_value = self.user - self.projects_mock.get.return_value = self.project + self.identity_sdk_client.create_credential.return_value = ( + self.credential + ) + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_project.return_value = self.project self.data = ( self.credential.blob, self.credential.id, @@ -81,12 +73,14 @@ def test_credential_create_no_options(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'user': self.credential.user_id, + 'user_id': self.credential.user_id, 'type': self.credential.type, 'blob': self.credential.blob, - 'project': None, + 'project_id': None, } - self.credentials_mock.create.assert_called_once_with(**kwargs) + self.identity_sdk_client.create_credential.assert_called_once_with( + **kwargs + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -111,47 +105,53 @@ def test_credential_create_with_options(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'user': self.credential.user_id, + 'user_id': self.credential.user_id, 'type': self.credential.type, 'blob': self.credential.blob, - 'project': self.credential.project_id, + 'project_id': self.credential.project_id, } - self.credentials_mock.create.assert_called_once_with(**kwargs) + self.identity_sdk_client.create_credential.assert_called_once_with( + **kwargs + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) -class TestCredentialDelete(TestCredential): - credentials = identity_fakes.FakeCredential.create_credentials(count=2) - +class TestCredentialDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.credentials_mock.delete.return_value = None + self.identity_sdk_client.delete_credential.return_value = None # Get the command object to test self.cmd = credential.DeleteCredential(self.app, None) def test_credential_delete(self): + credential = sdk_fakes.generate_fake_resource( + _credential.Credential, + ) arglist = [ - self.credentials[0].id, + credential.id, ] verifylist = [ - ('credential', [self.credentials[0].id]), + ('credential', [credential.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.credentials_mock.delete.assert_called_with( - self.credentials[0].id, + self.identity_sdk_client.delete_credential.assert_called_with( + credential.id, ) self.assertIsNone(result) def test_credential_multi_delete(self): + credentials = sdk_fakes.generate_fake_resources( + _credential.Credential, count=2 + ) arglist = [] - for c in self.credentials: + for c in credentials: arglist.append(c.id) verifylist = [ ('credential', arglist), @@ -161,25 +161,26 @@ def test_credential_multi_delete(self): result = self.cmd.take_action(parsed_args) calls = [] - for c in self.credentials: + for c in credentials: calls.append(call(c.id)) - self.credentials_mock.delete.assert_has_calls(calls) + self.identity_sdk_client.delete_credential.assert_has_calls(calls) self.assertIsNone(result) def test_credential_multi_delete_with_exception(self): + credential = sdk_fakes.generate_fake_resource( + _credential.Credential, + ) arglist = [ - self.credentials[0].id, + credential.id, 'unexist_credential', ] - verifylist = [ - ('credential', [self.credentials[0].id, 'unexist_credential']) - ] + verifylist = [('credential', [credential.id, 'unexist_credential'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - delete_mock_result = [None, exceptions.CommandError] - self.credentials_mock.delete = mock.Mock( - side_effect=delete_mock_result - ) + self.identity_sdk_client.delete_credential.side_effect = [ + None, + sdk_exceptions.NotFoundException, + ] try: self.cmd.take_action(parsed_args) @@ -187,12 +188,16 @@ def test_credential_multi_delete_with_exception(self): except exceptions.CommandError as e: self.assertEqual('1 of 2 credential failed to delete.', str(e)) - self.credentials_mock.delete.assert_any_call(self.credentials[0].id) - self.credentials_mock.delete.assert_any_call('unexist_credential') + self.identity_sdk_client.delete_credential.assert_any_call( + credential.id + ) + self.identity_sdk_client.delete_credential.assert_any_call( + 'unexist_credential' + ) -class TestCredentialList(TestCredential): - credential = identity_fakes.FakeCredential.create_one_credential() +class TestCredentialList(identity_fakes.TestIdentityv3): + credential = sdk_fakes.generate_fake_resource(_credential.Credential) columns = ('ID', 'Type', 'User ID', 'Data', 'Project ID') data = ( @@ -208,10 +213,10 @@ class TestCredentialList(TestCredential): def setUp(self): super().setUp() - self.user = identity_fakes.FakeUser.create_one_user() - self.users_mock.get.return_value = self.user + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client.find_user.return_value = self.user - self.credentials_mock.list.return_value = [self.credential] + self.identity_sdk_client.credentials.return_value = [self.credential] # Get the command object to test self.cmd = credential.ListCredential(self.app, None) @@ -223,7 +228,7 @@ def test_credential_list_no_options(self): columns, data = self.cmd.take_action(parsed_args) - self.credentials_mock.list.assert_called_with() + self.identity_sdk_client.credentials.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -246,15 +251,17 @@ def test_credential_list_with_options(self): 'user_id': self.user.id, 'type': self.credential.type, } - self.users_mock.get.assert_called_with(self.credential.user_id) - self.credentials_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.find_user.assert_called_with( + self.credential.user_id, domain_id=None, ignore_missing=False + ) + self.identity_sdk_client.credentials.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) -class TestCredentialSet(TestCredential): - credential = identity_fakes.FakeCredential.create_one_credential() +class TestCredentialSet(identity_fakes.TestIdentityv3): + credential = sdk_fakes.generate_fake_resource(_credential.Credential) def setUp(self): super().setUp() @@ -343,7 +350,7 @@ def test_credential_set_valid_with_project(self): self.assertIsNone(result) -class TestCredentialShow(TestCredential): +class TestCredentialShow(identity_fakes.TestIdentityv3): columns = ( 'blob', 'id', @@ -355,8 +362,10 @@ class TestCredentialShow(TestCredential): def setUp(self): super().setUp() - self.credential = identity_fakes.FakeCredential.create_one_credential() - self.credentials_mock.get.return_value = self.credential + self.credential = sdk_fakes.generate_fake_resource( + _credential.Credential + ) + self.identity_sdk_client.get_credential.return_value = self.credential self.data = ( self.credential.blob, self.credential.id, @@ -378,6 +387,8 @@ def test_credential_show(self): columns, data = self.cmd.take_action(parsed_args) - self.credentials_mock.get.assert_called_once_with(self.credential.id) + self.identity_sdk_client.get_credential.assert_called_once_with( + self.credential.id + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index fe3f655e4c..cc0593d1fe 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -10,33 +10,32 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.identity.v3 import domain as _domain +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import domain from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestDomain(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - -class TestDomainCreate(TestDomain): - columns = ('description', 'enabled', 'id', 'name', 'tags') +class TestDomainCreate(identity_fakes.TestIdentityv3): + columns = ( + 'id', + 'name', + 'enabled', + 'description', + 'options', + ) def setUp(self): super().setUp() - self.domain = identity_fakes.FakeDomain.create_one_domain() - self.domains_mock.create.return_value = self.domain + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.identity_sdk_client.create_domain.return_value = self.domain self.datalist = ( - self.domain.description, - True, self.domain.id, self.domain.name, - self.domain.tags, + self.domain.is_enabled, + self.domain.description, + self.domain.options, ) # Get the command object to test @@ -61,9 +60,9 @@ def test_domain_create_no_options(self): 'name': self.domain.name, 'description': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -90,9 +89,9 @@ def test_domain_create_description(self): 'name': self.domain.name, 'description': 'new desc', 'options': {}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -103,7 +102,7 @@ def test_domain_create_enable(self): self.domain.name, ] verifylist = [ - ('enable', True), + ('is_enabled', True), ('name', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -118,9 +117,9 @@ def test_domain_create_enable(self): 'name': self.domain.name, 'description': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -131,7 +130,7 @@ def test_domain_create_disable(self): self.domain.name, ] verifylist = [ - ('disable', True), + ('is_enabled', False), ('name', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -146,9 +145,9 @@ def test_domain_create_disable(self): 'name': self.domain.name, 'description': None, 'options': {}, - 'enabled': False, + 'is_enabled': False, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -174,9 +173,9 @@ def test_domain_create_with_immutable(self): 'name': self.domain.name, 'description': None, 'options': {'immutable': True}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -187,7 +186,7 @@ def test_domain_create_with_no_immutable(self): self.domain.name, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('name', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -202,23 +201,23 @@ def test_domain_create_with_no_immutable(self): 'name': self.domain.name, 'description': None, 'options': {'immutable': False}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) -class TestDomainDelete(TestDomain): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestDomainDelete(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() # This is the return value for utils.find_resource() - self.domains_mock.get.return_value = self.domain - self.domains_mock.delete.return_value = None + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.delete_domain.return_value = None # Get the command object to test self.cmd = domain.DeleteDomain(self.app, None) @@ -234,19 +233,35 @@ def test_domain_delete(self): result = self.cmd.take_action(parsed_args) - self.domains_mock.delete.assert_called_with( + self.identity_sdk_client.delete_domain.assert_called_with( self.domain.id, ) self.assertIsNone(result) -class TestDomainList(TestDomain): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestDomainList(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource( + resource_type=_domain.Domain, is_enabled=True + ) + columns = ( + 'ID', + 'Name', + 'Enabled', + 'Description', + ) def setUp(self): super().setUp() - self.domains_mock.list.return_value = [self.domain] + self.identity_sdk_client.domains.return_value = [self.domain] + self.datalist = ( + ( + self.domain.id, + self.domain.name, + self.domain.is_enabled, + self.domain.description, + ), + ) # Get the command object to test self.cmd = domain.ListDomain(self.app, None) @@ -260,19 +275,10 @@ def test_domain_list_no_options(self): # 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.domains_mock.list.assert_called_with() + self.identity_sdk_client.domains.assert_called_with() - collist = ('ID', 'Name', 'Enabled', 'Description') - self.assertEqual(collist, columns) - datalist = ( - ( - self.domain.id, - self.domain.name, - True, - self.domain.description, - ), - ) - self.assertEqual(datalist, tuple(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) def test_domain_list_with_option_name(self): arglist = ['--name', self.domain.name] @@ -285,23 +291,14 @@ def test_domain_list_with_option_name(self): columns, data = self.cmd.take_action(parsed_args) kwargs = {'name': self.domain.name} - self.domains_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.domains.assert_called_with(**kwargs) - collist = ('ID', 'Name', 'Enabled', 'Description') - self.assertEqual(collist, columns) - datalist = ( - ( - self.domain.id, - self.domain.name, - True, - self.domain.description, - ), - ) - self.assertEqual(datalist, tuple(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) def test_domain_list_with_option_enabled(self): arglist = ['--enabled'] - verifylist = [('enabled', True)] + verifylist = [('is_enabled', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() @@ -309,31 +306,22 @@ def test_domain_list_with_option_enabled(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - kwargs = {'enabled': True} - self.domains_mock.list.assert_called_with(**kwargs) + kwargs = {'is_enabled': True} + self.identity_sdk_client.domains.assert_called_with(**kwargs) - collist = ('ID', 'Name', 'Enabled', 'Description') - self.assertEqual(collist, columns) - datalist = ( - ( - self.domain.id, - self.domain.name, - True, - self.domain.description, - ), - ) - self.assertEqual(datalist, tuple(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) -class TestDomainSet(TestDomain): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestDomainSet(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_domain.return_value = self.domain - self.domains_mock.update.return_value = self.domain + self.identity_sdk_client.update_domain.return_value = self.domain # Get the command object to test self.cmd = domain.SetDomain(self.app, None) @@ -350,7 +338,9 @@ def test_domain_set_no_options(self): result = self.cmd.take_action(parsed_args) kwargs = {} - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_name(self): @@ -371,7 +361,9 @@ def test_domain_set_name(self): kwargs = { 'name': 'qwerty', } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_description(self): @@ -392,7 +384,9 @@ def test_domain_set_description(self): kwargs = { 'description': 'new desc', } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_enable(self): @@ -401,7 +395,7 @@ def test_domain_set_enable(self): self.domain.id, ] verifylist = [ - ('enable', True), + ('is_enabled', True), ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -410,9 +404,11 @@ def test_domain_set_enable(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_disable(self): @@ -421,7 +417,7 @@ def test_domain_set_disable(self): self.domain.id, ] verifylist = [ - ('disable', True), + ('is_enabled', False), ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -430,9 +426,11 @@ def test_domain_set_disable(self): # Set expected values kwargs = { - 'enabled': False, + 'is_enabled': False, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_immutable_option(self): @@ -452,7 +450,9 @@ def test_domain_set_immutable_option(self): kwargs = { 'options': {'immutable': True}, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_no_immutable_option(self): @@ -461,7 +461,7 @@ def test_domain_set_no_immutable_option(self): self.domain.id, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -472,16 +472,34 @@ def test_domain_set_no_immutable_option(self): kwargs = { 'options': {'immutable': False}, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) -class TestDomainShow(TestDomain): +class TestDomainShow(identity_fakes.TestIdentityv3): + columns = ( + 'id', + 'name', + 'enabled', + 'description', + 'options', + ) + def setUp(self): super().setUp() - self.domain = identity_fakes.FakeDomain.create_one_domain() - self.domains_mock.get.return_value = self.domain + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.identity_sdk_client.find_domain.return_value = self.domain + self.datalist = ( + self.domain.id, + self.domain.name, + self.domain.is_enabled, + self.domain.description, + self.domain.options, + ) + # Get the command object to test self.cmd = domain.ShowDomain(self.app, None) @@ -501,17 +519,9 @@ def test_domain_show(self): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.domains_mock.get.assert_called_with( - self.domain.id, + self.identity_sdk_client.find_domain.assert_called_with( + self.domain.id, ignore_missing=False ) - collist = ('description', 'enabled', 'id', 'name', 'tags') - self.assertEqual(collist, columns) - datalist = ( - self.domain.description, - True, - self.domain.id, - self.domain.name, - self.domain.tags, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py index 1dafe48e48..ba69317f5d 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -10,6 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import endpoint as _endpoint +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import region as _region +from openstack.identity.v3 import service as _service +from openstack.test import fakes as sdk_fakes + from openstackclient.identity.v3 import endpoint from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -37,30 +44,34 @@ def setUp(self): self.projects_mock.reset_mock() -class TestEndpointCreate(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - +class TestEndpointCreate(identity_fakes.TestIdentityv3): columns = ( 'enabled', 'id', 'interface', 'region', + 'region_id', 'service_id', + 'url', 'service_name', 'service_type', - 'url', ) def setUp(self): super().setUp() - self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': self.service.id} + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + region_id=self.region.id, ) - self.endpoints_mock.create.return_value = self.endpoint - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.create_endpoint.return_value = self.endpoint + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = endpoint.CreateEndpoint(self.app, None) @@ -79,6 +90,9 @@ def test_endpoint_create_no_options(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # Fake endpoints come with a region ID by default, so set it to None + setattr(self.endpoint, "region_id", None) + # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. @@ -86,25 +100,25 @@ def test_endpoint_create_no_options(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': True, - 'region': None, + 'is_enabled': True, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + None, + None, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) @@ -114,14 +128,14 @@ def test_endpoint_create_region(self): self.endpoint.interface, self.endpoint.url, '--region', - self.endpoint.region, + self.region.id, ] verifylist = [ ('enabled', True), ('service', self.service.id), ('interface', self.endpoint.interface), ('url', self.endpoint.url), - ('region', self.endpoint.region), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -132,25 +146,26 @@ def test_endpoint_create_region(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': True, - 'region': self.endpoint.region, + 'is_enabled': True, + 'region_id': self.region.id, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + self.region.id, + self.region.id, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) @@ -169,6 +184,9 @@ def test_endpoint_create_enable(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # Fake endpoints come with a region ID by default, so set it to None + setattr(self.endpoint, "region_id", None) + # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. @@ -176,25 +194,25 @@ def test_endpoint_create_enable(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': True, - 'region': None, + 'is_enabled': True, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + None, + None, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) @@ -213,6 +231,10 @@ def test_endpoint_create_disable(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # Fake endpoints come with a region ID by default, so set it to None + setattr(self.endpoint, "region_id", None) + setattr(self.endpoint, "is_enabled", False) + # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. @@ -220,38 +242,37 @@ def test_endpoint_create_disable(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': False, - 'region': None, + 'is_enabled': False, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( - True, + False, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + None, + None, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) -class TestEndpointDelete(TestEndpoint): - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint() - +class TestEndpointDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource(endpoint) - self.endpoints_mock.get.return_value = self.endpoint - self.endpoints_mock.delete.return_value = None + self.endpoint = sdk_fakes.generate_fake_resource(_endpoint.Endpoint) + + self.identity_sdk_client.find_endpoint.return_value = self.endpoint + self.identity_sdk_client.delete_endpoint.return_value = None # Get the command object to test self.cmd = endpoint.DeleteEndpoint(self.app, None) @@ -267,18 +288,13 @@ def test_endpoint_delete(self): result = self.cmd.take_action(parsed_args) - self.endpoints_mock.delete.assert_called_with( + self.identity_sdk_client.delete_endpoint.assert_called_with( self.endpoint.id, ) self.assertIsNone(result) -class TestEndpointList(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} - ) - +class TestEndpointList(identity_fakes.TestIdentityv3): columns = ( 'ID', 'Region', @@ -292,11 +308,19 @@ class TestEndpointList(TestEndpoint): def setUp(self): super().setUp() - self.endpoints_mock.list.return_value = [self.endpoint] + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + region_id=self.region.id, + ) - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service - self.services_mock.list.return_value = [self.service] + self.identity_sdk_client.endpoints.return_value = [self.endpoint] + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.services.return_value = [self.service] + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = endpoint.ListEndpoint(self.app, None) @@ -310,13 +334,13 @@ def test_endpoint_list_no_options(self): # 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.endpoints_mock.list.assert_called_with() + self.identity_sdk_client.endpoints.assert_called_with() self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -343,15 +367,15 @@ def test_endpoint_list_service(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, } - self.endpoints_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.endpoints.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -380,13 +404,13 @@ def test_endpoint_list_interface(self): kwargs = { 'interface': self.endpoint.interface, } - self.endpoints_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.endpoints.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -399,10 +423,10 @@ def test_endpoint_list_interface(self): def test_endpoint_list_region(self): arglist = [ '--region', - self.endpoint.region, + self.region.id, ] verifylist = [ - ('region', self.endpoint.region), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -413,15 +437,15 @@ def test_endpoint_list_region(self): # Set expected values kwargs = { - 'region': self.endpoint.region, + 'region_id': self.region.id, } - self.endpoints_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.endpoints.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -432,13 +456,13 @@ def test_endpoint_list_region(self): self.assertEqual(datalist, tuple(data)) def test_endpoint_list_project_with_project_domain(self): - project = identity_fakes.FakeProject.create_one_project() - domain = identity_fakes.FakeDomain.create_one_domain() + project = sdk_fakes.generate_fake_resource(_project.Project) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) - self.ep_filter_mock.list_endpoints_for_project.return_value = [ + self.identity_sdk_client.project_endpoints.return_value = [ self.endpoint ] - self.projects_mock.get.return_value = project + self.identity_sdk_client.find_project.return_value = project arglist = ['--project', project.name, '--project-domain', domain.name] verifylist = [ @@ -451,7 +475,7 @@ def test_endpoint_list_project_with_project_domain(self): # 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.ep_filter_mock.list_endpoints_for_project.assert_called_with( + self.identity_sdk_client.project_endpoints.assert_called_with( project=project.id ) @@ -459,7 +483,7 @@ def test_endpoint_list_project_with_project_domain(self): datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -470,22 +494,20 @@ def test_endpoint_list_project_with_project_domain(self): self.assertEqual(datalist, tuple(data)) -class TestEndpointSet(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} - ) - +class TestEndpointSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource(endpoint) - self.endpoints_mock.get.return_value = self.endpoint - - self.endpoints_mock.update.return_value = self.endpoint + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + ) - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.find_endpoint.return_value = self.endpoint + self.identity_sdk_client.update_endpoint.return_value = self.endpoint + self.identity_sdk_client.find_service.return_value = self.service # Get the command object to test self.cmd = endpoint.SetEndpoint(self.app, None) @@ -501,15 +523,8 @@ def test_endpoint_set_no_options(self): result = self.cmd.take_action(parsed_args) - kwargs = { - 'enabled': None, - 'interface': None, - 'region': None, - 'service': None, - 'url': None, - } - self.endpoints_mock.update.assert_called_with( - self.endpoint.id, **kwargs + self.identity_sdk_client.update_endpoint.assert_called_with( + self.endpoint.id ) self.assertIsNone(result) @@ -525,13 +540,9 @@ def test_endpoint_set_interface(self): # Set expected values kwargs = { - 'enabled': None, 'interface': 'public', - 'url': None, - 'region': None, - 'service': None, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -548,13 +559,9 @@ def test_endpoint_set_url(self): # Set expected values kwargs = { - 'enabled': None, - 'interface': None, 'url': 'http://localhost:5000', - 'region': None, - 'service': None, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -571,13 +578,9 @@ def test_endpoint_set_service(self): # Set expected values kwargs = { - 'enabled': None, - 'interface': None, - 'url': None, - 'region': None, - 'service': self.service.id, + 'service_id': self.service.id, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -594,13 +597,9 @@ def test_endpoint_set_region(self): # Set expected values kwargs = { - 'enabled': None, - 'interface': None, - 'url': None, - 'region': 'e-rzzz', - 'service': None, + 'region_id': 'e-rzzz', } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -617,13 +616,9 @@ def test_endpoint_set_enable(self): # Set expected values kwargs = { - 'enabled': True, - 'interface': None, - 'url': None, - 'region': None, - 'service': None, + 'is_enabled': True, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -640,31 +635,31 @@ def test_endpoint_set_disable(self): # Set expected values kwargs = { - 'enabled': False, - 'interface': None, - 'url': None, - 'region': None, - 'service': None, + 'is_enabled': False, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) -class TestEndpointShow(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} - ) - +class TestEndpointShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.endpoints_mock.get.return_value = self.endpoint + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + region_id=self.region.id, + ) + + self.identity_sdk_client.find_endpoint.return_value = self.endpoint - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = endpoint.ShowEndpoint(self.app, None) @@ -682,8 +677,8 @@ def test_endpoint_show(self): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.endpoints_mock.get.assert_called_with( - self.endpoint.id, + self.identity_sdk_client.find_endpoint.assert_called_with( + self.endpoint.id, ignore_missing=False ) collist = ( @@ -691,82 +686,82 @@ def test_endpoint_show(self): 'id', 'interface', 'region', + 'region_id', 'service_id', + 'url', 'service_name', 'service_type', - 'url', ) self.assertEqual(collist, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + self.region.id, + self.region.id, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) class TestEndpointCreateServiceWithoutName(TestEndpointCreate): - service = identity_fakes.FakeService.create_one_service( - attrs={'service_name': ''} + service = sdk_fakes.generate_fake_resource( + resource_type=_service.Service, + name='', + ) + region = sdk_fakes.generate_fake_resource(_region.Region) + endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=service.id, + interface='admin', + region_id=region.id, ) def setUp(self): - super(TestEndpointCreate, self).setUp() - - self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': self.service.id} - ) - - self.endpoints_mock.create.return_value = self.endpoint - - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + super().setUp() # Get the command object to test self.cmd = endpoint.CreateEndpoint(self.app, None) class TestEndpointListServiceWithoutName(TestEndpointList): - service = identity_fakes.FakeService.create_one_service( - attrs={'service_name': ''} + service = sdk_fakes.generate_fake_resource( + resource_type=_service.Service, + name='', ) - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} + region = sdk_fakes.generate_fake_resource(_region.Region) + endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=service.id, + interface='admin', + region_id=region.id, ) def setUp(self): - super(TestEndpointList, self).setUp() - - self.endpoints_mock.list.return_value = [self.endpoint] - - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service - self.services_mock.list.return_value = [self.service] + super().setUp() # Get the command object to test self.cmd = endpoint.ListEndpoint(self.app, None) class TestEndpointShowServiceWithoutName(TestEndpointShow): - service = identity_fakes.FakeService.create_one_service( - attrs={'service_name': ''} + service = sdk_fakes.generate_fake_resource( + resource_type=_service.Service, + name='', ) - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} + region = sdk_fakes.generate_fake_resource(_region.Region) + endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=service.id, + interface='admin', + region_id=region.id, ) def setUp(self): - super(TestEndpointShow, self).setUp() - - self.endpoints_mock.get.return_value = self.endpoint - - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + super().setUp() # Get the command object to test self.cmd = endpoint.ShowEndpoint(self.app, None) diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index 04e5a9cf9a..598402e070 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -14,45 +14,33 @@ from unittest import mock from unittest.mock import call -from keystoneauth1 import exceptions as ks_exc +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import group as _group +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions -from osc_lib import utils from openstackclient.identity.v3 import group from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestGroup(identity_fakes.TestIdentityv3): +class TestGroupAddUser(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - # Get a shortcut to the GroupManager Mock - self.groups_mock = self.identity_client.groups - self.groups_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - -class TestGroupAddUser(TestGroup): - _group = identity_fakes.FakeGroup.create_one_group() - users = identity_fakes.FakeUser.create_users(count=2) - - def setUp(self): - super().setUp() + self._group = sdk_fakes.generate_fake_resource(_group.Group) + self.users = tuple( + sdk_fakes.generate_fake_resources(_user.User, count=2) + ) - self.groups_mock.get.return_value = self._group - self.users_mock.get = identity_fakes.FakeUser.get_users(self.users) - self.users_mock.add_to_group.return_value = None + self.identity_sdk_client.find_group.return_value = self._group + self.identity_sdk_client.add_user_to_group.return_value = None self.cmd = group.AddUserToGroup(self.app, None) def test_group_add_user(self): + self.identity_sdk_client.find_user.return_value = self.users[0] arglist = [ self._group.name, self.users[0].name, @@ -64,12 +52,16 @@ def test_group_add_user(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.users_mock.add_to_group.assert_called_once_with( + self.identity_sdk_client.add_user_to_group.assert_called_once_with( self.users[0].id, self._group.id ) self.assertIsNone(result) def test_group_add_multi_users(self): + self.identity_sdk_client.find_user.side_effect = [ + self.users[0], + self.users[1], + ] arglist = [ self._group.name, self.users[0].name, @@ -86,13 +78,13 @@ def test_group_add_multi_users(self): call(self.users[0].id, self._group.id), call(self.users[1].id, self._group.id), ] - self.users_mock.add_to_group.assert_has_calls(calls) + self.identity_sdk_client.add_user_to_group.assert_has_calls(calls) self.assertIsNone(result) @mock.patch.object(group.LOG, 'error') def test_group_add_user_with_error(self, mock_error): - self.users_mock.add_to_group.side_effect = [ - exceptions.CommandError(), + self.identity_sdk_client.add_user_to_group.side_effect = [ + sdk_exc.ResourceNotFound, None, ] arglist = [ @@ -109,25 +101,22 @@ def test_group_add_user_with_error(self, mock_error): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - msg = "1 of 2 users not added to group %s." % self._group.name + msg = f"1 of 2 users not added to group {self._group.name}." self.assertEqual(msg, str(e)) - msg = ("%(user)s not added to group %(group)s: ") % { - 'user': self.users[0].name, - 'group': self._group.name, - } + msg = f"{self.users[0].name} not added to group {self._group.name}: {str(sdk_exc.ResourceNotFound())}" mock_error.assert_called_once_with(msg) -class TestGroupCheckUser(TestGroup): - group = identity_fakes.FakeGroup.create_one_group() - user = identity_fakes.FakeUser.create_one_user() - +class TestGroupCheckUser(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.groups_mock.get.return_value = self.group - self.users_mock.get.return_value = self.user - self.users_mock.check_in_group.return_value = None + self.group = sdk_fakes.generate_fake_resource(_group.Group) + self.user = sdk_fakes.generate_fake_resource(_user.User) + + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.check_user_in_group.return_value = True self.cmd = group.CheckUserInGroup(self.app, None) @@ -143,16 +132,15 @@ def test_group_check_user(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.users_mock.check_in_group.assert_called_once_with( + self.identity_sdk_client.check_user_in_group.assert_called_once_with( self.user.id, self.group.id ) self.assertIsNone(result) def test_group_check_user_server_error(self): - def server_error(*args): - raise ks_exc.http.InternalServerError - - self.users_mock.check_in_group.side_effect = server_error + self.identity_sdk_client.check_user_in_group.side_effect = ( + sdk_exc.SDKException + ) arglist = [ self.group.name, self.user.name, @@ -164,12 +152,12 @@ def server_error(*args): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - ks_exc.http.InternalServerError, self.cmd.take_action, parsed_args + sdk_exc.SDKException, self.cmd.take_action, parsed_args ) -class TestGroupCreate(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestGroupCreate(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'description', @@ -180,23 +168,20 @@ class TestGroupCreate(TestGroup): def setUp(self): super().setUp() - self.group = identity_fakes.FakeGroup.create_one_group( - attrs={'domain_id': self.domain.id} + self.group = sdk_fakes.generate_fake_resource( + _group.Group, description=None, domain_id=None ) - self.data = ( - self.group.description, - self.group.domain_id, - self.group.id, - self.group.name, + self.group_with_options = sdk_fakes.generate_fake_resource( + _group.Group, domain_id=self.domain.id ) - self.groups_mock.create.return_value = self.group - self.groups_mock.get.return_value = self.group - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.CreateGroup(self.app, None) def test_group_create(self): + self.identity_sdk_client.create_group.return_value = self.group arglist = [ self.group.name, ] @@ -206,40 +191,56 @@ def test_group_create(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.create.assert_called_once_with( + self.identity_sdk_client.create_group.assert_called_once_with( name=self.group.name, - domain=None, - description=None, ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group.description, + None, + self.group.id, + self.group.name, + ) + self.assertEqual(datalist, data) def test_group_create_with_options(self): + self.identity_sdk_client.create_group.return_value = ( + self.group_with_options + ) arglist = [ '--domain', self.domain.name, '--description', - self.group.description, - self.group.name, + self.group_with_options.description, + self.group_with_options.name, ] verifylist = [ ('domain', self.domain.name), - ('description', self.group.description), - ('name', self.group.name), + ('description', self.group_with_options.description), + ('name', self.group_with_options.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.create.assert_called_once_with( - name=self.group.name, - domain=self.domain.id, - description=self.group.description, + self.identity_sdk_client.create_group.assert_called_once_with( + name=self.group_with_options.name, + domain_id=self.domain.id, + description=self.group_with_options.description, ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group_with_options.description, + self.domain.id, + self.group_with_options.id, + self.group_with_options.name, + ) + self.assertEqual(datalist, data) def test_group_create_or_show(self): - self.groups_mock.create.side_effect = ks_exc.Conflict() + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.create_group.side_effect = ( + sdk_exc.ConflictException + ) arglist = [ '--or-show', self.group.name, @@ -251,46 +252,99 @@ def test_group_create_or_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_called_once_with(self.group.name) + self.identity_sdk_client.find_group.assert_called_once_with( + self.group.name, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group.description, + None, + self.group.id, + self.group.name, + ) + self.assertEqual(datalist, data) + def test_group_create_or_show_with_domain(self): + self.identity_sdk_client.find_group.return_value = ( + self.group_with_options + ) + self.identity_sdk_client.create_group.side_effect = ( + sdk_exc.ConflictException + ) + arglist = [ + '--or-show', + self.group_with_options.name, + '--domain', + self.domain.id, + ] + verifylist = [ + ('or_show', True), + ('name', self.group_with_options.name), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) -class TestGroupDelete(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() - groups = identity_fakes.FakeGroup.create_groups( - attrs={'domain_id': domain.id}, count=2 - ) + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_group.assert_called_once_with( + self.group_with_options.name, + domain_id=self.domain.id, + ignore_missing=False, + ) + self.assertEqual(self.columns, columns) + datalist = ( + self.group_with_options.description, + self.domain.id, + self.group_with_options.id, + self.group_with_options.name, + ) + self.assertEqual(datalist, data) + + +class TestGroupDelete(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - self.groups_mock.get = identity_fakes.FakeGroup.get_groups(self.groups) - self.groups_mock.delete.return_value = None - self.domains_mock.get.return_value = self.domain + self.group = sdk_fakes.generate_fake_resource( + _group.Group, + domain_id=None, + ) + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, + name=self.group.name, + domain_id=self.domain.id, + ) + self.identity_sdk_client.delete_group.return_value = None + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.DeleteGroup(self.app, None) def test_group_delete(self): + self.identity_sdk_client.find_group.return_value = self.group arglist = [ - self.groups[0].id, + self.group.id, ] verifylist = [ - ('groups', [self.groups[0].id]), + ('groups', [self.group.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_called_once_with(self.groups[0].id) - self.groups_mock.delete.assert_called_once_with(self.groups[0].id) + self.identity_sdk_client.find_group.assert_called_once_with( + name_or_id=self.group.id, ignore_missing=False + ) + self.identity_sdk_client.delete_group.assert_called_once_with( + self.group.id + ) self.assertIsNone(result) def test_group_multi_delete(self): - arglist = [] - verifylist = [] - - for g in self.groups: - arglist.append(g.id) + self.identity_sdk_client.find_group.side_effect = [ + self.group, + self.group_with_domain, + ] + arglist = [self.group.id, self.group_with_domain.id] verifylist = [ ('groups', arglist), ] @@ -298,39 +352,50 @@ def test_group_multi_delete(self): result = self.cmd.take_action(parsed_args) - calls = [] - for g in self.groups: - calls.append(call(g.id)) - self.groups_mock.delete.assert_has_calls(calls) + self.identity_sdk_client.delete_group.assert_has_calls( + [mock.call(self.group.id), mock.call(self.group_with_domain.id)] + ) self.assertIsNone(result) def test_group_delete_with_domain(self): - get_mock_result = [exceptions.CommandError, self.groups[0]] - self.groups_mock.get = mock.Mock(side_effect=get_mock_result) + self.identity_sdk_client.find_domain.side_effect = [ + sdk_exc.ForbiddenException + ] + self.identity_sdk_client.find_group.return_value = ( + self.group_with_domain + ) arglist = [ '--domain', - self.domain.id, - self.groups[0].id, + self.group_with_domain.domain_id, + self.group_with_domain.name, ] verifylist = [ - ('domain', self.groups[0].domain_id), - ('groups', [self.groups[0].id]), + ('domain', self.domain.id), + ('groups', [self.group_with_domain.name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_any_call( - self.groups[0].id, domain_id=self.domain.id + self.identity_sdk_client.find_group.assert_called_with( + name_or_id=self.group_with_domain.name, + ignore_missing=False, + domain_id=self.domain.id, + ) + self.identity_sdk_client.delete_group.assert_called_once_with( + self.group_with_domain.id ) - self.groups_mock.delete.assert_called_once_with(self.groups[0].id) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_groups_with_exception(self, find_mock): - find_mock.side_effect = [self.groups[0], exceptions.CommandError] + def test_delete_multi_groups_with_exception(self): + self.identity_sdk_client.find_group.side_effect = [ + self.group, + self.group_with_domain, + exceptions.CommandError, + ] arglist = [ - self.groups[0].id, + self.group.id, + self.group_with_domain.id, 'unexist_group', ] verifylist = [ @@ -342,45 +407,57 @@ def test_delete_multi_groups_with_exception(self, find_mock): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - self.assertEqual('1 of 2 groups failed to delete.', str(e)) - - find_mock.assert_any_call(self.groups_mock, self.groups[0].id) - find_mock.assert_any_call(self.groups_mock, 'unexist_group') + self.assertEqual('1 of 3 groups failed to delete.', str(e)) + + self.identity_sdk_client.find_group.assert_has_calls( + [ + mock.call(name_or_id=self.group.id, ignore_missing=False), + mock.call( + name_or_id=self.group_with_domain.id, ignore_missing=False + ), + mock.call(name_or_id='unexist_group', ignore_missing=False), + ] + ) - self.assertEqual(2, find_mock.call_count) - self.groups_mock.delete.assert_called_once_with(self.groups[0].id) + self.assertEqual(3, self.identity_sdk_client.find_group.call_count) + self.identity_sdk_client.delete_group.assert_has_calls( + [ + mock.call(self.group.id), + mock.call(self.group_with_domain.id), + ] + ) -class TestGroupList(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() - group = identity_fakes.FakeGroup.create_one_group() - user = identity_fakes.FakeUser.create_one_user() +class TestGroupList(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'ID', 'Name', ) - datalist = ( - ( - group.id, - group.name, - ), - ) def setUp(self): super().setUp() - self.groups_mock.get.return_value = self.group - self.groups_mock.list.return_value = [self.group] - - self.domains_mock.get.return_value = self.domain + self.group = sdk_fakes.generate_fake_resource( + _group.Group, description=None, domain_id=None + ) + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, domain_id=self.domain.id + ) + self.user = sdk_fakes.generate_fake_resource(_user.User) - self.users_mock.get.return_value = self.user + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_domain.return_value = self.domain # Get the command object to test self.cmd = group.ListGroup(self.app, None) def test_group_list_no_options(self): + self.identity_sdk_client.groups.return_value = [ + self.group, + self.group_with_domain, + ] arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -390,18 +467,23 @@ def test_group_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'domain': None, - 'user': None, - } - - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.groups.assert_called_with() self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + datalist = ( + ( + self.group.id, + self.group.name, + ), + ( + self.group_with_domain.id, + self.group_with_domain.name, + ), + ) + self.assertEqual(datalist, tuple(data)) def test_group_list_domain(self): + self.identity_sdk_client.groups.return_value = [self.group_with_domain] arglist = [ '--domain', self.domain.id, @@ -418,16 +500,17 @@ def test_group_list_domain(self): # Set expected values kwargs = { - 'domain': self.domain.id, - 'user': None, + 'domain_id': self.domain.id, } - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.groups.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + datalist = ((self.group_with_domain.id, self.group_with_domain.name),) + self.assertEqual(datalist, tuple(data)) def test_group_list_user(self): + self.identity_sdk_client.user_groups.return_value = [self.group] arglist = [ '--user', self.user.name, @@ -442,18 +525,53 @@ def test_group_list_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.user_groups.assert_called_with(self.user.id) + + self.assertEqual(self.columns, columns) + + datalist = ((self.group.id, self.group.name),) + self.assertEqual(datalist, tuple(data)) + + def test_group_list_user_domain(self): + self.identity_sdk_client.user_groups.return_value = [ + self.group_with_domain + ] + arglist = [ + '--user', + self.user.name, + '--domain', + self.domain.name, + ] + verifylist = [ + ('user', self.user.name), + ('domain', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + # Set expected values kwargs = { - 'domain': None, - 'user': self.user.id, + 'domain_id': self.domain.id, } - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.user_groups.assert_called_with( + self.user.id, **kwargs + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + + datalist = ((self.group_with_domain.id, self.group_with_domain.name),) + self.assertEqual(datalist, tuple(data)) def test_group_list_long(self): + self.identity_sdk_client.groups.return_value = [ + self.group, + self.group_with_domain, + ] arglist = [ '--long', ] @@ -467,15 +585,9 @@ def test_group_list_long(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'domain': None, - 'user': None, - } + self.identity_sdk_client.groups.assert_called_with() - self.groups_mock.list.assert_called_with(**kwargs) - - columns = self.columns + ( + long_columns = self.columns + ( 'Domain ID', 'Description', ) @@ -486,25 +598,33 @@ def test_group_list_long(self): self.group.domain_id, self.group.description, ), + ( + self.group_with_domain.id, + self.group_with_domain.name, + self.group_with_domain.domain_id, + self.group_with_domain.description, + ), ) - self.assertEqual(columns, columns) + self.assertEqual(long_columns, columns) self.assertEqual(datalist, tuple(data)) -class TestGroupRemoveUser(TestGroup): - _group = identity_fakes.FakeGroup.create_one_group() - users = identity_fakes.FakeUser.create_users(count=2) - +class TestGroupRemoveUser(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.groups_mock.get.return_value = self._group - self.users_mock.get = identity_fakes.FakeUser.get_users(self.users) - self.users_mock.remove_from_group.return_value = None + self._group = sdk_fakes.generate_fake_resource(_group.Group) + self.users = tuple( + sdk_fakes.generate_fake_resources(_user.User, count=2) + ) + + self.identity_sdk_client.find_group.return_value = self._group + self.identity_sdk_client.remove_user_from_group.return_value = None self.cmd = group.RemoveUserFromGroup(self.app, None) def test_group_remove_user(self): + self.identity_sdk_client.find_user.return_value = self.users[0] arglist = [ self._group.id, self.users[0].id, @@ -516,12 +636,16 @@ def test_group_remove_user(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.users_mock.remove_from_group.assert_called_once_with( + self.identity_sdk_client.remove_user_from_group.assert_called_once_with( self.users[0].id, self._group.id ) self.assertIsNone(result) def test_group_remove_multi_users(self): + self.identity_sdk_client.find_user.side_effect = [ + self.users[0], + self.users[1], + ] arglist = [ self._group.name, self.users[0].name, @@ -538,13 +662,13 @@ def test_group_remove_multi_users(self): call(self.users[0].id, self._group.id), call(self.users[1].id, self._group.id), ] - self.users_mock.remove_from_group.assert_has_calls(calls) + self.identity_sdk_client.remove_user_from_group.assert_has_calls(calls) self.assertIsNone(result) @mock.patch.object(group.LOG, 'error') def test_group_remove_user_with_error(self, mock_error): - self.users_mock.remove_from_group.side_effect = [ - exceptions.CommandError(), + self.identity_sdk_client.remove_user_from_group.side_effect = [ + sdk_exc.ResourceNotFound(), None, ] arglist = [ @@ -561,31 +685,31 @@ def test_group_remove_user_with_error(self, mock_error): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - msg = "1 of 2 users not removed from group %s." % self._group.id + msg = f"1 of 2 users not removed from group {self._group.id}." self.assertEqual(msg, str(e)) - msg = ("%(user)s not removed from group %(group)s: ") % { - 'user': self.users[0].id, - 'group': self._group.id, - } + msg = f"{self.users[0].id} not removed from group {self._group.id}: {str(sdk_exc.ResourceNotFound())}" mock_error.assert_called_once_with(msg) -class TestGroupSet(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() - group = identity_fakes.FakeGroup.create_one_group( - attrs={'domain_id': domain.id} - ) +class TestGroupSet(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() + self.group = sdk_fakes.generate_fake_resource( + _group.Group, domain_id=self.domain.id + ) + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, name=self.group.name, domain_id=self.domain.id + ) - self.groups_mock.get.return_value = self.group - self.domains_mock.get.return_value = self.domain - self.groups_mock.update.return_value = None + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.SetGroup(self.app, None) def test_group_set_nothing(self): + self.identity_sdk_client.update_group.return_value = self.group arglist = [ self.group.id, ] @@ -595,10 +719,13 @@ def test_group_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.update.assert_called_once_with(self.group.id) + self.identity_sdk_client.update_group.assert_called_once_with( + self.group.id + ) self.assertIsNone(result) def test_group_set_name_and_description(self): + self.identity_sdk_client.update_group.return_value = self.group arglist = [ '--name', 'new_name', @@ -618,36 +745,43 @@ def test_group_set_name_and_description(self): 'name': 'new_name', 'description': 'new_description', } - self.groups_mock.update.assert_called_once_with( + self.identity_sdk_client.update_group.assert_called_once_with( self.group.id, **kwargs ) self.assertIsNone(result) def test_group_set_with_domain(self): - get_mock_result = [exceptions.CommandError, self.group] - self.groups_mock.get = mock.Mock(side_effect=get_mock_result) - + self.identity_sdk_client.find_domain.side_effect = [ + sdk_exc.ForbiddenException + ] + self.identity_sdk_client.find_group.return_value = ( + self.group_with_domain + ) arglist = [ '--domain', self.domain.id, - self.group.id, + self.group_with_domain.name, ] verifylist = [ ('domain', self.domain.id), - ('group', self.group.id), + ('group', self.group_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_any_call( - self.group.id, domain_id=self.domain.id + self.identity_sdk_client.find_group.assert_called_once_with( + name_or_id=self.group_with_domain.name, + ignore_missing=False, + domain_id=self.domain.id, + ) + self.identity_sdk_client.update_group.assert_called_once_with( + self.group_with_domain.id ) - self.groups_mock.update.assert_called_once_with(self.group.id) self.assertIsNone(result) -class TestGroupShow(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestGroupShow(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'description', @@ -658,22 +792,19 @@ class TestGroupShow(TestGroup): def setUp(self): super().setUp() - self.group = identity_fakes.FakeGroup.create_one_group( - attrs={'domain_id': self.domain.id} + self.group = sdk_fakes.generate_fake_resource( + _group.Group, description=None, domain_id=None ) - self.data = ( - self.group.description, - self.group.domain_id, - self.group.id, - self.group.name, + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, name=self.group.name, domain_id=self.domain.id ) - self.groups_mock.get.return_value = self.group - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.ShowGroup(self.app, None) def test_group_show(self): + self.identity_sdk_client.find_group.return_value = self.group arglist = [ self.group.id, ] @@ -683,28 +814,44 @@ def test_group_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_called_once_with(self.group.id) + self.identity_sdk_client.find_group.assert_called_once_with( + self.group.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + None, + None, + self.group.id, + self.group.name, + ) + self.assertEqual(datalist, data) def test_group_show_with_domain(self): - get_mock_result = [exceptions.CommandError, self.group] - self.groups_mock.get = mock.Mock(side_effect=get_mock_result) - + self.identity_sdk_client.find_group.return_value = ( + self.group_with_domain + ) arglist = [ '--domain', self.domain.id, - self.group.id, + self.group_with_domain.name, ] verifylist = [ ('domain', self.domain.id), - ('group', self.group.id), + ('group', self.group_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_any_call( - self.group.id, domain_id=self.domain.id + self.identity_sdk_client.find_group.assert_called_once_with( + self.group_with_domain.name, + domain_id=self.domain.id, + ignore_missing=False, ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group_with_domain.description, + self.domain.id, + self.group_with_domain.id, + self.group_with_domain.name, + ) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py index 20e7f497ae..c65e947efa 100644 --- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -127,7 +127,7 @@ def test_create_identity_provider_remote_id(self): ] verifylist = [ ('identity_provider_id', identity_fakes.idp_id), - ('remote_id', identity_fakes.idp_remote_ids[:1]), + ('remote_ids', identity_fakes.idp_remote_ids[:1]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -157,7 +157,7 @@ def test_create_identity_provider_remote_ids_multiple(self): ] verifylist = [ ('identity_provider_id', identity_fakes.idp_id), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -561,7 +561,7 @@ def prepare(self): ('description', new_description), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) @@ -597,7 +597,7 @@ def prepare(self): ('description', None), ('enable', False), ('disable', True), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -637,7 +637,7 @@ def prepare(self): ('description', None), ('enable', True), ('disable', False), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -675,7 +675,7 @@ def prepare(self): ('description', None), ('enable', True), ('disable', False), - ('remote_id', [self.new_remote_id]), + ('remote_ids', [self.new_remote_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -756,7 +756,7 @@ def prepare(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -776,7 +776,7 @@ def prepare(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', 60), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -800,7 +800,7 @@ def prepare(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', 0), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -816,7 +816,7 @@ def test_identity_provider_set_authttl_negative(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', -1), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_limit.py b/openstackclient/tests/unit/identity/v3/test_limit.py index ef0fa4b1e8..a0c045e663 100644 --- a/openstackclient/tests/unit/identity/v3/test_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_limit.py @@ -10,87 +10,80 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - -from keystoneauth1.exceptions import http as ksa_exceptions +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import limit as _limit +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import region as _region +from openstack.identity.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import limit -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestLimit(identity_fakes.TestIdentityv3): +class TestLimitCreate(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - identity_manager = self.identity_client - - self.limit_mock = identity_manager.limits - - self.services_mock = identity_manager.services - self.services_mock.reset_mock() - - self.projects_mock = identity_manager.projects - self.projects_mock.reset_mock() - - self.regions_mock = identity_manager.regions - self.regions_mock.reset_mock() + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.resource_limit = 15 -class TestLimitCreate(TestLimit): - def setUp(self): - super().setUp() + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + self.identity_sdk_client.find_project.return_value = self.project - self.service = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.SERVICE), loaded=True + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=self.resource_limit, + region_id=None, ) - self.services_mock.get.return_value = self.service - - self.project = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.PROJECT), loaded=True + self.limit_with_options = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_limit=self.resource_limit, + resource_name='foobars', + description='test description', + region_id=self.region.id, ) - self.projects_mock.get.return_value = self.project - - self.region = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.REGION), loaded=True - ) - self.regions_mock.get.return_value = self.region self.cmd = limit.CreateLimit(self.app, None) def test_limit_create_without_options(self): - self.limit_mock.create.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT), loaded=True - ) + self.identity_sdk_client.create_limit.return_value = self.limit - resource_limit = 15 arglist = [ '--project', - identity_fakes.project_id, + self.project.id, '--service', - identity_fakes.service_id, + self.service.id, '--resource-limit', - str(resource_limit), - identity_fakes.limit_resource_name, + str(self.resource_limit), + self.limit.resource_name, ] verifylist = [ - ('project', identity_fakes.project_id), - ('service', identity_fakes.service_id), - ('resource_name', identity_fakes.limit_resource_name), - ('resource_limit', resource_limit), + ('project', self.project.id), + ('service', self.service.id), + ('resource_name', self.limit.resource_name), + ('resource_limit', self.resource_limit), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - kwargs = {'description': None, 'region': None} - self.limit_mock.create.assert_called_with( - self.project, - self.service, - identity_fakes.limit_resource_name, - resource_limit, - **kwargs + self.identity_sdk_client.create_limit.assert_called_with( + project_id=self.project.id, + service_id=self.service.id, + resource_name=self.limit.resource_name, + resource_limit=self.resource_limit, ) collist = ( @@ -105,56 +98,56 @@ def test_limit_create_without_options(self): self.assertEqual(collist, columns) datalist = ( None, - identity_fakes.limit_id, - identity_fakes.project_id, + self.limit.id, + self.project.id, None, - resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + self.resource_limit, + self.limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_limit_create_with_options(self): - self.limit_mock.create.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT_OPTIONS), loaded=True + self.identity_sdk_client.create_limit.return_value = ( + self.limit_with_options ) resource_limit = 15 arglist = [ '--project', - identity_fakes.project_id, + self.project.id, '--service', - identity_fakes.service_id, + self.service.id, '--resource-limit', str(resource_limit), '--region', - identity_fakes.region_id, + self.region.id, '--description', - identity_fakes.limit_description, - identity_fakes.limit_resource_name, + self.limit_with_options.description, + self.limit_with_options.resource_name, ] verifylist = [ - ('project', identity_fakes.project_id), - ('service', identity_fakes.service_id), - ('resource_name', identity_fakes.limit_resource_name), + ('project', self.project.id), + ('service', self.service.id), + ('resource_name', self.limit_with_options.resource_name), ('resource_limit', resource_limit), - ('region', identity_fakes.region_id), - ('description', identity_fakes.limit_description), + ('region', self.region.id), + ('description', self.limit_with_options.description), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'description': identity_fakes.limit_description, - 'region': self.region, + 'project_id': self.project.id, + 'service_id': self.service.id, + 'region_id': self.region.id, + 'resource_name': self.limit_with_options.resource_name, + 'resource_limit': resource_limit, + 'description': self.limit_with_options.description, } - self.limit_mock.create.assert_called_with( - self.project, - self.service, - identity_fakes.limit_resource_name, - resource_limit, - **kwargs + self.identity_sdk_client.create_limit.assert_called_with( + **kwargs, ) collist = ( @@ -168,37 +161,41 @@ def test_limit_create_with_options(self): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.limit_description, - identity_fakes.limit_id, - identity_fakes.project_id, - identity_fakes.region_id, + self.limit_with_options.description, + self.limit_with_options.id, + self.project.id, + self.region.id, resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + self.limit_with_options.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestLimitDelete(TestLimit): +class TestLimitDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() self.cmd = limit.DeleteLimit(self.app, None) def test_limit_delete(self): - self.limit_mock.delete.return_value = None + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit + ) + self.identity_sdk_client.delete_limit.return_value = None - arglist = [identity_fakes.limit_id] - verifylist = [('limit_id', [identity_fakes.limit_id])] + arglist = [self.limit.id] + verifylist = [('limit_id', [self.limit.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.limit_mock.delete.assert_called_with(identity_fakes.limit_id) + self.identity_sdk_client.delete_limit.assert_called_with(self.limit.id) self.assertIsNone(result) def test_limit_delete_with_exception(self): - return_value = ksa_exceptions.NotFound() - self.limit_mock.delete.side_effect = return_value + self.identity_sdk_client.delete_limit.side_effect = ( + sdk_exc.ResourceNotFound + ) arglist = ['fake-limit-id'] verifylist = [('limit_id', ['fake-limit-id'])] @@ -211,24 +208,42 @@ def test_limit_delete_with_exception(self): self.assertEqual('1 of 1 limits failed to delete.', str(e)) -class TestLimitShow(TestLimit): +class TestLimitShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.limit_mock.get.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT), loaded=True + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.resource_limit = 15 + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + self.identity_sdk_client.find_project.return_value = self.project + + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=self.resource_limit, + region_id=None, ) + self.identity_sdk_client.get_limit.return_value = self.limit + self.cmd = limit.ShowLimit(self.app, None) def test_limit_show(self): - arglist = [identity_fakes.limit_id] - verifylist = [('limit_id', identity_fakes.limit_id)] + arglist = [self.limit.id] + verifylist = [('limit_id', self.limit.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.get.assert_called_with(identity_fakes.limit_id) + self.identity_sdk_client.get_limit.assert_called_with(self.limit.id) collist = ( 'description', @@ -242,45 +257,61 @@ def test_limit_show(self): self.assertEqual(collist, columns) datalist = ( None, - identity_fakes.limit_id, - identity_fakes.project_id, + self.limit.id, + self.project.id, None, - identity_fakes.limit_resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + self.resource_limit, + self.limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestLimitSet(TestLimit): +class TestLimitSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() + + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.resource_limit = 15 + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + self.identity_sdk_client.find_project.return_value = self.project + self.cmd = limit.SetLimit(self.app, None) def test_limit_set_description(self): - limit = copy.deepcopy(identity_fakes.LIMIT) - limit['description'] = identity_fakes.limit_description - self.limit_mock.update.return_value = fakes.FakeResource( - None, limit, loaded=True + description = 'limit of foobars' + limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=description, + resource_limit=self.resource_limit, + region_id=None, ) + self.identity_sdk_client.update_limit.return_value = limit arglist = [ '--description', - identity_fakes.limit_description, - identity_fakes.limit_id, + description, + limit.id, ] verifylist = [ - ('description', identity_fakes.limit_description), - ('limit_id', identity_fakes.limit_id), + ('description', description), + ('limit_id', limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.update.assert_called_with( - identity_fakes.limit_id, - description=identity_fakes.limit_description, - resource_limit=None, + self.identity_sdk_client.update_limit.assert_called_with( + limit.id, + description=description, ) collist = ( @@ -294,40 +325,44 @@ def test_limit_set_description(self): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.limit_description, - identity_fakes.limit_id, - identity_fakes.project_id, + description, + limit.id, + self.project.id, None, - identity_fakes.limit_resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + limit.resource_limit, + limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_limit_set_resource_limit(self): resource_limit = 20 - limit = copy.deepcopy(identity_fakes.LIMIT) - limit['resource_limit'] = resource_limit - self.limit_mock.update.return_value = fakes.FakeResource( - None, limit, loaded=True + limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=resource_limit, + region_id=None, ) + self.identity_sdk_client.update_limit.return_value = limit arglist = [ '--resource-limit', str(resource_limit), - identity_fakes.limit_id, + limit.id, ] verifylist = [ ('resource_limit', resource_limit), - ('limit_id', identity_fakes.limit_id), + ('limit_id', limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.update.assert_called_with( - identity_fakes.limit_id, - description=None, + self.identity_sdk_client.update_limit.assert_called_with( + limit.id, resource_limit=resource_limit, ) @@ -343,25 +378,36 @@ def test_limit_set_resource_limit(self): self.assertEqual(collist, columns) datalist = ( None, - identity_fakes.limit_id, - identity_fakes.project_id, + limit.id, + self.project.id, None, resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestLimitList(TestLimit): +class TestLimitList(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.limit_mock.list.return_value = [ - fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT), loaded=True - ) - ] + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.resource_limit = 15 + + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=self.resource_limit, + region_id=None, + ) + self.identity_sdk_client.limits.return_value = [self.limit] self.cmd = limit.ListLimit(self.app, None) @@ -372,9 +418,7 @@ def test_limit_list(self): columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.list.assert_called_with( - service=None, resource_name=None, region=None, project=None - ) + self.identity_sdk_client.limits.assert_called_with() collist = ( 'ID', @@ -388,11 +432,11 @@ def test_limit_list(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.limit_id, - identity_fakes.project_id, - identity_fakes.service_id, - identity_fakes.limit_resource_name, - identity_fakes.limit_resource_limit, + self.limit.id, + self.project.id, + self.service.id, + self.limit.resource_name, + self.limit.resource_limit, None, None, ), diff --git a/openstackclient/tests/unit/identity/v3/test_mappings.py b/openstackclient/tests/unit/identity/v3/test_mappings.py index 60a89dd618..5a3fad97a8 100644 --- a/openstackclient/tests/unit/identity/v3/test_mappings.py +++ b/openstackclient/tests/unit/identity/v3/test_mappings.py @@ -55,7 +55,7 @@ def test_create_mapping(self): mocker = mock.Mock() mocker.return_value = identity_fakes.MAPPING_RULES with mock.patch( - "openstackclient.identity.v3.mapping." "CreateMapping._read_rules", + "openstackclient.identity.v3.mapping.CreateMapping._read_rules", mocker, ): columns, data = self.cmd.take_action(parsed_args) @@ -170,7 +170,7 @@ def test_set_new_rules(self): mocker = mock.Mock() mocker.return_value = identity_fakes.MAPPING_RULES_2 with mock.patch( - "openstackclient.identity.v3.mapping." "SetMapping._read_rules", + "openstackclient.identity.v3.mapping.SetMapping._read_rules", mocker, ): result = self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/identity/v3/test_oauth.py b/openstackclient/tests/unit/identity/v3/test_oauth.py index 576f8b20eb..9dcf0be89e 100644 --- a/openstackclient/tests/unit/identity/v3/test_oauth.py +++ b/openstackclient/tests/unit/identity/v3/test_oauth.py @@ -109,7 +109,7 @@ def test_authorize_request_tokens(self): ] verifylist = [ ('request_key', identity_fakes.request_token_id), - ('role', [identity_fakes.role_name]), + ('roles', [identity_fakes.role_name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 9b7fc8ceaa..065a65cb1f 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -11,7 +11,6 @@ # 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 unittest.mock import call @@ -78,8 +77,7 @@ def test_project_create_no_options(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), ] @@ -135,8 +133,7 @@ def test_project_create_description(self): ] verifylist = [ ('description', 'new desc'), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -173,8 +170,7 @@ def test_project_create_domain(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -211,8 +207,7 @@ def test_project_create_domain_no_perms(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -244,8 +239,7 @@ def test_project_create_enable(self): self.project.name, ] verifylist = [ - ('enable', True), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -280,8 +274,7 @@ def test_project_create_disable(self): self.project.name, ] verifylist = [ - ('enable', False), - ('disable', True), + ('enabled', False), ('name', self.project.name), ('parent', None), ] @@ -318,7 +311,7 @@ def test_project_create_property(self): self.project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -355,11 +348,10 @@ def test_project_create_is_domain_false_property(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), - ('property', {'is_domain': 'false'}), + ('properties', {'is_domain': 'false'}), ('name', self.project.name), ] @@ -394,11 +386,10 @@ def test_project_create_is_domain_true_property(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), - ('property', {'is_domain': 'true'}), + ('properties', {'is_domain': 'true'}), ('name', self.project.name), ] @@ -433,11 +424,10 @@ def test_project_create_is_domain_none_property(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), - ('property', {'is_domain': 'none'}), + ('properties', {'is_domain': 'none'}), ('name', self.project.name), ] @@ -482,8 +472,7 @@ def test_project_create_parent(self): verifylist = [ ('domain', self.project.domain_id), ('parent', self.parent.name), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), ] @@ -545,8 +534,7 @@ def test_project_create_invalid_parent(self): verifylist = [ ('domain', self.project.domain_id), ('parent', 'invalid'), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -567,8 +555,7 @@ def test_project_create_with_tags(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', ['foo']), @@ -603,8 +590,7 @@ def test_project_create_with_immutable_option(self): verifylist = [ ('immutable', True), ('description', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -639,10 +625,9 @@ def test_project_create_with_no_immutable_option(self): self.project.name, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('description', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -914,8 +899,7 @@ def test_project_list_my_projects(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_PROJECT_ID, ) - ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [ '--my-projects', @@ -943,6 +927,22 @@ def test_project_list_my_projects(self): ) self.assertEqual(datalist, tuple(data)) + def test_project_list_with_option_enabled(self): + arglist = ['--enabled'] + verifylist = [('is_enabled', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + kwargs = {'is_enabled': True} + self.projects_mock.list.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + class TestProjectSet(TestProject): domain = identity_fakes.FakeDomain.create_one_domain() @@ -967,8 +967,7 @@ def test_project_set_no_options(self): ] verifylist = [ ('project', self.project.name), - ('enable', False), - ('disable', False), + ('enabled', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -987,8 +986,7 @@ def test_project_set_name(self): verifylist = [ ('name', 'qwerty'), ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1015,8 +1013,7 @@ def test_project_set_description(self): verifylist = [ ('domain', self.project.domain_id), ('description', 'new desc'), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1039,8 +1036,7 @@ def test_project_set_enable(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', True), - ('disable', False), + ('enabled', True), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1063,8 +1059,7 @@ def test_project_set_disable(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', True), + ('enabled', False), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1090,7 +1085,7 @@ def test_project_set_property(self): ] verifylist = [ ('domain', self.project.domain_id), - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1118,8 +1113,7 @@ def test_project_set_tags(self): verifylist = [ ('name', 'qwerty'), ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ('tags', ['foo']), ] @@ -1146,10 +1140,9 @@ def test_project_remove_tags(self): self.project.name, ] verifylist = [ - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), - ('remove_tag', ['tag1', 'tag2']), + ('remove_tags', ['tag1', 'tag2']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1169,8 +1162,7 @@ def test_project_set_with_immutable_option(self): verifylist = [ ('domain', self.project.domain_id), ('immutable', True), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1193,9 +1185,8 @@ def test_project_set_with_no_immutable_option(self): ] verifylist = [ ('domain', self.project.domain_id), - ('no_immutable', True), - ('enable', False), - ('disable', False), + ('immutable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_region.py b/openstackclient/tests/unit/identity/v3/test_region.py index 9b66c3ce9d..eecb079133 100644 --- a/openstackclient/tests/unit/identity/v3/test_region.py +++ b/openstackclient/tests/unit/identity/v3/test_region.py @@ -11,55 +11,44 @@ # under the License. # -import copy + +from openstack.identity.v3 import region as _region +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import region -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestRegion(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the RegionManager Mock - self.regions_mock = self.identity_client.regions - self.regions_mock.reset_mock() - - -class TestRegionCreate(TestRegion): +class TestRegionCreate(identity_fakes.TestIdentityv3): + region = sdk_fakes.generate_fake_resource(_region.Region) columns = ( + 'region', 'description', 'parent_region', - 'region', ) datalist = ( - identity_fakes.region_description, - identity_fakes.region_parent_region_id, - identity_fakes.region_id, + region.id, + region.description, + region.parent_region_id, ) def setUp(self): super().setUp() - self.regions_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ) + self.identity_sdk_client.create_region.return_value = self.region # Get the command object to test self.cmd = region.CreateRegion(self.app, None) def test_region_create_description(self): arglist = [ - identity_fakes.region_id, + self.region.id, '--description', - identity_fakes.region_description, + self.region.description, ] verifylist = [ - ('region', identity_fakes.region_id), - ('description', identity_fakes.region_description), + ('region', self.region.id), + ('description', self.region.description), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -70,21 +59,21 @@ def test_region_create_description(self): # Set expected values kwargs = { - 'description': identity_fakes.region_description, - 'id': identity_fakes.region_id, - 'parent_region': None, + 'description': self.region.description, + 'id': self.region.id, + 'parent_region_id': None, } - self.regions_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_region.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) def test_region_create_no_options(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -96,23 +85,23 @@ def test_region_create_no_options(self): # Set expected values kwargs = { 'description': None, - 'id': identity_fakes.region_id, - 'parent_region': None, + 'id': self.region.id, + 'parent_region_id': None, } - self.regions_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_region.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) def test_region_create_parent_region_id(self): arglist = [ - identity_fakes.region_id, + self.region.id, '--parent-region', - identity_fakes.region_parent_region_id, + self.region.parent_region_id, ] verifylist = [ - ('region', identity_fakes.region_id), - ('parent_region', identity_fakes.region_parent_region_id), + ('region', self.region.id), + ('parent_region', self.region.parent_region_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -124,41 +113,43 @@ def test_region_create_parent_region_id(self): # Set expected values kwargs = { 'description': None, - 'id': identity_fakes.region_id, - 'parent_region': identity_fakes.region_parent_region_id, + 'id': self.region.id, + 'parent_region_id': self.region.parent_region_id, } - self.regions_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_region.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) -class TestRegionDelete(TestRegion): +class TestRegionDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.regions_mock.delete.return_value = None + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.identity_sdk_client.delete_region.return_value = None # Get the command object to test self.cmd = region.DeleteRegion(self.app, None) def test_region_delete_no_options(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', [identity_fakes.region_id]), + ('region', [self.region.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.regions_mock.delete.assert_called_with( - identity_fakes.region_id, + self.identity_sdk_client.delete_region.assert_called_with( + self.region.id, ) self.assertIsNone(result) -class TestRegionList(TestRegion): +class TestRegionList(identity_fakes.TestIdentityv3): + region = sdk_fakes.generate_fake_resource(_region.Region) columns = ( 'Region', 'Parent Region', @@ -166,22 +157,16 @@ class TestRegionList(TestRegion): ) datalist = ( ( - identity_fakes.region_id, - identity_fakes.region_parent_region_id, - identity_fakes.region_description, + region.id, + region.parent_region_id, + region.description, ), ) def setUp(self): super().setUp() - self.regions_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ), - ] + self.identity_sdk_client.regions.return_value = [self.region] # Get the command object to test self.cmd = region.ListRegion(self.app, None) @@ -195,7 +180,7 @@ def test_region_list_no_options(self): # 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.regions_mock.list.assert_called_with() + self.identity_sdk_client.regions.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -203,10 +188,10 @@ def test_region_list_no_options(self): def test_region_list_parent_region_id(self): arglist = [ '--parent-region', - identity_fakes.region_parent_region_id, + self.region.parent_region_id, ] verifylist = [ - ('parent_region', identity_fakes.region_parent_region_id), + ('parent_region', self.region.parent_region_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -214,41 +199,37 @@ def test_region_list_parent_region_id(self): # 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.regions_mock.list.assert_called_with( - parent_region_id=identity_fakes.region_parent_region_id + self.identity_sdk_client.regions.assert_called_with( + parent_region_id=self.region.parent_region_id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) -class TestRegionSet(TestRegion): +class TestRegionSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.regions_mock.update.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ) + self.region = sdk_fakes.generate_fake_resource(_region.Region) # Get the command object to test self.cmd = region.SetRegion(self.app, None) def test_region_set_no_options(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = {} - self.regions_mock.update.assert_called_with( - identity_fakes.region_id, **kwargs + self.identity_sdk_client.update_region.assert_called_with( + self.region.id, **kwargs ) self.assertIsNone(result) @@ -256,11 +237,11 @@ def test_region_set_description(self): arglist = [ '--description', 'qwerty', - identity_fakes.region_id, + self.region.id, ] verifylist = [ ('description', 'qwerty'), - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -270,8 +251,8 @@ def test_region_set_description(self): kwargs = { 'description': 'qwerty', } - self.regions_mock.update.assert_called_with( - identity_fakes.region_id, **kwargs + self.identity_sdk_client.update_region.assert_called_with( + self.region.id, **kwargs ) self.assertIsNone(result) @@ -279,11 +260,11 @@ def test_region_set_parent_region_id(self): arglist = [ '--parent-region', 'new_parent', - identity_fakes.region_id, + self.region.id, ] verifylist = [ ('parent_region', 'new_parent'), - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -291,33 +272,30 @@ def test_region_set_parent_region_id(self): # Set expected values kwargs = { - 'parent_region': 'new_parent', + 'parent_region_id': 'new_parent', } - self.regions_mock.update.assert_called_with( - identity_fakes.region_id, **kwargs + self.identity_sdk_client.update_region.assert_called_with( + self.region.id, **kwargs ) self.assertIsNone(result) -class TestRegionShow(TestRegion): +class TestRegionShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.regions_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = region.ShowRegion(self.app, None) def test_region_show(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -325,15 +303,15 @@ def test_region_show(self): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.regions_mock.get.assert_called_with( - identity_fakes.region_id, + self.identity_sdk_client.get_region.assert_called_with( + self.region.id, ) - collist = ('description', 'parent_region', 'region') + collist = ('region', 'description', 'parent_region') self.assertEqual(collist, columns) datalist = ( - identity_fakes.region_description, - identity_fakes.region_parent_region_id, - identity_fakes.region_id, + self.region.id, + self.region.description, + self.region.parent_region_id, ) self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py index 792096cdeb..a120714ec6 100644 --- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py @@ -168,7 +168,7 @@ def test_registered_limit_delete(self): arglist = [identity_fakes.registered_limit_id] verifylist = [ - ('registered_limit_id', [identity_fakes.registered_limit_id]) + ('registered_limits', [identity_fakes.registered_limit_id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -184,7 +184,7 @@ def test_registered_limit_delete_with_exception(self): self.registered_limit_mock.delete.side_effect = return_value arglist = ['fake-registered-limit-id'] - verifylist = [('registered_limit_id', ['fake-registered-limit-id'])] + verifylist = [('registered_limits', ['fake-registered-limit-id'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) try: diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 09b7536e3b..90b2d7121c 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -13,110 +13,96 @@ # under the License. # -import copy from unittest import mock +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import group as _group +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role as _role +from openstack.identity.v3 import system as _system +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions -from osc_lib import utils -from openstackclient.identity import common from openstackclient.identity.v3 import role -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestRole(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.groups_mock = self.identity_client.groups - self.groups_mock.reset_mock() - - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - - # Get a shortcut to the RoleManager Mock - self.roles_mock = self.identity_client.roles - self.roles_mock.reset_mock() - +class TestRoleInherited(identity_fakes.TestIdentityv3): def _is_inheritance_testcase(self): - return False + return True -class TestRoleInherited(TestRole): +class TestRoleAdd(identity_fakes.TestIdentityv3): def _is_inheritance_testcase(self): - return True + return False + user = sdk_fakes.generate_fake_resource(_user.User) + group = sdk_fakes.generate_fake_resource(_group.Group) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource(_project.Project) -class TestRoleAdd(TestRole): def setUp(self): super().setUp() - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = self.project - self.groups_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.GROUP), - loaded=True, + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, ) - - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.identity_sdk_client.find_role.return_value = self.role + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, + self.identity_sdk_client.assign_domain_role_to_user.return_value = ( + self.role ) - - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.identity_sdk_client.assign_domain_role_to_group.return_value = ( + self.role ) - self.roles_mock.grant.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.identity_sdk_client.assign_project_role_to_user.return_value = ( + self.role + ) + self.identity_sdk_client.assign_project_role_to_group.return_value = ( + self.role + ) + self.identity_sdk_client.assign_system_role_to_user.return_value = ( + self.role + ) + self.identity_sdk_client.assign_system_role_to_group.return_value = ( + self.role ) # Get the command object to test self.cmd = role.AddRole(self.app, None) - def test_role_add_user_system(self): + @mock.patch.object(role.LOG, 'warning') + def test_role_add_user_system(self, mock_warning): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -125,32 +111,36 @@ def test_role_add_user_system(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'role': self.role.id, } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_system_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) + if self._is_inheritance_testcase(): + mock_warning.assert_called_with( + "'--inherited' was given, which is not supported when adding a system role; this will be an error in a future release" + ) + def test_role_add_user_domain(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -159,32 +149,32 @@ def test_role_add_user_domain(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'domain': self.domain.id, + 'user': self.user.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_domain_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_user_project(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -193,33 +183,34 @@ def test_role_add_user_project(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'project': self.project.id, + 'user': self.user.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_project_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - def test_role_add_group_system(self): + @mock.patch.object(role.LOG, 'warning') + def test_role_add_group_system(self, mock_warning): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -228,32 +219,36 @@ def test_role_add_group_system(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'role': self.role.id, } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_system_role_to_group.assert_called_with( + **kwargs ) self.assertIsNone(result) + if self._is_inheritance_testcase(): + mock_warning.assert_called_with( + "'--inherited' was given, which is not supported when adding a system role; this will be an error in a future release" + ) + def test_role_add_group_domain(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), - ('domain', identity_fakes.domain_name), + ('group', self.group.name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -262,32 +257,32 @@ def test_role_add_group_domain(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'domain': self.domain.id, + 'group': self.group.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_domain_role_to_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_group_project(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -296,39 +291,36 @@ def test_role_add_group_project(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'project': self.project.id, + 'group': self.group.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_project_role_to_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_domain_role_on_user_project(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--project', - identity_fakes.project_name, + self.project.name, '--role-domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.ROLE_2['name']), + ('project', self.project.name), + ('role', self.role_with_domain.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -337,26 +329,26 @@ def test_role_add_domain_role_on_user_project(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'project': self.project.id, + 'user': self.user.id, + 'role': self.role_with_domain.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs + self.identity_sdk_client.assign_project_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_with_error(self): arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ ('user', None), ('group', None), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -369,31 +361,47 @@ class TestRoleAddInherited(TestRoleAdd, TestRoleInherited): pass -class TestRoleCreate(TestRole): +class TestRoleCreate(identity_fakes.TestIdentityv3): + collist = ('id', 'name', 'domain_id', 'description') + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + def setUp(self): super().setUp() - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, ) - - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, + ) + self.role_with_description = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description='role description', + ) + self.role_with_immutable_option = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + options={'immutable': True}, ) + self.identity_sdk_client.find_domain.return_value = self.domain # Get the command object to test self.cmd = role.CreateRole(self.app, None) def test_role_create_no_options(self): + self.identity_sdk_client.create_role.return_value = self.role + arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ - ('name', identity_fakes.role_name), + ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -404,39 +412,33 @@ def test_role_create_no_options(self): # Set expected values kwargs = { - 'domain': None, - 'name': identity_fakes.role_name, - 'description': None, - 'options': {}, + 'name': self.role.name, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( + self.role.id, + self.role.name, + None, None, - identity_fakes.role_id, - identity_fakes.role_name, ) self.assertEqual(datalist, data) def test_role_create_with_domain(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.identity_sdk_client.create_role.return_value = ( + self.role_with_domain ) arglist = [ '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] verifylist = [ - ('domain', identity_fakes.domain_name), - ('name', identity_fakes.ROLE_2['name']), + ('domain', self.domain.name), + ('name', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -447,38 +449,34 @@ def test_role_create_with_domain(self): # Set expected values kwargs = { - 'domain': identity_fakes.domain_id, - 'name': identity_fakes.ROLE_2['name'], - 'description': None, - 'options': {}, + 'domain_id': self.domain.id, + 'name': self.role_with_domain.name, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - identity_fakes.domain_id, - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role_with_domain.id, + self.role_with_domain.name, + self.domain.id, + None, ) self.assertEqual(datalist, data) def test_role_create_with_description(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.identity_sdk_client.create_role.return_value = ( + self.role_with_description ) + arglist = [ '--description', - identity_fakes.role_description, - identity_fakes.ROLE_2['name'], + self.role_with_description.description, + self.role_with_description.name, ] verifylist = [ - ('description', identity_fakes.role_description), - ('name', identity_fakes.ROLE_2['name']), + ('description', self.role_with_description.description), + ('name', self.role_with_description.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -489,37 +487,31 @@ def test_role_create_with_description(self): # Set expected values kwargs = { - 'description': identity_fakes.role_description, - 'name': identity_fakes.ROLE_2['name'], - 'domain': None, - 'options': {}, + 'name': self.role_with_description.name, + 'description': self.role_with_description.description, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - 'd1', - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role_with_description.id, + self.role_with_description.name, + None, + self.role_with_description.description, ) self.assertEqual(datalist, data) def test_role_create_with_immutable_option(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.create_role.return_value = self.role + arglist = [ '--immutable', - identity_fakes.ROLE_2['name'], + self.role.name, ] verifylist = [ ('immutable', True), - ('name', identity_fakes.ROLE_2['name']), + ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -531,36 +523,30 @@ def test_role_create_with_immutable_option(self): # Set expected values kwargs = { 'options': {'immutable': True}, - 'description': None, - 'name': identity_fakes.ROLE_2['name'], - 'domain': None, + 'name': self.role.name, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - 'd1', - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role.id, + self.role.name, + None, + None, ) self.assertEqual(datalist, data) def test_role_create_with_no_immutable_option(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.create_role.return_value = self.role + arglist = [ '--no-immutable', - identity_fakes.ROLE_2['name'], + self.role.name, ] verifylist = [ - ('no_immutable', True), - ('name', identity_fakes.ROLE_2['name']), + ('immutable', False), + ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -572,88 +558,94 @@ def test_role_create_with_no_immutable_option(self): # Set expected values kwargs = { 'options': {'immutable': False}, - 'description': None, - 'name': identity_fakes.ROLE_2['name'], - 'domain': None, + 'name': self.role.name, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - 'd1', - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role.id, + self.role.name, + None, + None, ) self.assertEqual(datalist, data) -class TestRoleDelete(TestRole): +class TestRoleDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) - self.roles_mock.delete.return_value = None - # Get the command object to test self.cmd = role.DeleteRole(self.app, None) def test_role_delete_no_options(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.return_value = self.role + self.identity_sdk_client.delete_role.return_value = None + arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ - ('roles', [identity_fakes.role_name]), + ('roles', [self.role.name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.roles_mock.delete.assert_called_with( - identity_fakes.role_id, + self.identity_sdk_client.delete_role.assert_called_with( + role=self.role.id, + ignore_missing=False, ) self.assertIsNone(result) def test_role_delete_with_domain(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) - self.roles_mock.delete.return_value = None + self.identity_sdk_client.find_role.return_value = self.role_with_domain + self.identity_sdk_client.delete_role.return_value = None arglist = [ '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] verifylist = [ - ('roles', [identity_fakes.ROLE_2['name']]), - ('domain', identity_fakes.domain_name), + ('roles', [self.role_with_domain.name]), + ('domain', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.roles_mock.delete.assert_called_with( - identity_fakes.ROLE_2['id'], + self.identity_sdk_client.delete_role.assert_called_with( + role=self.role_with_domain.id, + ignore_missing=False, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_roles_with_exception(self, find_mock): - find_mock.side_effect = [ - self.roles_mock.get.return_value, - exceptions.CommandError, + def test_delete_multi_roles_with_exception(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.side_effect = [ + self.role, + sdk_exc.ResourceNotFound, ] arglist = [ - identity_fakes.role_name, + self.role.id, 'unexist_role', ] verifylist = [ @@ -667,44 +659,62 @@ def test_delete_multi_roles_with_exception(self, find_mock): except exceptions.CommandError as e: self.assertEqual('1 of 2 roles failed to delete.', str(e)) - find_mock.assert_any_call( - self.roles_mock, identity_fakes.role_name, domain_id=None - ) - find_mock.assert_any_call( - self.roles_mock, 'unexist_role', domain_id=None + self.identity_sdk_client.find_role.assert_has_calls( + [ + mock.call( + name_or_id=self.role.id, + ignore_missing=False, + domain_id=None, + ), + mock.call( + name_or_id='unexist_role', + ignore_missing=False, + domain_id=None, + ), + ] ) - self.assertEqual(2, find_mock.call_count) - self.roles_mock.delete.assert_called_once_with(identity_fakes.role_id) + self.assertEqual(2, self.identity_sdk_client.find_role.call_count) + self.identity_sdk_client.delete_role.assert_called_once_with( + role=self.role.id, ignore_missing=False + ) -class TestRoleList(TestRole): +class TestRoleList(identity_fakes.TestIdentityv3): columns = ( 'ID', 'Name', ) - datalist = ( - ( - identity_fakes.role_id, - identity_fakes.role_name, - ), - ) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - self.roles_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ), + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, + ) + self.identity_sdk_client.roles.return_value = [ + self.role, + self.role_with_domain, ] + self.identity_sdk_client.find_domain.return_value = self.domain - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.datalist = ( + ( + self.role.id, + self.role.name, + ), + ( + self.role_with_domain.id, + self.role_with_domain.name, + ), ) # Get the command object to test @@ -720,25 +730,19 @@ def test_role_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.roles_mock.list.assert_called_with() + self.identity_sdk_client.roles.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_role_list_domain_role(self): - self.roles_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ), - ] + self.identity_sdk_client.roles.return_value = [self.role_with_domain] arglist = [ '--domain', - identity_fakes.domain_name, + self.domain.name, ] verifylist = [ - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -748,56 +752,51 @@ def test_role_list_domain_role(self): columns, data = self.cmd.take_action(parsed_args) # Set expected values - kwargs = {'domain_id': identity_fakes.domain_id} - # RoleManager.list(user=, group=, domain=, project=, **kwargs) - self.roles_mock.list.assert_called_with(**kwargs) + kwargs = {'domain_id': self.domain.id} + self.identity_sdk_client.roles.assert_called_with(**kwargs) collist = ('ID', 'Name', 'Domain') self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], - identity_fakes.domain_name, + self.role_with_domain.id, + self.role_with_domain.name, + self.domain.name, ), ) self.assertEqual(datalist, tuple(data)) -class TestRoleRemove(TestRole): - def setUp(self): - super().setUp() - - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) +class TestRoleRemove(identity_fakes.TestIdentityv3): + def _is_inheritance_testcase(self): + return False - self.groups_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.GROUP), - loaded=True, - ) + user = sdk_fakes.generate_fake_resource(_user.User) + group = sdk_fakes.generate_fake_resource(_group.Group) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource(_project.Project) + system = sdk_fakes.generate_fake_resource(_system.System) - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, - ) + def setUp(self): + super().setUp() - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, ) + self.identity_sdk_client.find_role.return_value = self.role + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = self.project - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) - self.roles_mock.revoke.return_value = None + self.identity_sdk_client.unassign_domain_role_from_user.return_value = None + self.identity_sdk_client.unassign_domain_role_from_group.return_value = None + self.identity_sdk_client.unassign_project_role_from_user.return_value = None + self.identity_sdk_client.unassign_project_role_from_group.return_value = None + self.identity_sdk_client.unassign_system_role_from_user.return_value = None + self.identity_sdk_client.unassign_system_role_from_group.return_value = None # Get the command object to test self.cmd = role.RemoveRole(self.app, None) @@ -805,20 +804,20 @@ def setUp(self): def test_role_remove_user_system(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -827,40 +826,39 @@ def test_role_remove_user_system(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, + 'user': self.user.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_user') - def test_role_remove_non_existent_user_system(self, find_mock): - # Simulate the user not being in keystone, the client should gracefully + def test_role_remove_non_existent_user_system(self): + # Simulate the user not being in keystone; the client should gracefully # handle this exception and send the request to remove the role since # keystone supports removing role assignments with non-existent actors # (e.g., users or groups). - find_mock.side_effect = exceptions.CommandError - + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--user', - identity_fakes.user_id, + self.user.id, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_id), + ('user', self.user.id), ('group', None), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -869,32 +867,31 @@ def test_role_remove_non_existent_user_system(self, find_mock): # Set expected values kwargs = { - 'user': identity_fakes.user_id, + 'user': self.user.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_user_domain(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -903,39 +900,39 @@ def test_role_remove_user_domain(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_user') - def test_role_remove_non_existent_user_domain(self, find_mock): + def test_role_remove_non_existent_user_domain(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError - + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--user', - identity_fakes.user_id, + self.user.id, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_id), + ('user', self.user.id), ('group', None), ('system', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -944,32 +941,32 @@ def test_role_remove_non_existent_user_domain(self, find_mock): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_user_project(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -978,39 +975,40 @@ def test_role_remove_user_project(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_user') - def test_role_remove_non_existent_user_project(self, find_mock): + def test_role_remove_non_existent_user_project(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--user', - identity_fakes.user_id, + self.user.id, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_id), + ('user', self.user.id), ('group', None), ('system', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1019,34 +1017,34 @@ def test_role_remove_non_existent_user_project(self, find_mock): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_group_system(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), - ('role', identity_fakes.role_name), + ('role', self.role.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1055,39 +1053,39 @@ def test_role_remove_group_system(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, + 'group': self.group.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_group') - def test_role_remove_non_existent_group_system(self, find_mock): + def test_role_remove_non_existent_group_system(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError + self.identity_sdk_client.find_group.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--group', - identity_fakes.group_id, + self.group.id, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_id), + ('group', self.group.id), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1096,33 +1094,32 @@ def test_role_remove_non_existent_group_system(self, find_mock): # Set expected values kwargs = { - 'group': identity_fakes.group_id, + 'group': self.group.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_group_domain(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), - ('domain', identity_fakes.domain_name), + ('group', self.group.name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), - ('role', identity_fakes.role_name), + ('role', self.role.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1131,39 +1128,40 @@ def test_role_remove_group_domain(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_group') - def test_role_remove_non_existent_group_domain(self, find_mock): + def test_role_remove_non_existent_group_domain(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError + self.identity_sdk_client.find_group.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--group', - identity_fakes.group_id, + self.group.id, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_id), + ('group', self.group.id), ('system', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1172,32 +1170,32 @@ def test_role_remove_non_existent_group_domain(self, find_mock): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_group_project(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1206,39 +1204,39 @@ def test_role_remove_group_project(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_group') - def test_role_remove_non_existent_group_project(self, find_mock): + def test_role_remove_non_existent_group_project(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError - + self.identity_sdk_client.find_group.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--group', - identity_fakes.group_id, + self.group.id, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_id), + ('group', self.group.id), ('system', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1247,37 +1245,38 @@ def test_role_remove_non_existent_group_project(self, find_mock): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_domain_role_on_group_domain(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), - ('domain', identity_fakes.domain_name), + ('group', self.group.name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.ROLE_2['name']), + ('role', self.role_with_domain.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1286,26 +1285,26 @@ def test_role_remove_domain_role_on_group_domain(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'domain': self.domain.id, + 'role': self.role_with_domain.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs + self.identity_sdk_client.unassign_domain_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_with_error(self): arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ ('user', None), ('group', None), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1314,29 +1313,37 @@ def test_role_remove_with_error(self): ) -class TestRoleSet(TestRole): +class TestRoleSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) - self.roles_mock.update.return_value = None # Get the command object to test self.cmd = role.SetRole(self.app, None) def test_role_set_no_options(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.return_value = self.role + self.identity_sdk_client.update_role.return_value = self.role + arglist = [ '--name', 'over', - identity_fakes.role_name, + self.role.name, ] verifylist = [ ('name', 'over'), - ('role', identity_fakes.role_name), + ('role', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1345,32 +1352,31 @@ def test_role_set_no_options(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, - 'options': {}, + 'role': self.role.id, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.role_id, **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_domain_role(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.domain2 = sdk_fakes.generate_fake_resource(_domain.Domain) + self.identity_sdk_client.find_domain.return_value = self.domain2 + + self.identity_sdk_client.find_role.return_value = self.role_with_domain + self.identity_sdk_client.update_role.return_value = ( + self.role_with_domain ) + arglist = [ '--name', 'over', '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain2.name, + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), - ('domain', identity_fakes.domain_name), - ('role', identity_fakes.ROLE_2['name']), + ('domain', self.domain2.name), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1379,32 +1385,26 @@ def test_role_set_domain_role(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, - 'options': {}, + 'role': self.role_with_domain.id, + 'domain_id': self.domain2.id, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_description(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--name', 'over', '--description', - identity_fakes.role_description, - identity_fakes.ROLE_2['name'], + 'role description', + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), - ('description', identity_fakes.role_description), - ('role', identity_fakes.ROLE_2['name']), + ('description', 'role description'), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1413,31 +1413,25 @@ def test_role_set_description(self): # Set expected values kwargs = { 'name': 'over', - 'description': identity_fakes.role_description, - 'options': {}, + 'description': 'role description', + 'role': self.role_with_domain.id, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_with_immutable(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--name', 'over', '--immutable', - identity_fakes.ROLE_2['name'], + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), ('immutable', True), - ('role', identity_fakes.ROLE_2['name']), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1446,31 +1440,25 @@ def test_role_set_with_immutable(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, + 'role': self.role_with_domain.id, 'options': {'immutable': True}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_with_no_immutable(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--name', 'over', '--no-immutable', - identity_fakes.ROLE_2['name'], + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), - ('no_immutable', True), - ('role', identity_fakes.ROLE_2['name']), + ('immutable', False), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1479,35 +1467,37 @@ def test_role_set_with_no_immutable(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, + 'role': self.role_with_domain.id, 'options': {'immutable': False}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) -class TestRoleShow(TestRole): +class TestRoleShow(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + def setUp(self): super().setUp() - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) + self.identity_sdk_client.find_domain.return_value = self.domain # Get the command object to test self.cmd = role.ShowRole(self.app, None) def test_role_show(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.return_value = self.role + arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ - ('role', identity_fakes.role_name), + ('role', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1516,34 +1506,38 @@ def test_role_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # RoleManager.get(role) - self.roles_mock.get.assert_called_with( - identity_fakes.role_name, + self.identity_sdk_client.find_role.assert_called_with( + name_or_id=self.role.name, + domain_id=None, + ignore_missing=False, ) - collist = ('domain', 'id', 'name') + collist = ('id', 'name', 'domain_id', 'description') self.assertEqual(collist, columns) datalist = ( + self.role.id, + self.role.name, + None, None, - identity_fakes.role_id, - identity_fakes.role_name, ) self.assertEqual(datalist, data) def test_role_show_domain_role(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.id, ] verifylist = [ - ('domain', identity_fakes.domain_name), - ('role', identity_fakes.ROLE_2['name']), + ('domain', self.domain.name), + ('role', self.role_with_domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1552,23 +1546,18 @@ def test_role_show_domain_role(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # RoleManager.get(role). This is called from utils.find_resource(). - # In fact, the current implementation calls the get(role) first with - # just the name, then with the name+domain_id. So technically we should - # mock this out with a call list, with the first call returning None - # and the second returning the object. However, if we did that we are - # then just testing the current sequencing within the utils method, and - # would become brittle to changes within that method. Hence we just - # check for the first call which is always lookup by name. - self.roles_mock.get.assert_called_with( - identity_fakes.ROLE_2['name'], + self.identity_sdk_client.find_role.assert_called_with( + name_or_id=self.role_with_domain.id, + domain_id=self.domain.id, + ignore_missing=False, ) - collist = ('domain', 'id', 'name') + collist = ('id', 'name', 'domain_id', 'description') self.assertEqual(collist, columns) datalist = ( - identity_fakes.domain_id, - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role_with_domain.id, + self.role_with_domain.name, + self.domain.id, + None, ) self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py index ec7c7da560..6fc66469c2 100644 --- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -165,10 +165,13 @@ def test_role_assignment_list_user(self): arglist = ['--user', self.user.name] verifylist = [ ('user', self.user.name), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -181,6 +184,79 @@ def test_role_assignment_list_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_user.assert_called_with( + name_or_id=self.user.name, ignore_missing=False, domain_id=None + ) + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=self.user.id, + group_id=None, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.role.id, + self.user.id, + '', + '', + self.domain.id, + '', + False, + ), + ( + self.role.id, + self.user.id, + '', + self.project.id, + '', + '', + False, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_user_with_domain(self): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_domain_id_and_user_id, + self.assignment_with_project_id_and_user_id, + ] + + arglist = ['--user', self.user.name, '--user-domain', self.domain.name] + verifylist = [ + ('user', self.user.name), + ('user_domain', self.domain.name), + ('group', None), + ('group_domain', None), + ('system', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', None), + ('inherited', False), + ('names', 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.identity_sdk_client.find_domain.assert_called_with( + name_or_id=self.domain.name, ignore_missing=False + ) + self.identity_sdk_client.find_user.assert_called_with( + name_or_id=self.user.name, + ignore_missing=False, + domain_id=self.domain.id, + ) self.identity_sdk_client.role_assignments.assert_called_with( role_id=None, user_id=self.user.id, @@ -230,10 +306,13 @@ def test_role_assignment_list_user_not_found(self, find_mock): arglist = ['--user', self.user.id] verifylist = [ ('user', self.user.id), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -290,10 +369,13 @@ def test_role_assignment_list_group(self): arglist = ['--group', self.group.name] verifylist = [ ('user', None), + ('user_domain', None), ('group', self.group.name), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -306,6 +388,85 @@ def test_role_assignment_list_group(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_group.assert_called_with( + name_or_id=self.group.name, ignore_missing=False, domain_id=None + ) + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=self.group.id, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.role.id, + '', + self.group.id, + '', + self.domain.id, + '', + False, + ), + ( + self.role.id, + '', + self.group.id, + self.project.id, + '', + '', + False, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_group_with_domain(self): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_domain_id_and_group_id, + self.assignment_with_project_id_and_group_id, + ] + + arglist = [ + '--group', + self.group.name, + '--group-domain', + self.domain.name, + ] + verifylist = [ + ('user', None), + ('user_domain', None), + ('group', self.group.name), + ('group_domain', self.domain.name), + ('system', None), + ('domain', None), + ('project', None), + ('project_domain', None), + ('role', None), + ('effective', None), + ('inherited', False), + ('names', 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.identity_sdk_client.find_domain.assert_called_with( + name_or_id=self.domain.name, ignore_missing=False + ) + self.identity_sdk_client.find_group.assert_called_with( + name_or_id=self.group.name, + ignore_missing=False, + domain_id=self.domain.id, + ) self.identity_sdk_client.role_assignments.assert_called_with( role_id=None, user_id=None, @@ -350,10 +511,13 @@ def test_role_assignment_list_domain(self): arglist = ['--domain', self.domain.name] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', self.domain.name), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -410,10 +574,13 @@ def test_role_assignment_list_project(self): arglist = ['--project', self.project.name] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', self.project.name), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -426,6 +593,85 @@ def test_role_assignment_list_project(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_project.assert_called_with( + name_or_id=self.project.name, ignore_missing=False, domain_id=None + ) + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=None, + scope_project_id=self.project.id, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.role.id, + self.user.id, + '', + self.project.id, + '', + '', + False, + ), + ( + self.role.id, + '', + self.group.id, + self.project.id, + '', + '', + False, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_project_with_domain(self): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_project_id_and_user_id, + self.assignment_with_project_id_and_group_id, + ] + + arglist = [ + '--project', + self.project.name, + '--project-domain', + self.domain.name, + ] + verifylist = [ + ('user', None), + ('user_domain', None), + ('group', None), + ('group_domain', None), + ('system', None), + ('domain', None), + ('project', self.project.name), + ('project_domain', self.domain.name), + ('role', None), + ('effective', None), + ('inherited', False), + ('names', 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.identity_sdk_client.find_domain.assert_called_with( + name_or_id=self.domain.name, ignore_missing=False + ) + self.identity_sdk_client.find_project.assert_called_with( + name_or_id=self.project.name, + ignore_missing=False, + domain_id=self.domain.id, + ) self.identity_sdk_client.role_assignments.assert_called_with( role_id=None, user_id=None, @@ -462,7 +708,8 @@ def test_role_assignment_list_project(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_def_creds(self): - auth_ref = self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref = mock.Mock() + auth_ref = self.app.client_manager.auth_ref auth_ref.project_id.return_value = self.project.id auth_ref.user_id.return_value = self.user.id @@ -476,10 +723,13 @@ def test_role_assignment_list_def_creds(self): ] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -529,10 +779,13 @@ def test_role_assignment_list_effective(self): arglist = ['--effective'] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', True), ('inherited', False), @@ -611,10 +864,13 @@ def test_role_assignment_list_inherited(self): arglist = ['--inherited'] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', True), @@ -707,10 +963,13 @@ def test_role_assignment_list_include_names(self): arglist = ['--names'] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -799,10 +1058,13 @@ def test_role_assignment_list_domain_role(self): ] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', role_2.name), ('effective', None), ('inherited', False), diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py index d447121244..d6dd0287b1 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -24,11 +24,11 @@ class TestServiceCreate(identity_fakes.TestIdentityv3): columns = ( - 'ID', - 'Name', - 'Type', - 'Enabled', - 'Description', + 'id', + 'name', + 'type', + 'enabled', + 'description', ) def setUp(self): @@ -311,7 +311,6 @@ def test_service_set_type(self): # Set expected values kwargs = { 'type': self.service.type, - 'is_enabled': None, } self.identity_sdk_client.update_service.assert_called_with( self.service.id, **kwargs @@ -338,7 +337,6 @@ def test_service_set_name(self): # Set expected values kwargs = { 'name': self.service.name, - 'is_enabled': None, } self.identity_sdk_client.update_service.assert_called_with( self.service.id, **kwargs @@ -365,7 +363,6 @@ def test_service_set_description(self): # Set expected values kwargs = { 'description': self.service.description, - 'is_enabled': None, } self.identity_sdk_client.update_service.assert_called_with( self.service.id, **kwargs @@ -455,7 +452,7 @@ def test_service_show(self): self.service.name, ignore_missing=False ) - collist = ('ID', 'Name', 'Type', 'Enabled', 'Description') + collist = ('id', 'name', 'type', 'enabled', 'description') self.assertEqual(collist, columns) datalist = ( self.service.id, diff --git a/openstackclient/tests/unit/identity/v3/test_service_provider.py b/openstackclient/tests/unit/identity/v3/test_service_provider.py index 23402d9486..185cdc6b03 100644 --- a/openstackclient/tests/unit/identity/v3/test_service_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_service_provider.py @@ -12,92 +12,88 @@ # License for the specific language governing permissions and limitations # under the License. -import copy + +from openstack.identity.v3 import service_provider as _service_provider +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import service_provider -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as service_fakes -class TestServiceProvider(service_fakes.TestFederatedIdentity): - def setUp(self): - super().setUp() - - federation_lib = self.identity_client.federation - self.service_providers_mock = federation_lib.service_providers - self.service_providers_mock.reset_mock() - - -class TestServiceProviderCreate(TestServiceProvider): +class TestServiceProviderCreate(service_fakes.TestFederatedIdentity): columns = ( - 'auth_url', - 'description', - 'enabled', 'id', + 'enabled', + 'description', + 'auth_url', 'sp_url', - ) - datalist = ( - service_fakes.sp_auth_url, - service_fakes.sp_description, - True, - service_fakes.sp_id, - service_fakes.service_provider_url, + 'relay_state_prefix', ) def setUp(self): super().setUp() - copied_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) - resource = fakes.FakeResource(None, copied_sp, loaded=True) - self.service_providers_mock.create.return_value = resource + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider + ) + self.identity_sdk_client.create_service_provider.return_value = ( + self.service_provider + ) + self.data = ( + self.service_provider.id, + self.service_provider.is_enabled, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, + ) self.cmd = service_provider.CreateServiceProvider(self.app, None) def test_create_service_provider_required_options_only(self): arglist = [ '--auth-url', - service_fakes.sp_auth_url, + self.service_provider.auth_url, '--service-provider-url', - service_fakes.service_provider_url, - service_fakes.sp_id, + self.service_provider.sp_url, + self.service_provider.id, ] verifylist = [ - ('auth_url', service_fakes.sp_auth_url), - ('service_provider_url', service_fakes.service_provider_url), - ('service_provider_id', service_fakes.sp_id), + ('auth_url', self.service_provider.auth_url), + ('service_provider_url', self.service_provider.sp_url), + ('service_provider_id', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, - 'description': None, - 'auth_url': service_fakes.sp_auth_url, - 'sp_url': service_fakes.service_provider_url, + 'is_enabled': True, + 'auth_url': self.service_provider.auth_url, + 'sp_url': self.service_provider.sp_url, } - self.service_providers_mock.create.assert_called_with( - id=service_fakes.sp_id, **kwargs + self.identity_sdk_client.create_service_provider.assert_called_with( + id=self.service_provider.id, **kwargs ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_create_service_provider_description(self): arglist = [ '--description', - service_fakes.sp_description, + self.service_provider.description, '--auth-url', - service_fakes.sp_auth_url, + self.service_provider.auth_url, '--service-provider-url', - service_fakes.service_provider_url, - service_fakes.sp_id, + self.service_provider.sp_url, + self.service_provider.id, ] verifylist = [ - ('description', service_fakes.sp_description), - ('auth_url', service_fakes.sp_auth_url), - ('service_provider_url', service_fakes.service_provider_url), - ('service_provider_id', service_fakes.sp_id), + ('description', self.service_provider.description), + ('auth_url', self.service_provider.auth_url), + ('service_provider_url', self.service_provider.sp_url), + ('service_provider_id', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -105,111 +101,85 @@ def test_create_service_provider_description(self): # Set expected values kwargs = { - 'description': service_fakes.sp_description, - 'auth_url': service_fakes.sp_auth_url, - 'sp_url': service_fakes.service_provider_url, - 'enabled': True, + 'description': self.service_provider.description, + 'auth_url': self.service_provider.auth_url, + 'sp_url': self.service_provider.sp_url, + 'is_enabled': self.service_provider.is_enabled, } - self.service_providers_mock.create.assert_called_with( - id=service_fakes.sp_id, **kwargs + self.identity_sdk_client.create_service_provider.assert_called_with( + id=self.service_provider.id, **kwargs ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_create_service_provider_disabled(self): - # Prepare FakeResource object - service_provider = copy.deepcopy(service_fakes.SERVICE_PROVIDER) - service_provider['enabled'] = False - service_provider['description'] = None - - resource = fakes.FakeResource(None, service_provider, loaded=True) - self.service_providers_mock.create.return_value = resource - arglist = [ '--auth-url', - service_fakes.sp_auth_url, + self.service_provider.auth_url, '--service-provider-url', - service_fakes.service_provider_url, + self.service_provider.sp_url, '--disable', - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('auth_url', service_fakes.sp_auth_url), - ('service_provider_url', service_fakes.service_provider_url), - ('service_provider_id', service_fakes.sp_id), + ('auth_url', self.service_provider.auth_url), + ('service_provider_url', self.service_provider.sp_url), + ('service_provider_id', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'auth_url': service_fakes.sp_auth_url, - 'sp_url': service_fakes.service_provider_url, - 'enabled': False, - 'description': None, + 'auth_url': self.service_provider.auth_url, + 'sp_url': self.service_provider.sp_url, + 'is_enabled': False, } - self.service_providers_mock.create.assert_called_with( - id=service_fakes.sp_id, **kwargs + self.identity_sdk_client.create_service_provider.assert_called_with( + id=self.service_provider.id, **kwargs ) self.assertEqual(self.columns, columns) - datalist = ( - service_fakes.sp_auth_url, - None, - False, - service_fakes.sp_id, - service_fakes.service_provider_url, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.data, data) -class TestServiceProviderDelete(TestServiceProvider): +class TestServiceProviderDelete(service_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.service_providers_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider ) - - self.service_providers_mock.delete.return_value = None + self.identity_sdk_client.delete_service_provider.return_value = None self.cmd = service_provider.DeleteServiceProvider(self.app, None) def test_delete_service_provider(self): arglist = [ - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', [service_fakes.sp_id]), + ('service_provider', [self.service_provider.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.service_providers_mock.delete.assert_called_with( - service_fakes.sp_id, + self.identity_sdk_client.delete_service_provider.assert_called_with( + self.service_provider.id, ) self.assertIsNone(result) -class TestServiceProviderList(TestServiceProvider): +class TestServiceProviderList(service_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - self.service_providers_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider ) - self.service_providers_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ), + self.identity_sdk_client.service_providers.return_value = [ + self.service_provider ] # Get the command object to test @@ -225,39 +195,56 @@ def test_service_provider_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.service_providers_mock.list.assert_called_with() + self.identity_sdk_client.service_providers.assert_called_with() - collist = ('ID', 'Enabled', 'Description', 'Auth URL') + collist = ( + 'ID', + 'Enabled', + 'Description', + 'Auth URL', + 'Service Provider URL', + 'Relay State Prefix', + ) self.assertEqual(collist, columns) datalist = ( ( - service_fakes.sp_id, + self.service_provider.id, True, - service_fakes.sp_description, - service_fakes.sp_auth_url, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, ), ) self.assertEqual(tuple(data), datalist) -class TestServiceProviderSet(TestServiceProvider): +class TestServiceProviderSet(service_fakes.TestFederatedIdentity): columns = ( - 'auth_url', - 'description', - 'enabled', 'id', + 'enabled', + 'description', + 'auth_url', 'sp_url', - ) - datalist = ( - service_fakes.sp_auth_url, - service_fakes.sp_description, - False, - service_fakes.sp_id, - service_fakes.service_provider_url, + 'relay_state_prefix', ) def setUp(self): super().setUp() + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider + ) + self.identity_sdk_client.update_service_provider.return_value = ( + self.service_provider + ) + self.data = ( + self.service_provider.id, + self.service_provider.is_enabled, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, + ) self.cmd = service_provider.SetServiceProvider(self.app, None) def test_service_provider_disable(self): @@ -265,144 +252,107 @@ def test_service_provider_disable(self): Set Service Provider's ``enabled`` attribute to False. """ - - def prepare(self): - """Prepare fake return objects before the test is executed""" - updated_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) - updated_sp['enabled'] = False - resources = fakes.FakeResource(None, updated_sp, loaded=True) - self.service_providers_mock.update.return_value = resources - - prepare(self) arglist = [ '--disable', - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), - ('enable', False), - ('disable', True), + ('service_provider', self.service_provider.id), + ('is_enabled', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.service_providers_mock.update.assert_called_with( - service_fakes.sp_id, - enabled=False, - description=None, - auth_url=None, - sp_url=None, + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.update_service_provider.assert_called_with( + self.service_provider.id, + is_enabled=False, ) + self.assertEqual(columns, self.columns) + self.assertEqual(data, self.data) def test_service_provider_enable(self): """Enable Service Provider. Set Service Provider's ``enabled`` attribute to True. """ - - def prepare(self): - """Prepare fake return objects before the test is executed""" - resources = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ) - self.service_providers_mock.update.return_value = resources - - prepare(self) arglist = [ '--enable', - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), - ('enable', True), - ('disable', False), + ('service_provider', self.service_provider.id), + ('is_enabled', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.cmd.take_action(parsed_args) - self.service_providers_mock.update.assert_called_with( - service_fakes.sp_id, - enabled=True, - description=None, - auth_url=None, - sp_url=None, + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.update_service_provider.assert_called_with( + self.service_provider.id, + is_enabled=True, ) + self.assertEqual(columns, self.columns) + self.assertEqual(data, self.data) def test_service_provider_no_options(self): - def prepare(self): - """Prepare fake return objects before the test is executed""" - resources = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ) - self.service_providers_mock.get.return_value = resources - - resources = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ) - self.service_providers_mock.update.return_value = resources - - prepare(self) arglist = [ - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), + ('service_provider', self.service_provider.id), ('description', None), - ('enable', False), - ('disable', False), + ('is_enabled', None), ('auth_url', None), ('service_provider_url', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(columns, self.columns) + self.assertEqual(data, self.data) -class TestServiceProviderShow(TestServiceProvider): +class TestServiceProviderShow(service_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - ret = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider + ) + self.identity_sdk_client.find_service_provider.return_value = ( + self.service_provider + ) + self.data = ( + self.service_provider.id, + self.service_provider.is_enabled, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, ) - self.service_providers_mock.get.side_effect = [ - Exception("Not found"), - ret, - ] - self.service_providers_mock.get.return_value = ret # Get the command object to test self.cmd = service_provider.ShowServiceProvider(self.app, None) def test_service_provider_show(self): arglist = [ - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), + ('service_provider', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.service_providers_mock.get.assert_called_with( - service_fakes.sp_id, id='BETA' + self.identity_sdk_client.find_service_provider.assert_called_with( + self.service_provider.id, + ignore_missing=False, ) - collist = ('auth_url', 'description', 'enabled', 'id', 'sp_url') - self.assertEqual(collist, columns) - datalist = ( - service_fakes.sp_auth_url, - service_fakes.sp_description, - True, - service_fakes.sp_id, - service_fakes.service_provider_url, + collist = ( + 'id', + 'enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', ) - self.assertEqual(data, datalist) + self.assertEqual(collist, columns) + self.assertEqual(data, self.data) diff --git a/openstackclient/tests/unit/identity/v3/test_token.py b/openstackclient/tests/unit/identity/v3/test_token.py index 96375ea35c..f8d09b72cc 100644 --- a/openstackclient/tests/unit/identity/v3/test_token.py +++ b/openstackclient/tests/unit/identity/v3/test_token.py @@ -11,24 +11,12 @@ # 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 openstackclient.identity.v3 import token from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestToken(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the Auth Ref Mock - self.ar_mock = mock.PropertyMock() - type(self.app.client_manager).auth_ref = self.ar_mock - - -class TestTokenIssue(TestToken): +class TestTokenIssue(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() @@ -38,8 +26,7 @@ def test_token_issue_with_project_id(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_PROJECT_ID, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -64,8 +51,7 @@ def test_token_issue_with_domain_id(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_DOMAIN_ID, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -90,8 +76,7 @@ def test_token_issue_with_unscoped(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.UNSCOPED_TOKEN, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -114,7 +99,7 @@ def test_token_issue_with_unscoped(self): self.assertEqual(datalist, data) -class TestTokenRevoke(TestToken): +class TestTokenRevoke(identity_fakes.TestIdentityv3): TOKEN = 'fob' def setUp(self): diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index fb7ac9b0c6..5c14b7ad98 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -11,58 +11,36 @@ # under the License. # -import copy from unittest import mock from osc_lib import exceptions -from osc_lib import utils + +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role as _role +from openstack.identity.v3 import trust as _trust +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import trust -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestTrust(identity_fakes.TestIdentityv3): +class TestTrustCreate(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.trusts_mock = self.identity_client.trusts - self.trusts_mock.reset_mock() - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - self.roles_mock = self.identity_client.roles - self.roles_mock.reset_mock() - + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.create_trust.return_value = self.trust -class TestTrustCreate(TestTrust): - def setUp(self): - super().setUp() + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.identity_sdk_client.find_project.return_value = self.project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client.find_user.return_value = self.user - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) - - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) - - self.trusts_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ) + self.role = sdk_fakes.generate_fake_resource(_role.Role) + self.identity_sdk_client.find_role.return_value = self.role # Get the command object to test self.cmd = trust.CreateTrust(self.app, None) @@ -70,18 +48,17 @@ def setUp(self): def test_trust_create_basic(self): arglist = [ '--project', - identity_fakes.project_id, + self.project.id, '--role', - identity_fakes.role_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.role.id, + self.user.id, + self.user.id, ] verifylist = [ - ('project', identity_fakes.project_id), - ('impersonate', False), - ('role', [identity_fakes.role_id]), - ('trustor', identity_fakes.user_id), - ('trustee', identity_fakes.user_id), + ('project', self.project.id), + ('roles', [self.role.id]), + ('trustor', self.user.id), + ('trustee', self.user.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -92,76 +69,74 @@ def test_trust_create_basic(self): # Set expected values kwargs = { + 'project_id': self.project.id, + 'roles': [{'id': self.role.id}], 'impersonation': False, - 'project': identity_fakes.project_id, - 'role_ids': [identity_fakes.role_id], - 'expires_at': None, } # TrustManager.create(trustee_id, trustor_id, impersonation=, # project=, role_names=, expires_at=) - self.trusts_mock.create.assert_called_with( - identity_fakes.user_id, identity_fakes.user_id, **kwargs + self.identity_sdk_client.create_trust.assert_called_with( + trustor_user_id=self.user.id, + trustee_user_id=self.user.id, + **kwargs, ) collist = ( 'expires_at', 'id', - 'impersonation', + 'is_impersonation', 'project_id', + 'redelegated_trust_id', + 'redelegation_count', + 'remaining_uses', 'roles', 'trustee_user_id', 'trustor_user_id', ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.trust_expires, - identity_fakes.trust_id, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.expires_at, + self.trust.id, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.redelegated_trust_id, + self.trust.redelegation_count, + self.trust.remaining_uses, + self.trust.roles, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ) self.assertEqual(datalist, data) -class TestTrustDelete(TestTrust): +class TestTrustDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.trusts_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ) - self.trusts_mock.delete.return_value = None + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.delete_trust.return_value = None + self.identity_sdk_client.find_trust.return_value = self.trust # Get the command object to test self.cmd = trust.DeleteTrust(self.app, None) def test_trust_delete(self): arglist = [ - identity_fakes.trust_id, + self.trust.id, ] - verifylist = [('trust', [identity_fakes.trust_id])] + verifylist = [('trust', [self.trust.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.trusts_mock.delete.assert_called_with( - identity_fakes.trust_id, + self.identity_sdk_client.delete_trust.assert_called_with( + self.trust.id, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_trusts_with_exception(self, find_mock): - find_mock.side_effect = [ - self.trusts_mock.get.return_value, - exceptions.CommandError, - ] + def test_delete_multi_trusts_with_exception(self): arglist = [ - identity_fakes.trust_id, + self.trust.id, 'unexist_trust', ] verifylist = [ @@ -169,32 +144,37 @@ def test_delete_multi_trusts_with_exception(self, find_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.identity_sdk_client.find_trust.side_effect = [ + self.trust, + sdk_exceptions.ResourceNotFound, + ] + try: self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual('1 of 2 trusts failed to delete.', str(e)) - find_mock.assert_any_call(self.trusts_mock, identity_fakes.trust_id) - find_mock.assert_any_call(self.trusts_mock, 'unexist_trust') - - self.assertEqual(2, find_mock.call_count) - self.trusts_mock.delete.assert_called_once_with( - identity_fakes.trust_id + self.identity_sdk_client.find_trust.assert_has_calls( + [ + mock.call(self.trust.id, ignore_missing=False), + mock.call('unexist_trust', ignore_missing=False), + ] + ) + self.identity_sdk_client.delete_trust.assert_called_once_with( + self.trust.id ) -class TestTrustList(TestTrust): +class TestTrustList(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.trusts_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ), - ] + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.trusts.return_value = [self.trust] + + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client.find_user.return_value = self.user # Get the command object to test self.cmd = trust.ListTrust(self.app, None) @@ -209,9 +189,9 @@ def test_trust_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_called_with( - trustor_user=None, - trustee_user=None, + self.identity_sdk_client.trusts.assert_called_with( + trustor_user_id=None, + trustee_user_id=None, ) collist = ( @@ -225,19 +205,19 @@ def test_trust_list_no_options(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) def test_trust_list_auth_user(self): - auth_ref = self.app.client_manager.auth_ref = mock.Mock() - auth_ref.user_id.return_value = identity_fakes.user_id + self.app.client_manager.auth_ref = mock.Mock() + auth_ref = self.app.client_manager.auth_ref arglist = ['--auth-user'] verifylist = [ @@ -252,11 +232,11 @@ def test_trust_list_auth_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_any_call( - trustor_user=self.users_mock.get() - ) - self.trusts_mock.list.assert_any_call( - trustee_user=self.users_mock.get() + self.identity_sdk_client.trusts.assert_has_calls( + [ + mock.call(trustor_user_id=auth_ref.user_id), + mock.call(trustee_user_id=auth_ref.user_id), + ] ) collist = ( @@ -270,21 +250,21 @@ def test_trust_list_auth_user(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) def test_trust_list_trustee(self): - arglist = ['--trustee', identity_fakes.user_name] + arglist = ['--trustee', self.user.name] verifylist = [ ('trustor', None), - ('trustee', identity_fakes.user_name), + ('trustee', self.user.name), ('authuser', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -294,9 +274,9 @@ def test_trust_list_trustee(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_any_call( - trustee_user=self.users_mock.get(), - trustor_user=None, + self.identity_sdk_client.trusts.assert_called_with( + trustee_user_id=self.user.id, + trustor_user_id=None, ) collist = ( @@ -310,21 +290,21 @@ def test_trust_list_trustee(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) def test_trust_list_trustor(self): - arglist = ['--trustor', identity_fakes.user_name] + arglist = ['--trustor', self.user.name] verifylist = [ ('trustee', None), - ('trustor', identity_fakes.user_name), + ('trustor', self.user.name), ('authuser', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -334,9 +314,9 @@ def test_trust_list_trustor(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_any_call( - trustor_user=self.users_mock.get(), - trustee_user=None, + self.identity_sdk_client.trusts.assert_called_once_with( + trustor_user_id=self.user.id, + trustee_user_id=None, ) collist = ( @@ -350,36 +330,33 @@ def test_trust_list_trustor(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) -class TestTrustShow(TestTrust): +class TestTrustShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.trusts_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ) + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.find_trust.return_value = self.trust # Get the command object to test self.cmd = trust.ShowTrust(self.app, None) def test_trust_show(self): arglist = [ - identity_fakes.trust_id, + self.trust.id, ] verifylist = [ - ('trust', identity_fakes.trust_id), + ('trust', self.trust.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -388,25 +365,33 @@ def test_trust_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.get.assert_called_with(identity_fakes.trust_id) + self.identity_sdk_client.find_trust.assert_called_with( + self.trust.id, ignore_missing=False + ) collist = ( 'expires_at', 'id', - 'impersonation', + 'is_impersonation', 'project_id', + 'redelegated_trust_id', + 'redelegation_count', + 'remaining_uses', 'roles', 'trustee_user_id', 'trustor_user_id', ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.trust_expires, - identity_fakes.trust_id, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.expires_at, + self.trust.id, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.redelegated_trust_id, + self.trust.redelegation_count, + self.trust.remaining_uses, + self.trust.roles, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ) self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 19c1a6dcc7..134ba5a0bc 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -44,6 +44,7 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name', 'description', 'password_expires_at', + 'options', ) def setUp(self): @@ -63,6 +64,7 @@ def setUp(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.identity_sdk_client.find_domain.return_value = self.domain @@ -91,13 +93,7 @@ def test_user_create_no_options(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -127,11 +123,6 @@ def test_user_create_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': 'secret', } @@ -146,7 +137,6 @@ def test_user_create_password_prompt(self): self.user.name, ] verifylist = [ - ('password', None), ('password_prompt', True), ('enable', False), ('disable', False), @@ -165,11 +155,52 @@ def test_user_create_password_prompt(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, + 'is_enabled': True, + 'password': 'abc123', + } + self.identity_sdk_client.create_user.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + def test_user_create_password_prompt_no_warning(self): + arglist = [ + '--password-prompt', + self.user.name, + ] + verifylist = [ + ('password_prompt', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + import logging + + # Mock the password prompt + mocker = mock.Mock() + mocker.return_value = 'abc123' + + # Use assertLogs to verify no warnings are logged + logger = 'openstackclient.identity.v3.user' + with mock.patch("osc_lib.utils.get_password", mocker): + with self.assertLogs(logger, level='WARNING') as log_ctx: + logging.getLogger(logger).warning( + "Dummy warning for test setup" + ) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(1, len(log_ctx.records)) + self.assertIn( + "Dummy warning for test setup", log_ctx.output[0] + ) + self.assertNotIn( + "No password was supplied", ''.join(log_ctx.output) + ) + + # Set expected values + kwargs = { + 'name': self.user.name, 'is_enabled': True, 'password': 'abc123', } @@ -200,13 +231,8 @@ def test_user_create_email(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, 'email': 'barney@example.com', 'is_enabled': True, - 'options': {}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -236,12 +262,7 @@ def test_user_create_project(self): kwargs = { 'name': self.user.name, 'default_project_id': self.project.id, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, - 'options': {}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -255,6 +276,7 @@ def test_user_create_project(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.assertEqual(datalist, data) @@ -284,14 +306,12 @@ def test_user_create_project_domain(self): kwargs = { 'name': self.user.name, 'default_project_id': self.project.id, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, - 'password': None, } - self.identity_sdk_client.create_user.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_once_with(**kwargs) + self.identity_sdk_client.find_domain.assert_called_once_with( + self.project.domain_id, ignore_missing=False + ) self.assertEqual(self.columns, columns) datalist = ( @@ -303,6 +323,7 @@ def test_user_create_project_domain(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.assertEqual(datalist, data) @@ -328,13 +349,8 @@ def test_user_create_domain(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, 'domain_id': self.domain.id, - 'email': None, - 'options': {}, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -361,13 +377,7 @@ def test_user_create_enable(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -394,13 +404,7 @@ def test_user_create_disable(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': False, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -428,13 +432,8 @@ def test_user_create_ignore_lockout_failure_attempts(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -462,13 +461,8 @@ def test_user_create_no_ignore_lockout_failure_attempts(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -496,13 +490,8 @@ def test_user_create_ignore_password_expiry(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_password_expiry': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -530,13 +519,8 @@ def test_user_create_no_ignore_password_expiry(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_password_expiry': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -564,13 +548,8 @@ def test_user_create_ignore_change_password_upon_first_use(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -598,13 +577,8 @@ def test_user_create_no_ignore_change_password_upon_first_use(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -632,13 +606,8 @@ def test_user_create_enables_lock_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'lock_password': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -666,13 +635,8 @@ def test_user_create_disables_lock_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'lock_password': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -700,13 +664,8 @@ def test_user_create_enable_multi_factor_auth(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -734,13 +693,8 @@ def test_user_create_disable_multi_factor_auth(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -757,7 +711,7 @@ def test_user_create_option_with_multi_factor_auth_rule(self): ] verifylist = [ ( - 'multi_factor_auth_rule', + 'multi_factor_auth_rules', [identity_fakes.mfa_opt1, identity_fakes.mfa_opt2], ), ('enable', False), @@ -774,15 +728,10 @@ def test_user_create_option_with_multi_factor_auth_rule(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': { 'multi_factor_auth_rules': [["password", "totp"], ["password"]] }, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -800,7 +749,7 @@ def test_user_create_with_multiple_options(self): verifylist = [ ('ignore_password_expiry', True), ('disable_multi_factor_auth', True), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('enable', False), ('disable', False), ('name', self.user.name), @@ -815,17 +764,12 @@ def test_user_create_with_multiple_options(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': { 'ignore_password_expiry': True, 'multi_factor_auth_enabled': False, 'multi_factor_auth_rules': [["password", "totp"]], }, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -926,7 +870,7 @@ def setUp(self): self.identity_sdk_client.find_domain.return_value = self.domain self.identity_sdk_client.find_group.return_value = self.group self.identity_sdk_client.find_project.return_value = self.project - self.identity_sdk_client.role_assignments_filter.return_value = [ + self.identity_sdk_client.role_assignments.return_value = [ self.role_assignment ] @@ -1064,12 +1008,28 @@ def test_user_list_project(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'project': self.project.id, + 'scope_project_id': self.project.id, } - self.identity_sdk_client.role_assignments_filter.assert_called_with( - **kwargs - ) + self.identity_sdk_client.role_assignments.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_user_list_with_option_enabled(self): + arglist = ['--enabled'] + verifylist = [('is_enabled', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + kwargs = {'domain_id': None, 'is_enabled': True} + self.identity_sdk_client.users.assert_called_with(**kwargs) + self.identity_sdk_client.find_user.assert_not_called() + self.identity_sdk_client.group_users.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1103,7 +1063,6 @@ def test_user_set_no_options(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1124,7 +1083,6 @@ def test_user_set_name(self): ] verifylist = [ ('name', 'qwerty'), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1155,7 +1113,6 @@ def test_user_set_specify_domain(self): ] verifylist = [ ('name', 'qwerty'), - ('password', None), ('domain', self.domain.id), ('email', None), ('project', None), @@ -1211,7 +1168,6 @@ def test_user_set_password_prompt(self): ] verifylist = [ ('name', None), - ('password', None), ('password_prompt', True), ('email', None), ('project', None), @@ -1244,7 +1200,6 @@ def test_user_set_email(self): ] verifylist = [ ('name', None), - ('password', None), ('email', 'barney@example.com'), ('project', None), ('enable', False), @@ -1273,7 +1228,6 @@ def test_user_set_project(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', self.project.id), ('enable', False), @@ -1292,6 +1246,17 @@ def test_user_set_project(self): self.identity_sdk_client.update_user.assert_called_with( user=self.user, **kwargs ) + self.identity_sdk_client.find_domain.assert_not_called() + + # Set expected values + kwargs = { + 'ignore_missing': False, + 'domain_id': None, + } + self.identity_sdk_client.find_project.assert_called_once_with( + name_or_id=self.project.id, **kwargs + ) + self.assertIsNone(result) def test_user_set_project_domain(self): @@ -1304,7 +1269,6 @@ def test_user_set_project_domain(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', self.project.id), ('project_domain', self.project.domain_id), @@ -1324,6 +1288,11 @@ def test_user_set_project_domain(self): self.identity_sdk_client.update_user.assert_called_with( user=self.user, **kwargs ) + + self.identity_sdk_client.find_domain.assert_called_once_with( + name_or_id=self.project.domain_id, ignore_missing=False + ) + self.assertIsNone(result) def test_user_set_enable(self): @@ -1333,7 +1302,6 @@ def test_user_set_enable(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', True), @@ -1360,7 +1328,6 @@ def test_user_set_disable(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1387,7 +1354,6 @@ def test_user_set_ignore_lockout_failure_attempts(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_lockout_failure_attempts', True), ('project', None), @@ -1415,7 +1381,6 @@ def test_user_set_no_ignore_lockout_failure_attempts(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_lockout_failure_attempts', True), ('project', None), @@ -1443,7 +1408,6 @@ def test_user_set_ignore_password_expiry(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_password_expiry', True), ('project', None), @@ -1471,7 +1435,6 @@ def test_user_set_no_ignore_password_expiry(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_password_expiry', True), ('project', None), @@ -1499,7 +1462,6 @@ def test_user_set_ignore_change_password_upon_first_use(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_change_password_upon_first_use', True), ('project', None), @@ -1527,7 +1489,6 @@ def test_user_set_no_ignore_change_password_upon_first_use(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_change_password_upon_first_use', True), ('project', None), @@ -1555,7 +1516,6 @@ def test_user_set_enable_lock_password(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('enable_lock_password', True), ('project', None), @@ -1583,7 +1543,6 @@ def test_user_set_disable_lock_password(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('disable_lock_password', True), ('project', None), @@ -1611,7 +1570,6 @@ def test_user_set_enable_multi_factor_auth(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('enable_multi_factor_auth', True), ('project', None), @@ -1639,7 +1597,6 @@ def test_user_set_disable_multi_factor_auth(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('disable_multi_factor_auth', True), ('project', None), @@ -1668,9 +1625,8 @@ def test_user_set_option_multi_factor_auth_rule(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), ('disable', False), @@ -1700,11 +1656,10 @@ def test_user_set_with_multiple_options(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_password_expiry', True), ('enable_multi_factor_auth', True), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), ('disable', False), @@ -1756,11 +1711,14 @@ def test_user_password_change(self): # Mock getting user current password. with self._mock_get_password(current_pass): result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) self.identity_sdk_client.update_user.assert_called_with( - current_password=current_pass, password=new_pass + user=user_id, current_password=current_pass, password=new_pass ) - self.assertIsNone(result) def test_user_create_password_prompt(self): current_pass = 'old_pass' @@ -1770,11 +1728,14 @@ def test_user_create_password_prompt(self): # Mock getting user current and new password. with self._mock_get_password(current_pass, new_pass): result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) self.identity_sdk_client.update_user.assert_called_with( - current_password=current_pass, password=new_pass + user=user_id, current_password=current_pass, password=new_pass ) - self.assertIsNone(result) def test_user_password_change_no_prompt(self): current_pass = 'old_pass' @@ -1792,11 +1753,14 @@ def test_user_password_change_no_prompt(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) self.identity_sdk_client.update_user.assert_called_with( - current_password=current_pass, password=new_pass + user=user_id, current_password=current_pass, password=new_pass ) - self.assertIsNone(result) class TestUserShow(identity_fakes.TestIdentityv3): @@ -1849,6 +1813,7 @@ def test_user_show(self): 'name', 'description', 'password_expires_at', + 'options', ) self.assertEqual(collist, columns) datalist = ( @@ -1860,6 +1825,7 @@ def test_user_show(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 44845fb82d..30503e1f02 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -20,7 +20,7 @@ from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes class FakeClientMixin: @@ -35,7 +35,7 @@ class TestImagev1(FakeClientMixin, utils.TestCommand): def setUp(self): super().setUp() - self.app.client_manager.volume = volume_fakes.FakeVolumev1Client( + self.app.client_manager.volume = volume_fakes.FakeVolumeClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index 971e969c7b..2d870ff89e 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -54,9 +54,9 @@ class TestImageCreate(image_fakes.TestImagev1): def setUp(self): super().setUp() - self.image_client.create_image = mock.Mock(return_value=self.new_image) - self.image_client.find_image = mock.Mock(return_value=self.new_image) - self.image_client.update_image = mock.Mock(return_image=self.new_image) + self.image_client.create_image.return_value = self.new_image + self.image_client.find_image.return_value = self.new_image + self.image_client.update_image.return_value = self.new_image # Get the command object to test self.cmd = image.CreateImage(self.app, None) @@ -212,8 +212,8 @@ def setUp(self): super().setUp() # This is the return value for utils.find_resource() - self.image_client.find_image = mock.Mock(return_value=self._image) - self.image_client.delete_image = mock.Mock(return_value=None) + self.image_client.find_image.return_value = self._image + self.image_client.delete_image.return_value = None # Get the command object to test self.cmd = image.DeleteImage(self.app, None) @@ -261,7 +261,6 @@ class TestImageList(image_fakes.TestImagev1): def setUp(self): super().setUp() - self.image_client.images = mock.Mock() self.image_client.images.side_effect = [ [self._image], [], @@ -441,8 +440,8 @@ def setUp(self): super().setUp() # This is the return value for utils.find_resource() - self.image_client.find_image = mock.Mock(return_value=self._image) - self.image_client.update_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image + self.image_client.update_image.return_value = self._image # Get the command object to test self.cmd = image.SetImage(self.app, None) @@ -712,7 +711,7 @@ class TestImageShow(image_fakes.TestImagev1): def setUp(self): super().setUp() - self.image_client.find_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image # Get the command object to test self.cmd = image.ShowImage(self.app, None) @@ -758,3 +757,50 @@ def test_image_show_human_readable(self): size_index = columns.index('size') self.assertEqual(data[size_index].human_readable(), '2K') + + +class TestImageSave(image_fakes.TestImagev1): + image = image_fakes.create_one_image({}) + + def setUp(self): + super().setUp() + + self.image_client.find_image.return_value = self.image + self.image_client.download_image.return_value = self.image + + # Get the command object to test + self.cmd = image.SaveImage(self.app, None) + + def test_save_data(self): + arglist = ['--file', '/path/to/file', self.image.id] + + verifylist = [('file', '/path/to/file'), ('image', self.image.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.download_image.assert_called_once_with( + self.image.id, output='/path/to/file', stream=True, chunk_size=1024 + ) + + def test_save_data_with_chunk_size(self): + arglist = [ + '--file', + '/path/to/file', + '--chunk-size', + '2048', + self.image.id, + ] + + verifylist = [ + ('file', '/path/to/file'), + ('chunk_size', 2048), + ('image', self.image.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.download_image.assert_called_once_with( + self.image.id, output='/path/to/file', stream=True, chunk_size=2048 + ) diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index e8aafa8cf5..e6de9f2ebd 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -17,8 +17,9 @@ import tempfile from unittest import mock -from cinderclient import api_versions +from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -37,12 +38,6 @@ def setUp(self): self.project_mock.reset_mock() self.domain_mock = self.identity_client.domains self.domain_mock.reset_mock() - self.volumes_mock = self.volume_client.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() class TestImageCreate(TestImage): @@ -55,6 +50,7 @@ def setUp(self): self.new_image = image_fakes.create_one_image() self.image_client.create_image.return_value = self.new_image self.image_client.update_image.return_value = self.new_image + self.image_client.get_image.return_value = self.new_image self.project_mock.get.return_value = self.project @@ -89,6 +85,7 @@ def test_image_reserve_no_options(self, raw_input): container_format=_image.DEFAULT_CONTAINER_FORMAT, disk_format=_image.DEFAULT_DISK_FORMAT, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -146,6 +143,7 @@ def test_image_reserve_options(self, raw_input): is_protected=self.new_image.is_protected, visibility=self.new_image.visibility, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -243,6 +241,7 @@ def test_image_create_file(self): tags=self.new_image.tags, filename=imagefile.name, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -275,6 +274,7 @@ def test_image_create__progress_ignore_with_stdin( data=fake_stdin, validate_checksum=False, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -307,7 +307,6 @@ def test_image_create_import(self, raw_input): columns, data = self.cmd.take_action(parsed_args) - # ImageManager.create(name=, **) self.image_client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, @@ -315,21 +314,21 @@ def test_image_create_import(self, raw_input): disk_format=_image.DEFAULT_DISK_FORMAT, use_import=True, ) + self.image_client.get_image.assert_called_once_with(self.new_image) - @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') - def test_image_create_from_volume(self, mock_get_data_f, mock_get_vol): - fake_vol_id = 'fake-volume-id' + def test_image_create_from_volume(self, mock_get_data_f): mock_get_data_f.return_value = None - class FakeVolume: - id = fake_vol_id - - mock_get_vol.return_value = FakeVolume() + volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = volume + self.volume_sdk_client.upload_volume_to_image.return_value = { + 'volume_type': {'name': 'fake_type'} + } arglist = [ '--volume', - fake_vol_id, + volume.id, self.new_image.name, ] verifylist = [ @@ -339,47 +338,60 @@ class FakeVolume: 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' + self.volume_sdk_client.upload_volume_to_image.assert_called_once_with( + volume.id, + self.new_image.name, + force=False, + disk_format='raw', + container_format='bare', + visibility=None, + protected=None, ) - @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') - def test_image_create_from_volume_fail( - self, mock_get_data_f, mock_get_vol - ): - fake_vol_id = 'fake-volume-id' + def test_image_create_from_volume_pre_v31(self, mock_get_data_f): mock_get_data_f.return_value = None - class FakeVolume: - id = fake_vol_id - - mock_get_vol.return_value = FakeVolume() + volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = volume + self.volume_sdk_client.upload_volume_to_image.return_value = { + 'volume_type': {'name': 'fake_type'} + } - arglist = ['--volume', fake_vol_id, self.new_image.name, '--public'] + arglist = [ + '--volume', + volume.id, + self.new_image.name, + '--public', + ] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn('--os-volume-api-version 3.1 or greater ', str(exc)) - @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') - def test_image_create_from_volume_v31(self, mock_get_data_f, mock_get_vol): - self.volume_client.api_version = api_versions.APIVersion('3.1') + def test_image_create_from_volume_v31(self, mock_get_data_f): + self.set_volume_api_version('3.1') - fake_vol_id = 'fake-volume-id' mock_get_data_f.return_value = None - class FakeVolume: - id = fake_vol_id - - mock_get_vol.return_value = FakeVolume() + volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = volume + self.volume_sdk_client.upload_volume_to_image.return_value = { + 'volume_type': {'name': 'fake_type'} + } - arglist = ['--volume', fake_vol_id, self.new_image.name, '--public'] + arglist = [ + '--volume', + volume.id, + self.new_image.name, + '--public', + ] verifylist = [ ('name', self.new_image.name), ] @@ -387,12 +399,12 @@ class FakeVolume: columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.upload_to_image.assert_called_with( - fake_vol_id, - False, + self.volume_sdk_client.upload_volume_to_image.assert_called_once_with( + volume.id, self.new_image.name, - 'bare', - 'raw', + force=False, + disk_format='raw', + container_format='bare', visibility='public', protected=False, ) @@ -810,6 +822,8 @@ def test_image_list_long_option(self): 'Visibility', 'Protected', 'Project', + 'Hash Algorithm', + 'Hash Value', 'Tags', ) @@ -818,14 +832,16 @@ def test_image_list_long_option(self): ( self._image.id, self._image.name, - None, - None, - None, - None, - None, + self._image.disk_format, + self._image.container_format, + self._image.size, + self._image.checksum, + self._image.status, self._image.visibility, self._image.is_protected, self._image.owner_id, + self._image.hash_algo, + self._image.hash_value, format_columns.ListColumn(self._image.tags), ), ) @@ -902,7 +918,7 @@ def test_image_list_limit_option(self): self.assertEqual(ret_limit, len(tuple(data))) def test_image_list_project_option(self): - self.image_client.find_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image arglist = [ '--project', 'nova', @@ -919,7 +935,7 @@ def test_image_list_project_option(self): @mock.patch('osc_lib.utils.find_resource') def test_image_list_marker_option(self, fr_mock): - self.image_client.find_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image arglist = [ '--marker', @@ -1279,6 +1295,117 @@ def test_image_set_membership_option_pending(self): # the 'update membership' route. self.image_client.update_image.assert_called_with(self._image.id) + def test_image_set_membership_accept_with_project_no_owner_change(self): + """Test that --project with --accept doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--accept', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'accepted'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='accepted', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + + def test_image_set_membership_reject_with_project_no_owner_change(self): + """Test that --project with --reject doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--reject', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'rejected'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='rejected', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + + def test_image_set_membership_pending_with_project_no_owner_change(self): + """Test that --project with --pending doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--pending', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'pending'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='pending', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + def test_image_set_options(self): arglist = [ '--name', @@ -1344,15 +1471,17 @@ def test_image_set_with_unexist_project(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - def test_image_set_bools1(self): + def test_image_set_bools_true(self): arglist = [ '--protected', '--private', + '--hidden', 'graven', ] verifylist = [ ('is_protected', True), ('visibility', 'private'), + ('is_hidden', True), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1362,6 +1491,7 @@ def test_image_set_bools1(self): kwargs = { 'is_protected': True, 'visibility': 'private', + 'is_hidden': True, } # ImageManager.update(image, **kwargs) self.image_client.update_image.assert_called_with( @@ -1369,15 +1499,17 @@ def test_image_set_bools1(self): ) self.assertIsNone(result) - def test_image_set_bools2(self): + def test_image_set_bools_false(self): arglist = [ '--unprotected', '--public', + '--unhidden', 'graven', ] verifylist = [ ('is_protected', False), ('visibility', 'public'), + ('is_hidden', False), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1387,6 +1519,7 @@ def test_image_set_bools2(self): kwargs = { 'is_protected': False, 'visibility': 'public', + 'is_hidden': False, } # ImageManager.update(image, **kwargs) self.image_client.update_image.assert_called_with( @@ -1709,7 +1842,7 @@ class TestImageShow(TestImage): def setUp(self): super().setUp() - self.image_client.find_image = mock.Mock(return_value=self._data) + self.image_client.find_image.return_value = self._data # Get the command object to test self.cmd = _image.ShowImage(self.app, None) @@ -1767,6 +1900,7 @@ def setUp(self): attrs['hw_rng_model'] = 'virtio' attrs['prop'] = 'test' attrs['prop2'] = 'fake' + attrs['os_secure_boot'] = 'required' self.image = image_fakes.create_one_image(attrs) self.image_client.find_image.return_value = self.image @@ -1810,11 +1944,18 @@ def test_image_unset_property_option(self): 'hw_rng_model', '--property', 'prop', + '--property', + 'os_secure_boot', self.image.id, ] + # openstacksdk translates 'os_secure_boot' property to + # 'needs_secure_boot' Image attribute. This is true for + # all IMAGE_ATTRIBUTES_CUSTOM_NAMES keys + self.assertEqual(self.image.needs_secure_boot, 'required') + self.assertFalse(hasattr(self.image, 'os_secure_boot')) verifylist = [ - ('properties', ['hw_rng_model', 'prop']), + ('properties', ['hw_rng_model', 'prop', 'os_secure_boot']), ('image', self.image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2027,6 +2168,36 @@ def test_import_image__web_download_invalid_options(self): self.image_client.import_image.assert_not_called() + def test_import_image__web_download_invalid_url(self): + arglist = [ + self.image.name, + '--method', + 'web-download', + '--uri', + 'invalid:1234', + ] + + verifylist = [ + ('image', self.image.name), + ('import_method', 'web-download'), + ('uri', 'invalid:1234'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertIn( + "'invalid:1234' is not a valid url", + str(exc), + ) + + self.image_client.import_image.assert_not_called() + def test_import_image__web_download_invalid_image_state(self): self.image.status = 'uploading' # != 'queued' arglist = [ @@ -2086,6 +2257,38 @@ def test_import_image__copy_image(self): all_stores_must_succeed=False, ) + def test_import_image__copy_image_disallow_failure(self): + self.image.status = 'active' + arglist = [ + self.image.name, + '--method', + 'copy-image', + '--store', + 'fast', + '--disallow-failure', + ] + verifylist = [ + ('image', self.image.name), + ('import_method', 'copy-image'), + ('stores', ['fast']), + ('allow_failure', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.import_image.assert_called_once_with( + self.image, + method='copy-image', + uri=None, + remote_region=None, + remote_image_id=None, + remote_service_interface=None, + stores=['fast'], + all_stores=None, + all_stores_must_succeed=True, + ) + def test_import_image__glance_download(self): arglist = [ self.image.name, @@ -2146,7 +2349,29 @@ def test_save_data(self): self.cmd.take_action(parsed_args) self.image_client.download_image.assert_called_once_with( - self.image.id, stream=True, output='/path/to/file' + self.image.id, output='/path/to/file', stream=True, chunk_size=1024 + ) + + def test_save_data_with_chunk_size(self): + arglist = [ + '--file', + '/path/to/file', + '--chunk-size', + '2048', + self.image.id, + ] + + verifylist = [ + ('filename', '/path/to/file'), + ('chunk_size', 2048), + ('image', self.image.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.download_image.assert_called_once_with( + self.image.id, output='/path/to/file', stream=True, chunk_size=2048 ) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index 323d8ea151..6306e23eb1 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -113,6 +113,7 @@ def setUp(self): super().setUp() self.image_client.delete_metadef_object.return_value = None + self.image_client.delete_all_metadef_objects.return_value = None self.cmd = metadef_objects.DeleteMetadefObject(self.app, None) def test_object_delete(self): @@ -126,8 +127,29 @@ def test_object_delete(self): result = self.cmd.take_action(parsed_args) + self.image_client.delete_metadef_object.assert_called_once_with( + self.image_client.get_metadef_object(), + self._metadef_namespace.namespace, + ) + self.image_client.delete_all_metadef_objects.assert_not_called() self.assertIsNone(result) + def test_object_delete_all(self): + arglist = [ + self._metadef_namespace.namespace, + ] + + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.image_client.delete_all_metadef_objects.assert_called_with( + self._metadef_namespace.namespace, + ) + self.image_client.delete_metadef_object.assert_not_called() + class TestMetadefObjectList(fakes.TestImagev2): _metadef_namespace = fakes.create_one_metadef_namespace() @@ -256,7 +278,6 @@ def test_neg_object_property_show(self): parsed_args, ) self.assertIn( - 'Property %s not found in object %s.' - % (parsed_args.property, parsed_args.object), + f'Property {parsed_args.property} not found in object {parsed_args.object}.', str(exc), ) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index 274ed66353..e2f83d6296 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -91,6 +91,7 @@ def setUp(self): super().setUp() self.cmd = metadef_properties.DeleteMetadefProperty(self.app, None) + self.image_client.delete_all_metadef_properties.return_value = None def test_metadef_property_delete(self): arglist = ['namespace', 'property'] @@ -100,6 +101,10 @@ def test_metadef_property_delete(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.image_client.delete_metadef_property.assert_called_with( + 'property', 'namespace', ignore_missing=False + ) + self.image_client.delete_all_metadef_properties.assert_not_called() def test_metadef_property_delete_missing_arguments(self): arglist = [] @@ -110,21 +115,13 @@ def test_metadef_property_delete_missing_arguments(self): arglist, [], ) - - arglist = ['namespace'] - self.assertRaises( - tests_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - [], - ) + self.image_client.delete_all_metadef_properties.assert_not_called() + self.image_client.delete_metadef_property.assert_not_called() def test_metadef_property_delete_exception(self): arglist = ['namespace', 'property'] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.image_client.delete_metadef_property.side_effect = ( sdk_exceptions.ResourceNotFound ) @@ -132,6 +129,23 @@ def test_metadef_property_delete_exception(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.image_client.delete_metadef_property.assert_called_with( + 'property', 'namespace', ignore_missing=False + ) + self.image_client.delete_all_metadef_properties.assert_not_called() + + def test_metadef_property_delete_all(self): + arglist = ['namespace'] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.image_client.delete_all_metadef_properties.assert_called_with( + 'namespace' + ) + self.image_client.delete_metadef_property.assert_not_called() class TestMetadefPropertyList(image_fakes.TestImagev2): diff --git a/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py b/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py index ac1d65008c..d7c1ee686b 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py @@ -42,9 +42,7 @@ class TestMetadefResourceTypeAssociationCreate( def setUp(self): super().setUp() - self.image_client.create_metadef_resource_type_association.return_value = ( - self.resource_type_association - ) + self.image_client.create_metadef_resource_type_association.return_value = self.resource_type_association self.cmd = metadef_resource_type_association.CreateMetadefResourceTypeAssociation( self.app, None ) @@ -74,9 +72,7 @@ class TestMetadefResourceTypeAssociationDelete( def setUp(self): super().setUp() - self.image_client.delete_metadef_resource_type_association.return_value = ( - self.resource_type_association - ) + self.image_client.delete_metadef_resource_type_association.return_value = self.resource_type_association self.cmd = metadef_resource_type_association.DeleteMetadefResourceTypeAssociation( self.app, None ) diff --git a/openstackclient/tests/unit/integ/base.py b/openstackclient/tests/unit/integ/base.py index cecf122b2a..e61945a771 100644 --- a/openstackclient/tests/unit/integ/base.py +++ b/openstackclient/tests/unit/integ/base.py @@ -18,7 +18,7 @@ HOST = "192.168.5.41" -URL_BASE = "http://%s/identity" % HOST +URL_BASE = f"http://{HOST}/identity" V2_AUTH_URL = URL_BASE + "/v2.0/" V2_VERSION_RESP = { diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py index 0a3a8ad430..6ac17be5f2 100644 --- a/openstackclient/tests/unit/integ/cli/test_shell.py +++ b/openstackclient/tests/unit/integ/cli/test_shell.py @@ -361,8 +361,7 @@ def test_shell_args_options(self): _shell = shell.OpenStackShell() _shell.run( - "--os-username zarquon --os-password qaz " - "extension list".split(), + "--os-username zarquon --os-password qaz extension list".split(), ) # Check general calls diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index a84697b2ca..cc84c8bdf0 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -126,17 +126,13 @@ def setUp(self): # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. - self.app.client_manager.network = mock.Mock() - self.network_client = self.app.client_manager.network - self.network_client.network_action = mock.Mock( - return_value='take_action_network' - ) + self.app.client_manager.network = mock.Mock() # noqa: O401 + self.network_client = self.app.client_manager.network # noqa: O401 + self.network_client.network_action.return_value = 'take_action_network' - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_client = self.app.client_manager.sdk_connection.compute - self.compute_client.compute_action = mock.Mock( - return_value='take_action_compute' - ) + self.app.client_manager.compute = mock.Mock() # noqa: O401 + self.compute_client = self.app.client_manager.compute # noqa: O401 + self.compute_client.compute_action.return_value = 'take_action_compute' self.cmd = FakeNetworkAndComputeCommand(self.app, None) @@ -205,9 +201,9 @@ def setUp(self): # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. - self.app.client_manager.network = mock.Mock() - self.network_client = self.app.client_manager.network - self.network_client.test_create_action = mock.Mock() + self.app.client_manager.network = mock.Mock() # noqa: O401 + self.network_client = self.app.client_manager.network # noqa: O401 + self.network_client.test_create_action = mock.Mock() # noqa: O402 # Subclasses can override the command object to test. self.cmd = FakeCreateNeutronCommandWithExtraArgs(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 999e1027cd..16bbfa5ea3 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -31,8 +31,24 @@ 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 ( + qos_bandwidth_limit_rule as _qos_bandwidth_limit_rule, +) +from openstack.network.v2 import ( + qos_dscp_marking_rule as _qos_dscp_marking_rule, +) +from openstack.network.v2 import ( + qos_minimum_bandwidth_rule as _qos_minimum_bandwidth_rule, +) +from openstack.network.v2 import ( + qos_minimum_packet_rate_rule as _qos_minimum_packet_rate_rule, +) +from openstack.network.v2 import qos_policy as _qos_policy +from openstack.network.v2 import qos_rule_type as _qos_rule_type from openstack.network.v2 import rbac_policy as network_rbac +from openstack.network.v2 import router as _router from openstack.network.v2 import security_group as _security_group +from openstack.network.v2 import security_group_rule as _security_group_rule from openstack.network.v2 import segment as _segment from openstack.network.v2 import service_profile as _service_profile from openstack.network.v2 import trunk as _trunk @@ -118,485 +134,6 @@ def create_one_extension(attrs=None): return extension -class FakeNetworkQosPolicy: - """Fake one or more QoS policies.""" - - @staticmethod - def create_one_qos_policy(attrs=None): - """Create a fake QoS policy. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - qos_id = attrs.get('id') or 'qos-policy-id-' + uuid.uuid4().hex - rule_attrs = {'qos_policy_id': qos_id} - rules = [FakeNetworkQosRule.create_one_qos_rule(rule_attrs)] - - # Set default attributes. - qos_policy_attrs = { - 'name': 'qos-policy-name-' + uuid.uuid4().hex, - 'id': qos_id, - 'is_default': False, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'shared': False, - 'description': 'qos-policy-description-' + uuid.uuid4().hex, - 'rules': rules, - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - qos_policy_attrs.update(attrs) - - qos_policy = fakes.FakeResource( - info=copy.deepcopy(qos_policy_attrs), loaded=True - ) - - # Set attributes with special mapping in OpenStack SDK. - qos_policy.is_shared = qos_policy_attrs['shared'] - - return qos_policy - - @staticmethod - def create_qos_policies(attrs=None, count=2): - """Create multiple fake QoS policies. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of QoS policies to fake - :return: - A list of FakeResource objects faking the QoS policies - """ - qos_policies = [] - for i in range(0, count): - qos_policies.append( - FakeNetworkQosPolicy.create_one_qos_policy(attrs) - ) - - return qos_policies - - @staticmethod - def get_qos_policies(qos_policies=None, count=2): - """Get an iterable MagicMock object with a list of faked QoS policies. - - If qos policies list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List qos_policies: - A list of FakeResource objects faking qos policies - :param int count: - The number of QoS policies to fake - :return: - An iterable Mock object with side_effect set to a list of faked - QoS policies - """ - if qos_policies is None: - qos_policies = FakeNetworkQosPolicy.create_qos_policies(count) - return mock.Mock(side_effect=qos_policies) - - -class FakeNetworkSecGroup: - """Fake one security group.""" - - @staticmethod - def create_one_security_group(attrs=None): - """Create a fake security group. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - sg_id = attrs.get('id') or 'security-group-id-' + uuid.uuid4().hex - - # Set default attributes. - security_group_attrs = { - 'name': 'security-group-name-' + uuid.uuid4().hex, - 'id': sg_id, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'description': 'security-group-description-' + uuid.uuid4().hex, - 'location': 'MUNCHMUNCHMUNCH', - } - - security_group = fakes.FakeResource( - info=copy.deepcopy(security_group_attrs), loaded=True - ) - - return security_group - - -class FakeNetworkQosRule: - """Fake one or more Network QoS rules.""" - - @staticmethod - def create_one_qos_rule(attrs=None): - """Create a fake Network QoS rule. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - type = attrs.get('type') or choice(VALID_QOS_RULES) - qos_rule_attrs = { - 'id': 'qos-rule-id-' + uuid.uuid4().hex, - 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'type': type, - 'location': 'MUNCHMUNCHMUNCH', - } - if type == RULE_TYPE_BANDWIDTH_LIMIT: - qos_rule_attrs['max_kbps'] = randint(1, 10000) - qos_rule_attrs['max_burst_kbits'] = randint(1, 10000) - qos_rule_attrs['direction'] = 'egress' - elif type == RULE_TYPE_DSCP_MARKING: - qos_rule_attrs['dscp_mark'] = choice(VALID_DSCP_MARKS) - 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) - - qos_rule = fakes.FakeResource( - info=copy.deepcopy(qos_rule_attrs), loaded=True - ) - - return qos_rule - - @staticmethod - def create_qos_rules(attrs=None, count=2): - """Create multiple fake Network QoS rules. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of Network QoS rule to fake - :return: - A list of FakeResource objects faking the Network QoS rules - """ - qos_rules = [] - for i in range(0, count): - qos_rules.append(FakeNetworkQosRule.create_one_qos_rule(attrs)) - return qos_rules - - @staticmethod - def get_qos_rules(qos_rules=None, count=2): - """Get a list of faked Network QoS rules. - - If Network QoS rules list is provided, then initialize the Mock - object with the list. Otherwise create one. - - :param List qos_rules: - A list of FakeResource objects faking Network QoS rules - :param int count: - The number of QoS minimum bandwidth rules to fake - :return: - An iterable Mock object with side_effect set to a list of faked - qos minimum bandwidth rules - """ - if qos_rules is None: - qos_rules = FakeNetworkQosRule.create_qos_rules(count) - return mock.Mock(side_effect=qos_rules) - - -class FakeNetworkQosRuleType: - """Fake one or more Network QoS rule types.""" - - @staticmethod - def create_one_qos_rule_type(attrs=None): - """Create a fake Network QoS rule type. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_rule_type_attrs = { - 'type': 'rule-type-' + uuid.uuid4().hex, - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - qos_rule_type_attrs.update(attrs) - - return fakes.FakeResource( - info=copy.deepcopy(qos_rule_type_attrs), loaded=True - ) - - @staticmethod - def create_qos_rule_types(attrs=None, count=2): - """Create multiple fake Network QoS rule types. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of QoS rule types to fake - :return: - A list of FakeResource objects faking the QoS rule types - """ - qos_rule_types = [] - for i in range(0, count): - qos_rule_types.append( - FakeNetworkQosRuleType.create_one_qos_rule_type(attrs) - ) - - return qos_rule_types - - -class FakeRouter: - """Fake one or more routers.""" - - @staticmethod - def create_one_router(attrs=None): - """Create a fake router. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, name, admin_state_up, - status, project_id - """ - attrs = attrs or {} - - # Set default attributes. - router_attrs = { - 'id': 'router-id-' + uuid.uuid4().hex, - 'name': 'router-name-' + uuid.uuid4().hex, - 'status': 'ACTIVE', - 'admin_state_up': True, - 'description': 'router-description-' + uuid.uuid4().hex, - 'distributed': False, - 'ha': False, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'routes': [], - 'external_gateway_info': {}, - 'availability_zone_hints': [], - 'availability_zones': [], - 'tags': [], - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - router_attrs.update(attrs) - - router = fakes.FakeResource( - info=copy.deepcopy(router_attrs), loaded=True - ) - - # Set attributes with special mapping in OpenStack SDK. - router.is_admin_state_up = router_attrs['admin_state_up'] - router.is_distributed = router_attrs['distributed'] - router.is_ha = router_attrs['ha'] - - return router - - @staticmethod - def create_routers(attrs=None, count=2): - """Create multiple fake routers. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of routers to fake - :return: - A list of FakeResource objects faking the routers - """ - routers = [] - for i in range(0, count): - routers.append(FakeRouter.create_one_router(attrs)) - - return routers - - @staticmethod - def get_routers(routers=None, count=2): - """Get an iterable Mock object with a list of faked routers. - - If routers list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List routers: - A list of FakeResource objects faking routers - :param int count: - The number of routers to fake - :return: - An iterable Mock object with side_effect set to a list of faked - routers - """ - if routers is None: - routers = FakeRouter.create_routers(count) - return mock.Mock(side_effect=routers) - - -class FakeSecurityGroup: - """Fake one or more security groups.""" - - @staticmethod - def create_one_security_group(attrs=None): - """Create a fake security group. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, name, etc. - """ - attrs = attrs or {} - - # Set default attributes. - security_group_attrs = { - 'id': 'security-group-id-' + uuid.uuid4().hex, - 'name': 'security-group-name-' + uuid.uuid4().hex, - 'description': 'security-group-description-' + uuid.uuid4().hex, - 'stateful': True, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'security_group_rules': [], - 'tags': [], - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - security_group_attrs.update(attrs) - - security_group = fakes.FakeResource( - info=copy.deepcopy(security_group_attrs), loaded=True - ) - - return security_group - - @staticmethod - def create_security_groups(attrs=None, count=2): - """Create multiple fake security groups. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of security groups to fake - :return: - A list of FakeResource objects faking the security groups - """ - security_groups = [] - for i in range(0, count): - security_groups.append( - FakeSecurityGroup.create_one_security_group(attrs) - ) - - return security_groups - - @staticmethod - def get_security_groups(security_groups=None, count=2): - """Get an iterable Mock object with a list of faked security groups. - - If security groups list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List security_groups: - A list of FakeResource objects faking security groups - :param int count: - The number of security groups to fake - :return: - An iterable Mock object with side_effect set to a list of faked - security groups - """ - if security_groups is None: - security_groups = FakeSecurityGroup.create_security_groups(count) - return mock.Mock(side_effect=security_groups) - - -class FakeSecurityGroupRule: - """Fake one or more security group rules.""" - - @staticmethod - def create_one_security_group_rule(attrs=None): - """Create a fake security group rule. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - security_group_rule_attrs = { - 'description': 'security-group-rule-description-' - + uuid.uuid4().hex, - 'direction': 'ingress', - 'ether_type': 'IPv4', - 'id': 'security-group-rule-id-' + uuid.uuid4().hex, - 'port_range_max': None, - 'port_range_min': None, - 'protocol': None, - 'remote_group_id': None, - 'remote_address_group_id': None, - 'remote_ip_prefix': '0.0.0.0/0', - 'security_group_id': 'security-group-id-' + uuid.uuid4().hex, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - security_group_rule_attrs.update(attrs) - - security_group_rule = fakes.FakeResource( - info=copy.deepcopy(security_group_rule_attrs), loaded=True - ) - - return security_group_rule - - @staticmethod - def create_security_group_rules(attrs=None, count=2): - """Create multiple fake security group rules. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of security group rules to fake - :return: - A list of FakeResource objects faking the security group rules - """ - security_group_rules = [] - for i in range(0, count): - security_group_rules.append( - FakeSecurityGroupRule.create_one_security_group_rule(attrs) - ) - - return security_group_rules - - @staticmethod - def get_security_group_rules(security_group_rules=None, count=2): - """Get an iterable Mock with a list of faked security group rules. - - If security group rules list is provided, then initialize the Mock - object with the list. Otherwise create one. - - :param List security_group_rules: - A list of FakeResource objects faking security group rules - :param int count: - The number of security group rules to fake - :return: - An iterable Mock object with side_effect set to a list of faked - security group rules - """ - if security_group_rules is None: - security_group_rules = ( - FakeSecurityGroupRule.create_security_group_rules(count) - ) - return mock.Mock(side_effect=security_group_rules) - - class FakeSubnet: """Fake one or more subnets.""" @@ -1419,6 +956,7 @@ def create_one_network(attrs=None): 'availability_zone_hints': [], 'is_default': False, 'is_vlan_transparent': True, + 'is_vlan_qinq': False, 'port_security_enabled': True, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex, @@ -1676,8 +1214,10 @@ def create_one_port(attrs=None): 'qos_network_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'tags': [], + 'trusted': None, 'propagate_uplink_status': False, 'location': 'MUNCHMUNCHMUNCH', + 'trunk_details': {}, } # Overwrite default attributes. @@ -1853,20 +1393,33 @@ def get_network_rbacs(rbac_policies=None, count=2): def create_one_security_group(attrs=None): - """Create a security group.""" + """Create a fake security group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A SecurityGroup object, with id, name, etc. + """ attrs = attrs or {} + # Set default attributes. security_group_attrs = { - 'name': 'security-group-name-' + uuid.uuid4().hex, 'id': 'security-group-id-' + uuid.uuid4().hex, - 'project_id': 'project-id-' + uuid.uuid4().hex, + 'name': 'security-group-name-' + uuid.uuid4().hex, 'description': 'security-group-description-' + uuid.uuid4().hex, + 'stateful': True, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'security_group_rules': [], + 'tags': [], 'location': 'MUNCHMUNCHMUNCH', + 'is_shared': False, } + # Overwrite default attributes. security_group_attrs.update(attrs) security_group = _security_group.SecurityGroup(**security_group_attrs) + security_group.tenant_id = None # unset deprecated opts return security_group @@ -1874,9 +1427,12 @@ def create_one_security_group(attrs=None): def create_security_groups(attrs=None, count=2): """Create multiple fake security groups. - :param dict attrs: A dictionary with all attributes - :param int count: The number of security groups to fake - :return: A list of fake SecurityGroup objects + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security groups to fake + :return: + A list of SecurityGroup objects faking the security groups """ security_groups = [] for i in range(0, count): @@ -1885,6 +1441,99 @@ def create_security_groups(attrs=None, count=2): return security_groups +def get_security_groups(security_groups=None, count=2): + """Get an iterable Mock object with a list of faked security groups. + + If security groups list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List security_groups: + A list of SecurityGroup objects faking security groups + :param int count: + The number of security groups to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security groups + """ + if security_groups is None: + security_groups = create_security_groups(count) + return mock.Mock(side_effect=security_groups) + + +def create_one_security_group_rule(attrs=None): + """Create a fake security group rule. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + security_group_rule_attrs = { + 'description': 'security-group-rule-description-' + uuid.uuid4().hex, + 'direction': 'ingress', + 'ether_type': 'IPv4', + 'id': 'security-group-rule-id-' + uuid.uuid4().hex, + 'port_range_max': None, + 'port_range_min': None, + 'protocol': None, + 'remote_group_id': None, + 'remote_address_group_id': None, + 'remote_ip_prefix': '0.0.0.0/0', + 'security_group_id': 'security-group-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + security_group_rule_attrs.update(attrs) + + security_group_rule = _security_group_rule.SecurityGroupRule( + **security_group_rule_attrs + ) + security_group_rule.tenant_id = None # unset deprecated opts + + return security_group_rule + + +def create_security_group_rules(attrs=None, count=2): + """Create multiple fake security group rules. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security group rules to fake + :return: + A list of SecurityGroupRule objects faking the security group rules + """ + security_group_rules = [] + for i in range(0, count): + security_group_rules.append(create_one_security_group_rule(attrs)) + + return security_group_rules + + +def get_security_group_rules(security_group_rules=None, count=2): + """Get an iterable Mock with a list of faked security group rules. + + If security group rules list is provided, then initialize the Mock + object with the list. Otherwise create one. + + :param List security_group_rules: + A list of SecurityGroupRule objects faking security group rules + :param int count: + The number of security group rules to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security group rules + """ + if security_group_rules is None: + security_group_rules = create_security_group_rules(count) + return mock.Mock(side_effect=security_group_rules) + + def create_one_service_profile(attrs=None): """Create service profile.""" attrs = attrs or {} @@ -1923,6 +1572,281 @@ def get_service_profile(flavor_profile=None, count=2): return mock.Mock(side_effect=flavor_profile) +def create_one_qos_policy(attrs=None): + """Create a fake QoS policy. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A QoSPolicy object with name, id, etc. + """ + attrs = attrs or {} + qos_id = attrs.get('id') or 'qos-policy-id-' + uuid.uuid4().hex + rules = [] + + # Set default attributes. + qos_policy_attrs = { + 'name': 'qos-policy-name-' + uuid.uuid4().hex, + 'id': qos_id, + 'is_default': False, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'shared': False, + 'description': 'qos-policy-description-' + uuid.uuid4().hex, + 'rules': rules, + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + qos_policy_attrs.update(attrs) + + qos_policy = _qos_policy.QoSPolicy(**qos_policy_attrs) + + return qos_policy + + +def create_qos_policies(attrs=None, count=2): + """Create multiple fake QoS policies. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of QoS policies to fake + :return: + A list of QoSPolicy objects faking the QoS policies + """ + qos_policies = [] + for i in range(0, count): + qos_policies.append(create_one_qos_policy(attrs)) + + return qos_policies + + +def get_qos_policies(qos_policies=None, count=2): + """Get an iterable MagicMock object with a list of faked QoS policies. + + If qos policies list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List qos_policies: + A list of QoSPolicy objects faking qos policies + :param int count: + The number of QoS policies to fake + :return: + An iterable Mock object with side_effect set to a list of faked + QoS policies + """ + if qos_policies is None: + qos_policies = create_qos_policies(count) + return mock.Mock(side_effect=qos_policies) + + +def create_one_qos_rule(attrs=None): + """Create a fake Network QoS rule. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A QoSRule object with id, type, etc. + """ + attrs = attrs or {} + + # Set default attributes. + type = attrs.get('type') or choice(VALID_QOS_RULES) + qos_rule_attrs = { + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'type': type, + 'location': 'MUNCHMUNCHMUNCH', + } + + if type == RULE_TYPE_BANDWIDTH_LIMIT: + qos_rule_attrs['max_kbps'] = randint(1, 10000) + qos_rule_attrs['max_burst_kbps'] = randint(1, 10000) + qos_rule_attrs['direction'] = 'egress' + + qos_rule_attrs.update(attrs) + qos_rule = _qos_bandwidth_limit_rule.QoSBandwidthLimitRule( + **qos_rule_attrs + ) + + elif type == RULE_TYPE_DSCP_MARKING: + qos_rule_attrs['dscp_mark'] = choice(VALID_DSCP_MARKS) + + qos_rule_attrs.update(attrs) + qos_rule = _qos_dscp_marking_rule.QoSDSCPMarkingRule(**qos_rule_attrs) + + elif type == RULE_TYPE_MINIMUM_BANDWIDTH: + qos_rule_attrs['min_kbps'] = randint(1, 10000) + qos_rule_attrs['direction'] = 'egress' + + qos_rule_attrs.update(attrs) + + qos_rule = _qos_minimum_bandwidth_rule.QoSMinimumBandwidthRule( + **qos_rule_attrs + ) + else: # type == RULE_TYPE_MINIMUM_PACKET_RATE: + qos_rule_attrs['min_kpps'] = randint(1, 10000) + qos_rule_attrs['direction'] = 'egress' + + qos_rule_attrs.update(attrs) + + qos_rule = _qos_minimum_packet_rate_rule.QoSMinimumPacketRateRule( + **qos_rule_attrs + ) + + return qos_rule + + +def create_qos_rules(attrs=None, count=2): + """Create multiple fake Network QoS rules. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of Network QoS rule to fake + :return: + A list of QoS Rules objects faking the Network QoS rules + """ + qos_rules = [] + for i in range(0, count): + qos_rules.append(create_one_qos_rule(attrs)) + return qos_rules + + +def get_qos_rules(qos_rules=None, count=2): + """Get a list of faked Network QoS rules. + + If Network QoS rules list is provided, then initialize the Mock + object with the list. Otherwise create one. + + :param List qos_rules: + A list of FakeResource objects faking Network QoS rules + :param int count: + The number of QoS minimum bandwidth rules to fake + :return: + An iterable Mock object with side_effect set to a list of faked + qos minimum bandwidth rules + """ + if qos_rules is None: + qos_rules = create_qos_rules(count) + return mock.Mock(side_effect=qos_rules) + + +def create_one_qos_rule_type(attrs=None): + """Create a fake Network QoS rule type. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A QoSRuleType object with name, id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + qos_rule_type_attrs = { + 'type': 'rule-type-' + uuid.uuid4().hex, + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + qos_rule_type_attrs.update(attrs) + qos_rule_type = _qos_rule_type.QoSRuleType(**qos_rule_type_attrs) + + return qos_rule_type + + +def create_qos_rule_types(attrs=None, count=2): + """Create multiple fake Network QoS rule types. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of QoS rule types to fake + :return: + A list of QoSRuleType objects faking the QoS rule types + """ + qos_rule_types = [] + for i in range(0, count): + qos_rule_types.append(create_one_qos_rule_type(attrs)) + + return qos_rule_types + + +def create_one_router(attrs=None): + """Create a fake router. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Router object, with id, name, admin_state_up, + status, project_id + """ + attrs = attrs or {} + + # Set default attributes. + router_attrs = { + 'id': 'router-id-' + uuid.uuid4().hex, + 'name': 'router-name-' + uuid.uuid4().hex, + 'status': 'ACTIVE', + 'is_admin_state_up': True, + 'description': 'router-description-' + uuid.uuid4().hex, + 'distributed': False, + 'ha': False, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'routes': [], + 'external_gateway_info': {}, + 'availability_zone_hints': [], + 'availability_zones': [], + 'tags': [], + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + router_attrs.update(attrs) + + router = _router.Router(**router_attrs) + router.tenant_id = None # unset deprecated opts + + return router + + +def create_routers(attrs=None, count=2): + """Create multiple fake routers. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of routers to fake + :return: + A list of Router objects faking the routers + """ + routers = [] + for i in range(0, count): + routers.append(create_one_router(attrs)) + + return routers + + +def get_routers(routers=None, count=2): + """Get an iterable Mock object with a list of faked routers. + + If routers list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List routers: + A list of Router objects faking routers + :param int count: + The number of routers to fake + :return: + An iterable Mock object with side_effect set to a list of faked + routers + """ + if routers is None: + routers = create_routers(count) + return mock.Mock(side_effect=routers) + + def create_one_local_ip(attrs=None): """Create a fake local ip. diff --git a/openstackclient/tests/unit/volume/v1/__init__.py b/openstackclient/tests/unit/network/v2/taas/__init__.py similarity index 100% rename from openstackclient/tests/unit/volume/v1/__init__.py rename to openstackclient/tests/unit/network/v2/taas/__init__.py diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py new file mode 100644 index 0000000000..8e4f185c8d --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py @@ -0,0 +1,276 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import operator +import uuid + +from openstack.network.v2 import tap_flow as _tap_flow +from openstack.network.v2 import tap_service as _tap_service +from openstack.test import fakes as sdk_fakes +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_flow as osc_tap_flow +from openstackclient.network.v2.taas import tap_service as osc_tap_service +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_flow._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapFlow(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'direction', + 'id', + 'name', + 'project_id', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.CreateTapFlow(self.app, None) + + def test_create_tap_flow(self): + """Test Create Tap Flow.""" + fake_tap_service = sdk_fakes.generate_fake_resource( + _tap_service.TapService + ) + port_id = str(uuid.uuid4()) + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_flow = sdk_fakes.generate_fake_resource( + _tap_flow.TapFlow, + **{ + 'source_port': port_id, + 'tap_service_id': fake_tap_service['id'], + 'direction': 'BOTH', + }, + ) + self.app.client_manager.network.create_tap_flow.return_value = ( + fake_tap_flow + ) + self.app.client_manager.network.find_port.return_value = fake_port + self.app.client_manager.network.find_tap_service.return_value = ( + fake_tap_service + ) + arg_list = [ + '--name', + fake_tap_flow['name'], + '--port', + fake_tap_flow['source_port'], + '--tap-service', + fake_tap_flow['tap_service_id'], + '--direction', + fake_tap_flow['direction'], + ] + + verify_list = [ + ('name', fake_tap_flow['name']), + ('port', fake_tap_flow['source_port']), + ('tap_service', fake_tap_flow['tap_service_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + mock_create_t_f = self.app.client_manager.network.create_tap_flow + mock_create_t_f.assert_called_once_with( + **{ + 'name': fake_tap_flow['name'], + 'source_port': fake_tap_flow['source_port'], + 'tap_service_id': fake_tap_flow['tap_service_id'], + 'direction': fake_tap_flow['direction'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_flow, osc_tap_service._get_columns(fake_tap_flow)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapFlow(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.ListTapFlow(self.app, None) + + def test_list_tap_flows(self): + """Test List Tap Flow.""" + fake_tap_flows = list( + sdk_fakes.generate_fake_resources(_tap_flow.TapFlow, count=2) + ) + self.app.client_manager.network.tap_flows.return_value = fake_tap_flows + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_flows.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_flow, columns_long) + for fake_tap_flow in fake_tap_flows + ], + ) + + +class TestDeleteTapFlow(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + self.cmd = osc_tap_flow.DeleteTapFlow(self.app, None) + + def test_delete_tap_flow(self): + """Test Delete tap flow.""" + + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, [fake_tap_flow['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_flow = self.app.client_manager.network.delete_tap_flow + mock_delete_tap_flow.assert_called_once_with(fake_tap_flow['id']) + self.assertIsNone(result) + + +class TestShowTapFlow(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'direction', + 'id', + 'name', + 'project_id', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + self.cmd = osc_tap_flow.ShowTapFlow(self.app, None) + + def test_show_tap_flow(self): + """Test Show tap flow.""" + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) + self.app.client_manager.network.get_tap_flow.return_value = ( + fake_tap_flow + ) + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, fake_tap_flow['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.get_tap_flow.assert_called_once_with( + fake_tap_flow['id'] + ) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_flow, osc_tap_service._get_columns(fake_tap_flow)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapFlow(network_fakes.TestNetworkV2): + _new_name = 'new_name' + + # NOTE(mtomaska): The Resource class from which TapFlow inherits from + # returns duplicate `ID and `Name` keys. + columns = ( + 'Direction', + 'ID', + 'ID', + 'Name', + 'Name', + 'Status', + 'Tenant', + 'description', + 'location', + 'project_id', + 'source_port', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.UpdateTapFlow(self.app, None) + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + + def test_update_tap_flow(self): + """Test update tap service""" + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) + new_tap_flow = copy.deepcopy(fake_tap_flow) + new_tap_flow['name'] = self._new_name + + self.app.client_manager.network.update_tap_flow.return_value = ( + new_tap_flow + ) + + arg_list = [ + fake_tap_flow['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_t_f = self.app.client_manager.network.update_tap_flow + mock_update_t_f.assert_called_once_with(new_tap_flow['id'], **attrs) + self.assertEqual(self.columns, columns) + self.assertEqual(_get_data(new_tap_flow, self.columns), data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py new file mode 100644 index 0000000000..10f3251c36 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py @@ -0,0 +1,288 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import operator +import uuid + +from openstack.network.v2 import tap_mirror +from openstack.test import fakes as sdk_fakes +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_mirror as osc_tap_mirror +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_mirror._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapMirror(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'project_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.CreateTapMirror(self.app, None) + + def test_create_tap_mirror(self): + port_id = str(uuid.uuid4()) + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror, **{'port_id': port_id, 'directions': 'IN=99'} + ) + self.app.client_manager.network.create_tap_mirror.return_value = ( + fake_tap_mirror + ) + self.app.client_manager.network.find_port.return_value = fake_port + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda _, name_or_id: {'id': name_or_id} + ) + arg_list = [ + '--name', + fake_tap_mirror['name'], + '--port', + fake_tap_mirror['port_id'], + '--directions', + fake_tap_mirror['directions'], + '--remote-ip', + fake_tap_mirror['remote_ip'], + '--mirror-type', + fake_tap_mirror['mirror_type'], + ] + + verify_directions = fake_tap_mirror['directions'].split('=') + verify_directions_dict = {verify_directions[0]: verify_directions[1]} + + verify_list = [ + ('name', fake_tap_mirror['name']), + ('port_id', fake_tap_mirror['port_id']), + ('directions', verify_directions_dict), + ('remote_ip', fake_tap_mirror['remote_ip']), + ('mirror_type', fake_tap_mirror['mirror_type']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_mirror.return_value = ( + fake_tap_mirror + ) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_m_mock = self.app.client_manager.network.create_tap_mirror + create_tap_m_mock.assert_called_once_with( + **{ + 'name': fake_tap_mirror['name'], + 'port_id': fake_tap_mirror['port_id'], + 'directions': verify_directions_dict, + 'remote_ip': fake_tap_mirror['remote_ip'], + 'mirror_type': fake_tap_mirror['mirror_type'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_mirror, osc_tap_mirror._get_columns(fake_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapMirror(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.ListTapMirror(self.app, None) + + def test_list_tap_mirror(self): + """Test List Tap Mirror.""" + fake_tap_mirrors = list( + sdk_fakes.generate_fake_resources(tap_mirror.TapMirror, count=4) + ) + self.app.client_manager.network.tap_mirrors.return_value = ( + fake_tap_mirrors + ) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_mirrors.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_mirror, columns_long) + for fake_tap_mirror in fake_tap_mirrors + ], + ) + + +class TestDeleteTapMirror(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + self.cmd = osc_tap_mirror.DeleteTapMirror(self.app, None) + + def test_delete_tap_mirror(self): + """Test Delete Tap Mirror.""" + + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror + ) + + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, [fake_tap_mirror['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_m = self.app.client_manager.network.delete_tap_mirror + mock_delete_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertIsNone(result) + + +class TestShowTapMirror(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'project_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + self.cmd = osc_tap_mirror.ShowTapMirror(self.app, None) + + def test_show_tap_mirror(self): + """Test Show Tap Mirror.""" + + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror + ) + self.app.client_manager.network.get_tap_mirror.return_value = ( + fake_tap_mirror + ) + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, fake_tap_mirror['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_m = self.app.client_manager.network.get_tap_mirror + mock_get_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_mirror, osc_tap_mirror._get_columns(fake_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapMirror(network_fakes.TestNetworkV2): + _new_name = 'new_name' + columns = ( + 'description', + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'project_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.UpdateTapMirror(self.app, None) + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + + def test_update_tap_mirror(self): + """Test update Tap Mirror""" + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror + ) + new_tap_mirror = copy.deepcopy(fake_tap_mirror) + new_tap_mirror['name'] = self._new_name + + self.app.client_manager.network.update_tap_mirror.return_value = ( + new_tap_mirror + ) + + arg_list = [ + fake_tap_mirror['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_m = self.app.client_manager.network.update_tap_mirror + mock_update_tap_m.assert_called_once_with( + fake_tap_mirror['id'], **attrs + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_mirror, osc_tap_mirror._get_columns(new_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py new file mode 100644 index 0000000000..fa766891ee --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py @@ -0,0 +1,271 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import operator +import uuid + +from openstack.network.v2 import tap_service +from openstack.test import fakes as sdk_fakes +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_service as osc_tap_service +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_service._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapService(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.CreateTapService(self.app, None) + + def test_create_tap_service(self): + """Test Create Tap Service.""" + port_id = str(uuid.uuid4()) + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService, **{'port_id': port_id} + ) + self.app.client_manager.network.create_tap_service.return_value = ( + fake_tap_service + ) + self.app.client_manager.network.find_port.return_value = fake_port + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda _, name_or_id: {'id': name_or_id} + ) + arg_list = [ + '--name', + fake_tap_service['name'], + '--port', + fake_tap_service['port_id'], + ] + + verify_list = [ + ('name', fake_tap_service['name']), + ('port_id', fake_tap_service['port_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_service.return_value = ( + fake_tap_service + ) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_s_mock = self.app.client_manager.network.create_tap_service + create_tap_s_mock.assert_called_once_with( + **{ + 'name': fake_tap_service['name'], + 'port_id': fake_tap_service['port_id'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_service, osc_tap_service._get_columns(fake_tap_service)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapService(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.ListTapService(self.app, None) + + def test_list_tap_service(self): + """Test List Tap Service.""" + fake_tap_services = list( + sdk_fakes.generate_fake_resources(tap_service.TapService, count=4) + ) + self.app.client_manager.network.tap_services.return_value = ( + fake_tap_services + ) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_services.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_service, columns_long) + for fake_tap_service in fake_tap_services + ], + ) + + +class TestDeleteTapService(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + self.cmd = osc_tap_service.DeleteTapService(self.app, None) + + def test_delete_tap_service(self): + """Test Delete tap service.""" + + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService + ) + + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, [fake_tap_service['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_s = self.app.client_manager.network.delete_tap_service + mock_delete_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertIsNone(result) + + +class TestShowTapService(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + self.cmd = osc_tap_service.ShowTapService(self.app, None) + + def test_show_tap_service(self): + """Test Show tap service.""" + + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService + ) + self.app.client_manager.network.get_tap_service.return_value = ( + fake_tap_service + ) + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, fake_tap_service['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_s = self.app.client_manager.network.get_tap_service + mock_get_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_service, osc_tap_service._get_columns(fake_tap_service)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapService(network_fakes.TestNetworkV2): + _new_name = 'new_name' + + columns = ( + 'description', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.UpdateTapService(self.app, None) + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + + def test_update_tap_service(self): + """Test update tap service""" + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService + ) + new_tap_service = copy.deepcopy(fake_tap_service) + new_tap_service['name'] = self._new_name + + self.app.client_manager.network.update_tap_service.return_value = ( + new_tap_service + ) + + arg_list = [ + fake_tap_service['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_s = self.app.client_manager.network.update_tap_service + mock_update_tap_s.assert_called_once_with( + fake_tap_service['id'], **attrs + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_service, osc_tap_service._get_columns(new_tap_service)[1] + ) + self.assertEqual(fake_data, data) diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index 411d199237..48f706631f 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -58,8 +57,8 @@ class TestCreateAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.create_address_group = mock.Mock( - return_value=self.new_address_group + self.network_client.create_address_group.return_value = ( + self.new_address_group ) # Get the command object to test @@ -145,7 +144,7 @@ class TestDeleteAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.delete_address_group = mock.Mock(return_value=None) + self.network_client.delete_address_group.return_value = None self.network_client.find_address_group = ( network_fakes.get_address_groups( address_groups=self._address_groups @@ -206,9 +205,7 @@ def test_multi_address_groups_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._address_groups[0], exceptions.CommandError] - self.network_client.find_address_group = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_address_group.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -251,9 +248,7 @@ class TestListAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.address_groups = mock.Mock( - return_value=self.address_groups - ) + self.network_client.address_groups.return_value = self.address_groups # Get the command object to test self.cmd = address_group.ListAddressGroup(self.app, None) @@ -333,13 +328,15 @@ class TestSetAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.update_address_group = mock.Mock(return_value=None) - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.update_address_group.return_value = None + self.network_client.find_address_group.return_value = ( + self._address_group ) - self.network_client.add_addresses_to_address_group = mock.Mock( - return_value=self._address_group + + self.network_client.add_addresses_to_address_group.return_value = ( + self._address_group ) + # Get the command object to test self.cmd = address_group.SetAddressGroup(self.app, None) @@ -442,8 +439,8 @@ class TestShowAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) # Get the command object to test @@ -486,12 +483,12 @@ class TestUnsetAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group - ) - self.network_client.remove_addresses_from_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) + + self.network_client.remove_addresses_from_address_group.return_value = self._address_group + # Get the command object to test self.cmd = address_group.UnsetAddressGroup(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py index c3eb83d060..6e2c05ed9c 100644 --- a/openstackclient/tests/unit/network/v2/test_address_scope.py +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -52,8 +51,8 @@ class TestCreateAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.create_address_scope = mock.Mock( - return_value=self.new_address_scope + self.network_client.create_address_scope.return_value = ( + self.new_address_scope ) # Get the command object to test @@ -160,7 +159,7 @@ class TestDeleteAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.delete_address_scope = mock.Mock(return_value=None) + self.network_client.delete_address_scope.return_value = None self.network_client.find_address_scope = ( network_fakes.get_address_scopes( address_scopes=self._address_scopes @@ -222,9 +221,7 @@ def test_multi_address_scopes_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._address_scopes[0], exceptions.CommandError] - self.network_client.find_address_scope = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_address_scope.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -267,9 +264,7 @@ class TestListAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.address_scopes = mock.Mock( - return_value=self.address_scopes - ) + self.network_client.address_scopes.return_value = self.address_scopes # Get the command object to test self.cmd = address_scope.ListAddressScope(self.app, None) @@ -398,9 +393,9 @@ class TestSetAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.update_address_scope = mock.Mock(return_value=None) - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.update_address_scope.return_value = None + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test @@ -488,8 +483,8 @@ class TestShowAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py index f1e9e28cc1..c44e553c76 100644 --- a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -11,11 +11,9 @@ # under the License. # -from unittest import mock from unittest.mock import call import uuid -from openstack.network.v2 import _proxy from openstack.network.v2 import ( default_security_group_rule as _default_security_group_rule, ) @@ -28,18 +26,7 @@ from openstackclient.tests.unit import utils as tests_utils -class TestDefaultSecurityGroupRule(network_fakes.TestNetworkV2): - def setUp(self): - super().setUp() - - self.app.client_manager.sdk_connection = mock.Mock() - self.app.client_manager.sdk_connection.network = mock.Mock( - spec=_proxy.Proxy, - ) - self.sdk_client = self.app.client_manager.sdk_connection.network - - -class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestCreateDefaultSecurityGroupRule(network_fakes.TestNetworkV2): expected_columns = ( 'description', 'direction', @@ -79,10 +66,10 @@ def _setup_default_security_group_rule(self, attrs=None): default_security_group_rule_attrs.update(attrs) self._default_sg_rule = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, - **default_security_group_rule_attrs + **default_security_group_rule_attrs, ) - self.sdk_client.create_default_security_group_rule.return_value = ( + self.network_client.create_default_security_group_rule.return_value = ( self._default_sg_rule ) self.expected_data = ( @@ -208,7 +195,7 @@ def test_create_default_rule(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -223,7 +210,9 @@ def test_create_default_rule(self): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) - def test_create_protocol_any(self): + def _test_create_protocol_any_helper( + self, for_default_sg=False, for_custom_sg=False + ): self._setup_default_security_group_rule( { 'protocol': None, @@ -236,27 +225,47 @@ def test_create_protocol_any(self): '--remote-ip', self._default_sg_rule.remote_ip_prefix, ] + if for_default_sg: + arglist.append('--for-default-sg') + if for_custom_sg: + arglist.append('--for-custom-sg') verifylist = [ ('protocol', 'any'), ('remote_ip', self._default_sg_rule.remote_ip_prefix), + ('for_default_sg', for_default_sg), + ('for_custom_sg', for_custom_sg), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, 'protocol': self._default_sg_rule.protocol, 'remote_ip_prefix': self._default_sg_rule.remote_ip_prefix, - 'used_in_default_sg': False, - 'used_in_non_default_sg': False, + 'used_in_default_sg': for_default_sg, + 'used_in_non_default_sg': for_custom_sg, } ) self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) + def test_create_protocol_any_not_for_default_sg(self): + self._test_create_protocol_any_helper() + + def test_create_protocol_any_for_default_sg(self): + self._test_create_protocol_any_helper(for_default_sg=True) + + def test_create_protocol_any_for_custom_sg(self): + self._test_create_protocol_any_helper(for_custom_sg=True) + + def test_create_protocol_any_for_default_and_custom_sg(self): + self._test_create_protocol_any_helper( + for_default_sg=True, for_custom_sg=True + ) + def test_create_remote_address_group(self): self._setup_default_security_group_rule( { @@ -280,7 +289,7 @@ def test_create_remote_address_group(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -325,7 +334,7 @@ def test_create_remote_group(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -359,7 +368,7 @@ def test_create_source_group(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -393,7 +402,7 @@ def test_create_source_ip(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -427,7 +436,7 @@ def test_create_remote_ip(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -577,7 +586,7 @@ def test_create_icmp_type(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -615,7 +624,7 @@ def test_create_icmp_type_zero(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -653,7 +662,7 @@ def test_create_icmp_type_greater_than_zero(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -691,7 +700,7 @@ def test_create_icmp_type_negative_value(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -732,7 +741,7 @@ def test_create_ipv6_icmp_type_code(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -772,7 +781,7 @@ def test_create_icmpv6_type(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -803,7 +812,7 @@ def test_create_with_description(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'description': self._default_sg_rule.description, 'direction': self._default_sg_rule.direction, @@ -818,7 +827,7 @@ def test_create_with_description(self): self.assertEqual(self.expected_data, data) -class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestDeleteDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The default security group rules to be deleted. default_security_group_rule_attrs = { 'direction': 'ingress', @@ -844,7 +853,9 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.delete_default_security_group_rule.return_value = None + self.network_client.delete_default_security_group_rule.return_value = ( + None + ) # Get the command object to test self.cmd = default_security_group_rule.DeleteDefaultSecurityGroupRule( @@ -858,7 +869,7 @@ def test_default_security_group_rule_delete(self): verifylist = [ ('rule', [self._default_sg_rules[0].id]), ] - self.sdk_client.find_default_security_group_rule.return_value = ( + self.network_client.find_default_security_group_rule.return_value = ( self._default_sg_rules[0] ) @@ -866,7 +877,7 @@ def test_default_security_group_rule_delete(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self.network_client.delete_default_security_group_rule.assert_called_once_with( self._default_sg_rules[0] ) self.assertIsNone(result) @@ -880,7 +891,7 @@ def test_multi_default_security_group_rules_delete(self): verifylist = [ ('rule', arglist), ] - self.sdk_client.find_default_security_group_rule.side_effect = ( + self.network_client.find_default_security_group_rule.side_effect = ( self._default_sg_rules ) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -890,7 +901,7 @@ def test_multi_default_security_group_rules_delete(self): calls = [] for s in self._default_sg_rules: calls.append(call(s)) - self.sdk_client.delete_default_security_group_rule.assert_has_calls( + self.network_client.delete_default_security_group_rule.assert_has_calls( calls ) self.assertIsNone(result) @@ -909,8 +920,8 @@ def test_multi_default_security_group_rules_delete_with_exception(self): self._default_sg_rules[0], exceptions.CommandError, ] - self.sdk_client.find_default_security_group_rule = mock.Mock( - side_effect=find_mock_result + self.network_client.find_default_security_group_rule.side_effect = ( + find_mock_result ) try: @@ -919,26 +930,26 @@ def test_multi_default_security_group_rules_delete_with_exception(self): except exceptions.CommandError as e: self.assertEqual('1 of 2 default rules failed to delete.', str(e)) - self.sdk_client.find_default_security_group_rule.assert_any_call( + self.network_client.find_default_security_group_rule.assert_any_call( self._default_sg_rules[0].id, ignore_missing=False ) - self.sdk_client.find_default_security_group_rule.assert_any_call( + self.network_client.find_default_security_group_rule.assert_any_call( 'unexist_rule', ignore_missing=False ) - self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self.network_client.delete_default_security_group_rule.assert_called_once_with( self._default_sg_rules[0] ) -class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestListDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The security group rule to be listed. _default_sg_rule_tcp = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, - **{'protocol': 'tcp', 'port_range_max': 80, 'port_range_min': 80} + **{'protocol': 'tcp', 'port_range_max': 80, 'port_range_min': 80}, ) _default_sg_rule_icmp = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, - **{'protocol': 'icmp', 'remote_ip_prefix': '10.0.2.0/24'} + **{'protocol': 'icmp', 'remote_ip_prefix': '10.0.2.0/24'}, ) _default_sg_rules = [ _default_sg_rule_tcp, @@ -979,7 +990,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.default_security_group_rules.return_value = ( + self.network_client.default_security_group_rules.return_value = ( self._default_sg_rules ) @@ -994,7 +1005,7 @@ def test_list_default(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{} ) self.assertEqual(self.expected_columns, columns) @@ -1013,7 +1024,7 @@ def test_list_with_protocol(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'protocol': 'tcp', } @@ -1033,7 +1044,7 @@ def test_list_with_ingress(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'direction': 'ingress', } @@ -1053,7 +1064,7 @@ def test_list_with_wrong_egress(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'direction': 'egress', } @@ -1062,7 +1073,7 @@ def test_list_with_wrong_egress(self): self.assertEqual(self.expected_data, list(data)) -class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestShowDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The default security group rule to be shown. _default_sg_rule = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule @@ -1101,7 +1112,7 @@ class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.find_default_security_group_rule.return_value = ( + self.network_client.find_default_security_group_rule.return_value = ( self._default_sg_rule ) @@ -1126,7 +1137,7 @@ def test_show_all_options(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_default_security_group_rule.assert_called_once_with( + self.network_client.find_default_security_group_rule.assert_called_once_with( self._default_sg_rule.id, ignore_missing=False ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py index d310f199af..89137d6e98 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py @@ -73,7 +73,7 @@ def test_floating_ip_create_default(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) fip_mock.assert_called_once_with( - self.compute_sdk_client, self._floating_ip['pool'] + self.compute_client, self._floating_ip['pool'] ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -103,7 +103,7 @@ def test_floating_ip_delete(self, fip_mock): result = self.cmd.take_action(parsed_args) fip_mock.assert_called_once_with( - self.compute_sdk_client, self._floating_ips[0]['id'] + self.compute_client, self._floating_ips[0]['id'] ) self.assertIsNone(result) @@ -122,12 +122,8 @@ def test_floating_ip_delete_multi(self, fip_mock): fip_mock.assert_has_calls( [ - mock.call( - self.compute_sdk_client, self._floating_ips[0]['id'] - ), - mock.call( - self.compute_sdk_client, self._floating_ips[1]['id'] - ), + mock.call(self.compute_client, self._floating_ips[0]['id']), + mock.call(self.compute_client, self._floating_ips[1]['id']), ] ) self.assertIsNone(result) @@ -157,11 +153,9 @@ def test_floating_ip_delete_multi_exception(self, fip_mock): self.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) fip_mock.assert_any_call( - self.compute_sdk_client, self._floating_ips[0]['id'] - ) - fip_mock.assert_any_call( - self.compute_sdk_client, 'unexist_floating_ip' + self.compute_client, self._floating_ips[0]['id'] ) + fip_mock.assert_any_call(self.compute_client, 'unexist_floating_ip') @mock.patch.object(compute_v2, 'list_floating_ips') @@ -203,7 +197,7 @@ def test_floating_ip_list(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with(self.compute_sdk_client) + fip_mock.assert_called_once_with(self.compute_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -248,7 +242,7 @@ def test_floating_ip_show(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) fip_mock.assert_called_once_with( - self.compute_sdk_client, self._floating_ip['id'] + self.compute_client, self._floating_ip['id'] ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) 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 34ca21ecd4..ab0ec176a0 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -11,9 +11,11 @@ # under the License. # -from unittest import mock from unittest.mock import call +from openstack.network.v2 import floating_ip as _floating_ip +from openstack.test import fakes as sdk_fakes +from osc_lib.cli import format_columns from osc_lib import exceptions from openstackclient.network.v2 import floating_ip as fip @@ -83,16 +85,14 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.create_ip = mock.Mock( - return_value=self.floating_ip - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_ip.return_value = self.floating_ip - self.network_client.find_network = mock.Mock( - return_value=self.floating_network - ) - self.network_client.find_subnet = mock.Mock(return_value=self.subnet) - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.set_tags.return_value = None + + self.network_client.find_network.return_value = self.floating_network + + self.network_client.find_subnet.return_value = self.subnet + self.network_client.find_port.return_value = self.port # Get the command object to test self.cmd = fip.CreateFloatingIP(self.app, None) @@ -231,10 +231,8 @@ def test_floating_ip_create_project_domain(self): self.assertEqual(self.data, data) def test_create_floating_ip_with_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy arglist = [ '--qos-policy', qos_policy.id, @@ -303,7 +301,7 @@ class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.delete_ip = mock.Mock(return_value=None) + self.network_client.delete_ip.return_value = None # Get the command object to test self.cmd = fip.DeleteFloatingIP(self.app, None) @@ -413,7 +411,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): 'id': 'fake_port_id', } ) - fake_router = network_fakes.FakeRouter.create_one_router( + fake_router = network_fakes.create_one_router( { 'id': 'fake_router_id', } @@ -469,14 +467,11 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.ips = mock.Mock(return_value=self.floating_ips) - self.network_client.find_network = mock.Mock( - return_value=self.fake_network - ) - self.network_client.find_port = mock.Mock(return_value=self.fake_port) - self.network_client.find_router = mock.Mock( - return_value=self.fake_router - ) + self.network_client.ips.return_value = self.floating_ips + self.network_client.find_network.return_value = self.fake_network + + self.network_client.find_port.return_value = self.fake_port + self.network_client.find_router.return_value = self.fake_router # Get the command object to test self.cmd = fip.ListFloatingIP(self.app, None) @@ -498,7 +493,7 @@ def test_floating_ip_list_network(self): 'fake_network_id', ] verifylist = [ - ('network', 'fake_network_id'), + ('networks', ['fake_network_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -506,7 +501,7 @@ def test_floating_ip_list_network(self): self.network_client.ips.assert_called_once_with( **{ - 'floating_network_id': 'fake_network_id', + 'floating_network_id': ['fake_network_id'], } ) self.assertEqual(self.columns, columns) @@ -518,7 +513,7 @@ def test_floating_ip_list_port(self): 'fake_port_id', ] verifylist = [ - ('port', 'fake_port_id'), + ('ports', ['fake_port_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -526,7 +521,7 @@ def test_floating_ip_list_port(self): self.network_client.ips.assert_called_once_with( **{ - 'port_id': 'fake_port_id', + 'port_id': ['fake_port_id'], } ) self.assertEqual(self.columns, columns) @@ -659,7 +654,7 @@ def test_floating_ip_list_router(self): '--long', ] verifylist = [ - ('router', 'fake_router_id'), + ('routers', ['fake_router_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -667,7 +662,7 @@ def test_floating_ip_list_router(self): self.network_client.ips.assert_called_once_with( **{ - 'router_id': 'fake_router_id', + 'router_id': ['fake_router_id'], } ) self.assertEqual(self.columns_long, columns) @@ -706,45 +701,56 @@ def test_list_with_tag_options(self): class TestShowFloatingIPNetwork(TestFloatingIPNetwork): - # The floating ip to display. - floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - - columns = ( - 'description', - 'dns_domain', - 'dns_name', - 'fixed_ip_address', - 'floating_ip_address', - 'floating_network_id', - 'id', - 'port_id', - 'project_id', - 'qos_policy_id', - 'router_id', - 'status', - 'tags', - ) - - data = ( - floating_ip.description, - floating_ip.dns_domain, - floating_ip.dns_name, - floating_ip.fixed_ip_address, - floating_ip.floating_ip_address, - floating_ip.floating_network_id, - floating_ip.id, - floating_ip.port_id, - floating_ip.project_id, - floating_ip.qos_policy_id, - floating_ip.router_id, - floating_ip.status, - floating_ip.tags, - ) - def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.floating_ip = sdk_fakes.generate_fake_resource( + _floating_ip.FloatingIP + ) + self.network_client.find_ip.return_value = self.floating_ip + + self.columns = ( + 'created_at', + 'description', + 'dns_domain', + 'dns_name', + 'fixed_ip_address', + 'floating_ip_address', + 'floating_network_id', + 'id', + 'name', + 'port_details', + 'port_id', + 'project_id', + 'qos_policy_id', + 'revision_number', + 'router_id', + 'status', + 'subnet_id', + 'tags', + 'updated_at', + ) + self.data = ( + self.floating_ip.created_at, + self.floating_ip.description, + self.floating_ip.dns_domain, + self.floating_ip.dns_name, + self.floating_ip.fixed_ip_address, + self.floating_ip.floating_ip_address, + self.floating_ip.floating_network_id, + self.floating_ip.id, + self.floating_ip.name, + format_columns.DictColumn(self.floating_ip.port_details), + self.floating_ip.port_id, + self.floating_ip.project_id, + self.floating_ip.qos_policy_id, + self.floating_ip.revision_number, + self.floating_ip.router_id, + self.floating_ip.status, + self.floating_ip.subnet_id, + self.floating_ip.tags, + self.floating_ip.updated_at, + ) # Get the command object to test self.cmd = fip.ShowFloatingIP(self.app, None) @@ -785,10 +791,10 @@ class TestSetFloatingIP(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) - self.network_client.find_port = mock.Mock(return_value=self.port) - self.network_client.update_ip = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_ip.return_value = self.floating_ip + self.network_client.find_port.return_value = self.port + self.network_client.update_ip.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = fip.SetFloatingIP(self.app, None) @@ -879,10 +885,8 @@ def test_description_option(self): ) def test_qos_policy_option(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy arglist = [ "--qos-policy", qos_policy.id, @@ -908,10 +912,8 @@ def test_qos_policy_option(self): ) def test_port_and_qos_policy_option(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy arglist = [ "--qos-policy", qos_policy.id, @@ -1036,9 +1038,9 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) - self.network_client.update_ip = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_ip.return_value = self.floating_ip + self.network_client.update_ip.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = fip.UnsetFloatingIP(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py index 26fc3918e5..90ded06280 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py @@ -44,6 +44,6 @@ def test_floating_ip_list(self, fipp_mock): columns, data = self.cmd.take_action(parsed_args) - fipp_mock.assert_called_once_with(self.compute_sdk_client) + fipp_mock.assert_called_once_with(self.compute_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) 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 6c0e163ce4..33b9011c65 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 @@ -33,7 +33,7 @@ def setUp(self): ) self.port = network_fakes.create_one_port() self.project = identity_fakes_v2.FakeProject.create_one_project() - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.find_port.return_value = self.port class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): @@ -54,11 +54,11 @@ def setUp(self): }, ) - self.network_client.create_floating_ip_port_forwarding = mock.Mock( - return_value=self.new_port_forwarding + self.network_client.create_floating_ip_port_forwarding.return_value = ( + self.new_port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.CreateFloatingIPPortForwarding( @@ -153,7 +153,7 @@ def test_create_all_options_with_range(self): 'internal_port_id': self.new_port_forwarding_with_ranges.internal_port_id, # noqa: E501 'protocol': self.new_port_forwarding_with_ranges.protocol, 'description': self.new_port_forwarding_with_ranges.description, # noqa: E501 - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -334,7 +334,7 @@ def test_create_all_options(self): 'internal_port_id': self.new_port_forwarding.internal_port_id, 'protocol': self.new_port_forwarding.protocol, 'description': self.new_port_forwarding.description, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -351,11 +351,11 @@ def setUp(self): }, ) ) - self.network_client.delete_floating_ip_port_forwarding = mock.Mock( - return_value=None + self.network_client.delete_floating_ip_port_forwarding.return_value = ( + None ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.DeleteFloatingIPPortForwarding( self.app, None @@ -490,10 +490,11 @@ def setUp(self): port_forwarding.description, ) ) - self.network_client.floating_ip_port_forwardings = mock.Mock( - return_value=self.port_forwardings + self.network_client.floating_ip_port_forwardings.return_value = ( + self.port_forwardings ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.ListFloatingIPPortForwarding( self.app, None @@ -557,14 +558,15 @@ def setUp(self): 'floatingip_id': self.floating_ip.id, } ) - self.network_client.update_floating_ip_port_forwarding = mock.Mock( - return_value=None + self.network_client.update_floating_ip_port_forwarding.return_value = ( + None ) - self.network_client.find_floating_ip_port_forwarding = mock.Mock( - return_value=self._port_forwarding + self.network_client.find_floating_ip_port_forwarding.return_value = ( + self._port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.SetFloatingIPPortForwarding( self.app, None @@ -587,7 +589,7 @@ def test_set_nothing(self): self.network_client.update_floating_ip_port_forwarding.assert_called_with( self._port_forwarding.floatingip_id, self._port_forwarding.id, - **attrs + **attrs, ) self.assertIsNone(result) @@ -648,7 +650,7 @@ def run_and_validate(arglist, verifylist, attrs): self.network_client.update_floating_ip_port_forwarding.assert_called_with( self._port_forwarding.floatingip_id, self._port_forwarding.id, - **attrs + **attrs, ) self.assertIsNone(result) @@ -690,10 +692,11 @@ def setUp(self): self._port_forwarding.internal_port_range, self._port_forwarding.protocol, ) - self.network_client.find_floating_ip_port_forwarding = mock.Mock( - return_value=self._port_forwarding + self.network_client.find_floating_ip_port_forwarding.return_value = ( + self._port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.ShowFloatingIPPortForwarding( self.app, None diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index 4224d12666..def3e17da0 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from osc_lib.cli import format_columns @@ -55,8 +54,8 @@ def setUp(self): super().setUp() self.cmd = ip_availability.ListIPAvailability(self.app, None) - self.network_client.network_ip_availabilities = mock.Mock( - return_value=self._ip_availability + self.network_client.network_ip_availabilities.return_value = ( + self._ip_availability ) def test_list_no_options(self): @@ -134,13 +133,12 @@ class TestShowIPAvailability(TestIPAvailability): def setUp(self): super().setUp() - self.network_client.find_network_ip_availability = mock.Mock( - return_value=self._ip_availability - ) - self.network_client.find_network = mock.Mock( - return_value=self._network + self.network_client.find_network_ip_availability.return_value = ( + self._ip_availability ) + self.network_client.find_network.return_value = self._network + # Get the command object to test self.cmd = ip_availability.ShowIPAvailability(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py index c7aadf0bfe..0769e2e561 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from osc_lib import exceptions @@ -24,8 +23,8 @@ class TestConntrackHelper(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - self.router = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock(return_value=self.router) + self.router = network_fakes.create_one_router() + self.network_client.find_router.return_value = self.router class TestCreateL3ConntrackHelper(TestConntrackHelper): @@ -46,8 +45,8 @@ def setUp(self): self.ct_helper.protocol, self.ct_helper.router_id, ) - self.network_client.create_conntrack_helper = mock.Mock( - return_value=self.ct_helper + self.network_client.create_conntrack_helper.return_value = ( + self.ct_helper ) # Get the command object to test @@ -119,9 +118,7 @@ def setUp(self): attrs ) ) - self.network_client.delete_conntrack_helper = mock.Mock( - return_value=None - ) + self.network_client.delete_conntrack_helper.return_value = None # Get the command object to test self.cmd = l3_conntrack_helper.DeleteConntrackHelper(self.app, None) @@ -181,9 +178,7 @@ def setUp(self): ct_helper.port, ) ) - self.network_client.conntrack_helpers = mock.Mock( - return_value=ct_helpers - ) + self.network_client.conntrack_helpers.return_value = ct_helpers # Get the command object to test self.cmd = l3_conntrack_helper.ListConntrackHelper(self.app, None) @@ -216,9 +211,7 @@ def setUp(self): attrs ) ) - self.network_client.update_conntrack_helper = mock.Mock( - return_value=None - ) + self.network_client.update_conntrack_helper.return_value = None # Get the command object to test self.cmd = l3_conntrack_helper.SetConntrackHelper(self.app, None) @@ -281,9 +274,7 @@ def setUp(self): self.ct_helper.protocol, self.ct_helper.router_id, ) - self.network_client.get_conntrack_helper = mock.Mock( - return_value=self.ct_helper - ) + self.network_client.get_conntrack_helper.return_value = self.ct_helper # Get the command object to test self.cmd = l3_conntrack_helper.ShowConntrackHelper(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py index cf4105b02d..585fec767c 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip.py @@ -13,7 +13,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -77,13 +76,11 @@ class TestCreateLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.create_local_ip = mock.Mock( - return_value=self.new_local_ip - ) - self.network_client.find_network = mock.Mock( - return_value=self.local_ip_network - ) - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.create_local_ip.return_value = self.new_local_ip + + self.network_client.find_network.return_value = self.local_ip_network + + self.network_client.find_port.return_value = self.port # Get the command object to test self.cmd = local_ip.CreateLocalIP(self.app, None) @@ -149,7 +146,7 @@ class TestDeleteLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.delete_local_ip = mock.Mock(return_value=None) + self.network_client.delete_local_ip.return_value = None self.network_client.find_local_ip = network_fakes.get_local_ips( local_ips=self._local_ips ) @@ -205,9 +202,7 @@ def test_multi_local_ips_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._local_ips[0], exceptions.CommandError] - self.network_client.find_local_ip = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_local_ip.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -258,10 +253,8 @@ class TestListLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.local_ips = mock.Mock(return_value=self.local_ips) - self.network_client.find_network = mock.Mock( - return_value=self.fake_network - ) + self.network_client.local_ips.return_value = self.local_ips + self.network_client.find_network.return_value = self.fake_network # Get the command object to test self.cmd = local_ip.ListLocalIP(self.app, None) @@ -402,10 +395,8 @@ class TestSetLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.update_local_ip = mock.Mock(return_value=None) - self.network_client.find_local_ip = mock.Mock( - return_value=self._local_ip - ) + self.network_client.update_local_ip.return_value = None + self.network_client.find_local_ip.return_value = self._local_ip # Get the command object to test self.cmd = local_ip.SetLocalIP(self.app, None) @@ -482,9 +473,7 @@ class TestShowLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.find_local_ip = mock.Mock( - return_value=self._local_ip - ) + self.network_client.find_local_ip.return_value = self._local_ip # Get the command object to test self.cmd = local_ip.ShowLocalIP(self.app, None) 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 35a1045433..9efdc295f1 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 @@ def setUp(self): self.local_ip = network_fakes.create_one_local_ip() self.fixed_port = network_fakes.create_one_port() self.project = identity_fakes_v2.FakeProject.create_one_project() - self.network_client.find_port = mock.Mock(return_value=self.fixed_port) + self.network_client.find_port.return_value = self.fixed_port class TestCreateLocalIPAssociation(TestLocalIPAssociation): @@ -43,13 +43,11 @@ def setUp(self): } ) ) - self.network_client.create_local_ip_association = mock.Mock( - return_value=self.new_local_ip_association + self.network_client.create_local_ip_association.return_value = ( + self.new_local_ip_association ) - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) + self.network_client.find_local_ip.return_value = self.local_ip # Get the command object to test self.cmd = local_ip_association.CreateLocalIPAssociation( @@ -86,7 +84,7 @@ def test_create_no_options(self): self.new_local_ip_association.local_ip_id, **{ 'fixed_port_id': self.new_local_ip_association.fixed_port_id, - } + }, ) self.assertEqual(set(self.columns), set(columns)) self.assertEqual(set(self.data), set(data)) @@ -111,7 +109,7 @@ def test_create_all_options(self): **{ 'fixed_port_id': self.new_local_ip_association.fixed_port_id, 'fixed_ip': self.new_local_ip_association.fixed_ip, - } + }, ) self.assertEqual(set(self.columns), set(columns)) self.assertEqual(set(self.data), set(data)) @@ -128,13 +126,10 @@ def setUp(self): }, ) ) - self.network_client.delete_local_ip_association = mock.Mock( - return_value=None - ) + self.network_client.delete_local_ip_association.return_value = None + + self.network_client.find_local_ip.return_value = self.local_ip - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) # Get the command object to test self.cmd = local_ip_association.DeleteLocalIPAssociation( self.app, None @@ -262,13 +257,13 @@ def setUp(self): lip_assoc.host, ) ) - self.network_client.local_ip_associations = mock.Mock( - return_value=self.local_ip_associations + self.network_client.local_ip_associations.return_value = ( + self.local_ip_associations ) - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) - self.network_client.find_port = mock.Mock(return_value=self.fixed_port) + + self.network_client.find_local_ip.return_value = self.local_ip + + self.network_client.find_port.return_value = self.fixed_port # Get the command object to test self.cmd = local_ip_association.ListLocalIPAssociation(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index bcbc0ae7da..0fe8740da5 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -30,12 +29,10 @@ def setUp(self): # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity_client.domains - self.router = network_fakes.FakeRouter.create_one_router( - {'id': 'fake-router-id'} - ) - self.network_client.find_router = mock.Mock(return_value=self.router) + self.router = network_fakes.create_one_router({'id': 'fake-router-id'}) + self.network_client.find_router.return_value = self.router self.port = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.find_port.return_value = self.port class TestCreateNDPProxy(TestNDPProxy): @@ -68,9 +65,7 @@ def setUp(self): self.ndp_proxy.router_id, self.ndp_proxy.updated_at, ) - self.network_client.create_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.create_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.CreateNDPProxy(self.app, None) @@ -129,10 +124,8 @@ def setUp(self): attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxies = network_fakes.create_ndp_proxies(attrs) self.ndp_proxy = self.ndp_proxies[0] - self.network_client.delete_ndp_proxy = mock.Mock(return_value=None) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.delete_ndp_proxy.return_value = None + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.DeleteNDPProxy(self.app, None) @@ -205,7 +198,7 @@ def setUp(self): ) ) - self.network_client.ndp_proxies = mock.Mock(return_value=ndp_proxies) + self.network_client.ndp_proxies.return_value = ndp_proxies # Get the command object to test self.cmd = ndp_proxy.ListNDPProxy(self.app, None) @@ -313,7 +306,7 @@ def test_ndp_proxy_list_project(self): **{'project_id': project.id} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_ndp_proxy_list_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -334,7 +327,7 @@ def test_ndp_proxy_list_project_domain(self): self.network_client.ndp_proxies.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestSetNDPProxy(TestNDPProxy): @@ -342,10 +335,8 @@ def setUp(self): super().setUp() attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxy = network_fakes.create_one_ndp_proxy(attrs) - self.network_client.update_ndp_proxy = mock.Mock(return_value=None) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.update_ndp_proxy.return_value = None + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.SetNDPProxy(self.app, None) @@ -436,12 +427,9 @@ def setUp(self): self.ndp_proxy.router_id, self.ndp_proxy.updated_at, ) - self.network_client.get_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.get_ndp_proxy.return_value = self.ndp_proxy + + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.ShowNDPProxy(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 33fe5be3a5..1e923d053f 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -12,7 +12,6 @@ # import random -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -47,7 +46,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'availability_zone_hints': ["nova"], } ) - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( + qos_policy = network_fakes.create_one_qos_policy( attrs={'id': _network.qos_policy_id} ) @@ -63,6 +62,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'ipv6_address_scope', 'is_default', 'is_vlan_transparent', + 'is_vlan_qinq', 'mtu', 'name', 'port_security_enabled', @@ -103,6 +103,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): network.RouterExternalColumn(_network.is_router_external), _network.is_shared, _network.is_vlan_transparent, + _network.is_vlan_qinq, _network.status, _network.segments, format_columns.ListColumn(_network.subnet_ids), @@ -114,19 +115,16 @@ class TestCreateNetworkIdentityV3(TestNetwork): def setUp(self): super().setUp() - self.network_client.create_network = mock.Mock( - return_value=self._network - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_network.return_value = self._network + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_qos_policy.return_value = self.qos_policy def test_create_no_options(self): arglist = [] @@ -190,6 +188,7 @@ def test_create_all_options(self): "--qos-policy", self.qos_policy.id, "--transparent-vlan", + "--no-qinq-vlan", "--enable-port-security", "--dns-domain", "example.org.", @@ -210,6 +209,7 @@ def test_create_all_options(self): ('segmentation_id', '400'), ('qos_policy', self.qos_policy.id), ('transparent_vlan', True), + ('qinq_vlan', False), ('enable_port_security', True), ('name', self._network.name), ('dns_domain', 'example.org.'), @@ -234,6 +234,7 @@ def test_create_all_options(self): 'provider:segmentation_id': '400', 'qos_policy_id': self.qos_policy.id, 'vlan_transparent': True, + 'vlan_qinq': False, 'port_security_enabled': True, 'dns_domain': 'example.org.', } @@ -246,6 +247,7 @@ def test_create_other_options(self): "--enable", "--no-share", "--disable-port-security", + "--qinq-vlan", self._network.name, ] verifylist = [ @@ -253,6 +255,7 @@ def test_create_other_options(self): ('no_share', True), ('name', self._network.name), ('external', False), + ('qinq_vlan', True), ('disable_port_security', True), ] @@ -264,6 +267,7 @@ def test_create_other_options(self): 'admin_state_up': True, 'name': self._network.name, 'shared': False, + 'vlan_qinq': True, 'port_security_enabled': False, } ) @@ -309,6 +313,36 @@ def test_create_with_tags(self): def test_create_with_no_tag(self): self._test_create_with_tag(add_tags=False) + def test_create_with_vlan_qinq_and_transparency_enabled(self): + arglist = [ + "--transparent-vlan", + "--qinq-vlan", + self._network.name, + ] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + def test_create_with_provider_segment_without_provider_type(self): + arglist = [ + "--provider-segment", + "123", + self._network.name, + ] + verifylist = [ + ('provider_network_type', None), + ('segmentation_id', "123"), + ('name', self._network.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + class TestCreateNetworkIdentityV2( identity_fakes_v2.FakeClientMixin, @@ -332,6 +366,7 @@ class TestCreateNetworkIdentityV2( 'ipv6_address_scope', 'is_default', 'is_vlan_transparent', + 'is_vlan_qinq', 'mtu', 'name', 'port_security_enabled', @@ -372,6 +407,7 @@ class TestCreateNetworkIdentityV2( network.RouterExternalColumn(_network.is_router_external), _network.is_shared, _network.is_vlan_transparent, + _network.is_vlan_qinq, _network.status, _network.segments, format_columns.ListColumn(_network.subnet_ids), @@ -383,10 +419,9 @@ class TestCreateNetworkIdentityV2( def setUp(self): super().setUp() - self.network_client.create_network = mock.Mock( - return_value=self._network - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_network.return_value = self._network + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) @@ -458,7 +493,7 @@ def setUp(self): # The networks to delete self._networks = network_fakes.create_networks(count=3) - self.network_client.delete_network = mock.Mock(return_value=None) + self.network_client.delete_network.return_value = None self.network_client.find_network = network_fakes.get_networks( networks=self._networks @@ -517,14 +552,14 @@ def test_delete_multiple_networks_exception(self): exceptions.NotFound('404'), self._networks[1], ] - self.network_client.find_network = mock.Mock(side_effect=ret_find) + self.network_client.find_network.side_effect = ret_find # Fake exception in delete_network() ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_network = mock.Mock(side_effect=ret_delete) + self.network_client.delete_network.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -596,13 +631,13 @@ def setUp(self): # Get the command object to test self.cmd = network.ListNetwork(self.app, None) - self.network_client.networks = mock.Mock(return_value=self._network) + self.network_client.networks.return_value = self._network self._agent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=self._agent) + self.network_client.get_agent.return_value = self._agent - self.network_client.dhcp_agent_hosting_networks = mock.Mock( - return_value=self._network + self.network_client.dhcp_agent_hosting_networks.return_value = ( + self._network ) # TestListTagMixin @@ -944,22 +979,19 @@ def test_list_with_tag_options(self): class TestSetNetwork(TestNetwork): # The network to set. _network = network_fakes.create_one_network({'tags': ['green', 'red']}) - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( + qos_policy = network_fakes.create_one_qos_policy( attrs={'id': _network.qos_policy_id} ) def setUp(self): super().setUp() - self.network_client.update_network = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_network.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_network.return_value = self._network + + self.network_client.find_qos_policy.return_value = self.qos_policy # Get the command object to test self.cmd = network.SetNetwork(self.app, None) @@ -1149,6 +1181,7 @@ class TestShowNetwork(TestNetwork): 'ipv6_address_scope', 'is_default', 'is_vlan_transparent', + 'is_vlan_qinq', 'mtu', 'name', 'port_security_enabled', @@ -1189,6 +1222,7 @@ class TestShowNetwork(TestNetwork): network.RouterExternalColumn(_network.is_router_external), _network.is_shared, _network.is_vlan_transparent, + _network.is_vlan_qinq, _network.status, _network.segments, format_columns.ListColumn(_network.subnet_ids), @@ -1200,9 +1234,7 @@ class TestShowNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.find_network = mock.Mock( - return_value=self._network - ) + self.network_client.find_network.return_value = self._network # Get the command object to test self.cmd = network.ShowNetwork(self.app, None) @@ -1241,22 +1273,19 @@ def test_show_all_options(self): class TestUnsetNetwork(TestNetwork): # The network to set. _network = network_fakes.create_one_network({'tags': ['green', 'red']}) - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( + qos_policy = network_fakes.create_one_qos_policy( attrs={'id': _network.qos_policy_id} ) def setUp(self): super().setUp() - self.network_client.update_network = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_network.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_network.return_value = self._network + + self.network_client.find_qos_policy.return_value = self.qos_policy # Get the command object to test self.cmd = network.UnsetNetwork(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index 15bacae581..48b394d7a5 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -34,8 +33,8 @@ class TestAddNetworkToAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self.agent) - self.network_client.find_network = mock.Mock(return_value=self.net) + self.network_client.get_agent.return_value = self.agent + self.network_client.find_network.return_value = self.net self.network_client.name = self.network_client.find_network.name self.cmd = network_agent.AddNetworkToAgent(self.app, None) @@ -70,14 +69,14 @@ def test_add_network_to_dhcp_agent(self): class TestAddRouterAgent(TestNetworkAgent): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() _agent = network_fakes.create_one_network_agent() def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self._agent) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.get_agent.return_value = self._agent + self.network_client.find_router.return_value = self._router self.cmd = network_agent.AddRouterToAgent(self.app, None) @@ -120,7 +119,7 @@ class TestDeleteNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.delete_agent = mock.Mock(return_value=None) + self.network_client.delete_agent.return_value = None # Get the command object to test self.cmd = network_agent.DeleteNetworkAgent(self.app, None) @@ -173,9 +172,7 @@ def test_multi_network_agents_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [True, exceptions.CommandError] - self.network_client.delete_agent = mock.Mock( - side_effect=delete_mock_result - ) + self.network_client.delete_agent.side_effect = delete_mock_result try: self.cmd.take_action(parsed_args) @@ -219,30 +216,24 @@ class TestListNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.agents = mock.Mock( - return_value=self.network_agents + + self.network_client.agents.return_value = self.network_agents + self.network_client.routers_hosting_l3_agents.return_value = ( + self.network_agents ) _testagent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=_testagent) + self.network_client.get_agent.return_value = _testagent + self.network_client.get_agent.return_value = _testagent self._testnetwork = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock( - return_value=self._testnetwork - ) - self.network_client.network_hosting_dhcp_agents = mock.Mock( - return_value=self.network_agents + self.network_client.find_network.return_value = self._testnetwork + self.network_client.network_hosting_dhcp_agents.return_value = ( + self.network_agents ) - self.network_client.get_agent = mock.Mock(return_value=_testagent) - - self._testrouter = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock( - return_value=self._testrouter - ) - self.network_client.routers_hosting_l3_agents = mock.Mock( - return_value=self.network_agents - ) + self._testrouter = network_fakes.create_one_router() + self.network_client.find_router.return_value = self._testrouter # Get the command object to test self.cmd = network_agent.ListNetworkAgent(self.app, None) @@ -323,15 +314,11 @@ def test_network_agents_list_routers(self): ] verifylist = [('router', self._testrouter.id), ('long', False)] - attrs = { - self._testrouter, - } - parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.routers_hosting_l3_agents.assert_called_once_with( - *attrs + self._testrouter ) self.assertEqual(self.columns, columns) @@ -345,15 +332,11 @@ def test_network_agents_list_routers_with_long_option(self): ] verifylist = [('router', self._testrouter.id), ('long', True)] - attrs = { - self._testrouter, - } - parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.routers_hosting_l3_agents.assert_called_once_with( - *attrs + self._testrouter ) # Add a column 'HA State' and corresponding data. @@ -371,8 +354,8 @@ class TestRemoveNetworkFromAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self.agent) - self.network_client.find_network = mock.Mock(return_value=self.net) + self.network_client.get_agent.return_value = self.agent + self.network_client.find_network.return_value = self.net self.network_client.name = self.network_client.find_network.name self.cmd = network_agent.RemoveNetworkFromAgent(self.app, None) @@ -422,14 +405,14 @@ def test_network_from_dhcp_agent(self): class TestRemoveRouterAgent(TestNetworkAgent): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() _agent = network_fakes.create_one_network_agent() def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self._agent) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.get_agent.return_value = self._agent + self.network_client.find_router.return_value = self._router self.cmd = network_agent.RemoveRouterFromAgent(self.app, None) @@ -472,10 +455,8 @@ class TestSetNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.update_agent = mock.Mock(return_value=None) - self.network_client.get_agent = mock.Mock( - return_value=self._network_agent - ) + self.network_client.update_agent.return_value = None + self.network_client.get_agent.return_value = self._network_agent # Get the command object to test self.cmd = network_agent.SetNetworkAgent(self.app, None) @@ -588,9 +569,7 @@ class TestShowNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock( - return_value=self._network_agent - ) + self.network_client.get_agent.return_value = self._network_agent # Get the command object to test self.cmd = network_agent.ShowNetworkAgent(self.app, None) 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 20c645bd4c..d13bd8cf95 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 @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import network_auto_allocated_topology from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -50,8 +49,8 @@ def setUp(self): self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None ) - self.network_client.get_auto_allocated_topology = mock.Mock( - return_value=self.topology + self.network_client.get_auto_allocated_topology.return_value = ( + self.topology ) def test_create_no_options(self): @@ -155,8 +154,8 @@ def setUp(self): self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None ) - self.network_client.validate_auto_allocated_topology = mock.Mock( - return_value=self.topology + self.network_client.validate_auto_allocated_topology.return_value = ( + self.topology ) def test_show_dry_run_no_project(self): @@ -228,9 +227,7 @@ def setUp(self): self.cmd = network_auto_allocated_topology.DeleteAutoAllocatedTopology( self.app, None ) - self.network_client.delete_auto_allocated_topology = mock.Mock( - return_value=None - ) + self.network_client.delete_auto_allocated_topology.return_value = None def test_delete_no_project(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_compute.py b/openstackclient/tests/unit/network/v2/test_network_compute.py index 045fa1f6b6..e08e6baf0c 100644 --- a/openstackclient/tests/unit/network/v2/test_network_compute.py +++ b/openstackclient/tests/unit/network/v2/test_network_compute.py @@ -148,7 +148,7 @@ def test_network_create_default_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) net_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, subnet=self._network['cidr'], name=self._network['label'], ) @@ -182,7 +182,7 @@ def test_network_delete_one(self, find_net_mock, delete_net_mock): result = self.cmd.take_action(parsed_args) delete_net_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._networks[0]['id'], ) self.assertIsNone(result) @@ -203,8 +203,8 @@ def test_network_delete_multi(self, find_net_mock, delete_net_mock): delete_net_mock.assert_has_calls( [ - mock.call(self.compute_sdk_client, self._networks[0]['id']), - mock.call(self.compute_sdk_client, self._networks[1]['id']), + mock.call(self.compute_client, self._networks[0]['id']), + mock.call(self.compute_client, self._networks[1]['id']), ] ) self.assertIsNone(result) @@ -238,15 +238,15 @@ def test_network_delete_multi_with_exception( find_net_mock.assert_has_calls( [ - mock.call(self.compute_sdk_client, self._networks[0]['id']), - mock.call(self.compute_sdk_client, 'xxxx-yyyy-zzzz'), - mock.call(self.compute_sdk_client, self._networks[1]['id']), + mock.call(self.compute_client, self._networks[0]['id']), + mock.call(self.compute_client, 'xxxx-yyyy-zzzz'), + mock.call(self.compute_client, self._networks[1]['id']), ] ) delete_net_mock.assert_has_calls( [ - mock.call(self.compute_sdk_client, self._networks[0]['id']), - mock.call(self.compute_sdk_client, self._networks[1]['id']), + mock.call(self.compute_client, self._networks[0]['id']), + mock.call(self.compute_client, self._networks[1]['id']), ] ) @@ -286,7 +286,7 @@ def test_network_list_no_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) - net_mock.assert_called_once_with(self.compute_sdk_client) + net_mock.assert_called_once_with(self.compute_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -398,7 +398,7 @@ def test_show_all_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) net_mock.assert_called_once_with( - self.compute_sdk_client, self._network['label'] + self.compute_client, self._network['label'] ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py index cfbe1f7b03..10038e3933 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py @@ -41,11 +41,9 @@ class TestAddNetworkFlavorToProfile(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.network_flavor - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.service_profile + self.network_client.find_flavor.return_value = self.network_flavor + self.network_client.find_service_profile.return_value = ( + self.service_profile ) self.cmd = network_flavor.AddNetworkFlavorToProfile(self.app, None) @@ -102,8 +100,8 @@ class TestCreateNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.create_flavor = mock.Mock( - return_value=self.new_network_flavor + self.network_client.create_flavor.return_value = ( + self.new_network_flavor ) # Get the command object to test @@ -218,7 +216,7 @@ class TestDeleteNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.delete_flavor = mock.Mock(return_value=None) + self.network_client.delete_flavor.return_value = None self.network_client.find_flavor = network_fakes.get_flavor( network_flavors=self._network_flavors ) @@ -278,9 +276,7 @@ def test_multi_network_flavors_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._network_flavors[0], exceptions.CommandError] - self.network_client.find_flavor = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_flavor.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -323,9 +319,7 @@ class TestListNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.flavors = mock.Mock( - return_value=self._network_flavors - ) + self.network_client.flavors.return_value = self._network_flavors # Get the command object to test self.cmd = network_flavor.ListNetworkFlavor(self.app, None) @@ -348,11 +342,9 @@ class TestRemoveNetworkFlavorFromProfile(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.network_flavor - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.service_profile + self.network_client.find_flavor.return_value = self.network_flavor + self.network_client.find_service_profile.return_value = ( + self.service_profile ) self.network_client.disassociate_flavor_from_service_profile = ( mock.Mock() @@ -412,9 +404,7 @@ class TestShowNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.new_network_flavor - ) + self.network_client.find_flavor.return_value = self.new_network_flavor # Get the command object to test self.cmd = network_flavor.ShowNetworkFlavor(self.app, None) @@ -456,10 +446,8 @@ class TestSetNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.update_flavor = mock.Mock(return_value=None) - self.network_client.find_flavor = mock.Mock( - return_value=self.new_network_flavor - ) + self.network_client.update_flavor.return_value = None + self.network_client.find_flavor.return_value = self.new_network_flavor # Get the command object to test self.cmd = network_flavor.SetNetworkFlavor(self.app, None) 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 44cc4f493c..c8235bfef8 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -23,14 +23,11 @@ class TestFlavorProfile(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - # Get the ProjectManager Mock - self.projects_mock = self.identity_client.projects # Get the DomainManager Mock self.domains_mock = self.identity_client.domains class TestCreateFlavorProfile(TestFlavorProfile): - project = identity_fakes_v3.FakeProject.create_one_project() domain = identity_fakes_v3.FakeDomain.create_one_domain() new_flavor_profile = network_fakes.create_one_service_profile() @@ -40,7 +37,6 @@ class TestCreateFlavorProfile(TestFlavorProfile): 'enabled', 'id', 'meta_info', - 'project_id', ) data = ( @@ -49,15 +45,14 @@ class TestCreateFlavorProfile(TestFlavorProfile): new_flavor_profile.is_enabled, new_flavor_profile.id, new_flavor_profile.meta_info, - new_flavor_profile.project_id, ) def setUp(self): super().setUp() - self.network_client.create_service_profile = mock.Mock( - return_value=self.new_flavor_profile + self.network_client.create_service_profile.return_value = ( + self.new_flavor_profile ) - self.projects_mock.get.return_value = self.project + # Get the command object to test self.cmd = network_flavor_profile.CreateNetworkFlavorProfile( self.app, None @@ -67,10 +62,6 @@ def test_create_all_options(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", "--driver", self.new_flavor_profile.driver, @@ -80,8 +71,6 @@ def test_create_all_options(self): verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ('driver', self.new_flavor_profile.driver), ('metainfo', self.new_flavor_profile.meta_info), @@ -93,7 +82,6 @@ def test_create_all_options(self): self.network_client.create_service_profile.assert_called_once_with( **{ 'description': self.new_flavor_profile.description, - 'project_id': self.project.id, 'enabled': self.new_flavor_profile.is_enabled, 'driver': self.new_flavor_profile.driver, 'metainfo': self.new_flavor_profile.meta_info, @@ -106,10 +94,6 @@ def test_create_with_metainfo(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", "--metainfo", self.new_flavor_profile.meta_info, @@ -117,8 +101,6 @@ def test_create_with_metainfo(self): verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ('metainfo', self.new_flavor_profile.meta_info), ] @@ -129,7 +111,6 @@ def test_create_with_metainfo(self): self.network_client.create_service_profile.assert_called_once_with( **{ 'description': self.new_flavor_profile.description, - 'project_id': self.project.id, 'enabled': self.new_flavor_profile.is_enabled, 'metainfo': self.new_flavor_profile.meta_info, } @@ -141,10 +122,6 @@ def test_create_with_driver(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", "--driver", self.new_flavor_profile.driver, @@ -152,8 +129,6 @@ def test_create_with_driver(self): verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ('driver', self.new_flavor_profile.driver), ] @@ -164,7 +139,6 @@ def test_create_with_driver(self): self.network_client.create_service_profile.assert_called_once_with( **{ 'description': self.new_flavor_profile.description, - 'project_id': self.project.id, 'enabled': self.new_flavor_profile.is_enabled, 'driver': self.new_flavor_profile.driver, } @@ -176,17 +150,11 @@ def test_create_without_driver_and_metainfo(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", ] verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ] @@ -228,9 +196,8 @@ class TestDeleteFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.delete_service_profile = mock.Mock( - return_value=None - ) + self.network_client.delete_service_profile.return_value = None + self.network_client.find_service_profile = ( network_fakes.get_service_profile( flavor_profile=self._network_flavor_profiles @@ -299,9 +266,7 @@ def test_multi_network_flavor_profiles_delete_with_exception(self): self._network_flavor_profiles[0], exceptions.CommandError, ] - self.network_client.find_service_profile = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_service_profile.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -347,8 +312,8 @@ class TestListFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.service_profiles = mock.Mock( - return_value=self._network_flavor_profiles + self.network_client.service_profiles.return_value = ( + self._network_flavor_profiles ) # Get the command object to test @@ -377,7 +342,6 @@ class TestShowFlavorProfile(TestFlavorProfile): 'enabled', 'id', 'meta_info', - 'project_id', ) data = ( network_flavor_profile.description, @@ -385,13 +349,12 @@ class TestShowFlavorProfile(TestFlavorProfile): network_flavor_profile.is_enabled, network_flavor_profile.id, network_flavor_profile.meta_info, - network_flavor_profile.project_id, ) def setUp(self): super().setUp() - self.network_client.find_service_profile = mock.Mock( - return_value=self.network_flavor_profile + self.network_client.find_service_profile.return_value = ( + self.network_flavor_profile ) # Get the command object to test @@ -423,11 +386,10 @@ class TestSetFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.update_service_profile = mock.Mock( - return_value=None - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.network_flavor_profile + self.network_client.update_service_profile.return_value = None + + self.network_client.find_service_profile.return_value = ( + self.network_flavor_profile ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py index a92035d6d6..f13839a626 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -55,9 +54,8 @@ class TestCreateMeter(TestMeter): def setUp(self): super().setUp() - self.network_client.create_metering_label = mock.Mock( - return_value=self.new_meter - ) + self.network_client.create_metering_label.return_value = self.new_meter + self.projects_mock.get.return_value = self.project self.cmd = network_meter.CreateMeter(self.app, None) @@ -132,9 +130,7 @@ def setUp(self): self.meter_list = network_fakes.FakeNetworkMeter.create_meter(count=2) - self.network_client.delete_metering_label = mock.Mock( - return_value=None - ) + self.network_client.delete_metering_label.return_value = None self.network_client.find_metering_label = ( network_fakes.FakeNetworkMeter.get_meter(meter=self.meter_list) @@ -194,15 +190,13 @@ def test_delete_multiple_meter_exception(self): exceptions.NotFound('404'), self.meter_list[1], ] - self.network_client.find_meter = mock.Mock(side_effect=return_find) + self.network_client.find_metering_label.side_effect = return_find ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_metering_label = mock.Mock( - side_effect=ret_delete - ) + self.network_client.delete_metering_label.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -240,9 +234,7 @@ class TestListMeter(TestMeter): def setUp(self): super().setUp() - self.network_client.metering_labels = mock.Mock( - return_value=self.meter_list - ) + self.network_client.metering_labels.return_value = self.meter_list self.cmd = network_meter.ListMeter(self.app, None) @@ -282,9 +274,7 @@ def setUp(self): self.cmd = network_meter.ShowMeter(self.app, None) - self.network_client.find_metering_label = mock.Mock( - return_value=self.new_meter - ) + self.network_client.find_metering_label.return_value = self.new_meter def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py index e90ed05f3b..407fb04bb4 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -65,14 +64,13 @@ def setUp(self): {'id': self.new_rule.metering_label_id} ) - self.network_client.create_metering_label_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_metering_label_rule.return_value = ( + self.new_rule ) + self.projects_mock.get.return_value = self.project self.cmd = network_meter_rule.CreateMeterRule(self.app, None) - self.network_client.find_metering_label = mock.Mock( - return_value=fake_meter - ) + self.network_client.find_metering_label.return_value = fake_meter def test_create_no_options(self): arglist = [] @@ -146,9 +144,7 @@ def setUp(self): self.rule_list = network_fakes.FakeNetworkMeterRule.create_meter_rule( count=2 ) - self.network_client.delete_metering_label_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_metering_label_rule.return_value = None self.network_client.find_metering_label_rule = ( network_fakes.FakeNetworkMeterRule.get_meter_rule( @@ -210,17 +206,13 @@ def test_delete_multiple_rules_exception(self): exceptions.NotFound('404'), self.rule_list[1], ] - self.network_client.find_metering_label_rule = mock.Mock( - side_effect=return_find - ) + self.network_client.find_metering_label_rule.side_effect = return_find ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_metering_label_rule = mock.Mock( - side_effect=ret_delete - ) + self.network_client.delete_metering_label_rule.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -262,9 +254,7 @@ class TestListMeterRule(TestMeterRule): def setUp(self): super().setUp() - self.network_client.metering_label_rules = mock.Mock( - return_value=self.rule_list - ) + self.network_client.metering_label_rules.return_value = self.rule_list self.cmd = network_meter_rule.ListMeterRule(self.app, None) @@ -311,8 +301,8 @@ def setUp(self): self.cmd = network_meter_rule.ShowMeterRule(self.app, None) - self.network_client.find_metering_label_rule = mock.Mock( - return_value=self.new_rule + self.network_client.find_metering_label_rule.return_value = ( + self.new_rule ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py index 0a7cbb980a..17f40ef6b9 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py @@ -35,11 +35,10 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): project = identity_fakes_v3.FakeProject.create_one_project() # The new qos policy created. - new_qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( - attrs={ - 'project_id': project.id, - } + new_qos_policy = network_fakes.create_one_qos_policy( + attrs={'project_id': project.id} ) + columns = ( 'description', 'id', @@ -48,6 +47,7 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): 'project_id', 'rules', 'shared', + 'tags', ) data = ( @@ -57,13 +57,14 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): new_qos_policy.name, new_qos_policy.project_id, new_qos_policy.rules, - new_qos_policy.shared, + new_qos_policy.is_shared, + new_qos_policy.tags, ) def setUp(self): super().setUp() - self.network_client.create_qos_policy = mock.Mock( - return_value=self.new_qos_policy + self.network_client.create_qos_policy.return_value = ( + self.new_qos_policy ) # Get the command object to test @@ -158,17 +159,13 @@ def test_create_no_default(self): class TestDeleteNetworkQosPolicy(TestQosPolicy): # The address scope to delete. - _qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies( - count=2 - ) + _qos_policies = network_fakes.create_qos_policies(count=2) def setUp(self): super().setUp() - self.network_client.delete_qos_policy = mock.Mock(return_value=None) - self.network_client.find_qos_policy = ( - network_fakes.FakeNetworkQosPolicy.get_qos_policies( - qos_policies=self._qos_policies - ) + self.network_client.delete_qos_policy.return_value = None + self.network_client.find_qos_policy = network_fakes.get_qos_policies( + qos_policies=self._qos_policies ) # Get the command object to test @@ -245,9 +242,7 @@ def test_multi_qos_policies_delete_with_exception(self): class TestListNetworkQosPolicy(TestQosPolicy): # The QoS policies to list up. - qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies( - count=3 - ) + qos_policies = network_fakes.create_qos_policies(count=3) columns = ( 'ID', 'Name', @@ -261,7 +256,7 @@ class TestListNetworkQosPolicy(TestQosPolicy): ( qos_policy.id, qos_policy.name, - qos_policy.shared, + qos_policy.is_shared, qos_policy.is_default, qos_policy.project_id, ) @@ -269,9 +264,7 @@ class TestListNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.qos_policies = mock.Mock( - return_value=self.qos_policies - ) + self.network_client.qos_policies.return_value = self.qos_policies # Get the command object to test self.cmd = network_qos_policy.ListNetworkQosPolicy(self.app, None) @@ -345,14 +338,12 @@ def test_network_qos_list_project(self): class TestSetNetworkQosPolicy(TestQosPolicy): # The QoS policy to set. - _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + _qos_policy = network_fakes.create_one_qos_policy() def setUp(self): super().setUp() - self.network_client.update_qos_policy = mock.Mock(return_value=None) - self.network_client.find_qos_policy = mock.Mock( - return_value=self._qos_policy - ) + self.network_client.update_qos_policy.return_value = None + self.network_client.find_qos_policy.return_value = self._qos_policy # Get the command object to test self.cmd = network_qos_policy.SetNetworkQosPolicy(self.app, None) @@ -428,7 +419,7 @@ def test_set_no_share_no_default(self): class TestShowNetworkQosPolicy(TestQosPolicy): # The QoS policy to show. - _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + _qos_policy = network_fakes.create_one_qos_policy() columns = ( 'description', 'id', @@ -437,6 +428,7 @@ class TestShowNetworkQosPolicy(TestQosPolicy): 'project_id', 'rules', 'shared', + 'tags', ) data = ( _qos_policy.description, @@ -445,14 +437,13 @@ class TestShowNetworkQosPolicy(TestQosPolicy): _qos_policy.name, _qos_policy.project_id, network_qos_policy.RulesColumn(_qos_policy.rules), - _qos_policy.shared, + _qos_policy.is_shared, + _qos_policy.tags, ) def setUp(self): super().setUp() - self.network_client.find_qos_policy = mock.Mock( - return_value=self._qos_policy - ) + self.network_client.find_qos_policy.return_value = self._qos_policy # Get the command object to test self.cmd = network_qos_policy.ShowNetworkQosPolicy(self.app, None) 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 1f6fe093ee..4300304022 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -14,6 +14,7 @@ # under the License. from unittest import mock +import uuid from osc_lib import exceptions @@ -54,12 +55,8 @@ class TestNetworkQosRule(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - self.qos_policy = ( - network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = self.qos_policy class TestCreateNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): @@ -72,15 +69,12 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'direction', 'id', 'min_kbps', 'project_id', - 'qos_policy_id', 'type', ) @@ -89,11 +83,10 @@ def setUp(self): self.new_rule.id, self.new_rule.min_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) - self.network_client.create_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -137,7 +130,7 @@ def test_create_default_options(self): **{ 'min_kbps': self.new_rule.min_kbps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -179,15 +172,12 @@ def setUp(self): '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.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'direction', 'id', 'min_kpps', 'project_id', - 'qos_policy_id', 'type', ) @@ -196,12 +186,9 @@ def setUp(self): 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_client.create_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule - ) + self.network_client.create_qos_minimum_packet_rate_rule.return_value = self.new_rule # Get the command object to test self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) @@ -244,7 +231,7 @@ def test_create_default_options(self): **{ 'min_kpps': self.new_rule.min_kpps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -286,14 +273,11 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'dscp_mark', 'id', 'project_id', - 'qos_policy_id', 'type', ) @@ -301,11 +285,10 @@ def setUp(self): self.new_rule.dscp_mark, self.new_rule.id, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) - self.network_client.create_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_dscp_marking_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -384,30 +367,26 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'direction', 'id', - 'max_burst_kbits', + 'max_burst_kbps', 'max_kbps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( self.new_rule.direction, self.new_rule.id, - self.new_rule.max_burst_kbits, + self.new_rule.max_burst_kbps, self.new_rule.max_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) - self.network_client.create_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -443,20 +422,19 @@ def test_create_default_options(self): ('qos_policy', self.new_rule.qos_policy_id), ] - rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( + rule = network_fakes.create_one_qos_rule( { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } ) - rule.max_burst_kbits = 0 + rule.max_burst_kbps = 0 expected_data = ( rule.direction, rule.id, - rule.max_burst_kbits, + rule.max_burst_kbps, rule.max_kbps, rule.project_id, - rule.qos_policy_id, rule.type, ) @@ -473,7 +451,7 @@ def test_create_default_options(self): **{ 'max_kbps': self.new_rule.max_kbps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(expected_data, data) @@ -485,7 +463,7 @@ def test_create_all_options(self): '--max-kbps', str(self.new_rule.max_kbps), '--max-burst-kbits', - str(self.new_rule.max_burst_kbits), + str(self.new_rule.max_burst_kbps), '--egress', self.new_rule.qos_policy_id, ] @@ -493,7 +471,7 @@ def test_create_all_options(self): verifylist = [ ('type', RULE_TYPE_BANDWIDTH_LIMIT), ('max_kbps', self.new_rule.max_kbps), - ('max_burst_kbits', self.new_rule.max_burst_kbits), + ('max_burst_kbits', self.new_rule.max_burst_kbps), ('egress', True), ('qos_policy', self.new_rule.qos_policy_id), ] @@ -505,9 +483,9 @@ def test_create_all_options(self): self.qos_policy.id, **{ 'max_kbps': self.new_rule.max_kbps, - 'max_burst_kbps': self.new_rule.max_burst_kbits, + 'max_burst_kbps': self.new_rule.max_burst_kbps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -545,17 +523,14 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_minimum_bandwidth_rule = mock.Mock( - return_value=None + self.network_client.delete_qos_minimum_bandwidth_rule.return_value = ( + None ) + self.network_client.find_qos_minimum_bandwidth_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -612,17 +587,12 @@ def setUp(self): '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.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_minimum_packet_rate_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_minimum_packet_rate_rule.return_value = None + self.network_client.find_qos_minimum_packet_rate_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -679,17 +649,12 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_dscp_marking_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_dscp_marking_rule.return_value = None + self.network_client.find_qos_dscp_marking_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -746,17 +711,12 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_bandwidth_limit_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_bandwidth_limit_rule.return_value = None + self.network_client.find_qos_bandwidth_limit_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -813,20 +773,18 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_minimum_bandwidth_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.update_qos_minimum_bandwidth_rule.return_value = ( + None ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + + self.network_client.find_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -903,9 +861,9 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"minimum-bandwidth" only requires arguments: direction, ' - 'min_kbps' % {'rule': self.new_rule.id} + 'min_kbps' ) self.assertEqual(msg, str(e)) @@ -917,20 +875,16 @@ def setUp(self): '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.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_minimum_packet_rate_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_minimum_packet_rate_rule.return_value = None + + self.network_client.find_qos_minimum_packet_rate_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -1007,9 +961,9 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"minimum-packet-rate" only requires arguments: direction, ' - 'min_kpps' % {'rule': self.new_rule.id} + 'min_kpps' ) self.assertEqual(msg, str(e)) @@ -1021,20 +975,16 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_dscp_marking_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_dscp_marking_rule.return_value = None + + self.network_client.find_qos_dscp_marking_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -1111,9 +1061,8 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"dscp-marking" only requires arguments: dscp_mark' - % {'rule': self.new_rule.id} ) self.assertEqual(msg, str(e)) @@ -1125,21 +1074,14 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_bandwidth_limit_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_bandwidth_limit_rule.return_value = None + self.network_client.find_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy - # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) def test_set_nothing(self): @@ -1204,23 +1146,23 @@ def test_set_max_burst_kbits_to_zero(self): self._set_max_burst_kbits(max_burst_kbits=0) def _reset_max_burst_kbits(self, max_burst_kbits): - self.new_rule.max_burst_kbits = max_burst_kbits + self.new_rule.max_burst_kbps = max_burst_kbits def _set_max_burst_kbits(self, max_burst_kbits=None): if max_burst_kbits: self.addCleanup( - self._reset_max_burst_kbits, self.new_rule.max_burst_kbits + self._reset_max_burst_kbits, self.new_rule.max_burst_kbps ) - self.new_rule.max_burst_kbits = max_burst_kbits + self.new_rule.max_burst_kbps = max_burst_kbits arglist = [ '--max-burst-kbits', - str(self.new_rule.max_burst_kbits), + str(self.new_rule.max_burst_kbps), self.new_rule.qos_policy_id, self.new_rule.id, ] verifylist = [ - ('max_burst_kbits', self.new_rule.max_burst_kbits), + ('max_burst_kbits', self.new_rule.max_burst_kbps), ('qos_policy', self.new_rule.qos_policy_id), ('id', self.new_rule.id), ] @@ -1229,7 +1171,7 @@ def _set_max_burst_kbits(self, max_burst_kbits=None): result = self.cmd.take_action(parsed_args) attrs = { - 'max_burst_kbps': self.new_rule.max_burst_kbits, + 'max_burst_kbps': self.new_rule.max_burst_kbps, } self.network_client.update_qos_bandwidth_limit_rule.assert_called_with( self.new_rule, self.qos_policy.id, **attrs @@ -1249,7 +1191,7 @@ def _set_direction(self, direction): self.addCleanup(self._reset_direction, self.new_rule.direction) arglist = [ - '--%s' % direction, + f'--{direction}', self.new_rule.qos_policy_id, self.new_rule.id, ] @@ -1288,9 +1230,9 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"bandwidth-limit" only requires arguments: direction, ' - 'max_burst_kbps, max_kbps' % {'rule': self.new_rule.id} + 'max_burst_kbps, max_kbps' ) self.assertEqual(msg, str(e)) @@ -1298,43 +1240,36 @@ def test_set_wrong_options(self): class TestListNetworkQosRule(TestNetworkQosRule): def setUp(self): super().setUp() - attrs = { - 'qos_policy_id': self.qos_policy.id, - '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) - ) - attrs['type'] = RULE_TYPE_BANDWIDTH_LIMIT - 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, + { + 'max_kbps': 1024, + 'max_burst_kbps': 1024, + 'direction': 'egress', + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'bandwidth_limit', + }, + { + 'dscp_mark': 0, + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'dscp_marking', + }, + { + 'min_kbps': 1024, + 'direction': 'egress', + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'minimum_bandwidth', + }, + { + 'min_kpps': 2800, + 'direction': 'egress', + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'minimum_packet_rate', + }, ] - self.network_client.find_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule_min_bw - ) - self.network_client.find_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule_min_pps - ) - self.network_client.find_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule_dscp_mark - ) - self.network_client.find_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule_max_bw - ) self.columns = ( 'ID', 'QoS Policy ID', @@ -1350,20 +1285,17 @@ def setUp(self): for index in range(len(self.qos_policy.rules)): self.data.append( ( - self.qos_policy.rules[index].id, - self.qos_policy.rules[index].qos_policy_id, - self.qos_policy.rules[index].type, - 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', ''), + self.qos_policy.rules[index]['id'], + self.qos_policy.id, + self.qos_policy.rules[index]['type'], + self.qos_policy.rules[index].get('max_kbps', ''), + self.qos_policy.rules[index].get('max_burst_kbps', ''), + self.qos_policy.rules[index].get('min_kbps', ''), + self.qos_policy.rules[index].get('min_kpps', ''), + self.qos_policy.rules[index].get('dscp_mark', ''), + self.qos_policy.rules[index].get('direction', ''), ) ) - # Get the command object to test self.cmd = network_qos_rule.ListNetworkQosRule(self.app, None) def test_qos_rule_list(self): @@ -1392,16 +1324,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'direction', 'id', 'min_kbps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( @@ -1409,12 +1338,11 @@ def setUp(self): self.new_rule.id, self.new_rule.min_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) - self.network_client.get_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1460,16 +1388,13 @@ def setUp(self): '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.new_rule = network_fakes.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 = ( @@ -1477,12 +1402,11 @@ def setUp(self): 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_client.get_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_minimum_packet_rate_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1528,27 +1452,23 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'dscp_mark', 'id', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( self.new_rule.dscp_mark, self.new_rule.id, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) - self.network_client.get_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_dscp_marking_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1594,31 +1514,27 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'direction', 'id', - 'max_burst_kbits', + 'max_burst_kbps', 'max_kbps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( self.new_rule.direction, self.new_rule.id, - self.new_rule.max_burst_kbits, + self.new_rule.max_burst_kbps, self.new_rule.max_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) - self.network_client.get_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py index 4838df2e8b..1c50b9e938 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import network_qos_rule_type as _qos_rule_type from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -28,17 +27,14 @@ def setUp(self): class TestShowNetworkQosRuleType(TestNetworkQosRuleType): attrs = {'drivers': [{'name': 'driver 1', 'supported_parameters': []}]} # The QoS policies to show. - qos_rule_type = ( - network_fakes.FakeNetworkQosRuleType.create_one_qos_rule_type(attrs) - ) + qos_rule_type = network_fakes.create_one_qos_rule_type(attrs) + columns = ('drivers', 'rule_type_name') columns = ('drivers', 'rule_type_name') data = [qos_rule_type.drivers, qos_rule_type.type] def setUp(self): super().setUp() - self.network_client.get_qos_rule_type = mock.Mock( - return_value=self.qos_rule_type - ) + self.network_client.get_qos_rule_type.return_value = self.qos_rule_type # Get the command object to test self.cmd = _qos_rule_type.ShowNetworkQosRuleType(self.app, None) @@ -76,9 +72,8 @@ def test_show_all_options(self): class TestListNetworkQosRuleType(TestNetworkQosRuleType): # The QoS policies to list up. - qos_rule_types = ( - network_fakes.FakeNetworkQosRuleType.create_qos_rule_types(count=3) - ) + qos_rule_types = network_fakes.create_qos_rule_types(count=3) + columns = ('Type',) data = [] for qos_rule_type in qos_rule_types: @@ -86,9 +81,7 @@ class TestListNetworkQosRuleType(TestNetworkQosRuleType): def setUp(self): super().setUp() - self.network_client.qos_rule_types = mock.Mock( - return_value=self.qos_rule_types - ) + self.network_client.qos_rule_types.return_value = self.qos_rule_types # Get the command object to test self.cmd = _qos_rule_type.ListNetworkQosRuleType(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index c13b72bb49..d3a7192453 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call import ddt @@ -34,8 +33,8 @@ def setUp(self): @ddt.ddt class TestCreateNetworkRBAC(TestNetworkRBAC): 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() + qos_object = network_fakes.create_one_qos_policy() + sg_object = network_fakes.create_one_security_group() as_object = network_fakes.create_one_address_scope() snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool() ag_object = network_fakes.create_one_address_group() @@ -72,27 +71,20 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.CreateNetworkRBAC(self.app, None) - self.network_client.create_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) - self.network_client.find_network = mock.Mock( - return_value=self.network_object - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_object - ) - self.network_client.find_security_group = mock.Mock( - return_value=self.sg_object - ) - self.network_client.find_address_scope = mock.Mock( - return_value=self.as_object - ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self.snp_object - ) - self.network_client.find_address_group = mock.Mock( - return_value=self.ag_object - ) + self.network_client.create_rbac_policy.return_value = self.rbac_policy + + self.network_client.find_network.return_value = self.network_object + + self.network_client.find_qos_policy.return_value = self.qos_object + + self.network_client.find_security_group.return_value = self.sg_object + + self.network_client.find_address_scope.return_value = self.as_object + + self.network_client.find_subnet_pool.return_value = self.snp_object + + self.network_client.find_address_group.return_value = self.ag_object + self.projects_mock.get.return_value = self.project def test_network_rbac_create_no_type(self): @@ -343,7 +335,7 @@ class TestDeleteNetworkRBAC(TestNetworkRBAC): def setUp(self): super().setUp() - self.network_client.delete_rbac_policy = mock.Mock(return_value=None) + self.network_client.delete_rbac_policy.return_value = None self.network_client.find_rbac_policy = network_fakes.get_network_rbacs( rbac_policies=self.rbac_policies ) @@ -400,9 +392,7 @@ def test_multi_network_policies_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self.rbac_policies[0], exceptions.CommandError] - self.network_client.find_rbac_policy = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_rbac_policy.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -462,9 +452,7 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.ListNetworkRBAC(self.app, None) - self.network_client.rbac_policies = mock.Mock( - return_value=self.rbac_policies - ) + self.network_client.rbac_policies.return_value = self.rbac_policies self.project = identity_fakes_v3.FakeProject.create_one_project() self.projects_mock.get.return_value = self.project @@ -564,10 +552,9 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.SetNetworkRBAC(self.app, None) - self.network_client.find_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) - self.network_client.update_rbac_policy = mock.Mock(return_value=None) + self.network_client.find_rbac_policy.return_value = self.rbac_policy + + self.network_client.update_rbac_policy.return_value = None self.projects_mock.get.return_value = self.project def test_network_rbac_set_nothing(self): @@ -639,9 +626,7 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.ShowNetworkRBAC(self.app, None) - self.network_client.find_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) + self.network_client.find_rbac_policy.return_value = self.rbac_policy def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py index 936a20aefd..ab71c32547 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -58,12 +57,9 @@ class TestCreateNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.create_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) + self.network_client.create_segment.return_value = self._network_segment + + self.network_client.find_network.return_value = self._network # Get the command object to test self.cmd = network_segment.CreateNetworkSegment(self.app, None) @@ -172,10 +168,8 @@ class TestDeleteNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.delete_segment = mock.Mock(return_value=None) - self.network_client.find_segment = mock.Mock( - side_effect=self._network_segments - ) + self.network_client.delete_segment.return_value = None + self.network_client.find_segment.side_effect = self._network_segments # Get the command object to test self.cmd = network_segment.DeleteNetworkSegment(self.app, None) @@ -224,9 +218,7 @@ def test_delete_multiple_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._network_segments[0], exceptions.CommandError] - self.network_client.find_segment = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_segment.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -291,12 +283,9 @@ def setUp(self): # Get the command object to test self.cmd = network_segment.ListNetworkSegment(self.app, None) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.segments = mock.Mock( - return_value=self._network_segments - ) + self.network_client.find_network.return_value = self._network + + self.network_client.segments.return_value = self._network_segments def test_list_no_option(self): arglist = [] @@ -352,12 +341,9 @@ class TestSetNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.update_segment = mock.Mock( - return_value=self._network_segment - ) + self.network_client.find_segment.return_value = self._network_segment + + self.network_client.update_segment.return_value = self._network_segment # Get the command object to test self.cmd = network_segment.SetNetworkSegment(self.app, None) @@ -432,9 +418,7 @@ class TestShowNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) + self.network_client.find_segment.return_value = self._network_segment # Get the command object to test self.cmd = network_segment.ShowNetworkSegment(self.app, None) 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 d5e406f396..9c9c900e36 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py @@ -78,8 +78,8 @@ class TestCreateNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.create_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.create_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test @@ -351,11 +351,10 @@ class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.delete_network_segment_range = mock.Mock( - return_value=None - ) - self.network_client.find_network_segment_range = mock.Mock( - side_effect=self._network_segment_ranges + self.network_client.delete_network_segment_range.return_value = None + + self.network_client.find_network_segment_range.side_effect = ( + self._network_segment_ranges ) # Get the command object to test @@ -412,8 +411,8 @@ def test_delete_multiple_with_exception(self): self._network_segment_ranges[0], exceptions.CommandError, ] - self.network_client.find_network_segment_range = mock.Mock( - side_effect=find_mock_result + self.network_client.find_network_segment_range.side_effect = ( + find_mock_result ) try: @@ -493,8 +492,8 @@ class TestListNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.network_segment_ranges = mock.Mock( - return_value=self._network_segment_ranges + self.network_client.network_segment_ranges.return_value = ( + self._network_segment_ranges ) # Get the command object to test @@ -567,8 +566,8 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.find_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.find_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test @@ -583,9 +582,10 @@ def test_set_no_options(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.update_network_segment_range.return_value = ( + self._network_segment_range ) + result = self.cmd.take_action(parsed_args) self.network_client.update_network_segment_range.assert_called_once_with( @@ -611,9 +611,10 @@ def test_set_all_options(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_network_segment_range = mock.Mock( - return_value=self._network_segment_range_updated + self.network_client.update_network_segment_range.return_value = ( + self._network_segment_range_updated ) + result = self.cmd.take_action(parsed_args) attrs = { @@ -662,8 +663,8 @@ class TestShowNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.find_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.find_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_service_provider.py b/openstackclient/tests/unit/network/v2/test_network_service_provider.py index 9f61bce593..f84bcd38d7 100644 --- a/openstackclient/tests/unit/network/v2/test_network_service_provider.py +++ b/openstackclient/tests/unit/network/v2/test_network_service_provider.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import ( network_service_provider as service_provider, @@ -52,9 +51,7 @@ class TestListNetworkServiceProvider(TestNetworkServiceProvider): def setUp(self): super().setUp() - self.network_client.service_providers = mock.Mock( - return_value=self.provider_list - ) + self.network_client.service_providers.return_value = self.provider_list self.cmd = service_provider.ListNetworkServiceProvider(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 153d7f9959..1056c21c30 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -11,7 +11,6 @@ # under the License. import copy -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -84,12 +83,12 @@ class TestCreateNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.create_trunk = mock.Mock( - return_value=self.new_trunk - ) - self.network_client.find_port = mock.Mock( - side_effect=[self.parent_port, self.sub_port] - ) + self.network_client.create_trunk.return_value = self.new_trunk + + self.network_client.find_port.side_effect = [ + self.parent_port, + self.sub_port, + ] # Get the command object to test self.cmd = network_trunk.CreateNetworkTrunk(self.app, None) @@ -143,13 +142,12 @@ def test_create_full_options(self): "--parent-port", self.new_trunk.port_id, "--subport", - 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id'], - }, + 'port={port},segmentation-type={seg_type},' + 'segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + port=subport['port_id'], + ), self.new_trunk.name, ] verifylist = [ @@ -194,12 +192,11 @@ def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): "--parent-port", self.new_trunk.port_id, "--subport", - "port=%(port)s,segmentation-type=%(seg_type)s," - "segmentation-id=boom" - % { - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id'], - }, + "port={port},segmentation-type={seg_type}," + "segmentation-id=boom".format( + seg_type=subport['segmentation_type'], + port=subport['port_id'], + ), self.new_trunk.name, ] verifylist = [ @@ -265,12 +262,10 @@ def test_create_network_trunk_subports_without_required_key_fail(self): '--parent-port', self.new_trunk.port_id, '--subport', - 'segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - }, + 'segmentation-type={seg_type},segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + ), self.new_trunk.name, ] verifylist = [ @@ -317,13 +312,16 @@ class TestDeleteNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock( - side_effect=[self.new_trunks[0], self.new_trunks[1]] - ) - self.network_client.delete_trunk = mock.Mock(return_value=None) - self.network_client.find_port = mock.Mock( - side_effect=[self.parent_port, self.sub_port] - ) + self.network_client.find_trunk.side_effect = [ + self.new_trunks[0], + self.new_trunks[1], + ] + + self.network_client.delete_trunk.return_value = None + self.network_client.find_port.side_effect = [ + self.parent_port, + self.sub_port, + ] self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -375,9 +373,11 @@ def test_delete_trunk_multiple_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.find_trunk = mock.Mock( - side_effect=[self.new_trunks[0], exceptions.CommandError] - ) + self.network_client.find_trunk.side_effect = [ + self.new_trunks[0], + exceptions.CommandError, + ] + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual('1 of 2 trunks failed to delete.', str(e)) @@ -416,8 +416,8 @@ class TestShowNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self.new_trunk) - self.network_client.get_trunk = mock.Mock(return_value=self.new_trunk) + self.network_client.find_trunk.return_value = self.new_trunk + self.network_client.get_trunk.return_value = self.new_trunk self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -489,7 +489,7 @@ class TestListNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.trunks = mock.Mock(return_value=self.new_trunks) + self.network_client.trunks.return_value = self.new_trunks self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -568,14 +568,14 @@ class TestSetNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.update_trunk = mock.Mock(return_value=self._trunk) - self.network_client.add_trunk_subports = mock.Mock( - return_value=self._trunk - ) - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.find_port = mock.Mock( - side_effect=[self.sub_port, self.sub_port] - ) + self.network_client.update_trunk.return_value = self._trunk + self.network_client.add_trunk_subports.return_value = self._trunk + + self.network_client.find_trunk.return_value = self._trunk + self.network_client.find_port.side_effect = [ + self.sub_port, + self.sub_port, + ] self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -585,7 +585,7 @@ def setUp(self): def _test_set_network_trunk_attr(self, attr, value): arglist = [ - '--%s' % attr, + f'--{attr}', value, self._trunk[attr], ] @@ -674,13 +674,12 @@ def test_set_network_trunk_subports(self): subport = self._trunk['sub_ports'][0] arglist = [ '--subport', - 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id'], - }, + 'port={port},segmentation-type={seg_type},' + 'segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + port=subport['port_id'], + ), self._trunk['name'], ] verifylist = [ @@ -732,12 +731,10 @@ def test_set_network_trunk_subports_without_required_key_fail(self): subport = self._trunk['sub_ports'][0] arglist = [ '--subport', - 'segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - }, + 'segmentation-type={seg_type},segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + ), self._trunk['name'], ] verifylist = [ @@ -770,13 +767,13 @@ def test_set_trunk_attrs_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_trunk = mock.Mock( - side_effect=exceptions.CommandError - ) + self.network_client.update_trunk.side_effect = exceptions.CommandError + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( - "Failed to set trunk '%s': " % self._trunk['name'], str(e) + "Failed to set trunk '{}': ".format(self._trunk['name']), + str(e), ) attrs = {'name': 'reallylongname'} self.network_client.update_trunk.assert_called_once_with( @@ -796,16 +793,20 @@ def test_set_trunk_add_subport_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.add_trunk_subports = mock.Mock( - side_effect=exceptions.CommandError - ) - self.network_client.find_port = mock.Mock( - return_value={'id': 'invalid_subport'} + self.network_client.add_trunk_subports.side_effect = ( + exceptions.CommandError ) + + self.network_client.find_port.side_effect = [ + network_fakes.create_one_port({'id': 'invalid_subport'}) + ] + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( - "Failed to add subports to trunk '%s': " % self._trunk['name'], + "Failed to add subports to trunk '{}': ".format( + self._trunk['name'] + ), str(e), ) self.network_client.update_trunk.assert_called_once_with(self._trunk) @@ -836,10 +837,10 @@ class TestListNetworkSubport(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.get_trunk_subports = mock.Mock( - return_value={network_trunk.SUB_PORTS: self._subports} - ) + self.network_client.find_trunk.return_value = self._trunk + self.network_client.get_trunk_subports.return_value = { + network_trunk.SUB_PORTS: self._subports + } # Get the command object to test self.cmd = network_trunk.ListNetworkSubport(self.app, None) @@ -906,13 +907,13 @@ class TestUnsetNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.find_port = mock.Mock( - side_effect=[self.sub_port, self.sub_port] - ) - self.network_client.delete_trunk_subports = mock.Mock( - return_value=None - ) + self.network_client.find_trunk.return_value = self._trunk + self.network_client.find_port.side_effect = [ + self.sub_port, + self.sub_port, + ] + + self.network_client.delete_trunk_subports.return_value = None # Get the command object to test self.cmd = network_trunk.UnsetNetworkTrunk(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 7d19636510..11a8711c7b 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -12,6 +12,7 @@ from unittest import mock from unittest.mock import call +import uuid from osc_lib.cli import format_columns from osc_lib import exceptions @@ -23,8 +24,13 @@ from openstackclient.tests.unit import utils as test_utils -LIST_FIELDS_TO_RETRIEVE = ('id', 'name', 'mac_address', 'fixed_ips', 'status') -LIST_FIELDS_TO_RETRIEVE_LONG = ('security_group_ids', 'device_owner', 'tags') +LIST_FIELDS_TO_RETRIEVE = ['id', 'name', 'mac_address', 'fixed_ips', 'status'] +LIST_FIELDS_TO_RETRIEVE_LONG = [ + 'security_groups', + 'device_owner', + 'tags', + 'trunk_details', +] class TestPort(network_fakes.TestNetworkV2): @@ -73,6 +79,7 @@ def _get_common_cols_data(fake_port): 'security_group_ids', 'status', 'tags', + 'trusted', 'trunk_details', 'updated_at', ) @@ -114,6 +121,7 @@ def _get_common_cols_data(fake_port): format_columns.ListColumn(fake_port.security_group_ids), fake_port.status, format_columns.ListColumn(fake_port.tags), + fake_port.trusted, fake_port.trunk_details, fake_port.updated_at, ) @@ -128,19 +136,18 @@ class TestCreatePort(TestPort): def setUp(self): super().setUp() - self.network_client.create_port = mock.Mock(return_value=self._port) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_port.return_value = self._port + self.network_client.set_tags.return_value = None fake_net = network_fakes.create_one_network( { 'id': self._port.network_id, } ) - self.network_client.find_network = mock.Mock(return_value=fake_net) + self.network_client.find_network.return_value = fake_net self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_extension = mock.Mock(return_value=[]) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_extension.return_value = [] # Get the command object to test self.cmd = port.CreatePort(self.app, None) @@ -179,7 +186,7 @@ def test_create_full_options(self): '--mac-address', 'aa:aa:aa:aa:aa:aa', '--fixed-ip', - 'subnet=%s,ip-address=10.0.0.2' % self.fake_subnet.id, + f'subnet={self.fake_subnet.id},ip-address=10.0.0.2', '--description', self._port.description, '--device', @@ -312,10 +319,9 @@ def test_create_json_binding_profile(self): self.assertCountEqual(self.data, data) def test_create_with_security_group(self): - secgroup = network_fakes.FakeSecurityGroup.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - return_value=secgroup - ) + secgroup = network_fakes.create_one_security_group() + self.network_client.find_security_group.return_value = secgroup + arglist = [ '--network', self._port.network_id, @@ -330,7 +336,7 @@ def test_create_with_security_group(self): self._port.network_id, ), ('enable', True), - ('security_group', [secgroup.id]), + ('security_groups', [secgroup.id]), ('name', 'test-port'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -383,11 +389,10 @@ def test_create_port_with_dns_name(self): self.assertCountEqual(self.data, data) def test_create_with_security_groups(self): - sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - side_effect=[sg_1, sg_2] - ) + sg_1 = network_fakes.create_one_security_group() + sg_2 = network_fakes.create_one_security_group() + self.network_client.find_security_group.side_effect = [sg_1, sg_2] + arglist = [ '--network', self._port.network_id, @@ -403,7 +408,7 @@ def test_create_with_security_groups(self): self._port.network_id, ), ('enable', True), - ('security_group', [sg_1.id, sg_2.id]), + ('security_groups', [sg_1.id, sg_2.id]), ('name', 'test-port'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -432,7 +437,7 @@ def test_create_with_no_security_groups(self): verifylist = [ ('network', self._port.network_id), ('enable', True), - ('no_security_group', True), + ('security_groups', []), ('name', 'test-port'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -574,10 +579,9 @@ def test_create_port_with_allowed_address_pair(self): self.assertCountEqual(self.data, data) def test_create_port_with_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ '--network', self._port.network_id, @@ -693,9 +697,7 @@ def _test_create_with_tag(self, add_tags=True, add_tags_in_post=True): else: verifylist.append(('no_tag', True)) - self.network_client.find_extension = mock.Mock( - return_value=add_tags_in_post - ) + self.network_client.find_extension.return_value = add_tags_in_post parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -799,8 +801,7 @@ def test_create_port_with_extra_dhcp_option(self): extra_dhcp_options = [ { 'opt_name': 'classless-static-route', - 'opt_value': '169.254.169.254/32,22.2.0.2,' - '0.0.0.0/0,22.2.0.1', + 'opt_value': '169.254.169.254/32,22.2.0.2,0.0.0.0/0,22.2.0.1', 'ip_version': '4', }, { @@ -818,7 +819,7 @@ def test_create_port_with_extra_dhcp_option(self): '0.0.0.0/0,22.2.0.1,' 'ip-version=4', '--extra-dhcp-option', - 'name=dns-server,value=240C::6666,' 'ip-version=6', + 'name=dns-server,value=240C::6666,ip-version=6', 'test-port', ] @@ -866,7 +867,7 @@ def _test_create_with_numa_affinity_policy(self, policy=None): 'test-port', ] if policy: - arglist += ['--numa-policy-%s' % policy] + arglist += [f'--numa-policy-{policy}'] numa_affinity_policy = None if not policy else policy verifylist = [ @@ -877,7 +878,7 @@ def _test_create_with_numa_affinity_policy(self, policy=None): ('name', 'test-port'), ] if policy: - verifylist.append(('numa_policy_%s' % policy, True)) + verifylist.append((f'numa_policy_{policy}', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1111,6 +1112,50 @@ def test_create_with_hardware_offload_type_switchdev(self): def test_create_with_hardware_offload_type_null(self): self._test_create_with_hardware_offload_type() + def _test_create_with_trusted_field(self, trusted): + arglist = [ + '--network', + self._port.network_id, + 'test-port', + ] + if trusted: + arglist += ['--trusted'] + else: + arglist += ['--not-trusted'] + + verifylist = [ + ( + 'network', + self._port.network_id, + ), + ('name', 'test-port'), + ] + if trusted: + verifylist.append(('trusted', True)) + else: + verifylist.append(('trusted', False)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + create_args = { + 'admin_state_up': True, + 'network_id': self._port.network_id, + 'name': 'test-port', + } + create_args['trusted'] = trusted + self.network_client.create_port.assert_called_once_with(**create_args) + + self.assertEqual(set(self.columns), set(columns)) + self.assertCountEqual(self.data, data) + + def test_create_with_trusted_true(self): + self._test_create_with_trusted_field(True) + + def test_create_with_trusted_false(self): + self._test_create_with_trusted_field(False) + class TestDeletePort(TestPort): # Ports to delete. @@ -1119,7 +1164,7 @@ class TestDeletePort(TestPort): def setUp(self): super().setUp() - self.network_client.delete_port = mock.Mock(return_value=None) + self.network_client.delete_port.return_value = None self.network_client.find_port = network_fakes.get_ports( ports=self._ports ) @@ -1172,7 +1217,7 @@ def test_multi_ports_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._ports[0], exceptions.CommandError] - self.network_client.find_port = mock.Mock(side_effect=find_mock_result) + self.network_client.find_port.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -1190,17 +1235,47 @@ def test_multi_ports_delete_with_exception(self): class TestListPort(compute_fakes.FakeClientMixin, TestPort): - _ports = network_fakes.create_ports(count=3) + _project = identity_fakes.FakeProject.create_one_project() + _networks = network_fakes.create_networks(count=3) + _sport1 = network_fakes.create_one_port( + attrs={'project_id': _project.id, 'network_id': _networks[1]['id']} + ) + _sport2 = network_fakes.create_one_port( + attrs={'project_id': _project.id, 'network_id': _networks[2]['id']} + ) + _trunk_details = { + 'trunk_id': str(uuid.uuid4()), + 'sub_ports': [ + { + 'segmentation_id': 100, + 'segmentation_type': 'vlan', + 'port_id': _sport1.id, + }, + { + 'segmentation_id': 102, + 'segmentation_type': 'vlan', + 'port_id': _sport2.id, + }, + ], + } + _pport = network_fakes.create_one_port( + attrs={ + 'project_id': _project.id, + 'network_id': _networks[0]['id'], + 'trunk_details': _trunk_details, + } + ) + _ports = (_pport, _sport1, _sport2) - columns = ( + columns = [ 'ID', 'Name', 'MAC Address', 'Fixed IP Addresses', 'Status', - ) + ] - columns_long = ( + columns_long = [ 'ID', 'Name', 'MAC Address', @@ -1209,7 +1284,8 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): 'Security Groups', 'Device Owner', 'Tags', - ) + 'Trunk subports', + ] data = [] for prt in _ports: @@ -1235,14 +1311,15 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): format_columns.ListColumn(prt.security_group_ids), prt.device_owner, format_columns.ListColumn(prt.tags), + port.SubPortColumn(prt.trunk_details), ) ) def setUp(self): super().setUp() - self.network_client.ports = mock.Mock(return_value=self._ports) - fake_router = network_fakes.FakeRouter.create_one_router( + self.network_client.ports.return_value = self._ports + fake_router = network_fakes.create_one_router( { 'id': 'fake-router-id', } @@ -1252,8 +1329,8 @@ def setUp(self): 'id': 'fake-network-id', } ) - self.network_client.find_router = mock.Mock(return_value=fake_router) - self.network_client.find_network = mock.Mock(return_value=fake_network) + self.network_client.find_router.return_value = fake_router + self.network_client.find_network.return_value = fake_network # Get the command object to test self.cmd = port.ListPort(self.app, None) @@ -1294,8 +1371,8 @@ def test_port_list_router_opt(self): self.assertCountEqual(self.data, list(data)) def test_port_list_with_server_option(self): - fake_server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = fake_server + fake_server = compute_fakes.create_one_server() + self.compute_client.find_server.return_value = fake_server arglist = [ '--server', @@ -1310,7 +1387,7 @@ def test_port_list_with_server_option(self): self.network_client.ports.assert_called_once_with( device_id=fake_server.id, fields=LIST_FIELDS_TO_RETRIEVE ) - self.compute_sdk_client.find_server.aassert_called_once_with( + self.compute_client.find_server.aassert_called_once_with( mock.ANY, 'fake-server-name' ) self.assertEqual(self.columns, columns) @@ -1418,7 +1495,7 @@ def test_port_list_fixed_ip_opt_ip_address(self): ip_address = self._ports[0].fixed_ips[0]['ip_address'] arglist = [ '--fixed-ip', - "ip-address=%s" % ip_address, + f"ip-address={ip_address}", ] verifylist = [('fixed_ip', [{'ip-address': ip_address}])] @@ -1428,7 +1505,7 @@ def test_port_list_fixed_ip_opt_ip_address(self): self.network_client.ports.assert_called_once_with( **{ - 'fixed_ips': ['ip_address=%s' % ip_address], + 'fixed_ips': [f'ip_address={ip_address}'], 'fields': LIST_FIELDS_TO_RETRIEVE, } ) @@ -1439,7 +1516,7 @@ def test_port_list_fixed_ip_opt_ip_address_substr(self): ip_address_ss = self._ports[0].fixed_ips[0]['ip_address'][:-1] arglist = [ '--fixed-ip', - "ip-substring=%s" % ip_address_ss, + f"ip-substring={ip_address_ss}", ] verifylist = [('fixed_ip', [{'ip-substring': ip_address_ss}])] @@ -1449,7 +1526,7 @@ def test_port_list_fixed_ip_opt_ip_address_substr(self): self.network_client.ports.assert_called_once_with( **{ - 'fixed_ips': ['ip_address_substr=%s' % ip_address_ss], + 'fixed_ips': [f'ip_address_substr={ip_address_ss}'], 'fields': LIST_FIELDS_TO_RETRIEVE, } ) @@ -1460,22 +1537,21 @@ def test_port_list_fixed_ip_opt_subnet_id(self): subnet_id = self._ports[0].fixed_ips[0]['subnet_id'] arglist = [ '--fixed-ip', - "subnet=%s" % subnet_id, + f"subnet={subnet_id}", ] verifylist = [('fixed_ip', [{'subnet': subnet_id}])] self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': subnet_id} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.ports.assert_called_once_with( **{ - 'fixed_ips': ['subnet_id=%s' % subnet_id], + 'fixed_ips': [f'subnet_id={subnet_id}'], 'fields': LIST_FIELDS_TO_RETRIEVE, } ) @@ -1496,17 +1572,16 @@ def test_port_list_fixed_ip_opts(self): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': subnet_id} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.ports.assert_called_once_with( **{ 'fixed_ips': [ - 'subnet_id=%s' % subnet_id, - 'ip_address=%s' % ip_address, + f'subnet_id={subnet_id}', + f'ip_address={ip_address}', ], 'fields': LIST_FIELDS_TO_RETRIEVE, } @@ -1519,9 +1594,9 @@ def test_port_list_fixed_ips(self): ip_address = self._ports[0].fixed_ips[0]['ip_address'] arglist = [ '--fixed-ip', - "subnet=%s" % subnet_id, + f"subnet={subnet_id}", '--fixed-ip', - "ip-address=%s" % ip_address, + f"ip-address={ip_address}", ] verifylist = [ ('fixed_ip', [{'subnet': subnet_id}, {'ip-address': ip_address}]) @@ -1533,17 +1608,16 @@ def test_port_list_fixed_ips(self): 'fields': LIST_FIELDS_TO_RETRIEVE, } ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.ports.assert_called_once_with( **{ 'fixed_ips': [ - 'subnet_id=%s' % subnet_id, - 'ip_address=%s' % ip_address, + f'subnet_id={subnet_id}', + f'ip_address={ip_address}', ], 'fields': LIST_FIELDS_TO_RETRIEVE, } @@ -1712,6 +1786,29 @@ def test_port_list_security_group(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) + def test_port_list_status(self): + arglist = [ + '--status', + 'ACTIVE', + ] + verifylist = [ + ('status', 'ACTIVE'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = { + 'status': 'ACTIVE', + 'fields': LIST_FIELDS_TO_RETRIEVE, + } + + self.network_client.ports.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual( + self.data, + list(data), + ) + class TestSetPort(TestPort): _port = network_fakes.create_one_port({'tags': ['green', 'red']}) @@ -1719,12 +1816,11 @@ class TestSetPort(TestPort): def setUp(self): super().setUp() self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_port = mock.Mock(return_value=self._port) - self.network_client.update_port = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_port.return_value = self._port + self.network_client.update_port.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = port.SetPort(self.app, None) @@ -1747,7 +1843,7 @@ def test_set_port_fixed_ip(self): _testport = network_fakes.create_one_port( {'fixed_ips': [{'ip_address': '0.0.0.1'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--fixed-ip', 'ip-address=10.0.0.12', @@ -1775,7 +1871,7 @@ def test_set_port_fixed_ip_clear(self): _testport = network_fakes.create_one_port( {'fixed_ips': [{'ip_address': '0.0.0.1'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--fixed-ip', 'ip-address=10.0.0.12', @@ -1825,7 +1921,7 @@ def test_set_port_overwrite_binding_profile(self): _testport = network_fakes.create_one_port( {'binding_profile': {'lok_i': 'visi_on'}} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--binding-profile', 'lok_i=than_os', @@ -1850,7 +1946,7 @@ def test_overwrite_mac_address(self): _testport = network_fakes.create_one_port( {'mac_address': '11:22:33:44:55:66'} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--mac-address', '66:55:44:33:22:11', @@ -1990,15 +2086,15 @@ def test_set_port_mixed_binding_profile(self): self.assertIsNone(result) def test_set_port_security_group(self): - sg = network_fakes.FakeSecurityGroup.create_one_security_group() - self.network_client.find_security_group = mock.Mock(return_value=sg) + sg = network_fakes.create_one_security_group() + self.network_client.find_security_group.return_value = sg arglist = [ '--security-group', sg.id, self._port.name, ] verifylist = [ - ('security_group', [sg.id]), + ('security_groups', [sg.id]), ('port', self._port.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2013,16 +2109,15 @@ def test_set_port_security_group(self): self.assertIsNone(result) def test_set_port_security_group_append(self): - sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg_3 = network_fakes.FakeSecurityGroup.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - side_effect=[sg_2, sg_3] - ) + sg_1 = network_fakes.create_one_security_group() + sg_2 = network_fakes.create_one_security_group() + sg_3 = network_fakes.create_one_security_group() + self.network_client.find_security_group.side_effect = [sg_2, sg_3] + _testport = network_fakes.create_one_port( {'security_group_ids': [sg_1.id]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--security-group', sg_2.id, @@ -2031,7 +2126,7 @@ def test_set_port_security_group_append(self): _testport.name, ] verifylist = [ - ('security_group', [sg_2.id, sg_3.id]), + ('security_groups', [sg_2.id, sg_3.id]), ('port', _testport.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2066,13 +2161,13 @@ def test_set_port_security_group_clear(self): self.assertIsNone(result) def test_set_port_security_group_replace(self): - sg1 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg2 = network_fakes.FakeSecurityGroup.create_one_security_group() + sg1 = network_fakes.create_one_security_group() + sg2 = network_fakes.create_one_security_group() _testport = network_fakes.create_one_port( {'security_group_ids': [sg1.id]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) - self.network_client.find_security_group = mock.Mock(return_value=sg2) + self.network_client.find_port.return_value = _testport + self.network_client.find_security_group.return_value = sg2 arglist = [ '--security-group', sg2.id, @@ -2080,7 +2175,7 @@ def test_set_port_security_group_replace(self): _testport.name, ] verifylist = [ - ('security_group', [sg2.id]), + ('security_groups', [sg2.id]), ('no_security_group', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2120,7 +2215,7 @@ def test_set_port_append_allowed_address_pair(self): _testport = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2149,7 +2244,7 @@ def test_set_port_overwrite_allowed_address_pair(self): _testport = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2265,12 +2360,11 @@ def test_set_port_security_disabled(self): ) def test_set_port_with_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + _testport = network_fakes.create_one_port({'qos_policy_id': None}) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--qos-policy', qos_policy.id, @@ -2294,7 +2388,7 @@ def test_set_port_with_qos(self): def test_set_port_data_plane_status(self): _testport = network_fakes.create_one_port({'data_plane_status': None}) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--data-plane-status', 'ACTIVE', @@ -2360,11 +2454,11 @@ def test_set_with_no_tag(self): def _test_create_with_numa_affinity_policy(self, policy): arglist = [ - '--numa-policy-%s' % policy, + f'--numa-policy-{policy}', self._port.id, ] verifylist = [ - ('numa_policy_%s' % policy, True), + (f'numa_policy_{policy}', True), ( 'port', self._port.id, @@ -2440,10 +2534,12 @@ def test_set_hints_invalid_value(self): def test_set_hints_valid_alias_value(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) - self.network_client.find_extension = mock.Mock( - return_value=['port-hints', 'port-hint-ovs-tx-steering'] - ) + self.network_client.find_port.return_value = testport + self.network_client.find_extension.return_value = [ + 'port-hints', + 'port-hint-ovs-tx-steering', + ] + arglist = [ '--hint', 'ovs-tx-steering=hash', @@ -2468,10 +2564,12 @@ def test_set_hints_valid_alias_value(self): def test_set_hints_valid_json(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) - self.network_client.find_extension = mock.Mock( - return_value=['port-hints', 'port-hint-ovs-tx-steering'] - ) + self.network_client.find_port.return_value = testport + self.network_client.find_extension.return_value = [ + 'port-hints', + 'port-hint-ovs-tx-steering', + ] + arglist = [ '--hint', '{"openvswitch": {"other_config": {"tx-steering": "hash"}}}', @@ -2497,6 +2595,64 @@ def test_set_hints_valid_json(self): ) self.assertIsNone(result) + def _test_set_trusted_field(self, trusted): + arglist = [self._port.id] + if trusted: + arglist += ['--trusted'] + else: + arglist += ['--not-trusted'] + + verifylist = [ + ('port', self._port.id), + ] + if trusted: + verifylist.append(('trusted', True)) + else: + verifylist.append(('trusted', False)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network_client.update_port.assert_called_once_with( + self._port, **{'trusted': trusted} + ) + self.assertIsNone(result) + + def test_set_trusted_true(self): + self._test_set_trusted_field(True) + + def test_set_trusted_false(self): + self._test_set_trusted_field(False) + + def _test_set_uplink_status_propagation(self, uspropagation): + arglist = [self._port.id] + if uspropagation: + arglist += ['--enable-uplink-status-propagation'] + else: + arglist += ['--disable-uplink-status-propagation'] + + verifylist = [ + ('port', self._port.id), + ] + if uspropagation: + verifylist.append(('enable_uplink_status_propagation', True)) + else: + verifylist.append(('enable_uplink_status_propagation', False)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network_client.update_port.assert_called_once_with( + self._port, **{'propagate_uplink_status': uspropagation} + ) + self.assertIsNone(result) + + def test_set_uplink_status_propagation_true(self): + self._test_set_uplink_status_propagation(True) + + def test_set_uplink_status_propagation_false(self): + self._test_set_uplink_status_propagation(False) + class TestShowPort(TestPort): # The port to show. @@ -2506,7 +2662,7 @@ class TestShowPort(TestPort): def setUp(self): super().setUp() - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_port.return_value = self._port # Get the command object to test self.cmd = port.ShowPort(self.app, None) @@ -2567,12 +2723,11 @@ def setUp(self): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': '042eb10a-3a18-4658-ab-cf47c8d03152'} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_port = mock.Mock(return_value=self._testport) - self.network_client.update_port = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_port.return_value = self._testport + self.network_client.update_port.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = port.UnsetPort(self.app, None) @@ -2657,22 +2812,21 @@ def test_unset_port_binding_profile_not_existent(self): ) 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_sg1 = network_fakes.create_one_security_group() + _fake_sg2 = network_fakes.create_one_security_group() _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id, _fake_sg2.id]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) - self.network_client.find_security_group = mock.Mock( - return_value=_fake_sg2 - ) + self.network_client.find_port.return_value = _fake_port + self.network_client.find_security_group.return_value = _fake_sg2 + arglist = [ '--security-group', _fake_sg2.id, _fake_port.name, ] verifylist = [ - ('security_group_ids', [_fake_sg2.id]), + ('security_groups', [_fake_sg2.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2685,21 +2839,20 @@ def test_unset_security_group(self): self.assertIsNone(result) 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_sg1 = network_fakes.create_one_security_group() + _fake_sg2 = network_fakes.create_one_security_group() _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id]} ) - self.network_client.find_security_group = mock.Mock( - return_value=_fake_sg2 - ) + self.network_client.find_security_group.return_value = _fake_sg2 + arglist = [ '--security-group', _fake_sg2.id, _fake_port.name, ] verifylist = [ - ('security_group_ids', [_fake_sg2.id]), + ('security_groups', [_fake_sg2.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2711,7 +2864,7 @@ def test_unset_port_allowed_address_pair(self): _fake_port = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--allowed-address', 'ip-address=192.168.1.123', @@ -2737,7 +2890,7 @@ def test_unset_port_allowed_address_pair_not_existent(self): _fake_port = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2756,7 +2909,7 @@ def test_unset_port_data_plane_status(self): _fake_port = network_fakes.create_one_port( {'data_plane_status': 'ACTIVE'} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--data-plane-status', _fake_port.name, @@ -2809,7 +2962,7 @@ def test_unset_numa_affinity_policy(self): _fake_port = network_fakes.create_one_port( {'numa_affinity_policy': 'required'} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--numa-policy', _fake_port.name, @@ -2833,7 +2986,7 @@ def test_unset_numa_affinity_policy(self): def test_unset_hints(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--hints', testport.name, @@ -2850,3 +3003,43 @@ def test_unset_hints(self): **{'hints': None}, ) self.assertIsNone(result) + + def test_unset_device(self): + testport = network_fakes.create_one_port() + self.network_client.find_port.return_value = testport + arglist = [ + '--device', + testport.name, + ] + verifylist = [ + ('device', True), + ('port', testport.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.network_client.update_port.assert_called_once_with( + testport, + **{'device_id': ''}, + ) + self.assertIsNone(result) + + def test_unset_device_owner(self): + testport = network_fakes.create_one_port() + self.network_client.find_port.return_value = testport + arglist = [ + '--device-owner', + testport.name, + ] + verifylist = [ + ('device_owner', True), + ('port', testport.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.network_client.update_port.assert_called_once_with( + testport, + **{'device_owner': ''}, + ) + self.assertIsNone(result) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index e420f80968..6ebb7809eb 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -34,15 +34,13 @@ class TestAddPortToRouter(TestRouter): '''Add port to Router''' _port = network_fakes.create_one_port() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'port': _port.id} - ) + _router = network_fakes.create_one_router(attrs={'port': _port.id}) def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_router.return_value = self._router + self.network_client.find_port.return_value = self._port self.cmd = router.AddPortToRouter(self.app, None) @@ -84,15 +82,13 @@ class TestAddSubnetToRouter(TestRouter): '''Add subnet to Router''' _subnet = network_fakes.FakeSubnet.create_one_subnet() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'subnet': _subnet.id} - ) + _router = network_fakes.create_one_router(attrs={'subnet': _subnet.id}) def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_router.return_value = self._router + self.network_client.find_subnet.return_value = self._subnet self.cmd = router.AddSubnetToRouter(self.app, None) @@ -129,49 +125,58 @@ def test_add_subnet_required_options(self): class TestCreateRouter(TestRouter): # The new router created. - new_router = network_fakes.FakeRouter.create_one_router() + new_router = network_fakes.create_one_router() _extensions = {'fake': network_fakes.create_one_extension()} columns = ( 'admin_state_up', 'availability_zone_hints', 'availability_zones', + 'created_at', 'description', 'distributed', + 'enable_ndp_proxy', 'external_gateway_info', + 'flavor_id', 'ha', 'id', 'name', 'project_id', + 'revision_number', 'routes', 'status', 'tags', + 'updated_at', ) data = ( - router.AdminStateColumn(new_router.admin_state_up), + router.AdminStateColumn(new_router.is_admin_state_up), format_columns.ListColumn(new_router.availability_zone_hints), format_columns.ListColumn(new_router.availability_zones), + new_router.created_at, new_router.description, - new_router.distributed, + new_router.is_distributed, + new_router.enable_ndp_proxy, router.RouterInfoColumn(new_router.external_gateway_info), - new_router.ha, + new_router.flavor_id, + new_router.is_ha, new_router.id, new_router.name, new_router.project_id, + new_router.revision_number, router.RoutesColumn(new_router.routes), new_router.status, format_columns.ListColumn(new_router.tags), + new_router.updated_at, ) def setUp(self): super().setUp() - self.network_client.create_router = mock.Mock( - return_value=self.new_router - ) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.create_router.return_value = self.new_router + + self.network_client.set_tags.return_value = None + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) # Get the command object to test self.cmd = router.CreateRouter(self.app, None) @@ -216,8 +221,8 @@ def test_create_default_options(self): def test_create_with_gateway(self): _network = network_fakes.create_one_network() _subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=_network) - self.network_client.find_subnet = mock.Mock(return_value=_subnet) + self.network_client.find_network.return_value = _network + self.network_client.find_subnet.return_value = _subnet arglist = [ self.new_router.name, '--external-gateway', @@ -384,15 +389,14 @@ def test_create_with_tags(self): def test_create_with_no_tag(self): self._test_create_with_tag(add_tags=False) - def test_create_with_flavor_id_or_name(self): + def test_create_with_flavor_id_id(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [ self.new_router.name, '--flavor-id', _flavor.id, ] - arglist_with_name = [self.new_router.name, '--flavor-id', _flavor.name] verifylist = [ ('name', self.new_router.name), ('enable', True), @@ -412,18 +416,69 @@ def test_create_with_flavor_id_or_name(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - self.network_client.create_router.reset_mock() - verifylist_w_name = [ + def test_create_with_flavor_id_name(self): + _flavor = network_fakes.create_one_network_flavor() + self.network_client.find_flavor.return_value = _flavor + arglist = [self.new_router.name, '--flavor-id', _flavor.name] + verifylist = [ ('name', self.new_router.name), ('enable', True), ('distributed', False), ('ha', False), ('flavor_id', _flavor.name), ] - parsed_args_w_name = self.check_parser( - self.cmd, arglist_with_name, verifylist_w_name + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'flavor_id': _flavor.id, + } ) - columns, data = self.cmd.take_action(parsed_args_w_name) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_flavor_id(self): + _flavor = network_fakes.create_one_network_flavor() + self.network_client.find_flavor.return_value = _flavor + arglist = [ + self.new_router.name, + '--flavor', + _flavor.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('flavor', _flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'flavor_id': _flavor.id, + } + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_flavor_name(self): + _flavor = network_fakes.create_one_network_flavor() + self.network_client.find_flavor.return_value = _flavor + arglist = [self.new_router.name, '--flavor', _flavor.name] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('flavor', _flavor.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) self.network_client.create_router.assert_called_once_with( **{ 'admin_state_up': True, @@ -435,11 +490,12 @@ def test_create_with_flavor_id_or_name(self): self.assertCountEqual(self.data, data) def test_create_with_enable_default_route_bfd(self): - self.network_client.find_extension = mock.Mock( - return_value=network_fakes.create_one_extension( + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( attrs={'name': 'external-gateway-multihoming'} - ) - ) + ), + } + arglist = [self.new_router.name, '--enable-default-route-bfd'] verifylist = [ ('name', self.new_router.name), @@ -469,11 +525,12 @@ def test_create_with_enable_default_route_bfd_no_extension(self): ) def test_create_with_enable_default_route_ecmp(self): - self.network_client.find_extension = mock.Mock( - return_value=network_fakes.create_one_extension( + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( attrs={'name': 'external-gateway-multihoming'} - ) - ) + ), + } + arglist = [self.new_router.name, '--enable-default-route-ecmp'] verifylist = [ ('name', self.new_router.name), @@ -502,17 +559,74 @@ def test_create_with_enable_default_route_ecmp_no_extension(self): parsed_args, ) + def test_create_with_qos_policy(self): + _network = network_fakes.create_one_network() + self.network_client.find_network.return_value = _network + _qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = _qos_policy + + arglist = [ + self.new_router.name, + '--external-gateway', + _network.id, + '--qos-policy', + _qos_policy.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('qos_policy', _qos_policy.id), + ('external_gateways', [_network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + gw_info = {'network_id': _network.id, 'qos_policy_id': _qos_policy.id} + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + **{'external_gateway_info': gw_info}, + } + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_qos_policy_no_external_gateway(self): + _qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = _qos_policy + + arglist = [ + self.new_router.name, + '--qos-policy', + _qos_policy.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('qos_policy', _qos_policy.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + class TestDeleteRouter(TestRouter): # The routers to delete. - _routers = network_fakes.FakeRouter.create_routers(count=2) + _routers = network_fakes.create_routers(count=2) def setUp(self): super().setUp() - self.network_client.delete_router = mock.Mock(return_value=None) + self.network_client.delete_router.return_value = None - self.network_client.find_router = network_fakes.FakeRouter.get_routers( + self.network_client.find_router = network_fakes.get_routers( self._routers ) @@ -564,9 +678,7 @@ def test_multi_routers_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._routers[0], exceptions.CommandError] - self.network_client.find_router = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_router.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -587,7 +699,7 @@ def test_multi_routers_delete_with_exception(self): class TestListRouter(TestRouter): # The routers going to be listed up. - routers = network_fakes.FakeRouter.create_routers(count=3) + routers = network_fakes.create_routers(count=3) extensions = network_fakes.create_one_extension() columns = ( @@ -618,10 +730,10 @@ class TestListRouter(TestRouter): r.id, r.name, r.status, - router.AdminStateColumn(r.admin_state_up), + router.AdminStateColumn(r.is_admin_state_up), r.project_id, - r.distributed, - r.ha, + r.is_distributed, + r.is_ha, ) ) @@ -671,21 +783,16 @@ def setUp(self): # Get the command object to test self.cmd = router.ListRouter(self.app, None) - self.network_client.agent_hosted_routers = mock.Mock( - return_value=self.routers - ) - self.network_client.routers = mock.Mock(return_value=self.routers) - self.network_client.find_extension = mock.Mock( - return_value=self.extensions - ) - self.network_client.find_router = mock.Mock( - return_value=self.routers[0] - ) + self.network_client.agent_hosted_routers.return_value = self.routers + + self.network_client.routers.return_value = self.routers + self.network_client.find_extension.return_value = self.extensions + + self.network_client.find_router.return_value = self.routers[0] + self._testagent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=self._testagent) - self.network_client.get_router = mock.Mock( - return_value=self.routers[0] - ) + self.network_client.get_agent.return_value = self._testagent + self.network_client.get_router.return_value = self.routers[0] def test_router_list_no_options(self): arglist = [] @@ -704,7 +811,7 @@ def test_router_list_no_options(self): self.assertCountEqual(self.data, list(data)) def test_router_list_no_ha_no_distributed(self): - _routers = network_fakes.FakeRouter.create_routers( + _routers = network_fakes.create_routers( {'ha': None, 'distributed': None}, count=3 ) @@ -750,7 +857,7 @@ def test_router_list_long_no_az(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # to mock, that no availability zone - self.network_client.find_extension = mock.Mock(return_value=None) + self.network_client.find_extension.return_value = None # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable @@ -924,15 +1031,13 @@ class TestRemovePortFromRouter(TestRouter): '''Remove port from a Router''' _port = network_fakes.create_one_port() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'port': _port.id} - ) + _router = network_fakes.create_one_router(attrs={'port': _port.id}) def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_router.return_value = self._router + self.network_client.find_port.return_value = self._port self.cmd = router.RemovePortFromRouter(self.app, None) @@ -971,15 +1076,13 @@ class TestRemoveSubnetFromRouter(TestRouter): '''Remove subnet from Router''' _subnet = network_fakes.FakeSubnet.create_one_subnet() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'subnet': _subnet.id} - ) + _router = network_fakes.create_one_router(attrs={'subnet': _subnet.id}) def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_router.return_value = self._router + self.network_client.find_subnet.return_value = self._subnet self.cmd = router.RemoveSubnetFromRouter(self.app, None) @@ -1014,15 +1117,16 @@ def test_remove_subnet_required_options(self): class TestAddExtraRoutesToRouter(TestRouter): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() def setUp(self): super().setUp() - self.network_client.add_extra_routes_to_router = mock.Mock( - return_value=self._router + self.network_client.add_extra_routes_to_router.return_value = ( + self._router ) + self.cmd = router.AddExtraRoutesToRouter(self.app, None) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def test_add_no_extra_route(self): arglist = [ @@ -1103,15 +1207,16 @@ def test_add_multiple_extra_routes(self): class TestRemoveExtraRoutesFromRouter(TestRouter): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() def setUp(self): super().setUp() - self.network_client.remove_extra_routes_from_router = mock.Mock( - return_value=self._router + self.network_client.remove_extra_routes_from_router.return_value = ( + self._router ) + self.cmd = router.RemoveExtraRoutesFromRouter(self.app, None) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def test_remove_no_extra_route(self): arglist = [ @@ -1198,22 +1303,21 @@ class TestSetRouter(TestRouter): _subnet = network_fakes.FakeSubnet.create_one_subnet( attrs={'network_id': _network.id} ) - _router = network_fakes.FakeRouter.create_one_router( + _router = network_fakes.create_one_router( attrs={'routes': [_default_route], 'tags': ['green', 'red']} ) _extensions = {'fake': network_fakes.create_one_extension()} def setUp(self): super().setUp() - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.update_router.return_value = None + self.network_client.set_tags.return_value = None + self.network_client.find_router.return_value = self._router + self.network_client.find_network.return_value = self._network + + self.network_client.find_subnet.return_value = self._subnet + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) # Get the command object to test self.cmd = router.SetRouter(self.app, None) @@ -1345,10 +1449,10 @@ def test_set_no_route(self): self.assertIsNone(result) def test_set_route_overwrite_route(self): - _testrouter = network_fakes.FakeRouter.create_one_router( + _testrouter = network_fakes.create_one_router( {'routes': [{"destination": "10.0.0.2", "nexthop": "1.1.1.1"}]} ) - self.network_client.find_router = mock.Mock(return_value=_testrouter) + self.network_client.find_router.return_value = _testrouter arglist = [ _testrouter.name, '--route', @@ -1555,10 +1659,9 @@ def test_set_with_no_tag(self): self._test_set_tags(with_tags=False) def test_set_gateway_ip_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ "--external-gateway", self._network.id, @@ -1612,10 +1715,9 @@ def test_unset_gateway_ip_qos(self): self.assertIsNone(result) def test_set_unset_gateway_ip_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ "--external-gateway", self._network.id, @@ -1640,12 +1742,11 @@ def test_set_unset_gateway_ip_qos(self): ) def test_set_gateway_ip_qos_no_gateway(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) - router = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + + router = network_fakes.create_one_router() + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", qos_policy.id, @@ -1662,12 +1763,11 @@ def test_set_gateway_ip_qos_no_gateway(self): ) def test_unset_gateway_ip_qos_no_gateway(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) - router = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + + router = network_fakes.create_one_router() + self.network_client.find_router.return_value = router arglist = [ "--no-qos-policy", router.id, @@ -1684,7 +1784,7 @@ def test_unset_gateway_ip_qos_no_gateway(self): class TestShowRouter(TestRouter): # The router to set. - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() _port = network_fakes.create_one_port( {'device_owner': 'network:router_interface', 'device_id': _router.id} ) @@ -1704,40 +1804,50 @@ class TestShowRouter(TestRouter): 'admin_state_up', 'availability_zone_hints', 'availability_zones', + 'created_at', 'description', 'distributed', + 'enable_ndp_proxy', 'external_gateway_info', + 'flavor_id', 'ha', 'id', 'interfaces_info', 'name', 'project_id', + 'revision_number', 'routes', 'status', 'tags', + 'updated_at', ) data = ( - router.AdminStateColumn(_router.admin_state_up), + router.AdminStateColumn(_router.is_admin_state_up), format_columns.ListColumn(_router.availability_zone_hints), format_columns.ListColumn(_router.availability_zones), + _router.created_at, _router.description, - _router.distributed, + _router.is_distributed, + _router.enable_ndp_proxy, router.RouterInfoColumn(_router.external_gateway_info), - _router.ha, + _router.flavor_id, + _router.is_ha, _router.id, router.RouterInfoColumn(_router.interfaces_info), _router.name, _router.project_id, + _router.revision_number, router.RoutesColumn(_router.routes), _router.status, format_columns.ListColumn(_router.tags), + _router.updated_at, ) def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.ports = mock.Mock(return_value=[self._port]) + self.network_client.find_router.return_value = self._router + self.network_client.ports.return_value = [self._port] # Get the command object to test self.cmd = router.ShowRouter(self.app, None) @@ -1775,7 +1885,7 @@ def test_show_all_options(self): self.assertCountEqual(self.data, data) def test_show_no_ha_no_distributed(self): - _router = network_fakes.FakeRouter.create_one_router( + _router = network_fakes.create_one_router( {'ha': None, 'distributed': None} ) @@ -1796,7 +1906,7 @@ def test_show_no_ha_no_distributed(self): self.assertNotIn("is_ha", columns) def test_show_no_extra_route_extension(self): - _router = network_fakes.FakeRouter.create_one_router({'routes': None}) + _router = network_fakes.create_one_router({'routes': None}) arglist = [ _router.name, @@ -1819,10 +1929,8 @@ class TestUnsetRouter(TestRouter): def setUp(self): super().setUp() 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( + self.fake_qos_policy = network_fakes.create_one_qos_policy() + self._testrouter = network_fakes.create_one_router( { 'routes': [ { @@ -1842,18 +1950,16 @@ def setUp(self): } ) self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_router = mock.Mock( - return_value=self._testrouter - ) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_router.return_value = self._testrouter + + self.network_client.update_router.return_value = None + self.network_client.set_tags.return_value = None self._extensions = {'fake': network_fakes.create_one_extension()} - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) - ) - self.network_client.remove_external_gateways = mock.Mock( - return_value=None + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) + self.network_client.remove_external_gateways.return_value = None + # Get the command object to test self.cmd = router.UnsetRouter(self.app, None) @@ -1989,12 +2095,11 @@ def test_unset_router_qos_policy(self): self.assertIsNone(result) def test_unset_gateway_ip_qos_no_network(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) - router = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + + router = network_fakes.create_one_router() + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", router.id, @@ -2009,14 +2114,13 @@ def test_unset_gateway_ip_qos_no_network(self): ) def test_unset_gateway_ip_qos_no_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) - router = network_fakes.FakeRouter.create_one_router( + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy + + router = network_fakes.create_one_router( {"external_gateway_info": {"network_id": "fake-id"}} ) - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", router.id, @@ -2038,7 +2142,7 @@ def setUp(self): self._network = network_fakes.create_one_network() self._networks.append(self._network) - self._router = network_fakes.FakeRouter.create_one_router( + self._router = network_fakes.create_one_router( { 'external_gateway_info': { 'network_id': self._network.id, @@ -2053,10 +2157,10 @@ def setUp(self): attrs={'name': 'external-gateway-multihoming'} ) } - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def _find_network(name_or_id, ignore_missing): for network in self._networks: @@ -2066,15 +2170,12 @@ def _find_network(name_or_id, ignore_missing): return None raise Exception('Test resource not found') - self.network_client.find_network = mock.Mock(side_effect=_find_network) + self.network_client.find_network.side_effect = _find_network - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.network_client.add_external_gateways = mock.Mock( - return_value=None - ) - self.network_client.remove_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.find_subnet.return_value = self._subnet + self.network_client.add_external_gateways.return_value = None + + self.network_client.remove_external_gateways.return_value = None class TestCreateMultipleGateways(TestGatewayOps): @@ -2082,16 +2183,21 @@ class TestCreateMultipleGateways(TestGatewayOps): 'admin_state_up', 'availability_zone_hints', 'availability_zones', + 'created_at', 'description', 'distributed', + 'enable_ndp_proxy', 'external_gateway_info', + 'flavor_id', 'ha', 'id', 'name', 'project_id', + 'revision_number', 'routes', 'status', 'tags', + 'updated_at', ) def setUp(self): @@ -2099,28 +2205,30 @@ def setUp(self): self._second_network = network_fakes.create_one_network() self._networks.append(self._second_network) - self.network_client.create_router = mock.Mock( - return_value=self._router - ) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.update_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.create_router.return_value = self._router + + self.network_client.update_router.return_value = None + self.network_client.update_external_gateways.return_value = None self._data = ( - router.AdminStateColumn(self._router.admin_state_up), + router.AdminStateColumn(self._router.is_admin_state_up), format_columns.ListColumn(self._router.availability_zone_hints), format_columns.ListColumn(self._router.availability_zones), + self._router.created_at, self._router.description, - self._router.distributed, + self._router.is_distributed, + self._router.enable_ndp_proxy, router.RouterInfoColumn(self._router.external_gateway_info), - self._router.ha, + self._router.flavor_id, + self._router.is_ha, self._router.id, self._router.name, self._router.project_id, + self._router.revision_number, router.RoutesColumn(self._router.routes), self._router.status, format_columns.ListColumn(self._router.tags), + self._router.updated_at, ) self.cmd = router.CreateRouter(self.app, None) @@ -2224,10 +2332,9 @@ def setUp(self): self._second_network = network_fakes.create_one_network() self._networks.append(self._second_network) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.update_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.update_router.return_value = None + self.network_client.update_external_gateways.return_value = None + self.cmd = router.SetRouter(self.app, None) def test_update_one_gateway(self): diff --git a/openstackclient/tests/unit/network/v2/test_security_group_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_compute.py index 243f830210..e23bb24977 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -72,7 +72,7 @@ def test_security_group_create_min_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_group['name'], self._security_group['name'], ) @@ -95,7 +95,7 @@ def test_security_group_create_all_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_group['name'], self._security_group['description'], ) @@ -133,7 +133,7 @@ def test_security_group_delete(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_groups[0]['id'], ) self.assertIsNone(result) @@ -153,12 +153,8 @@ def test_security_group_multi_delete(self, sg_mock): sg_mock.assert_has_calls( [ - mock.call( - self.compute_sdk_client, self._security_groups[0]['id'] - ), - mock.call( - self.compute_sdk_client, self._security_groups[1]['id'] - ), + mock.call(self.compute_client, self._security_groups[0]['id']), + mock.call(self.compute_client, self._security_groups[1]['id']), ] ) self.assertIsNone(result) @@ -187,9 +183,7 @@ def test_security_group_multi_delete_with_exception(self, sg_mock): sg_mock.assert_has_calls( [ - mock.call( - self.compute_sdk_client, self._security_groups[0]['id'] - ), + mock.call(self.compute_client, self._security_groups[0]['id']), ] ) @@ -250,7 +244,7 @@ def test_security_group_list_no_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, all_projects=False + self.compute_client, all_projects=False ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -267,9 +261,7 @@ def test_security_group_list_all_projects(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) - sg_mock.assert_called_once_with( - self.compute_sdk_client, all_projects=True - ) + sg_mock.assert_called_once_with(self.compute_client, all_projects=True) self.assertEqual(self.columns_all_projects, columns) self.assertCountEqual(self.data_all_projects, list(data)) @@ -309,7 +301,7 @@ def test_security_group_set_no_updates(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, self._security_group['id'] + self.compute_client, self._security_group['id'] ) self.assertIsNone(result) @@ -334,7 +326,7 @@ def test_security_group_set_all_options(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_group['id'], name=new_name, description=new_description, @@ -394,7 +386,7 @@ def test_security_group_show_all_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, self._security_group['id'] + self.compute_client, self._security_group['id'] ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py index 9619b29637..c5ccf628ee 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -36,40 +35,46 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() # The security group to be created. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group() - ) + _security_group = network_fakes.create_one_security_group() columns = ( + 'created_at', 'description', 'id', + 'is_shared', 'name', 'project_id', + 'revision_number', 'rules', 'stateful', 'tags', + 'updated_at', ) data = ( + _security_group.created_at, _security_group.description, _security_group.id, + _security_group.is_shared, _security_group.name, _security_group.project_id, + _security_group.revision_number, security_group.NetworkSecurityGroupRulesColumn([]), _security_group.stateful, _security_group.tags, + _security_group.updated_at, ) def setUp(self): super().setUp() - self.network_client.create_security_group = mock.Mock( - return_value=self._security_group + self.network_client.create_security_group.return_value = ( + self._security_group ) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.CreateSecurityGroup(self.app, None) @@ -163,7 +168,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network_client.set_tags.called) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) + self.assertEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -174,19 +179,15 @@ def test_create_with_no_tag(self): class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): # The security groups to be deleted. - _security_groups = network_fakes.FakeSecurityGroup.create_security_groups() + _security_groups = network_fakes.create_security_groups() def setUp(self): super().setUp() - self.network_client.delete_security_group = mock.Mock( - return_value=None - ) + self.network_client.delete_security_group.return_value = None self.network_client.find_security_group = ( - network_fakes.FakeSecurityGroup.get_security_groups( - self._security_groups - ) + network_fakes.get_security_groups(self._security_groups) ) # Get the command object to test @@ -241,9 +242,7 @@ def test_multi_security_groups_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._security_groups[0], exceptions.CommandError] - self.network_client.find_security_group = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_security_group.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -264,9 +263,7 @@ def test_multi_security_groups_delete_with_exception(self): class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group to be listed. - _security_groups = network_fakes.FakeSecurityGroup.create_security_groups( - count=3 - ) + _security_groups = network_fakes.create_security_groups(count=3) columns = ( 'ID', @@ -274,6 +271,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): 'Description', 'Project', 'Tags', + 'Shared', ) data = [] @@ -285,14 +283,15 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): grp.description, grp.project_id, grp.tags, + grp.is_shared, ) ) def setUp(self): super().setUp() - self.network_client.security_groups = mock.Mock( - return_value=self._security_groups + self.network_client.security_groups.return_value = ( + self._security_groups ) # Get the command object to test @@ -412,23 +411,20 @@ def test_list_with_tag_options(self): class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group to be set. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group( - attrs={'tags': ['green', 'red']} - ) + _security_group = network_fakes.create_one_security_group( + attrs={'tags': ['green', 'red']} ) def setUp(self): super().setUp() - self.network_client.update_security_group = mock.Mock( - return_value=None - ) + self.network_client.update_security_group.return_value = None - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.set_tags = mock.Mock(return_value=None) + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.SetSecurityGroup(self.app, None) @@ -515,44 +511,48 @@ def test_set_with_no_tag(self): class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group rule to be shown with the group. - _security_group_rule = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() - ) + _security_group_rule = network_fakes.create_one_security_group_rule() # The security group to be shown. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group( - attrs={'security_group_rules': [_security_group_rule._info]} - ) + _security_group = network_fakes.create_one_security_group( + attrs={'security_group_rules': [dict(_security_group_rule)]} ) columns = ( + 'created_at', 'description', 'id', + 'is_shared', 'name', 'project_id', + 'revision_number', 'rules', 'stateful', 'tags', + 'updated_at', ) data = ( + _security_group.created_at, _security_group.description, _security_group.id, + _security_group.is_shared, _security_group.name, _security_group.project_id, + _security_group.revision_number, security_group.NetworkSecurityGroupRulesColumn( - [_security_group_rule._info] + [dict(_security_group_rule)] ), _security_group.stateful, _security_group.tags, + _security_group.updated_at, ) def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) # Get the command object to test @@ -583,23 +583,20 @@ def test_show_all_options(self): class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group to be unset. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group( - attrs={'tags': ['green', 'red']} - ) + _security_group = network_fakes.create_one_security_group( + attrs={'tags': ['green', 'red']} ) def setUp(self): super().setUp() - self.network_client.update_security_group = mock.Mock( - return_value=None - ) + self.network_client.update_security_group.return_value = None - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.set_tags = mock.Mock(return_value=None) + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.UnsetSecurityGroup(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py index fc3c0ddb19..9cab52e392 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py @@ -159,7 +159,7 @@ def test_security_group_rule_create_default_rule(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -202,7 +202,7 @@ def test_security_group_rule_create_remote_group(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -240,7 +240,7 @@ def test_security_group_rule_create_remote_ip(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -279,7 +279,7 @@ def test_security_group_rule_create_proto_option(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -316,7 +316,7 @@ def test_security_group_rule_delete(self, sgr_mock): result = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, self._security_group_rules[0]['id'] + self.compute_client, self._security_group_rules[0]['id'] ) self.assertIsNone(result) @@ -335,11 +335,11 @@ def test_security_group_rule_delete_multi(self, sgr_mock): sgr_mock.assert_has_calls( [ mock.call( - self.compute_sdk_client, + self.compute_client, self._security_group_rules[0]['id'], ), mock.call( - self.compute_sdk_client, + self.compute_client, self._security_group_rules[1]['id'], ), ] @@ -367,10 +367,10 @@ def test_security_group_rule_delete_multi_with_exception(self, sgr_mock): sgr_mock.assert_has_calls( [ mock.call( - self.compute_sdk_client, + self.compute_client, self._security_group_rules[0]['id'], ), - mock.call(self.compute_sdk_client, 'unexist_rule'), + mock.call(self.compute_client, 'unexist_rule'), ] ) @@ -383,7 +383,6 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): _security_group_rule_tcp = compute_fakes.create_one_security_group_rule( { 'ip_protocol': 'tcp', - 'ethertype': 'IPv4', 'from_port': 80, 'to_port': 80, 'group': {'name': _security_group['name']}, @@ -392,7 +391,6 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): _security_group_rule_icmp = compute_fakes.create_one_security_group_rule( { 'ip_protocol': 'icmp', - 'ethertype': 'IPv4', 'from_port': -1, 'to_port': -1, 'ip_range': {'cidr': '10.0.2.0/24'}, @@ -426,7 +424,7 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): expected_rule_with_group = ( rule['id'], rule['ip_protocol'], - rule['ethertype'], + '', # ethertype is a neutron-only thing rule['ip_range'], rule['port_range'], rule['remote_security_group'], @@ -457,7 +455,7 @@ def test_security_group_rule_list_default(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client, all_projects=False + self.compute_client, all_projects=False ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -473,7 +471,7 @@ def test_security_group_rule_list_with_group(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.find_security_group.assert_called_once_with( - self.compute_sdk_client, self._security_group['id'] + self.compute_client, self._security_group['id'] ) self.assertEqual(self.expected_columns_with_group, columns) self.assertEqual(self.expected_data_with_group, list(data)) @@ -489,7 +487,7 @@ def test_security_group_rule_list_all_projects(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client, all_projects=True + self.compute_client, all_projects=True ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -505,7 +503,7 @@ def test_security_group_rule_list_with_ignored_options(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client, all_projects=False + self.compute_client, all_projects=False ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -551,7 +549,7 @@ def test_security_group_rule_show_all_options(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client + self.compute_client ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) 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 c9018db5c4..920e891413 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 @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -40,14 +39,13 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): _security_group_rule = None # The security group that will contain the rule created. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group() - ) + _security_group = network_fakes.create_one_security_group() # The address group to be used in security group rules _address_group = network_fakes.create_one_address_group() expected_columns = ( + 'created_at', 'description', 'direction', 'ether_type', @@ -59,21 +57,23 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): 'remote_address_group_id', 'remote_group_id', 'remote_ip_prefix', + 'revision_number', 'security_group_id', + 'updated_at', ) expected_data = None def _setup_security_group_rule(self, attrs=None): self._security_group_rule = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( - attrs - ) + network_fakes.create_one_security_group_rule(attrs) ) - self.network_client.create_security_group_rule = mock.Mock( - return_value=self._security_group_rule + self.network_client.create_security_group_rule.return_value = ( + self._security_group_rule ) + self.expected_data = ( + self._security_group_rule.created_at, self._security_group_rule.description, self._security_group_rule.direction, self._security_group_rule.ether_type, @@ -85,18 +85,20 @@ def _setup_security_group_rule(self, attrs=None): self._security_group_rule.remote_address_group_id, self._security_group_rule.remote_group_id, self._security_group_rule.remote_ip_prefix, + self._security_group_rule.revision_number, self._security_group_rule.security_group_id, + self._security_group_rule.updated_at, ) def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) self.projects_mock.get.return_value = self.project @@ -963,23 +965,15 @@ def test_create_with_description(self): class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group rules to be deleted. - _security_group_rules = ( - network_fakes.FakeSecurityGroupRule.create_security_group_rules( - count=2 - ) - ) + _security_group_rules = network_fakes.create_security_group_rules(count=2) def setUp(self): super().setUp() - self.network_client.delete_security_group_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_security_group_rule.return_value = None self.network_client.find_security_group_rule = ( - network_fakes.FakeSecurityGroupRule.get_security_group_rules( - self._security_group_rules - ) + network_fakes.get_security_group_rules(self._security_group_rules) ) # Get the command object to test @@ -1034,8 +1028,8 @@ def test_multi_security_group_rules_delete_with_exception(self): self._security_group_rules[0], exceptions.CommandError, ] - self.network_client.find_security_group_rule = mock.Mock( - side_effect=find_mock_result + self.network_client.find_security_group_rule.side_effect = ( + find_mock_result ) try: @@ -1057,33 +1051,27 @@ def test_multi_security_group_rules_delete_with_exception(self): class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group to hold the rules. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group() - ) + _security_group = network_fakes.create_one_security_group() # The security group rule to be listed. - _security_group_rule_tcp = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( - { - 'protocol': 'tcp', - 'port_range_max': 80, - 'port_range_min': 80, - 'security_group_id': _security_group.id, - } - ) + _security_group_rule_tcp = network_fakes.create_one_security_group_rule( + { + 'protocol': 'tcp', + 'port_range_max': 80, + 'port_range_min': 80, + 'security_group_id': _security_group.id, + } ) - _security_group_rule_icmp = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( - { - 'protocol': 'icmp', - 'remote_ip_prefix': '10.0.2.0/24', - 'security_group_id': _security_group.id, - } - ) + _security_group_rule_icmp = network_fakes.create_one_security_group_rule( + { + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + 'security_group_id': _security_group.id, + } ) _security_group.security_group_rules = [ - _security_group_rule_tcp._info, - _security_group_rule_icmp._info, + dict(_security_group_rule_tcp), + dict(_security_group_rule_icmp), ] _security_group_rules = [ _security_group_rule_tcp, @@ -1144,11 +1132,12 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.security_group_rules = mock.Mock( - return_value=self._security_group_rules + + self.network_client.security_group_rules.return_value = ( + self._security_group_rules ) # Get the command object to test @@ -1261,14 +1250,62 @@ def test_list_with_wrong_egress(self): self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) + def test_list_with_project(self): + project = identity_fakes.FakeProject.create_one_project() + self._security_group_rule_tcp.port_range_min = 80 + self.projects_mock.get.return_value = project + + arglist = [ + '--project', + project.id, + ] + verifylist = [ + ('project', project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': project.id, 'project_id': project.id} + + self.network_client.security_group_rules.assert_called_once_with( + **filters + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_project_domain(self): + project = identity_fakes.FakeProject.create_one_project() + self._security_group_rule_tcp.port_range_min = 80 + self.projects_mock.get.return_value = project + + arglist = [ + '--project', + project.id, + '--project-domain', + project.domain_id, + ] + verifylist = [ + ('project', project.id), + ('project_domain', project.domain_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': project.id, 'project_id': project.id} + + self.network_client.security_group_rules.assert_called_once_with( + **filters + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group rule to be shown. - _security_group_rule = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() - ) + _security_group_rule = network_fakes.create_one_security_group_rule() columns = ( + 'created_at', 'description', 'direction', 'ether_type', @@ -1280,10 +1317,13 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): 'remote_address_group_id', 'remote_group_id', 'remote_ip_prefix', + 'revision_number', 'security_group_id', + 'updated_at', ) data = ( + _security_group_rule.created_at, _security_group_rule.description, _security_group_rule.direction, _security_group_rule.ether_type, @@ -1295,14 +1335,16 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): _security_group_rule.remote_address_group_id, _security_group_rule.remote_group_id, _security_group_rule.remote_ip_prefix, + _security_group_rule.revision_number, _security_group_rule.security_group_id, + _security_group_rule.updated_at, ) def setUp(self): super().setUp() - self.network_client.find_security_group_rule = mock.Mock( - return_value=self._security_group_rule + self.network_client.find_security_group_rule.return_value = ( + self._security_group_rule ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 8b82074116..e59168e517 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -266,19 +265,14 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Mock SDK calls for all tests. - self.network_client.create_subnet = mock.Mock( - return_value=self._subnet - ) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.create_subnet.return_value = self._subnet + + self.network_client.set_tags.return_value = None + self.network_client.find_network.return_value = self._network + + self.network_client.find_segment.return_value = self._network_segment + + self.network_client.find_subnet_pool.return_value = self._subnet_pool def test_create_no_options(self): arglist = [] @@ -333,7 +327,7 @@ def test_create_default_options(self): def test_create_from_subnet_pool_options(self): # Mock SDK calls for this test. self.network_client.create_subnet.return_value = self._subnet_from_pool - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.set_tags.return_value = None self._network.id = self._subnet_from_pool.network_id arglist = [ @@ -721,7 +715,7 @@ class TestDeleteSubnet(TestSubnet): def setUp(self): super().setUp() - self.network_client.delete_subnet = mock.Mock(return_value=None) + self.network_client.delete_subnet.return_value = None self.network_client.find_subnet = network_fakes.FakeSubnet.get_subnets( self._subnets @@ -775,9 +769,7 @@ def test_multi_subnets_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._subnets[0], exceptions.CommandError] - self.network_client.find_subnet = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_subnet.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -855,7 +847,7 @@ def setUp(self): # Get the command object to test self.cmd = subnet_v2.ListSubnet(self.app, None) - self.network_client.subnets = mock.Mock(return_value=self._subnet) + self.network_client.subnets.return_value = self._subnet def test_subnet_list_no_options(self): arglist = [] @@ -1019,7 +1011,7 @@ def test_subnet_list_project_domain(self): def test_subnet_list_network(self): network = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock(return_value=network) + self.network_client.find_network.return_value = network arglist = [ '--network', network.id, @@ -1038,7 +1030,7 @@ def test_subnet_list_network(self): def test_subnet_list_gateway(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--gateway', subnet.gateway_ip, @@ -1057,7 +1049,7 @@ def test_subnet_list_gateway(self): def test_subnet_list_name(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--name', subnet.name, @@ -1076,7 +1068,7 @@ def test_subnet_list_name(self): def test_subnet_list_subnet_range(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--subnet-range', subnet.cidr, @@ -1098,10 +1090,9 @@ def test_subnet_list_subnetpool_by_name(self): subnet = network_fakes.FakeSubnet.create_one_subnet( {'subnetpool_id': subnet_pool.id} ) - self.network_client.find_network = mock.Mock(return_value=subnet) - self.network_client.find_subnet_pool = mock.Mock( - return_value=subnet_pool - ) + self.network_client.find_network.return_value = subnet + self.network_client.find_subnet_pool.return_value = subnet_pool + arglist = [ '--subnet-pool', subnet_pool.name, @@ -1123,10 +1114,9 @@ def test_subnet_list_subnetpool_by_id(self): subnet = network_fakes.FakeSubnet.create_one_subnet( {'subnetpool_id': subnet_pool.id} ) - self.network_client.find_network = mock.Mock(return_value=subnet) - self.network_client.find_subnet_pool = mock.Mock( - return_value=subnet_pool - ) + self.network_client.find_network.return_value = subnet + self.network_client.find_subnet_pool.return_value = subnet_pool + arglist = [ '--subnet-pool', subnet_pool.id, @@ -1182,9 +1172,9 @@ class TestSetSubnet(TestSubnet): def setUp(self): super().setUp() - self.network_client.update_subnet = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.update_subnet.return_value = None + self.network_client.set_tags.return_value = None + self.network_client.find_subnet.return_value = self._subnet self.cmd = subnet_v2.SetSubnet(self.app, None) def test_set_this(self): @@ -1263,7 +1253,7 @@ def test_append_options(self): 'service_types': ["network:router_gateway"], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--dns-nameserver', '10.0.0.2', @@ -1329,7 +1319,7 @@ def test_overwrite_options(self): 'dns_nameservers': ["10.0.0.1"], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', @@ -1379,7 +1369,7 @@ def test_clear_options(self): 'dns_nameservers': ['10.0.0.1'], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--no-host-route', '--no-allocation-pool', @@ -1448,8 +1438,8 @@ def test_set_segment(self): 'segment_id': None, } ) - self.network_client.find_subnet = mock.Mock(return_value=_subnet) - self.network_client.find_segment = mock.Mock(return_value=_segment) + self.network_client.find_subnet.return_value = _subnet + self.network_client.find_segment.return_value = _segment arglist = ['--network-segment', _segment.id, _subnet.name] verifylist = [('network_segment', _segment.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1514,7 +1504,7 @@ def setUp(self): # Get the command object to test self.cmd = subnet_v2.ShowSubnet(self.app, None) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_subnet.return_value = self._subnet def test_show_no_options(self): arglist = [] @@ -1572,11 +1562,10 @@ def setUp(self): 'tags': ['green', 'red'], } ) - self.network_client.find_subnet = mock.Mock( - return_value=self._testsubnet - ) - self.network_client.update_subnet = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self._testsubnet + + self.network_client.update_subnet.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_v2.UnsetSubnet(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 214bfdce52..013550ec1e 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -76,16 +75,15 @@ class TestCreateSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.create_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_subnet_pool.return_value = self._subnet_pool + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_pool.CreateSubnetPool(self.app, None) - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) self.projects_mock.get.return_value = self.project @@ -387,7 +385,7 @@ class TestDeleteSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.delete_subnet_pool = mock.Mock(return_value=None) + self.network_client.delete_subnet_pool.return_value = None self.network_client.find_subnet_pool = ( network_fakes.FakeSubnetPool.get_subnet_pools(self._subnet_pools) @@ -445,9 +443,7 @@ def test_multi_subnet_pools_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._subnet_pools[0], exceptions.CommandError] - self.network_client.find_subnet_pool = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_subnet_pool.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -514,9 +510,7 @@ def setUp(self): # Get the command object to test self.cmd = subnet_pool.ListSubnetPool(self.app, None) - self.network_client.subnet_pools = mock.Mock( - return_value=self._subnet_pools - ) + self.network_client.subnet_pools.return_value = self._subnet_pools def test_subnet_pool_list_no_option(self): arglist = [] @@ -653,7 +647,7 @@ def test_subnet_pool_list_project_domain(self): def test_subnet_pool_list_name(self): subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() - self.network_client.find_network = mock.Mock(return_value=subnet_pool) + self.network_client.find_network.return_value = subnet_pool arglist = [ '--name', subnet_pool.name, @@ -672,9 +666,8 @@ def test_subnet_pool_list_name(self): def test_subnet_pool_list_address_scope(self): addr_scope = network_fakes.create_one_address_scope() - self.network_client.find_address_scope = mock.Mock( - return_value=addr_scope - ) + self.network_client.find_address_scope.return_value = addr_scope + arglist = [ '--address-scope', addr_scope.id, @@ -734,15 +727,13 @@ class TestSetSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.update_subnet_pool = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_subnet_pool.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.find_subnet_pool.return_value = self._subnet_pool - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test @@ -1006,7 +997,7 @@ def test_set_with_default_quota(self): self._subnet_pool, **{ 'default_quota': 20, - } + }, ) self.assertIsNone(result) @@ -1079,9 +1070,7 @@ class TestShowSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.find_subnet_pool.return_value = self._subnet_pool # Get the command object to test self.cmd = subnet_pool.ShowSubnetPool(self.app, None) @@ -1122,11 +1111,10 @@ def setUp(self): self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool( {'tags': ['green', 'red']} ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnetpool - ) - self.network_client.update_subnet_pool = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet_pool.return_value = self._subnetpool + + self.network_client.update_subnet_pool.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_pool.UnsetSubnetPool(self.app, None) diff --git a/openstackclient/tests/unit/object/v1/test_object.py b/openstackclient/tests/unit/object/v1/test_object.py index 665a71d144..f1777f963c 100644 --- a/openstackclient/tests/unit/object/v1/test_object.py +++ b/openstackclient/tests/unit/object/v1/test_object.py @@ -352,7 +352,7 @@ def test_object_show(self, c_mock): c_mock.assert_called_with( container=object_fakes.container_name, object=object_fakes.object_name_1, - **kwargs + **kwargs, ) collist = ('bytes', 'content_type', 'hash', 'last_modified', 'name') diff --git a/openstackclient/tests/unit/object/v1/test_object_all.py b/openstackclient/tests/unit/object/v1/test_object_all.py index 484ac1b910..968667b68e 100644 --- a/openstackclient/tests/unit/object/v1/test_object_all.py +++ b/openstackclient/tests/unit/object/v1/test_object_all.py @@ -252,9 +252,10 @@ def __enter__(self): def __exit__(self, *a): self.context_manager_calls.append('__exit__') - with mock.patch('sys.stdout') as fake_stdout, mock.patch( - 'os.fdopen', return_value=FakeStdout() - ) as fake_fdopen: + with ( + mock.patch('sys.stdout') as fake_stdout, + mock.patch('os.fdopen', return_value=FakeStdout()) as fake_fdopen, + ): fake_stdout.fileno.return_value = 123 self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py index fd95fce615..628e362bba 100644 --- a/openstackclient/tests/unit/test_shell.py +++ b/openstackclient/tests/unit/test_shell.py @@ -183,14 +183,14 @@ def _assert_token_auth(self, cmd_options, default_args): osc_lib_test_utils.fake_execute(_shell, _cmd) self.app.assert_called_with(["list", "role"]) - self.assertEqual( - default_args.get("token", ''), _shell.options.token, "token" - ) - self.assertEqual( - default_args.get("auth_url", ''), - _shell.options.auth_url, - "auth_url", - ) + + if default_args.get('token'): + self.assertEqual(default_args['token'], _shell.options.token) + + if default_args.get('auth_url'): + self.assertEqual( + default_args['auth_url'], _shell.options.auth_url + ) def _assert_cli(self, cmd_options, default_args): with mock.patch( @@ -204,25 +204,28 @@ def _assert_cli(self, cmd_options, default_args): osc_lib_test_utils.fake_execute(_shell, _cmd) self.app.assert_called_with(["list", "server"]) + + # TODO(stephenfin): Remove "or ''" when we bump osc-lib minimum to + # a version that includes I1d26133c9d9ed299d1035f207059aa8fe463a001 self.assertEqual( default_args["compute_api_version"], - _shell.options.os_compute_api_version, + _shell.options.os_compute_api_version or '', ) self.assertEqual( default_args["identity_api_version"], - _shell.options.os_identity_api_version, + _shell.options.os_identity_api_version or '', ) self.assertEqual( default_args["image_api_version"], - _shell.options.os_image_api_version, + _shell.options.os_image_api_version or '', ) self.assertEqual( default_args["volume_api_version"], - _shell.options.os_volume_api_version, + _shell.options.os_volume_api_version or '', ) self.assertEqual( default_args["network_api_version"], - _shell.options.os_network_api_version, + _shell.options.os_network_api_version or '', ) diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index b9f38f950e..607047f14e 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -62,7 +62,7 @@ def assertNotCalled(self, m, msg=None): if m.called: if not msg: - msg = 'method %s should not have been called' % m + msg = f'method {m} should not have been called' self.fail(msg) @@ -90,11 +90,17 @@ def check_parser(self, cmd, args, verify_args): argparse.ArgumentError, ): raise ParserException( - "Argument parse failed: %s" % stderr.getvalue() + f"Argument parse failed: {stderr.getvalue()}" ) for av in verify_args: - attr, value = av + attr, expected_value = av if attr: + actual_value = getattr(parsed_args, attr) self.assertIn(attr, parsed_args) - self.assertEqual(value, getattr(parsed_args, attr)) + self.assertEqual( + expected_value, + actual_value, + f'args.{attr}: expected: {expected_value}, got: ' + f'{actual_value}', + ) return parsed_args diff --git a/openstackclient/tests/unit/volume/test_find_resource.py b/openstackclient/tests/unit/volume/test_find_resource.py index df087dd5d3..614fa9a518 100644 --- a/openstackclient/tests/unit/volume/test_find_resource.py +++ b/openstackclient/tests/unit/volume/test_find_resource.py @@ -21,15 +21,6 @@ from osc_lib import utils from openstackclient.tests.unit import utils as test_utils -from openstackclient.volume import client # noqa - - -# Monkey patch for v1 cinderclient -# NOTE(dtroyer): Do here because openstackclient.volume.client -# doesn't do it until the client object is created now. -volumes.Volume.NAME_ATTR = 'display_name' -volume_snapshots.Snapshot.NAME_ATTR = 'display_name' - ID = '1after909' NAME = 'PhilSpector' @@ -42,14 +33,14 @@ def setUp(self): api.client = mock.Mock() api.client.get = mock.Mock() resp = mock.Mock() - body = {"volumes": [{"id": ID, 'display_name': NAME}]} + body = {"volumes": [{"id": ID, 'name': NAME}]} api.client.get.side_effect = [Exception("Not found"), (resp, body)] self.manager = volumes.VolumeManager(api) def test_find(self): result = utils.find_resource(self.manager, NAME) self.assertEqual(ID, result.id) - self.assertEqual(NAME, result.display_name) + self.assertEqual(NAME, result.name) def test_not_find(self): self.assertRaises( @@ -67,14 +58,14 @@ def setUp(self): api.client = mock.Mock() api.client.get = mock.Mock() resp = mock.Mock() - body = {"snapshots": [{"id": ID, 'display_name': NAME}]} + body = {"snapshots": [{"id": ID, 'name': NAME}]} api.client.get.side_effect = [Exception("Not found"), (resp, body)] self.manager = volume_snapshots.SnapshotManager(api) def test_find(self): result = utils.find_resource(self.manager, NAME) self.assertEqual(ID, result.id) - self.assertEqual(NAME, result.display_name) + self.assertEqual(NAME, result.name) def test_not_find(self): self.assertRaises( diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py deleted file mode 100644 index 9b4dc126de..0000000000 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ /dev/null @@ -1,615 +0,0 @@ -# Copyright 2013 Nebula Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy -import random -from unittest import mock -import uuid - -from openstack.image.v1 import _proxy as image_v1_proxy - -from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests.unit import utils - - -class FakeVolumev1Client: - def __init__(self, **kwargs): - self.volumes = mock.Mock() - self.volumes.resource_class = fakes.FakeResource(None, {}) - self.services = mock.Mock() - self.services.resource_class = fakes.FakeResource(None, {}) - self.extensions = mock.Mock() - self.extensions.resource_class = fakes.FakeResource(None, {}) - self.qos_specs = mock.Mock() - self.qos_specs.resource_class = fakes.FakeResource(None, {}) - self.volume_types = mock.Mock() - self.volume_types.resource_class = fakes.FakeResource(None, {}) - self.volume_encryption_types = mock.Mock() - self.volume_encryption_types.resource_class = fakes.FakeResource( - None, {} - ) - self.transfers = mock.Mock() - self.transfers.resource_class = fakes.FakeResource(None, {}) - self.volume_snapshots = mock.Mock() - self.volume_snapshots.resource_class = fakes.FakeResource(None, {}) - self.backups = mock.Mock() - self.backups.resource_class = fakes.FakeResource(None, {}) - self.restores = mock.Mock() - self.restores.resource_class = fakes.FakeResource(None, {}) - self.auth_token = kwargs['token'] - self.management_url = kwargs['endpoint'] - - -class FakeClientMixin: - def setUp(self): - super().setUp() - - self.app.client_manager.volume = FakeVolumev1Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.volume_client = self.app.client_manager.volume - - -class TestVolumev1( - identity_fakes.FakeClientMixin, - FakeClientMixin, - utils.TestCommand, -): - def setUp(self): - super().setUp() - - # avoid circular imports by defining this manually rather than using - # openstackclient.tests.unit.image.v1.fakes.FakeClientMixin - self.app.client_manager.image = mock.Mock(spec=image_v1_proxy.Proxy) - self.image_client = self.app.client_manager.image - - -def create_one_transfer(attrs=None): - """Create a fake transfer. - - :param Dictionary attrs: - A dictionary with all attributes of Transfer Request - :return: - A FakeResource object with volume_id, name, id. - """ - # Set default attribute - transfer_info = { - 'volume_id': 'volume-id-' + uuid.uuid4().hex, - 'name': 'fake_transfer_name', - 'id': 'id-' + uuid.uuid4().hex, - 'links': 'links-' + uuid.uuid4().hex, - } - - # Overwrite default attributes if there are some attributes set - attrs = attrs or {} - - transfer_info.update(attrs) - - transfer = fakes.FakeResource(None, transfer_info, loaded=True) - - return transfer - - -def create_transfers(attrs=None, count=2): - """Create multiple fake transfers. - - :param Dictionary attrs: - A dictionary with all attributes of transfer - :param Integer count: - The number of transfers to be faked - :return: - A list of FakeResource objects - """ - transfers = [] - for n in range(0, count): - transfers.append(create_one_transfer(attrs)) - - return transfers - - -def get_transfers(transfers=None, count=2): - """Get an iterable MagicMock object with a list of faked transfers. - - If transfers list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List transfers: - A list of FakeResource objects faking transfers - :param Integer count: - The number of transfers to be faked - :return - An iterable Mock object with side_effect set to a list of faked - transfers - """ - if transfers is None: - transfers = create_transfers(count) - - return mock.Mock(side_effect=transfers) - - -def create_one_service(attrs=None): - """Create a fake service. - - :param Dictionary attrs: - A dictionary with all attributes of service - :return: - A FakeResource object with host, status, etc. - """ - # Set default attribute - service_info = { - 'host': 'host_test', - 'binary': 'cinder_test', - 'status': 'enabled', - 'disabled_reason': 'LongHoliday-GoldenWeek', - 'zone': 'fake_zone', - 'updated_at': 'fake_date', - 'state': 'fake_state', - } - - # Overwrite default attributes if there are some attributes set - attrs = attrs or {} - - service_info.update(attrs) - - service = fakes.FakeResource(None, service_info, loaded=True) - - return service - - -def create_services(attrs=None, count=2): - """Create multiple fake services. - - :param Dictionary attrs: - A dictionary with all attributes of service - :param Integer count: - The number of services to be faked - :return: - A list of FakeResource objects - """ - services = [] - for n in range(0, count): - services.append(create_one_service(attrs)) - - return services - - -def get_services(services=None, count=2): - """Get an iterable MagicMock object with a list of faked services. - - If services list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List services: - A list of FakeResource objects faking services - :param Integer count: - The number of services to be faked - :return - An iterable Mock object with side_effect set to a list of faked - services - """ - if services is None: - services = create_services(count) - - return mock.Mock(side_effect=services) - - -def create_one_qos(attrs=None): - """Create a fake Qos specification. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, consumer, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_info = { - "id": 'qos-id-' + uuid.uuid4().hex, - "name": 'qos-name-' + uuid.uuid4().hex, - "consumer": 'front-end', - "specs": {"foo": "bar", "iops": "9001"}, - } - - # Overwrite default attributes. - qos_info.update(attrs) - - qos = fakes.FakeResource(info=copy.deepcopy(qos_info), loaded=True) - return qos - - -def create_one_qos_association(attrs=None): - """Create a fake Qos specification association. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, association_type, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_association_info = { - "id": 'type-id-' + uuid.uuid4().hex, - "name": 'type-name-' + uuid.uuid4().hex, - "association_type": 'volume_type', - } - - # Overwrite default attributes. - qos_association_info.update(attrs) - - qos_association = fakes.FakeResource( - info=copy.deepcopy(qos_association_info), loaded=True - ) - return qos_association - - -def create_qoses(attrs=None, count=2): - """Create multiple fake Qos specifications. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of Qos specifications to fake - :return: - A list of FakeResource objects faking the Qos specifications - """ - qoses = [] - for i in range(0, count): - qos = create_one_qos(attrs) - qoses.append(qos) - - return qoses - - -def get_qoses(qoses=None, count=2): - """Get an iterable MagicMock object with a list of faked qoses. - - If qoses list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking qoses - :param Integer count: - The number of qoses to be faked - :return - An iterable Mock object with side_effect set to a list of faked - qoses - """ - if qoses is None: - qoses = create_qoses(count) - - return mock.Mock(side_effect=qoses) - - -def create_one_volume(attrs=None): - """Create a fake volume. - - :param Dictionary attrs: - A dictionary with all attributes of volume - :return: - A FakeResource object with id, name, status, etc. - """ - attrs = attrs or {} - - # Set default attribute - volume_info = { - 'id': 'volume-id' + uuid.uuid4().hex, - 'display_name': 'volume-name' + uuid.uuid4().hex, - 'display_description': 'description' + uuid.uuid4().hex, - 'status': 'available', - 'size': 10, - 'volume_type': random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']), - 'bootable': 'true', - 'metadata': { - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - }, - 'snapshot_id': 'snapshot-id-' + uuid.uuid4().hex, - 'availability_zone': 'zone' + uuid.uuid4().hex, - 'attachments': [ - { - 'device': '/dev/' + uuid.uuid4().hex, - 'server_id': uuid.uuid4().hex, - }, - ], - 'created_at': 'time-' + uuid.uuid4().hex, - } - - # Overwrite default attributes if there are some attributes set - volume_info.update(attrs) - - volume = fakes.FakeResource(None, volume_info, loaded=True) - return volume - - -def create_volumes(attrs=None, count=2): - """Create multiple fake volumes. - - :param Dictionary attrs: - A dictionary with all attributes of volume - :param Integer count: - The number of volumes to be faked - :return: - A list of FakeResource objects - """ - volumes = [] - for n in range(0, count): - volumes.append(create_one_volume(attrs)) - - return volumes - - -def get_volumes(volumes=None, count=2): - """Get an iterable MagicMock object with a list of faked volumes. - - If volumes list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking volumes - :param Integer count: - The number of volumes to be faked - :return - An iterable Mock object with side_effect set to a list of faked - volumes - """ - if volumes is None: - volumes = create_volumes(count) - - return mock.Mock(side_effect=volumes) - - -def create_one_volume_type(attrs=None, methods=None): - """Create a fake volume type. - - :param Dictionary attrs: - A dictionary with all attributes - :param Dictionary methods: - A dictionary with all methods - :return: - A FakeResource object with id, name, description, etc. - """ - attrs = attrs or {} - methods = methods or {} - - # Set default attributes. - volume_type_info = { - "id": 'type-id-' + uuid.uuid4().hex, - "name": 'type-name-' + uuid.uuid4().hex, - "description": 'type-description-' + uuid.uuid4().hex, - "extra_specs": {"foo": "bar"}, - "is_public": True, - } - - # Overwrite default attributes. - volume_type_info.update(attrs) - - volume_type = fakes.FakeResource( - info=copy.deepcopy(volume_type_info), methods=methods, loaded=True - ) - return volume_type - - -def create_volume_types(attrs=None, count=2): - """Create multiple fake types. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of types to fake - :return: - A list of FakeResource objects faking the types - """ - volume_types = [] - for i in range(0, count): - volume_type = create_one_volume_type(attrs) - volume_types.append(volume_type) - - return volume_types - - -def get_volume_types(volume_types=None, count=2): - """Get an iterable MagicMock object with a list of faked types. - - If types list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volume_types: - A list of FakeResource objects faking types - :param Integer count: - The number of types to be faked - :return - An iterable Mock object with side_effect set to a list of faked - types - """ - if volume_types is None: - volume_types = create_volume_types(count) - - return mock.Mock(side_effect=volume_types) - - -def create_one_encryption_volume_type(attrs=None): - """Create a fake encryption volume type. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with volume_type_id etc. - """ - attrs = attrs or {} - - # Set default attributes. - encryption_info = { - "volume_type_id": 'type-id-' + uuid.uuid4().hex, - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - - # Overwrite default attributes. - encryption_info.update(attrs) - - encryption_type = fakes.FakeResource( - info=copy.deepcopy(encryption_info), loaded=True - ) - return encryption_type - - -def create_one_snapshot(attrs=None): - """Create a fake snapshot. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, description, etc. - """ - attrs = attrs or {} - - # Set default attributes. - snapshot_info = { - "id": 'snapshot-id-' + uuid.uuid4().hex, - "display_name": 'snapshot-name-' + uuid.uuid4().hex, - "display_description": 'snapshot-description-' + uuid.uuid4().hex, - "size": 10, - "status": "available", - "metadata": {"foo": "bar"}, - "created_at": "2015-06-03T18:49:19.000000", - "volume_id": 'vloume-id-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - snapshot_info.update(attrs) - - snapshot_method = {'update': None} - - snapshot = fakes.FakeResource( - info=copy.deepcopy(snapshot_info), - methods=copy.deepcopy(snapshot_method), - loaded=True, - ) - return snapshot - - -def create_snapshots(attrs=None, count=2): - """Create multiple fake snapshots. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of snapshots to fake - :return: - A list of FakeResource objects faking the snapshots - """ - snapshots = [] - for i in range(0, count): - snapshot = create_one_snapshot(attrs) - snapshots.append(snapshot) - - return snapshots - - -def get_snapshots(snapshots=None, count=2): - """Get an iterable MagicMock object with a list of faked snapshots. - - If snapshots list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking snapshots - :param Integer count: - The number of snapshots to be faked - :return - An iterable Mock object with side_effect set to a list of faked - snapshots - """ - if snapshots is None: - snapshots = create_snapshots(count) - - return mock.Mock(side_effect=snapshots) - - -def create_one_backup(attrs=None): - """Create a fake backup. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, volume_id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - backup_info = { - "id": 'backup-id-' + uuid.uuid4().hex, - "name": 'backup-name-' + uuid.uuid4().hex, - "volume_id": 'volume-id-' + uuid.uuid4().hex, - "snapshot_id": 'snapshot-id' + uuid.uuid4().hex, - "description": 'description-' + uuid.uuid4().hex, - "object_count": None, - "container": 'container-' + uuid.uuid4().hex, - "size": random.randint(1, 20), - "status": "error", - "availability_zone": 'zone' + uuid.uuid4().hex, - "links": 'links-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - backup_info.update(attrs) - - backup = fakes.FakeResource(info=copy.deepcopy(backup_info), loaded=True) - return backup - - -def create_backups(attrs=None, count=2): - """Create multiple fake backups. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of backups to fake - :return: - A list of FakeResource objects faking the backups - """ - backups = [] - for i in range(0, count): - backup = create_one_backup(attrs) - backups.append(backup) - - return backups - - -def get_backups(backups=None, count=2): - """Get an iterable MagicMock object with a list of faked backups. - - If backups list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking backups - :param Integer count: - The number of backups to be faked - :return - An iterable Mock object with side_effect set to a list of faked - backups - """ - if backups is None: - backups = create_backups(count) - - return mock.Mock(side_effect=backups) diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py deleted file mode 100644 index 004749fa28..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py +++ /dev/null @@ -1,471 +0,0 @@ -# Copyright 2015 iWeb Technologies Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import copy -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import qos_specs - - -class TestQos(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.qos_mock = self.volume_client.qos_specs - self.qos_mock.reset_mock() - - self.types_mock = self.volume_client.volume_types - self.types_mock.reset_mock() - - -class TestQosAssociate(TestQos): - volume_type = volume_fakes.create_one_volume_type() - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - self.types_mock.get.return_value = self.volume_type - # Get the command object to test - self.cmd = qos_specs.AssociateQos(self.app, None) - - def test_qos_associate(self): - arglist = [self.qos_spec.id, self.volume_type.id] - verifylist = [ - ('qos_spec', self.qos_spec.id), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.associate.assert_called_with( - self.qos_spec.id, self.volume_type.id - ) - self.assertIsNone(result) - - -class TestQosCreate(TestQos): - columns = ('consumer', 'id', 'name', 'properties') - - def setUp(self): - super().setUp() - self.new_qos_spec = volume_fakes.create_one_qos() - self.datalist = ( - self.new_qos_spec.consumer, - self.new_qos_spec.id, - self.new_qos_spec.name, - format_columns.DictColumn(self.new_qos_spec.specs), - ) - self.qos_mock.create.return_value = self.new_qos_spec - # Get the command object to test - self.cmd = qos_specs.CreateQos(self.app, None) - - def test_qos_create_without_properties(self): - arglist = [ - self.new_qos_spec.name, - ] - verifylist = [ - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, {'consumer': 'both'} - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_qos_create_with_consumer(self): - arglist = [ - '--consumer', - self.new_qos_spec.consumer, - self.new_qos_spec.name, - ] - verifylist = [ - ('consumer', self.new_qos_spec.consumer), - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, {'consumer': self.new_qos_spec.consumer} - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_qos_create_with_properties(self): - arglist = [ - '--consumer', - self.new_qos_spec.consumer, - '--property', - 'foo=bar', - '--property', - 'iops=9001', - self.new_qos_spec.name, - ] - verifylist = [ - ('consumer', self.new_qos_spec.consumer), - ('property', self.new_qos_spec.specs), - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.new_qos_spec.specs.update( - {'consumer': self.new_qos_spec.consumer} - ) - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, self.new_qos_spec.specs - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - -class TestQosDelete(TestQos): - qos_specs = volume_fakes.create_qoses(count=2) - - def setUp(self): - super().setUp() - - self.qos_mock.get = volume_fakes.get_qoses(self.qos_specs) - # Get the command object to test - self.cmd = qos_specs.DeleteQos(self.app, None) - - def test_qos_delete_with_id(self): - arglist = [self.qos_specs[0].id] - verifylist = [('qos_specs', [self.qos_specs[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False) - self.assertIsNone(result) - - def test_qos_delete_with_name(self): - arglist = [self.qos_specs[0].name] - verifylist = [('qos_specs', [self.qos_specs[0].name])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False) - self.assertIsNone(result) - - def test_qos_delete_with_force(self): - arglist = ['--force', self.qos_specs[0].id] - verifylist = [('force', True), ('qos_specs', [self.qos_specs[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, True) - self.assertIsNone(result) - - def test_delete_multiple_qoses(self): - arglist = [] - for q in self.qos_specs: - arglist.append(q.id) - verifylist = [ - ('qos_specs', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for q in self.qos_specs: - calls.append(call(q.id, False)) - self.qos_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_qoses_with_exception(self): - arglist = [ - self.qos_specs[0].id, - 'unexist_qos', - ] - verifylist = [ - ('qos_specs', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.qos_specs[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 QoS specifications failed to delete.', str(e) - ) - - find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id) - find_mock.assert_any_call(self.qos_mock, 'unexist_qos') - - self.assertEqual(2, find_mock.call_count) - self.qos_mock.delete.assert_called_once_with( - self.qos_specs[0].id, False - ) - - -class TestQosDisassociate(TestQos): - volume_type = volume_fakes.create_one_volume_type() - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - self.types_mock.get.return_value = self.volume_type - # Get the command object to test - self.cmd = qos_specs.DisassociateQos(self.app, None) - - def test_qos_disassociate_with_volume_type(self): - arglist = [ - '--volume-type', - self.volume_type.id, - self.qos_spec.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.disassociate.assert_called_with( - self.qos_spec.id, self.volume_type.id - ) - self.assertIsNone(result) - - def test_qos_disassociate_with_all_volume_types(self): - arglist = [ - '--all', - self.qos_spec.id, - ] - verifylist = [('qos_spec', self.qos_spec.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.disassociate_all.assert_called_with(self.qos_spec.id) - self.assertIsNone(result) - - -class TestQosList(TestQos): - qos_specs = volume_fakes.create_qoses(count=2) - qos_association = volume_fakes.create_one_qos_association() - - columns = ( - 'ID', - 'Name', - 'Consumer', - 'Associations', - 'Properties', - ) - data = [] - for q in qos_specs: - data.append( - ( - q.id, - q.name, - q.consumer, - format_columns.ListColumn([qos_association.name]), - format_columns.DictColumn(q.specs), - ) - ) - - def setUp(self): - super().setUp() - - self.qos_mock.list.return_value = self.qos_specs - self.qos_mock.get_associations.return_value = [self.qos_association] - - # Get the command object to test - self.cmd = qos_specs.ListQos(self.app, None) - - def test_qos_list(self): - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called_with() - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_qos_list_no_association(self): - self.qos_mock.reset_mock() - self.qos_mock.get_associations.side_effect = [ - [self.qos_association], - exceptions.NotFound("NotFound"), - ] - - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called_with() - - self.assertEqual(self.columns, columns) - - ex_data = copy.deepcopy(self.data) - ex_data[1] = ( - self.qos_specs[1].id, - self.qos_specs[1].name, - self.qos_specs[1].consumer, - format_columns.ListColumn(None), - format_columns.DictColumn(self.qos_specs[1].specs), - ) - self.assertCountEqual(ex_data, list(data)) - - -class TestQosSet(TestQos): - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - # Get the command object to test - self.cmd = qos_specs.SetQos(self.app, None) - - def test_qos_set_with_properties_with_id(self): - arglist = [ - '--no-property', - '--property', - 'a=b', - '--property', - 'c=d', - self.qos_spec.id, - ] - new_property = {"a": "b", "c": "d"} - verifylist = [ - ('no_property', True), - ('property', new_property), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.unset_keys.assert_called_with( - self.qos_spec.id, - list(self.qos_spec.specs.keys()), - ) - self.qos_mock.set_keys.assert_called_with( - self.qos_spec.id, - {"a": "b", "c": "d"}, - ) - self.assertIsNone(result) - - -class TestQosShow(TestQos): - qos_spec = volume_fakes.create_one_qos() - qos_association = volume_fakes.create_one_qos_association() - - def setUp(self): - super().setUp() - self.qos_mock.get.return_value = self.qos_spec - self.qos_mock.get_associations.return_value = [self.qos_association] - # Get the command object to test - self.cmd = qos_specs.ShowQos(self.app, None) - - def test_qos_show(self): - arglist = [self.qos_spec.id] - verifylist = [('qos_spec', self.qos_spec.id)] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.get.assert_called_with(self.qos_spec.id) - - collist = ('associations', 'consumer', 'id', 'name', 'properties') - self.assertEqual(collist, columns) - datalist = ( - format_columns.ListColumn([self.qos_association.name]), - self.qos_spec.consumer, - self.qos_spec.id, - self.qos_spec.name, - format_columns.DictColumn(self.qos_spec.specs), - ) - self.assertCountEqual(datalist, tuple(data)) - - -class TestQosUnset(TestQos): - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - # Get the command object to test - self.cmd = qos_specs.UnsetQos(self.app, None) - - def test_qos_unset_with_properties(self): - arglist = [ - '--property', - 'iops', - '--property', - 'foo', - self.qos_spec.id, - ] - verifylist = [ - ('property', ['iops', 'foo']), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.unset_keys.assert_called_with( - self.qos_spec.id, ['iops', 'foo'] - ) - self.assertIsNone(result) - - def test_qos_unset_nothing(self): - arglist = [ - self.qos_spec.id, - ] - - verifylist = [ - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v1/test_service.py b/openstackclient/tests/unit/volume/v1/test_service.py deleted file mode 100644 index 95a21994fb..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_service.py +++ /dev/null @@ -1,295 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from osc_lib import exceptions - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import service - - -class TestService(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() - - def setUp(self): - super().setUp() - - self.service_mock.list.return_value = [self.services] - - # Get the command object to test - self.cmd = service.ListService(self.app, None) - - def test_service_list(self): - arglist = [ - '--host', - self.services.host, - '--service', - self.services.binary, - ] - verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), - ] - 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) - - expected_columns = [ - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, - ) - - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - - def test_service_list_with_long_option(self): - arglist = [ - '--host', - self.services.host, - '--service', - self.services.binary, - '--long', - ] - verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), - ('long', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, - ) - - -class TestServiceSet(TestService): - service = volume_fakes.create_one_service() - - def setUp(self): - super().setUp() - - self.service_mock.enable.return_value = self.service - self.service_mock.disable.return_value = self.service - self.service_mock.disable_log_reason.return_value = self.service - - self.cmd = service.SetService(self.app, None) - - def test_service_set_nothing(self): - arglist = [ - self.service.host, - self.service.binary, - ] - verifylist = [ - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.service_mock.enable.assert_not_called() - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_enable(self): - arglist = [ - '--enable', - self.service.host, - self.service.binary, - ] - verifylist = [ - ('enable', True), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.enable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_disable(self): - arglist = [ - '--disable', - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable', True), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.disable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.enable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_disable_with_reason(self): - reason = 'earthquake' - arglist = [ - '--disable', - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable', True), - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.disable_log_reason.assert_called_with( - self.service.host, self.service.binary, reason - ) - self.assertIsNone(result) - - def test_service_set_only_with_disable_reason(self): - reason = 'earthquake' - arglist = [ - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail("CommandError should be raised.") - except exceptions.CommandError as e: - self.assertEqual( - "Cannot specify option --disable-reason without " - "--disable specified.", - str(e), - ) - - def test_service_set_enable_with_disable_reason(self): - reason = 'earthquake' - arglist = [ - '--enable', - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('enable', True), - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail("CommandError should be raised.") - except exceptions.CommandError as e: - self.assertEqual( - "Cannot specify option --disable-reason without " - "--disable specified.", - str(e), - ) diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py deleted file mode 100644 index e73a4c3ba4..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_transfer_request.py +++ /dev/null @@ -1,380 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_transfer_request - - -class TestTransfer(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the TransferManager Mock - self.transfer_mock = self.volume_client.transfers - self.transfer_mock.reset_mock() - - # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - -class TestTransferAccept(TestTransfer): - columns = ( - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer() - self.data = ( - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.get.return_value = self.volume_transfer - self.transfer_mock.accept.return_value = self.volume_transfer - - # Get the command object to test - self.cmd = volume_transfer_request.AcceptTransferRequest( - self.app, None - ) - - def test_transfer_accept(self): - arglist = [ - '--auth-key', - 'key_value', - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ('auth_key', 'key_value'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.get.assert_called_once_with( - self.volume_transfer.id, - ) - self.transfer_mock.accept.assert_called_once_with( - self.volume_transfer.id, - 'key_value', - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_accept_no_option(self): - arglist = [ - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, - ) - - -class TestTransferCreate(TestTransfer): - volume = volume_fakes.create_one_volume() - - columns = ( - 'auth_key', - 'created_at', - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer( - attrs={ - 'volume_id': self.volume.id, - 'auth_key': 'key', - 'created_at': 'time', - }, - ) - self.data = ( - self.volume_transfer.auth_key, - self.volume_transfer.created_at, - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.create.return_value = self.volume_transfer - self.volumes_mock.get.return_value = self.volume - - # Get the command object to test - self.cmd = volume_transfer_request.CreateTransferRequest( - self.app, None - ) - - def test_transfer_create_without_name(self): - arglist = [ - self.volume.id, - ] - verifylist = [ - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.create.assert_called_once_with(self.volume.id, None) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_create_with_name(self): - arglist = [ - '--name', - self.volume_transfer.name, - self.volume.id, - ] - verifylist = [ - ('name', self.volume_transfer.name), - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.create.assert_called_once_with( - self.volume.id, - self.volume_transfer.name, - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - -class TestTransferDelete(TestTransfer): - volume_transfers = volume_fakes.create_transfers(count=2) - - def setUp(self): - super().setUp() - - self.transfer_mock.get = volume_fakes.get_transfers( - self.volume_transfers, - ) - self.transfer_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_transfer_request.DeleteTransferRequest( - self.app, None - ) - - def test_transfer_delete(self): - arglist = [self.volume_transfers[0].id] - verifylist = [("transfer_request", [self.volume_transfers[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.transfer_mock.delete.assert_called_with( - self.volume_transfers[0].id - ) - self.assertIsNone(result) - - def test_delete_multiple_transfers(self): - arglist = [] - for v in self.volume_transfers: - arglist.append(v.id) - verifylist = [ - ('transfer_request', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for v in self.volume_transfers: - calls.append(call(v.id)) - self.transfer_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_transfers_with_exception(self): - arglist = [ - self.volume_transfers[0].id, - 'unexist_transfer', - ] - verifylist = [ - ('transfer_request', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.volume_transfers[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 volume transfer requests failed ' 'to delete', - str(e), - ) - - find_mock.assert_any_call( - self.transfer_mock, self.volume_transfers[0].id - ) - find_mock.assert_any_call(self.transfer_mock, 'unexist_transfer') - - self.assertEqual(2, find_mock.call_count) - self.transfer_mock.delete.assert_called_once_with( - self.volume_transfers[0].id, - ) - - -class TestTransferList(TestTransfer): - # The Transfers to be listed - volume_transfers = volume_fakes.create_one_transfer() - - def setUp(self): - super().setUp() - - self.transfer_mock.list.return_value = [self.volume_transfers] - - # Get the command object to test - self.cmd = volume_transfer_request.ListTransferRequest(self.app, None) - - def test_transfer_list_without_argument(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'ID', - 'Name', - 'Volume', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.volume_transfers.id, - self.volume_transfers.name, - self.volume_transfers.volume_id, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list volume_transfers - self.transfer_mock.list.assert_called_with( - detailed=True, search_opts={'all_tenants': 0} - ) - - def test_transfer_list_with_argument(self): - arglist = ["--all-projects"] - verifylist = [("all_projects", True)] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'ID', - 'Name', - 'Volume', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.volume_transfers.id, - self.volume_transfers.name, - self.volume_transfers.volume_id, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list volume_transfers - self.transfer_mock.list.assert_called_with( - detailed=True, search_opts={'all_tenants': 1} - ) - - -class TestTransferShow(TestTransfer): - columns = ( - 'created_at', - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer( - attrs={'created_at': 'time'} - ) - self.data = ( - self.volume_transfer.created_at, - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.get.return_value = self.volume_transfer - - # Get the command object to test - self.cmd = volume_transfer_request.ShowTransferRequest(self.app, None) - - def test_transfer_show(self): - arglist = [ - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.get.assert_called_once_with(self.volume_transfer.id) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py deleted file mode 100644 index daa495995c..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ /dev/null @@ -1,633 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit import utils as tests_utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_type - - -class TestType(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.types_mock = self.volume_client.volume_types - self.types_mock.reset_mock() - - self.encryption_types_mock = self.volume_client.volume_encryption_types - self.encryption_types_mock.reset_mock() - - -class TestTypeCreate(TestType): - columns = ( - 'description', - 'id', - 'is_public', - 'name', - ) - - def setUp(self): - super().setUp() - - self.new_volume_type = volume_fakes.create_one_volume_type( - methods={'set_keys': {'myprop': 'myvalue'}}, - ) - self.data = ( - self.new_volume_type.description, - self.new_volume_type.id, - True, - self.new_volume_type.name, - ) - - self.types_mock.create.return_value = self.new_volume_type - # Get the command object to test - self.cmd = volume_type.CreateVolumeType(self.app, None) - - def test_type_create(self): - arglist = [ - self.new_volume_type.name, - ] - verifylist = [ - ("name", self.new_volume_type.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.create.assert_called_with( - self.new_volume_type.name, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_type_create_with_encryption(self): - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': '128', - 'control_location': 'front-end', - } - encryption_type = volume_fakes.create_one_encryption_volume_type( - attrs=encryption_info, - ) - self.new_volume_type = volume_fakes.create_one_volume_type( - attrs={'encryption': encryption_info}, - ) - self.types_mock.create.return_value = self.new_volume_type - self.encryption_types_mock.create.return_value = encryption_type - encryption_columns = ( - 'description', - 'encryption', - 'id', - 'is_public', - 'name', - ) - encryption_data = ( - self.new_volume_type.description, - format_columns.DictColumn(encryption_info), - self.new_volume_type.id, - True, - self.new_volume_type.name, - ) - arglist = [ - '--encryption-provider', - 'LuksEncryptor', - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.new_volume_type.name, - ] - verifylist = [ - ('encryption_provider', 'LuksEncryptor'), - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('name', self.new_volume_type.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.create.assert_called_with( - self.new_volume_type.name, - ) - body = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - self.encryption_types_mock.create.assert_called_with( - self.new_volume_type, - body, - ) - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, data) - - -class TestTypeDelete(TestType): - volume_types = volume_fakes.create_volume_types(count=2) - - def setUp(self): - super().setUp() - - self.types_mock.get = volume_fakes.get_volume_types(self.volume_types) - self.types_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_type.DeleteVolumeType(self.app, None) - - def test_type_delete(self): - arglist = [self.volume_types[0].id] - verifylist = [("volume_types", [self.volume_types[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.types_mock.delete.assert_called_with(self.volume_types[0]) - self.assertIsNone(result) - - def test_delete_multiple_types(self): - arglist = [] - for t in self.volume_types: - arglist.append(t.id) - verifylist = [ - ('volume_types', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for t in self.volume_types: - calls.append(call(t)) - self.types_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_types_with_exception(self): - arglist = [ - self.volume_types[0].id, - 'unexist_type', - ] - verifylist = [ - ('volume_types', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.volume_types[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 volume types failed to delete.', str(e) - ) - - find_mock.assert_any_call(self.types_mock, self.volume_types[0].id) - find_mock.assert_any_call(self.types_mock, 'unexist_type') - - self.assertEqual(2, find_mock.call_count) - self.types_mock.delete.assert_called_once_with( - self.volume_types[0] - ) - - -class TestTypeList(TestType): - volume_types = volume_fakes.create_volume_types() - - columns = [ - "ID", - "Name", - "Is Public", - ] - columns_long = ["ID", "Name", "Is Public", "Properties"] - - data = [] - for t in volume_types: - data.append( - ( - t.id, - t.name, - t.is_public, - ) - ) - data_long = [] - for t in volume_types: - data_long.append( - ( - t.id, - t.name, - t.is_public, - format_columns.DictColumn(t.extra_specs), - ) - ) - - def setUp(self): - super().setUp() - - self.types_mock.list.return_value = self.volume_types - self.encryption_types_mock.create.return_value = None - self.encryption_types_mock.update.return_value = None - # get the command to test - self.cmd = volume_type.ListVolumeType(self.app, None) - - def test_type_list_without_options(self): - arglist = [] - verifylist = [ - ("long", False), - ("encryption_type", False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.list.assert_called_once_with() - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_type_list_with_options(self): - arglist = [ - "--long", - ] - verifylist = [ - ("long", True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.list.assert_called_once_with() - self.assertEqual(self.columns_long, columns) - self.assertCountEqual(self.data_long, list(data)) - - def test_type_list_with_encryption(self): - encryption_type = volume_fakes.create_one_encryption_volume_type( - attrs={'volume_type_id': self.volume_types[0].id}, - ) - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - encryption_columns = self.columns + [ - "Encryption", - ] - encryption_data = [] - encryption_data.append( - ( - self.volume_types[0].id, - self.volume_types[0].name, - self.volume_types[0].is_public, - volume_type.EncryptionInfoColumn( - self.volume_types[0].id, - {self.volume_types[0].id: encryption_info}, - ), - ) - ) - encryption_data.append( - ( - self.volume_types[1].id, - self.volume_types[1].name, - self.volume_types[1].is_public, - volume_type.EncryptionInfoColumn(self.volume_types[1].id, {}), - ) - ) - - self.encryption_types_mock.list.return_value = [encryption_type] - arglist = [ - "--encryption-type", - ] - verifylist = [ - ("encryption_type", True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.encryption_types_mock.list.assert_called_once_with() - self.types_mock.list.assert_called_once_with() - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, list(data)) - - -class TestTypeSet(TestType): - volume_type = volume_fakes.create_one_volume_type( - methods={'set_keys': None}, - ) - - def setUp(self): - super().setUp() - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.SetVolumeType(self.app, None) - - def test_type_set_nothing(self): - arglist = [ - self.volume_type.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.assertIsNone(result) - - def test_type_set_property(self): - arglist = [ - '--property', - 'myprop=myvalue', - self.volume_type.id, - ] - verifylist = [ - ('property', {'myprop': 'myvalue'}), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volume_type.set_keys.assert_called_once_with( - {'myprop': 'myvalue'} - ) - self.assertIsNone(result) - - def test_type_set_new_encryption(self): - arglist = [ - '--encryption-provider', - 'LuksEncryptor', - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.volume_type.id, - ] - verifylist = [ - ('encryption_provider', 'LuksEncryptor'), - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - body = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - self.encryption_types_mock.create.assert_called_with( - self.volume_type, - body, - ) - self.assertIsNone(result) - - def test_type_set_new_encryption_without_provider(self): - arglist = [ - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.volume_type.id, - ] - verifylist = [ - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - "Command Failed: One or more of" " the operations failed", - str(e), - ) - self.encryption_types_mock.create.assert_not_called() - self.encryption_types_mock.update.assert_not_called() - - -class TestTypeShow(TestType): - columns = ( - 'description', - 'id', - 'is_public', - 'name', - 'properties', - ) - - def setUp(self): - super().setUp() - - self.volume_type = volume_fakes.create_one_volume_type() - self.data = ( - self.volume_type.description, - self.volume_type.id, - True, - self.volume_type.name, - format_columns.DictColumn(self.volume_type.extra_specs), - ) - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.ShowVolumeType(self.app, None) - - def test_type_show(self): - arglist = [self.volume_type.id] - verifylist = [ - ("volume_type", self.volume_type.id), - ("encryption_type", False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.get.assert_called_with(self.volume_type.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_type_show_with_encryption(self): - encryption_type = volume_fakes.create_one_encryption_volume_type() - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - self.volume_type = volume_fakes.create_one_volume_type( - attrs={'encryption': encryption_info}, - ) - self.types_mock.get.return_value = self.volume_type - self.encryption_types_mock.get.return_value = encryption_type - encryption_columns = ( - 'description', - 'encryption', - 'id', - 'is_public', - 'name', - 'properties', - ) - encryption_data = ( - self.volume_type.description, - format_columns.DictColumn(encryption_info), - self.volume_type.id, - True, - self.volume_type.name, - format_columns.DictColumn(self.volume_type.extra_specs), - ) - arglist = ['--encryption-type', self.volume_type.id] - verifylist = [ - ('encryption_type', True), - ("volume_type", self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.get.assert_called_with(self.volume_type.id) - self.encryption_types_mock.get.assert_called_with(self.volume_type.id) - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, data) - - -class TestTypeUnset(TestType): - volume_type = volume_fakes.create_one_volume_type( - methods={'unset_keys': None}, - ) - - def setUp(self): - super().setUp() - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.UnsetVolumeType(self.app, None) - - def test_type_unset_property(self): - arglist = [ - '--property', - 'property', - '--property', - 'multi_property', - self.volume_type.id, - ] - verifylist = [ - ('encryption_type', False), - ('property', ['property', 'multi_property']), - ('volume_type', self.volume_type.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volume_type.unset_keys.assert_called_once_with( - ['property', 'multi_property'] - ) - self.encryption_types_mock.delete.assert_not_called() - self.assertIsNone(result) - - def test_type_unset_failed_with_missing_volume_type_argument(self): - arglist = [ - '--property', - 'property', - '--property', - 'multi_property', - ] - verifylist = [ - ('property', ['property', 'multi_property']), - ] - - self.assertRaises( - tests_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_type_unset_nothing(self): - arglist = [ - self.volume_type.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_type_unset_encryption_type(self): - arglist = [ - '--encryption-type', - self.volume_type.id, - ] - verifylist = [ - ('encryption_type', True), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.encryption_types_mock.delete.assert_called_with(self.volume_type) - self.assertIsNone(result) - - -class TestColumns(TestType): - def test_encryption_info_column_with_info(self): - fake_volume_type = volume_fakes.create_one_volume_type() - type_id = fake_volume_type.id - - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - col = volume_type.EncryptionInfoColumn( - type_id, {type_id: encryption_info} - ) - self.assertEqual( - utils.format_dict(encryption_info), col.human_readable() - ) - self.assertEqual(encryption_info, col.machine_readable()) - - def test_encryption_info_column_without_info(self): - fake_volume_type = volume_fakes.create_one_volume_type() - type_id = fake_volume_type.id - - col = volume_type.EncryptionInfoColumn(type_id, {}) - self.assertEqual('-', col.human_readable()) - self.assertIsNone(col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py deleted file mode 100644 index 0f0d532dde..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ /dev/null @@ -1,1447 +0,0 @@ -# Copyright 2013 Nebula Inc. -# -# 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 unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests.unit.image.v1 import fakes as image_fakes -from openstackclient.tests.unit import utils as test_utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume - - -class TestVolume(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - # Get a shortcut to the TenantManager Mock - self.projects_mock = self.identity_client.tenants - self.projects_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - def setup_volumes_mock(self, count): - volumes = volume_fakes.create_volumes(count=count) - - self.volumes_mock.get = volume_fakes.get_volumes(volumes, 0) - return volumes - - -class TestVolumeCreate(TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() - - columns = ( - 'attachments', - 'availability_zone', - 'bootable', - 'created_at', - 'display_description', - 'id', - 'name', - 'properties', - 'size', - 'snapshot_id', - 'status', - 'type', - ) - - def setUp(self): - super().setUp() - self.new_volume = volume_fakes.create_one_volume() - self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.created_at, - self.new_volume.display_description, - self.new_volume.id, - self.new_volume.display_name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, - ) - self.volumes_mock.create.return_value = self.new_volume - - # Get the command object to test - self.cmd = volume.CreateVolume(self.app, None) - - def test_volume_create_min_options(self): - arglist = [ - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_options(self): - arglist = [ - '--size', - str(self.new_volume.size), - '--description', - self.new_volume.display_description, - '--type', - self.new_volume.volume_type, - '--availability-zone', - self.new_volume.availability_zone, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.display_description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - self.new_volume.display_description, - self.new_volume.volume_type, - None, - None, - self.new_volume.availability_zone, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_user_project_id(self): - # Return a project - self.projects_mock.get.return_value = self.project - # Return a user - self.users_mock.get.return_value = self.user - - arglist = [ - '--size', - str(self.new_volume.size), - '--project', - self.project.id, - '--user', - self.user.id, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('project', self.project.id), - ('user', self.user.id), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - self.user.id, - self.project.id, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_user_project_name(self): - # Return a project - self.projects_mock.get.return_value = self.project - # Return a user - self.users_mock.get.return_value = self.user - - arglist = [ - '--size', - str(self.new_volume.size), - '--project', - self.project.name, - '--user', - self.user.name, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('project', self.project.name), - ('user', self.user.name), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - self.user.id, - self.project.id, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_properties(self): - arglist = [ - '--property', - 'Alpha=a', - '--property', - 'Beta=b', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - {'Alpha': 'a', 'Beta': 'b'}, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_id(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.id, - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - image.id, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_name(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.name, - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - image.id, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_with_source(self): - self.volumes_mock.get.return_value = self.new_volume - arglist = [ - '--source', - self.new_volume.id, - self.new_volume.display_name, - ] - verifylist = [ - ('source', self.new_volume.id), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - None, - None, - self.new_volume.id, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_bootable_and_readonly(self, mock_wait): - arglist = [ - '--bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', True), - ('non_bootable', False), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) - - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): - arglist = [ - '--non-bootable', - '--read-write', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', False), - ('non_bootable', True), - ('read_only', False), - ('read_write', True), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False - ) - - @mock.patch.object(volume.LOG, 'error') - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_bootable_and_readonly_fail( - self, mock_wait, mock_error - ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() - ) - - arglist = [ - '--bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', True), - ('non_bootable', False), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(2, mock_error.call_count) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) - - @mock.patch.object(volume.LOG, 'error') - @mock.patch.object(utils, 'wait_for_status', return_value=False) - def test_volume_create_non_available_with_readonly( - self, mock_wait, mock_error - ): - arglist = [ - '--non-bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', False), - ('non_bootable', True), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(2, mock_error.call_count) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_without_size(self): - arglist = [ - self.new_volume.display_name, - ] - verifylist = [ - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_create_with_multi_source(self): - arglist = [ - '--image', - 'source_image', - '--source', - 'source_volume', - '--snapshot', - 'source_snapshot', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', 'source_image'), - ('source', 'source_volume'), - ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_volume_create_backward_compatibility(self): - arglist = [ - '-c', - 'display_name', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('columns', ['display_name']), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertIn('display_name', columns) - self.assertNotIn('name', columns) - self.assertIn(self.new_volume.display_name, data) - - -class TestVolumeDelete(TestVolume): - def setUp(self): - super().setUp() - - self.volumes_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume.DeleteVolume(self.app, None) - - def test_volume_delete_one_volume(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [volumes[0].id] - verifylist = [ - ("force", False), - ("volumes", [volumes[0].id]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.delete.assert_called_once_with(volumes[0].id) - self.assertIsNone(result) - - def test_volume_delete_multi_volumes(self): - volumes = self.setup_volumes_mock(count=3) - - arglist = [v.id for v in volumes] - verifylist = [ - ('force', False), - ('volumes', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [call(v.id) for v in volumes] - self.volumes_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_volume_delete_multi_volumes_with_exception(self): - volumes = self.setup_volumes_mock(count=2) - - arglist = [ - volumes[0].id, - 'unexist_volume', - ] - verifylist = [ - ('force', False), - ('volumes', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [volumes[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with(volumes[0].id) - - def test_volume_delete_with_force(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [ - '--force', - volumes[0].id, - ] - verifylist = [ - ('force', True), - ('volumes', [volumes[0].id]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) - self.assertIsNone(result) - - -class TestVolumeList(TestVolume): - _volume = volume_fakes.create_one_volume() - columns = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Attached to', - ) - datalist = ( - ( - _volume.id, - _volume.display_name, - _volume.status, - _volume.size, - volume.AttachmentsColumn(_volume.attachments), - ), - ) - - def setUp(self): - super().setUp() - - self.volumes_mock.list.return_value = [self._volume] - - # Get the command object to test - self.cmd = volume.ListVolume(self.app, None) - - def test_volume_list_no_options(self): - arglist = [] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_name(self): - arglist = [ - '--name', - self._volume.display_name, - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', self._volume.display_name), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_status(self): - arglist = [ - '--status', - self._volume.status, - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', self._volume.status), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_all_projects(self): - arglist = [ - '--all-projects', - ] - verifylist = [ - ('long', False), - ('all_projects', True), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_long(self): - arglist = [ - '--long', - ] - verifylist = [ - ('long', True), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - collist = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Type', - 'Bootable', - 'Attached to', - 'Properties', - ) - self.assertEqual(collist, columns) - - datalist = ( - ( - self._volume.id, - self._volume.display_name, - self._volume.status, - self._volume.size, - self._volume.volume_type, - self._volume.bootable, - volume.AttachmentsColumn(self._volume.attachments), - format_columns.DictColumn(self._volume.metadata), - ), - ) - self.assertCountEqual(datalist, tuple(data)) - - def test_volume_list_with_limit_and_offset(self): - arglist = [ - '--limit', - '2', - '--offset', - '5', - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', 2), - ('offset', 5), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.list.assert_called_once_with( - limit=2, - search_opts={ - 'offset': 5, - 'status': None, - 'display_name': None, - 'all_tenants': False, - }, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_negative_limit(self): - arglist = [ - "--limit", - "-2", - ] - verifylist = [ - ("limit", -2), - ] - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_volume_list_backward_compatibility(self): - arglist = [ - '-c', - 'Display Name', - ] - verifylist = [ - ('columns', ['Display Name']), - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.assertIn('Display Name', columns) - self.assertNotIn('Name', columns) - for each_volume in data: - self.assertIn(self._volume.display_name, each_volume) - - -class TestVolumeMigrate(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - self.volumes_mock.migrate_volume.return_value = None - # Get the command object to test - self.cmd = volume.MigrateVolume(self.app, None) - - def test_volume_migrate(self): - arglist = [ - "--host", - "host@backend-name#pool", - self._volume.id, - ] - verifylist = [ - ("force_host_copy", False), - ("host", "host@backend-name#pool"), - ("volume", self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", False - ) - self.assertIsNone(result) - - def test_volume_migrate_with_option(self): - arglist = [ - "--force-host-copy", - "--host", - "host@backend-name#pool", - self._volume.id, - ] - verifylist = [ - ("force_host_copy", True), - ("host", "host@backend-name#pool"), - ("volume", self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", True - ) - self.assertIsNone(result) - - def test_volume_migrate_without_host(self): - arglist = [ - self._volume.id, - ] - verifylist = [ - ("force_host_copy", False), - ("volume", self._volume.id), - ] - - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - -class TestVolumeSet(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - - self.volumes_mock.update.return_value = self._volume - # Get the command object to test - self.cmd = volume.SetVolume(self.app, None) - - def test_volume_set_no_options(self): - arglist = [ - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_volume_set_name(self): - arglist = [ - '--name', - 'qwerty', - self._volume.display_name, - ] - verifylist = [ - ('name', 'qwerty'), - ('description', None), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'display_name': 'qwerty', - } - self.volumes_mock.update.assert_called_with(self._volume.id, **kwargs) - self.assertIsNone(result) - - def test_volume_set_description(self): - arglist = [ - '--description', - 'new desc', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', 'new desc'), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'display_description': 'new desc', - } - self.volumes_mock.update.assert_called_with(self._volume.id, **kwargs) - self.assertIsNone(result) - - def test_volume_set_size(self): - arglist = [ - '--size', - '130', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 130), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - size = 130 - self.volumes_mock.extend.assert_called_with(self._volume.id, size) - self.assertIsNone(result) - - def test_volume_set_size_smaller(self): - self._volume.status = 'available' - arglist = [ - '--size', - '1', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 1), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_set_size_not_available(self): - self._volume.status = 'error' - arglist = [ - '--size', - '130', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 130), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_set_property(self): - arglist = [ - '--no-property', - '--property', - 'myprop=myvalue', - self._volume.display_name, - ] - verifylist = [ - ('read_only', False), - ('read_write', False), - ('name', None), - ('description', None), - ('size', None), - ('no_property', True), - ('property', {'myprop': 'myvalue'}), - ('volume', self._volume.display_name), - ('bootable', False), - ('non_bootable', False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - metadata = {'myprop': 'myvalue'} - self.volumes_mock.set_metadata.assert_called_with( - self._volume.id, metadata - ) - self.volumes_mock.delete_metadata.assert_called_with( - self._volume.id, self._volume.metadata.keys() - ) - self.volumes_mock.update_readonly_flag.assert_not_called() - self.assertIsNone(result) - - def test_volume_set_bootable(self): - arglist = [ - ['--bootable', self._volume.id], - ['--non-bootable', self._volume.id], - ] - verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self._volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self._volume.id), - ], - ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) - - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self._volume.id, verifylist[index][0][1] - ) - - def test_volume_set_readonly(self): - arglist = ['--read-only', self._volume.id] - verifylist = [ - ('read_only', True), - ('read_write', False), - ('volume', self._volume.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.update_readonly_flag.assert_called_once_with( - self._volume.id, True - ) - self.assertIsNone(result) - - def test_volume_set_read_write(self): - arglist = ['--read-write', self._volume.id] - verifylist = [ - ('read_only', False), - ('read_write', True), - ('volume', self._volume.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.update_readonly_flag.assert_called_once_with( - self._volume.id, False - ) - self.assertIsNone(result) - - -class TestVolumeShow(TestVolume): - columns = ( - 'attachments', - 'availability_zone', - 'bootable', - 'created_at', - 'display_description', - 'id', - 'name', - 'properties', - 'size', - 'snapshot_id', - 'status', - 'type', - ) - - def setUp(self): - super().setUp() - self._volume = volume_fakes.create_one_volume() - self.datalist = ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.created_at, - self._volume.display_description, - self._volume.id, - self._volume.display_name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ) - self.volumes_mock.get.return_value = self._volume - # Get the command object to test - self.cmd = volume.ShowVolume(self.app, None) - - def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_show_backward_compatibility(self): - arglist = [ - '-c', - 'display_name', - self._volume.id, - ] - verifylist = [ - ('columns', ['display_name']), - ('volume', self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.get.assert_called_with(self._volume.id) - - self.assertIn('display_name', columns) - self.assertNotIn('name', columns) - self.assertIn(self._volume.display_name, data) - - -class TestVolumeUnset(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - - self.volumes_mock.delete_metadata.return_value = None - # Get the command object to test - self.cmd = volume.UnsetVolume(self.app, None) - - def test_volume_unset_no_options(self): - arglist = [ - self._volume.display_name, - ] - verifylist = [ - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_volume_unset_property(self): - arglist = [ - '--property', - 'myprop', - self._volume.display_name, - ] - verifylist = [ - ('property', ['myprop']), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.delete_metadata.assert_called_with( - self._volume.id, ['myprop'] - ) - self.assertIsNone(result) - - -class TestColumns(TestVolume): - def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] - - col = volume.AttachmentsColumn(_volume.attachments, {}) - self.assertEqual( - f'Attached to {server_id} on {device} ', - col.human_readable(), - ) - self.assertEqual(_volume.attachments, col.machine_readable()) - - def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() - - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] - fake_server = mock.Mock() - fake_server.name = 'fake-server-name' - server_cache = {server_id: fake_server} - - col = volume.AttachmentsColumn(_volume.attachments, server_cache) - self.assertEqual( - 'Attached to {} on {} '.format('fake-server-name', device), - col.human_readable(), - ) - self.assertEqual(_volume.attachments, col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v1/test_volume_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py deleted file mode 100644 index c551d159e7..0000000000 --- a/openstackclient/tests/unit/volume/v1/test_volume_backup.py +++ /dev/null @@ -1,435 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_backup - - -class TestBackup(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - -class TestBackupCreate(TestBackup): - volume = volume_fakes.create_one_volume() - - columns = ( - 'availability_zone', - 'container', - 'description', - 'id', - 'name', - 'object_count', - 'size', - 'snapshot_id', - 'status', - 'volume_id', - ) - - def setUp(self): - super().setUp() - self.new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': self.volume.id}, - ) - self.data = ( - self.new_backup.availability_zone, - self.new_backup.container, - self.new_backup.description, - self.new_backup.id, - self.new_backup.name, - self.new_backup.object_count, - self.new_backup.size, - self.new_backup.snapshot_id, - self.new_backup.status, - self.new_backup.volume_id, - ) - self.volumes_mock.get.return_value = self.volume - self.backups_mock.create.return_value = self.new_backup - - # Get the command object to test - self.cmd = volume_backup.CreateVolumeBackup(self.app, None) - - def test_backup_create(self): - arglist = [ - "--name", - self.new_backup.name, - "--description", - self.new_backup.description, - "--container", - self.new_backup.container, - self.new_backup.volume_id, - ] - verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.backups_mock.create.assert_called_with( - self.new_backup.volume_id, - self.new_backup.container, - self.new_backup.name, - self.new_backup.description, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_backup_create_without_name(self): - arglist = [ - "--description", - self.new_backup.description, - "--container", - self.new_backup.container, - self.new_backup.volume_id, - ] - verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.backups_mock.create.assert_called_with( - self.new_backup.volume_id, - self.new_backup.container, - None, - self.new_backup.description, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - -class TestBackupDelete(TestBackup): - backups = volume_fakes.create_backups(count=2) - - def setUp(self): - super().setUp() - - self.backups_mock.get = volume_fakes.get_backups(self.backups) - self.backups_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) - - def test_backup_delete(self): - arglist = [self.backups[0].id] - verifylist = [("backups", [self.backups[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.backups_mock.delete.assert_called_with(self.backups[0].id) - self.assertIsNone(result) - - def test_delete_multiple_backups(self): - arglist = [] - for b in self.backups: - arglist.append(b.id) - verifylist = [ - ('backups', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for b in self.backups: - calls.append(call(b.id)) - self.backups_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_backups_with_exception(self): - arglist = [ - self.backups[0].id, - 'unexist_backup', - ] - verifylist = [ - ('backups', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.backups[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 backups failed to delete.', str(e)) - - find_mock.assert_any_call(self.backups_mock, self.backups[0].id) - find_mock.assert_any_call(self.backups_mock, 'unexist_backup') - - self.assertEqual(2, find_mock.call_count) - self.backups_mock.delete.assert_called_once_with( - self.backups[0].id, - ) - - -class TestBackupList(TestBackup): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.display_name}, - count=3, - ) - - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', - ] - columns_long = columns + [ - 'Availability Zone', - 'Volume', - 'Container', - ] - - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - - def setUp(self): - super().setUp() - - self.volumes_mock.list.return_value = [self.volume] - self.backups_mock.list.return_value = self.backups - self.volumes_mock.get.return_value = self.volume - # Get the command to test - self.cmd = volume_backup.ListVolumeBackup(self.app, None) - - def test_backup_list_without_options(self): - arglist = [] - verifylist = [ - ("long", False), - ("name", None), - ("status", None), - ("volume", None), - ('all_projects', False), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - search_opts = { - "name": None, - "status": None, - "volume_id": None, - "all_tenants": False, - } - self.volumes_mock.get.assert_not_called() - self.backups_mock.list.assert_called_with( - search_opts=search_opts, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_backup_list_with_options(self): - arglist = [ - "--long", - "--name", - self.backups[0].name, - "--status", - "error", - "--volume", - self.volume.id, - "--all-projects", - ] - verifylist = [ - ("long", True), - ("name", self.backups[0].name), - ("status", "error"), - ("volume", self.volume.id), - ('all_projects', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - search_opts = { - "name": self.backups[0].name, - "status": "error", - "volume_id": self.volume.id, - "all_tenants": True, - } - self.volumes_mock.get.assert_called_once_with(self.volume.id) - self.backups_mock.list.assert_called_with( - search_opts=search_opts, - ) - self.assertEqual(self.columns_long, columns) - self.assertCountEqual(self.data_long, list(data)) - - -class TestBackupRestore(TestBackup): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, - ) - - def setUp(self): - 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.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, - ] - 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) - 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): - columns = ( - 'availability_zone', - 'container', - 'description', - 'id', - 'name', - 'object_count', - 'size', - 'snapshot_id', - 'status', - 'volume_id', - ) - - def setUp(self): - super().setUp() - self.backup = volume_fakes.create_one_backup() - self.data = ( - self.backup.availability_zone, - self.backup.container, - self.backup.description, - self.backup.id, - self.backup.name, - self.backup.object_count, - self.backup.size, - self.backup.snapshot_id, - self.backup.status, - self.backup.volume_id, - ) - self.backups_mock.get.return_value = self.backup - # Get the command object to test - self.cmd = volume_backup.ShowVolumeBackup(self.app, None) - - def test_backup_show(self): - arglist = [self.backup.id] - verifylist = [("backup", self.backup.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.backups_mock.get.assert_called_with(self.backup.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 1c183728c0..c2303d3880 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -14,7 +14,6 @@ import copy import random -import typing as ty from unittest import mock import uuid @@ -92,7 +91,7 @@ def setUp(self): self.volume_sdk_client = self.app.client_manager.sdk_connection.volume self.set_volume_api_version() # default to the lowest - def set_volume_api_version(self, version: ty.Optional[str] = None): + def set_volume_api_version(self, version: str | None = None): """Set a fake block storage API version. :param version: The fake microversion to "support". This must be None diff --git a/openstackclient/tests/unit/volume/v2/test_service.py b/openstackclient/tests/unit/volume/v2/test_service.py index a553985647..e230a39a9a 100644 --- a/openstackclient/tests/unit/volume/v2/test_service.py +++ b/openstackclient/tests/unit/volume/v2/test_service.py @@ -12,108 +12,83 @@ # under the License. # +from unittest import mock + +from openstack.block_storage.v2 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import service -class TestService(volume_fakes.TestVolume): +class TestServiceList(volume_fakes.TestVolume): def setUp(self): super().setUp() - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.volume_sdk_client.services.return_value = [self.service] - def setUp(self): - super().setUp() - - self.service_mock.list.return_value = [self.services] - - # Get the command object to test self.cmd = service.ListService(self.app, None) def test_service_list(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ] 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) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', 'Status', 'State', 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - def test_service_list_with_long_option(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, '--long', ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -121,41 +96,34 @@ def test_service_list_with_long_option(self): 'State', 'Updated At', 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) -class TestServiceSet(TestService): - service = volume_fakes.create_one_service() - +class TestServiceSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.enable.return_value = self.service - self.service_mock.disable.return_value = self.service - self.service_mock.disable_log_reason.return_value = self.service + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.service.enable = mock.Mock(autospec=True) + self.service.disable = mock.Mock(autospec=True) + self.volume_sdk_client.find_service.return_value = self.service self.cmd = service.SetService(self.app, None) @@ -171,9 +139,8 @@ def test_service_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.service_mock.enable.assert_not_called() - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() + self.service.enable.assert_not_called() + self.service.disable.assert_not_called() self.assertIsNone(result) def test_service_set_enable(self): @@ -191,11 +158,8 @@ def test_service_set_enable(self): result = self.cmd.take_action(parsed_args) - self.service_mock.enable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() + self.service.enable.assert_called_with(self.volume_sdk_client) + self.service.disable.assert_not_called() self.assertIsNone(result) def test_service_set_disable(self): @@ -213,11 +177,10 @@ def test_service_set_disable(self): result = self.cmd.take_action(parsed_args) - self.service_mock.disable.assert_called_with( - self.service.host, self.service.binary + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=None ) - self.service_mock.enable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() self.assertIsNone(result) def test_service_set_disable_with_reason(self): @@ -239,8 +202,9 @@ def test_service_set_disable_with_reason(self): result = self.cmd.take_action(parsed_args) - self.service_mock.disable_log_reason.assert_called_with( - self.service.host, self.service.binary, reason + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=reason ) self.assertIsNone(result) @@ -258,6 +222,7 @@ def test_service_set_only_with_disable_reason(self): ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: self.cmd.take_action(parsed_args) self.fail("CommandError should be raised.") @@ -284,6 +249,7 @@ def test_service_set_enable_with_disable_reason(self): ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: self.cmd.take_action(parsed_args) self.fail("CommandError should be raised.") diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 1667f38cd6..b68020fa95 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -12,11 +12,17 @@ # under the License. from unittest import mock +import uuid +from openstack.block_storage.v2 import snapshot as _snapshot +from openstack.block_storage.v2 import volume as _volume +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v2 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 import utils as test_utils @@ -46,136 +52,157 @@ def setUp(self): self.consistencygroups_mock = self.volume_client.consistencygroups self.consistencygroups_mock.reset_mock() - def setup_volumes_mock(self, count): - volumes = volume_fakes.create_volumes(count=count) - - self.volumes_mock.get = volume_fakes.get_volumes(volumes, 0) - return volumes - - -class TestVolumeCreate(TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() +class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', 'bootable', + 'consistencygroup_id', + 'created_at', 'description', + 'encrypted', 'id', + 'multiattach', 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', + 'os-volume-replication:driver_data', + 'os-volume-replication:extended_status', 'properties', + 'replication_status', 'size', 'snapshot_id', + 'source_volid', 'status', 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', ) def setUp(self): super().setUp() - self.new_volume = volume_fakes.create_one_volume() - self.volumes_mock.create.return_value = self.new_volume + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.create_volume.return_value = self.volume self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.description, - self.new_volume.id, - self.new_volume.name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, + self.volume.attachments, + self.volume.availability_zone, + self.volume.is_bootable, + self.volume.consistency_group_id, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + self.volume.replication_driver_data, + self.volume.extended_replication_status, + format_columns.DictColumn(self.volume.metadata), + self.volume.replication_status, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, ) - # Get the command object to test self.cmd = volume.CreateVolume(self.app, None) def test_volume_create_min_options(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, name=None, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_options(self): - consistency_group = volume_fakes.create_one_consistency_group() - self.consistencygroups_mock.get.return_value = consistency_group + consistency_group_id = 'cg123' arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--description', - self.new_volume.description, + self.volume.description, '--type', - self.new_volume.volume_type, + self.volume.volume_type, '--availability-zone', - self.new_volume.availability_zone, + self.volume.availability_zone, '--consistency-group', - consistency_group.id, + consistency_group_id, '--hint', 'k=v', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('consistency_group', consistency_group.id), + ('size', self.volume.size), + ('description', self.volume.description), + ('type', self.volume.volume_type), + ('availability_zone', self.volume.availability_zone), + ('consistency_group', consistency_group_id), ('hint', {'k': 'v'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + with mock.patch.object( + volume_v2, + 'find_consistency_group', + return_value={'id': consistency_group_id}, + ) as mock_find_cg: + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, - description=self.new_volume.description, - volume_type=self.new_volume.volume_type, - availability_zone=self.new_volume.availability_zone, + name=self.volume.name, + description=self.volume.description, + volume_type=self.volume.volume_type, + availability_zone=self.volume.availability_zone, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=consistency_group.id, + image_id=None, + source_volume_id=None, + consistency_group_id=consistency_group_id, scheduler_hints={'k': 'v'}, ) + mock_find_cg.assert_called_once_with( + self.volume_sdk_client, consistency_group_id + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_properties(self): arglist = [ @@ -184,39 +211,36 @@ def test_volume_create_properties(self): '--property', 'Beta=b', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata={'Alpha': 'a', 'Beta': 'b'}, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) - def test_volume_create_image_id(self): + def test_volume_create_image(self): image = image_fakes.create_one_image() self.image_client.find_image.return_value = image @@ -224,152 +248,111 @@ def test_volume_create_image_id(self): '--image', image.id, '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, + image_id=image.id, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_name(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.name, - '--size', - str(self.new_volume.size), - self.new_volume.name, - ] - verifylist = [ - ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, - snapshot_id=None, - name=self.new_volume.name, - description=None, - volume_type=None, - availability_zone=None, - metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, - scheduler_hints=None, + self.image_client.find_image.assert_called_once_with( + image.id, ignore_missing=False ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_snapshot(self): - snapshot = volume_fakes.create_one_snapshot() - self.new_volume.snapshot_id = snapshot.id + snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = snapshot + arglist = [ '--snapshot', - self.new_volume.snapshot_id, - self.new_volume.name, + snapshot.id, + self.volume.name, ] verifylist = [ - ('snapshot', self.new_volume.snapshot_id), - ('name', self.new_volume.name), + ('snapshot', snapshot.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.snapshots_mock.get.return_value = snapshot - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( + self.volume_sdk_client.create_volume.assert_called_with( size=snapshot.size, snapshot_id=snapshot.id, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_source_volume(self): - source_vol = "source_vol" + source_volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = source_volume + arglist = [ '--source', - self.new_volume.id, - source_vol, + source_volume.id, + self.volume.name, ] verifylist = [ - ('source', self.new_volume.id), - ('name', source_vol), + ('source', source_volume.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.volumes_mock.get.return_value = self.new_volume - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=source_volume.size, snapshot_id=None, - name=source_vol, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=self.new_volume.id, - consistencygroup_id=None, + image_id=None, + source_volume_id=source_volume.id, + consistency_group_id=None, scheduler_hints=None, ) + self.volume_sdk_client.find_volume.assert_called_once_with( + source_volume.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly(self, mock_wait): @@ -377,196 +360,187 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): arglist = [ '--non-bootable', '--read-write', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', False), - ('read_write', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, False ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, False ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly_fail( self, mock_wait, mock_error ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() + self.volume_sdk_client.set_volume_bootable_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + self.volume_sdk_client.set_volume_readonly.side_effect = ( + sdk_exceptions.NotFoundException('foo') ) arglist = [ '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True + ) + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True + ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) + self.assertEqual(self.datalist, data) @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=False) def test_volume_create_non_available_with_readonly( - self, - mock_wait, - mock_error, + self, mock_wait, mock_error ): arglist = [ '--non-bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_without_size(self): arglist = [ - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -583,15 +557,15 @@ def test_volume_create_with_multi_source(self): '--snapshot', 'source_snapshot', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', 'source_image'), ('source', 'source_volume'), ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] self.assertRaises( @@ -610,7 +584,7 @@ def test_volume_create_hints(self): """ arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--hint', 'k=v', '--hint', @@ -625,10 +599,10 @@ def test_volume_create_hints(self): 'local_to_instance=v6', '--hint', 'different_host=v7', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ( 'hint', { @@ -638,26 +612,23 @@ def test_volume_create_hints(self): 'different_host': ['v5', 'v7'], }, ), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints={ 'k': 'v2', 'same_host': ['v3', 'v4'], @@ -667,40 +638,40 @@ def test_volume_create_hints(self): ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) -class TestVolumeDelete(TestVolume): +class TestVolumeDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock.delete.return_value = None + self.volumes = list(sdk_fakes.generate_fake_resources(_volume.Volume)) + self.volume_sdk_client.find_volume.side_effect = self.volumes + self.volume_sdk_client.delete_volume.return_value = None - # Get the command object to mock self.cmd = volume.DeleteVolume(self.app, None) def test_volume_delete_one_volume(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [volumes[0].id] + arglist = [self.volumes[0].id] verifylist = [ ("force", False), ("purge", False), - ("volumes", [volumes[0].id]), + ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - volumes[0].id, cascade=False + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=False ) - self.assertIsNone(result) def test_volume_delete_multi_volumes(self): - volumes = self.setup_volumes_mock(count=3) - - arglist = [v.id for v in volumes] + arglist = [v.id for v in self.volumes] verifylist = [ ('force', False), ('purge', False), @@ -709,83 +680,95 @@ def test_volume_delete_multi_volumes(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - calls = [mock.call(v.id, cascade=False) for v in volumes] - self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_has_calls( + [mock.call(v.id, ignore_missing=False) for v in self.volumes] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [mock.call(v.id, cascade=False, force=False) for v in self.volumes] + ) + def test_volume_delete_multi_volumes_with_exception(self): - volumes = self.setup_volumes_mock(count=2) + self.volume_sdk_client.find_volume.side_effect = [ + self.volumes[0], + sdk_exceptions.NotFoundException(), + ] arglist = [ - volumes[0].id, + self.volumes[0].id, 'unexist_volume', ] verifylist = [ ('force', False), ('purge', False), - ('volumes', arglist), + ('volumes', [self.volumes[0].id, 'unexist_volume']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [volumes[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with( - volumes[0].id, cascade=False - ) + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 volumes failed to delete.', str(exc)) - def test_volume_delete_with_purge(self): - volumes = self.setup_volumes_mock(count=1) + self.volume_sdk_client.find_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, ignore_missing=False), + mock.call('unexist_volume', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, cascade=False, force=False), + ] + ) + def test_volume_delete_with_purge(self): arglist = [ '--purge', - volumes[0].id, + self.volumes[0].id, ] verifylist = [ ('force', False), ('purge', True), - ('volumes', [volumes[0].id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - volumes[0].id, cascade=True + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=True, force=False ) - self.assertIsNone(result) def test_volume_delete_with_force(self): - volumes = self.setup_volumes_mock(count=1) - arglist = [ '--force', - volumes[0].id, + self.volumes[0].id, ] verifylist = [ ('force', True), ('purge', False), - ('volumes', [volumes[0].id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=True + ) + class TestVolumeList(TestVolume): project = identity_fakes.FakeProject.create_one_project() @@ -1311,69 +1294,80 @@ def test_volume_list_backward_compatibility(self): self.assertIn(self.mock_volume.name, each_volume) -class TestVolumeMigrate(TestVolume): - _volume = volume_fakes.create_one_volume() - +class TestVolumeMigrate(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock.get.return_value = self._volume - self.volumes_mock.migrate_volume.return_value = None - # Get the command object to test + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.migrate_volume.return_value = None + self.cmd = volume.MigrateVolume(self.app, None) def test_volume_migrate(self): arglist = [ "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("volume", self.volume.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", False, False - ) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=False, + lock_volume=False, + ) + def test_volume_migrate_with_option(self): arglist = [ "--force-host-copy", "--lock-volume", "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", True), ("lock_volume", True), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("volume", self.volume.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", True, True - ) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=True, + lock_volume=True, + ) + def test_volume_migrate_without_host(self): arglist = [ - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), - ("volume", self._volume.id), + ("volume", self.volume.id), ] self.assertRaises( @@ -1384,6 +1378,9 @@ def test_volume_migrate_without_host(self): verifylist, ) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.migrate_volume.assert_not_called() + class TestVolumeSet(TestVolume): volume_type = volume_fakes.create_one_volume_type() @@ -1407,16 +1404,16 @@ def test_volume_set_property(self): self.new_volume.id, ] verifylist = [ - ('property', {'a': 'b', 'c': 'd'}), + ('properties', {'a': 'b', 'c': 'd'}), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), + ('bootable', None), + ('read_only', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.volumes_mock.set_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) def test_volume_set_image_property(self): @@ -1428,10 +1425,10 @@ def test_volume_set_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), + ('bootable', None), + ('read_only', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1439,14 +1436,13 @@ def test_volume_set_image_property(self): # returns nothing self.cmd.take_action(parsed_args) self.volumes_mock.set_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) def test_volume_set_state(self): arglist = ['--state', 'error', self.new_volume.id] verifylist = [ - ('read_only', False), - ('read_write', False), + ('read_only', None), ('state', 'error'), ('volume', self.new_volume.id), ] @@ -1511,36 +1507,40 @@ def test_volume_set_detached(self): def test_volume_set_bootable(self): arglist = [ - ['--bootable', self.new_volume.id], - ['--non-bootable', self.new_volume.id], + '--bootable', + self.new_volume.id, ] verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self.new_volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self.new_volume.id), - ], + ('bootable', True), + ('volume', self.new_volume.id), ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, verifylist[index][0][1] - ) + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) + + def test_volume_set_non_bootable(self): + arglist = [ + '--non-bootable', + self.new_volume.id, + ] + verifylist = [ + ('bootable', False), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - def test_volume_set_readonly(self): + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) + + def test_volume_set_read_only(self): arglist = ['--read-only', self.new_volume.id] verifylist = [ ('read_only', True), - ('read_write', False), ('volume', self.new_volume.id), ] @@ -1556,7 +1556,6 @@ def test_volume_set_read_write(self): arglist = ['--read-write', self.new_volume.id] verifylist = [ ('read_only', False), - ('read_write', True), ('volume', self.new_volume.id), ] @@ -1619,47 +1618,88 @@ def test_volume_set_with_only_retype_policy(self, mock_warning): result = self.cmd.take_action(parsed_args) self.volumes_mock.retype.assert_not_called() mock_warning.assert_called_with( - "'--retype-policy' option will " "not work without '--type' option" + "'--retype-policy' option will not work without '--type' option" ) self.assertIsNone(result) -class TestVolumeShow(TestVolume): +class TestVolumeShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self._volume = volume_fakes.create_one_volume() - self.volumes_mock.get.return_value = self._volume - # Get the command object to test + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + + self.columns = ( + 'attachments', + 'availability_zone', + 'bootable', + 'consistencygroup_id', + 'created_at', + 'description', + 'encrypted', + 'id', + 'multiattach', + 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', + 'os-volume-replication:driver_data', + 'os-volume-replication:extended_status', + 'properties', + 'replication_status', + 'size', + 'snapshot_id', + 'source_volid', + 'status', + 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', + ) + self.data = ( + self.volume.attachments, + self.volume.availability_zone, + self.volume.is_bootable, + self.volume.consistency_group_id, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + self.volume.replication_driver_data, + self.volume.extended_replication_status, + format_columns.DictColumn(self.volume.metadata), + self.volume.replication_status, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, + ) + self.cmd = volume.ShowVolume(self.app, None) def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] + arglist = [self.volume.id] + verifylist = [("volume", self.volume.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - self.assertEqual( - tuple(sorted(self._volume.keys())), - columns, - ) - self.assertTupleEqual( - ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.description, - self._volume.id, - self._volume.name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ), - data, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False ) @@ -1686,7 +1726,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_set, arglist, verifylist) @@ -1702,7 +1742,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist_unset = [ - ('image_property', ['Alpha']), + ('image_properties', ['Alpha']), ('volume', self.new_volume.id), ] parsed_args_unset = self.check_parser( @@ -1714,7 +1754,7 @@ def test_volume_unset_image_property(self): self.cmd_unset.take_action(parsed_args_unset) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args_unset.image_property + self.new_volume.id, parsed_args_unset.image_properties ) def test_volume_unset_image_property_fail(self): @@ -1729,8 +1769,8 @@ def test_volume_unset_image_property_fail(self): self.new_volume.id, ] verifylist = [ - ('image_property', ['Alpha']), - ('property', ['Beta']), + ('image_properties', ['Alpha']), + ('properties', ['Beta']), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_unset, arglist, verifylist) @@ -1743,38 +1783,54 @@ def test_volume_unset_image_property_fail(self): 'One or more of the unset operations failed', str(e) ) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) self.volumes_mock.delete_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) -class TestColumns(TestVolume): +class TestColumns(volume_fakes.TestVolume): def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] - col = volume.AttachmentsColumn(_volume.attachments, {}) + col = volume.AttachmentsColumn(vol.attachments, {}) self.assertEqual( f'Attached to {server_id} on {device} ', col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] fake_server = mock.Mock() fake_server.name = 'fake-server-name' server_cache = {server_id: fake_server} - col = volume.AttachmentsColumn(_volume.attachments, server_cache) + col = volume.AttachmentsColumn(vol.attachments, server_cache) self.assertEqual( 'Attached to {} on {} '.format('fake-server-name', device), col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index e9a0024fab..e7bbb69999 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -13,89 +13,81 @@ from unittest.mock import call +from openstack.block_storage.v2 import backup as _backup +from openstack.block_storage.v2 import snapshot as _snapshot +from openstack.block_storage.v2 import volume as _volume +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import volume_backup -class TestBackupLegacy(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - class TestBackupCreate(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - snapshot = volume_fakes.create_one_snapshot() - new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id} - ) - columns = ( 'id', 'name', 'volume_id', ) - data = ( - new_backup.id, - new_backup.name, - new_backup.volume_id, - ) def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) self.volume_sdk_client.find_snapshot.return_value = self.snapshot - self.volume_sdk_client.create_backup.return_value = self.new_backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, + volume_id=self.volume.id, + snapshot_id=self.snapshot.id, + ) + self.volume_sdk_client.create_backup.return_value = self.backup + + self.data = ( + self.backup.id, + self.backup.name, + self.backup.volume_id, + ) - # Get the command object to test self.cmd = volume_backup.CreateVolumeBackup(self.app, None) def test_backup_create(self): arglist = [ "--name", - self.new_backup.name, + self.backup.name, "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, + self.backup.container, "--force", "--incremental", "--snapshot", - self.new_backup.snapshot_id, - self.new_backup.volume_id, + self.backup.snapshot_id, + self.backup.volume_id, ] verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), + ("name", self.backup.name), + ("description", self.backup.description), + ("container", self.backup.container), ("force", True), ("incremental", True), - ("snapshot", self.new_backup.snapshot_id), - ("volume", self.new_backup.volume_id), + ("snapshot", self.backup.snapshot_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, - name=self.new_backup.name, - description=self.new_backup.description, + volume_id=self.backup.volume_id, + container=self.backup.container, + name=self.backup.name, + description=self.backup.description, force=True, is_incremental=True, - snapshot_id=self.new_backup.snapshot_id, + snapshot_id=self.backup.snapshot_id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -103,25 +95,25 @@ def test_backup_create(self): def test_backup_create_without_name(self): arglist = [ "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, - self.new_backup.volume_id, + self.backup.container, + self.backup.volume_id, ] verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), + ("description", self.backup.description), + ("container", self.backup.container), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, + volume_id=self.backup.volume_id, + container=self.backup.container, name=None, - description=self.new_backup.description, + description=self.backup.description, force=False, is_incremental=False, ) @@ -130,17 +122,13 @@ def test_backup_create_without_name(self): class TestBackupDelete(volume_fakes.TestVolume): - backups = volume_fakes.create_backups(count=2) - def setUp(self): super().setUp() - self.volume_sdk_client.find_backup = volume_fakes.get_backups( - self.backups - ) + self.backups = list(sdk_fakes.generate_fake_resources(_backup.Backup)) + self.volume_sdk_client.find_backup.side_effect = self.backups self.volume_sdk_client.delete_backup.return_value = None - # Get the command object to mock self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) def test_backup_delete(self): @@ -223,11 +211,6 @@ def test_delete_multiple_backups_with_exception(self): class TestBackupList(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.name}, count=3 - ) - columns = ( 'ID', 'Name', @@ -235,6 +218,7 @@ class TestBackupList(volume_fakes.TestVolume): 'Status', 'Size', 'Incremental', + 'Created At', ) columns_long = columns + ( 'Availability Zone', @@ -242,43 +226,51 @@ class TestBackupList(volume_fakes.TestVolume): 'Container', ) - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.volumes.return_value = [self.volume] + self.backups = list( + sdk_fakes.generate_fake_resources( + _backup.Backup, + attrs={'volume_id': self.volume.id}, + ) + ) self.volume_sdk_client.backups.return_value = self.backups - self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.find_backup.return_value = self.backups[0] - # Get the command to test + self.data = [] + for b in self.backups: + self.data.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + ) + ) + self.data_long = [] + for b in self.backups: + self.data_long.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + b.availability_zone, + volume_backup.VolumeIdColumn(b.volume_id), + b.container, + ) + ) + self.cmd = volume_backup.ListVolumeBackup(self.app, None) def test_backup_list_without_options(self): @@ -356,23 +348,33 @@ def test_backup_list_with_options(self): class TestBackupRestore(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, + columns = ( + "id", + "volume_id", + "volume_name", ) def setUp(self): super().setUp() - self.volume_sdk_client.find_backup.return_value = self.backup + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume - self.volume_sdk_client.restore_backup.return_value = ( - volume_fakes.create_one_volume( - {'id': self.volume['id']}, - ) + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, volume_id=self.volume.id + ) + self.volume_sdk_client.find_backup.return_value = self.backup + self.volume_sdk_client.restore_backup.return_value = { + 'id': self.backup['id'], + 'volume_id': self.volume['id'], + 'volume_name': self.volume['name'], + } + + self.data = ( + self.backup.id, + self.volume.id, + self.volume.name, ) - # Get the command object to mock self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) def test_backup_restore(self): @@ -386,13 +388,15 @@ def test_backup_restore(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume(self): self.volume_sdk_client.find_volume.side_effect = ( @@ -408,13 +412,15 @@ def test_backup_restore_with_volume(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=self.backup.volume_id, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_force(self): arglist = [ @@ -429,13 +435,15 @@ def test_backup_restore_with_volume_force(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=self.volume.id, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_existing(self): arglist = [ @@ -455,17 +463,15 @@ def test_backup_restore_with_volume_existing(self): ) -class TestBackupSet(TestBackupLegacy): - backup = volume_fakes.create_one_backup( - attrs={'metadata': {'wow': 'cool'}}, - ) - +class TestBackupSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock.get.return_value = self.backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, metadata={'wow': 'cool'} + ) + self.volume_sdk_client.find_backup.return_value = self.backup - # Get the command object to test self.cmd = volume_backup.SetVolumeBackup(self.app, None) def test_backup_set_state(self): @@ -475,32 +481,38 @@ def test_backup_set_state(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.backups_mock.reset_state.assert_called_once_with( - self.backup.id, 'error' - ) self.assertIsNone(result) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' + ) + def test_backup_set_state_failed(self): - self.backups_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_backup_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + arglist = ['--state', 'error', self.backup.id] verifylist = [('state', 'error'), ('backup', self.backup.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.backups_mock.reset_state.assert_called_with( - self.backup.id, 'error' + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertEqual('One or more of the set operations failed', str(exc)) + + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' ) class TestBackupShow(volume_fakes.TestVolume): - backup = volume_fakes.create_one_backup() - columns = ( "availability_zone", "container", @@ -519,30 +531,32 @@ class TestBackupShow(volume_fakes.TestVolume): "updated_at", "volume_id", ) - data = ( - backup.availability_zone, - backup.container, - backup.created_at, - backup.data_timestamp, - backup.description, - backup.fail_reason, - backup.has_dependent_backups, - backup.id, - backup.is_incremental, - backup.name, - backup.object_count, - backup.size, - backup.snapshot_id, - backup.status, - backup.updated_at, - backup.volume_id, - ) def setUp(self): super().setUp() - self.volume_sdk_client.get_backup.return_value = self.backup - # Get the command object to test + self.backup = sdk_fakes.generate_fake_resource(_backup.Backup) + self.volume_sdk_client.find_backup.return_value = self.backup + + self.data = ( + self.backup.availability_zone, + self.backup.container, + self.backup.created_at, + self.backup.data_timestamp, + self.backup.description, + self.backup.fail_reason, + self.backup.has_dependent_backups, + self.backup.id, + self.backup.is_incremental, + self.backup.name, + self.backup.object_count, + self.backup.size, + self.backup.snapshot_id, + self.backup.status, + self.backup.updated_at, + self.backup.volume_id, + ) + self.cmd = volume_backup.ShowVolumeBackup(self.app, None) def test_backup_show(self): @@ -551,7 +565,9 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_sdk_client.get_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index 6e7e02ae0f..0df379bb24 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -13,9 +13,12 @@ from unittest import mock +from openstack.block_storage.v2 import snapshot as _snapshot +from openstack.block_storage.v3 import volume as _volume +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions -from osc_lib import utils from openstackclient.tests.unit.identity.v3 import fakes as project_fakes from openstackclient.tests.unit import utils as test_utils @@ -23,19 +26,7 @@ from openstackclient.volume.v2 import volume_snapshot -class TestVolumeSnapshot(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.project_mock = self.identity_client.projects - self.project_mock.reset_mock() - - -class TestVolumeSnapshotCreate(TestVolumeSnapshot): +class TestVolumeSnapshotCreate(volume_fakes.TestVolume): columns = ( 'created_at', 'description', @@ -50,69 +41,71 @@ class TestVolumeSnapshotCreate(TestVolumeSnapshot): def setUp(self): super().setUp() - self.volume = volume_fakes.create_one_volume() - self.new_snapshot = volume_fakes.create_one_snapshot( - attrs={'volume_id': self.volume.id} + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, volume_id=self.volume.id ) + self.volume_sdk_client.create_snapshot.return_value = self.snapshot + self.volume_sdk_client.manage_snapshot.return_value = self.snapshot self.data = ( - self.new_snapshot.created_at, - self.new_snapshot.description, - self.new_snapshot.id, - self.new_snapshot.name, - format_columns.DictColumn(self.new_snapshot.metadata), - self.new_snapshot.size, - self.new_snapshot.status, - self.new_snapshot.volume_id, - ) - - self.volumes_mock.get.return_value = self.volume - self.snapshots_mock.create.return_value = self.new_snapshot - self.snapshots_mock.manage.return_value = self.new_snapshot - # Get the command object to test + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + format_columns.DictColumn(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, + ) + self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) def test_snapshot_create(self): arglist = [ "--volume", - self.new_snapshot.volume_id, + self.snapshot.volume_id, "--description", - self.new_snapshot.description, + self.snapshot.description, "--force", '--property', 'Alpha=a', '--property', 'Beta=b', - self.new_snapshot.name, + self.snapshot.name, ] verifylist = [ - ("volume", self.new_snapshot.volume_id), - ("description", self.new_snapshot.description), + ("volume", self.snapshot.volume_id), + ("description", self.snapshot.description), ("force", True), - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ("snapshot_name", self.new_snapshot.name), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.create.assert_called_with( - self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, force=True, - name=self.new_snapshot.name, - description=self.new_snapshot.description, + name=self.snapshot.name, + description=self.snapshot.description, metadata={'Alpha': 'a', 'Beta': 'b'}, ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) def test_snapshot_create_without_name(self): arglist = [ "--volume", - self.new_snapshot.volume_id, + self.snapshot.volume_id, ] verifylist = [ - ("volume", self.new_snapshot.volume_id), + ("volume", self.snapshot.volume_id), ] self.assertRaises( test_utils.ParserException, @@ -125,29 +118,31 @@ def test_snapshot_create_without_name(self): def test_snapshot_create_without_volume(self): arglist = [ "--description", - self.new_snapshot.description, + self.snapshot.description, "--force", - self.new_snapshot.name, + self.snapshot.name, ] verifylist = [ - ("description", self.new_snapshot.description), + ("description", self.snapshot.description), ("force", True), - ("snapshot_name", self.new_snapshot.name), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self.new_snapshot.name) - self.snapshots_mock.create.assert_called_once_with( - self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.name, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_once_with( + volume_id=self.snapshot.volume_id, force=True, - name=self.new_snapshot.name, - description=self.new_snapshot.description, + name=self.snapshot.name, + description=self.snapshot.description, metadata=None, ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) def test_snapshot_create_with_remote_source(self): arglist = [ @@ -156,8 +151,8 @@ def test_snapshot_create_with_remote_source(self): '--remote-source', 'source-id=test_source_id', '--volume', - self.new_snapshot.volume_id, - self.new_snapshot.name, + self.snapshot.volume_id, + self.snapshot.name, ] ref_dict = { 'source-name': 'test_source_name', @@ -165,35 +160,38 @@ def test_snapshot_create_with_remote_source(self): } verifylist = [ ('remote_source', ref_dict), - ('volume', self.new_snapshot.volume_id), - ("snapshot_name", self.new_snapshot.name), + ('volume', self.snapshot.volume_id), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.manage.assert_called_with( - volume_id=self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.manage_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, ref=ref_dict, - name=self.new_snapshot.name, + name=self.snapshot.name, description=None, metadata=None, ) - self.snapshots_mock.create.assert_not_called() - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.volume_sdk_client.create_snapshot.assert_not_called() -class TestVolumeSnapshotDelete(TestVolumeSnapshot): - snapshots = volume_fakes.create_snapshots(count=2) - +class TestVolumeSnapshotDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get = volume_fakes.get_snapshots(self.snapshots) - self.snapshots_mock.delete.return_value = None + self.snapshots = list( + sdk_fakes.generate_fake_resources(_snapshot.Snapshot) + ) + self.volume_sdk_client.find_snapshot.side_effect = self.snapshots + self.volume_sdk_client.delete_snapshot.return_value = None - # Get the command object to mock self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None) def test_snapshot_delete(self): @@ -202,11 +200,14 @@ def test_snapshot_delete(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.snapshots_mock.delete.assert_called_with( - self.snapshots[0].id, False + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=False ) - self.assertIsNone(result) def test_snapshot_delete_with_force(self): arglist = ['--force', self.snapshots[0].id] @@ -214,11 +215,14 @@ def test_snapshot_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.snapshots_mock.delete.assert_called_with( - self.snapshots[0].id, True + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=True ) - self.assertIsNone(result) def test_delete_multiple_snapshots(self): arglist = [] @@ -227,17 +231,24 @@ def test_delete_multiple_snapshots(self): verifylist = [ ('snapshots', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - calls = [] - for s in self.snapshots: - calls.append(mock.call(s.id, False)) - self.snapshots_mock.delete.assert_has_calls(calls) + result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_has_calls( + [mock.call(x.id, ignore_missing=False) for x in self.snapshots] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [mock.call(x.id, force=False) for x in self.snapshots] + ) + def test_delete_multiple_snapshots_with_exception(self): + self.volume_sdk_client.find_snapshot.side_effect = [ + self.snapshots[0], + sdk_exceptions.NotFoundException(), + ] + arglist = [ self.snapshots[0].id, 'unexist_snapshot', @@ -248,73 +259,77 @@ def test_delete_multiple_snapshots_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [self.snapshots[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 snapshots failed to delete.', str(e)) - - find_mock.assert_any_call( - self.snapshots_mock, self.snapshots[0].id - ) - find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 snapshots failed to delete.', str(exc)) - self.assertEqual(2, find_mock.call_count) - self.snapshots_mock.delete.assert_called_once_with( - self.snapshots[0].id, False - ) + self.volume_sdk_client.find_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, ignore_missing=False), + mock.call('unexist_snapshot', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, force=False), + ] + ) -class TestVolumeSnapshotList(TestVolumeSnapshot): - volume = volume_fakes.create_one_volume() - project = project_fakes.FakeProject.create_one_project() - snapshots = volume_fakes.create_snapshots( - attrs={'volume_id': volume.name}, count=3 - ) +class TestVolumeSnapshotList(volume_fakes.TestVolume): + def setUp(self): + super().setUp() - columns = ["ID", "Name", "Description", "Status", "Size"] - columns_long = columns + ["Created At", "Volume", "Properties"] - - data = [] - for s in snapshots: - data.append( - ( - s.id, - s.name, - s.description, - s.status, - s.size, + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.snapshots = list( + sdk_fakes.generate_fake_resources( + _snapshot.Snapshot, attrs={'volume_id': self.volume.name} ) ) - data_long = [] - for s in snapshots: - data_long.append( - ( - s.id, - s.name, - s.description, - s.status, - s.size, - s.created_at, - volume_snapshot.VolumeIdColumn( - s.volume_id, volume_cache={volume.id: volume} - ), - format_columns.DictColumn(s.metadata), - ) + self.project = project_fakes.FakeProject.create_one_project() + self.volume_sdk_client.volumes.return_value = [self.volume] + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.snapshots.return_value = self.snapshots + self.project_mock = self.identity_client.projects + self.project_mock.get.return_value = self.project + + self.columns = ("ID", "Name", "Description", "Status", "Size") + self.columns_long = self.columns + ( + "Created At", + "Volume", + "Properties", ) - def setUp(self): - super().setUp() + self.data = [] + self.data_long = [] + for s in self.snapshots: + self.data.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + ) + ) + self.data_long.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + s.created_at, + volume_snapshot.VolumeIdColumn( + s.volume_id, volume_cache={self.volume.id: self.volume} + ), + format_columns.DictColumn(s.metadata), + ) + ) - self.volumes_mock.list.return_value = [self.volume] - self.volumes_mock.get.return_value = self.volume - self.project_mock.get.return_value = self.project - self.snapshots_mock.list.return_value = self.snapshots - # Get the command to test self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None) def test_snapshot_list_without_options(self): @@ -324,16 +339,14 @@ def test_snapshot_list_without_options(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -359,16 +372,14 @@ def test_snapshot_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=2, marker=self.snapshots[0].id, - search_opts={ - 'all_tenants': True, - 'project_id': self.project.id, - 'name': None, - 'status': None, - 'volume_id': None, - }, + all_projects=True, + project_id=self.project.id, + name=None, + status=None, + volume_id=None, ) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) @@ -382,16 +393,14 @@ def test_snapshot_list_all_projects(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': True, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=True, + name=None, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -410,16 +419,14 @@ def test_snapshot_list_name_option(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': self.snapshots[0].name, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=self.snapshots[0].name, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -427,27 +434,25 @@ def test_snapshot_list_name_option(self): def test_snapshot_list_status_option(self): arglist = [ '--status', - self.snapshots[0].status, + 'available', ] verifylist = [ ('all_projects', False), ('long', False), - ('status', self.snapshots[0].status), + ('status', 'available'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': self.snapshots[0].status, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=None, + status='available', + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -466,16 +471,14 @@ def test_snapshot_list_volumeid_option(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': self.volume.id, - }, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=self.volume.id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -497,16 +500,18 @@ def test_snapshot_list_negative_limit(self): ) -class TestVolumeSnapshotSet(TestVolumeSnapshot): - snapshot = volume_fakes.create_one_snapshot() - +class TestVolumeSnapshotSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get.return_value = self.snapshot - self.snapshots_mock.set_metadata.return_value = None - self.snapshots_mock.update.return_value = None - # Get the command object to mock + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, metadata={'foo': 'bar'} + ) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.delete_snapshot_metadata.return_value = None + self.volume_sdk_client.set_snapshot_metadata.return_value = None + self.volume_sdk_client.update_snapshot.return_value = None + self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None) def test_snapshot_set_no_option(self): @@ -519,11 +524,14 @@ def test_snapshot_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.assertNotCalled(self.snapshots_mock.set_metadata) + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() def test_snapshot_set_name_and_property(self): arglist = [ @@ -535,26 +543,22 @@ def test_snapshot_set_name_and_property(self): "foo=foo", self.snapshot.id, ] - new_property = {"x": "y", "foo": "foo"} verifylist = [ ("name", "new_snapshot"), - ("property", new_property), + ("properties", {"x": "y", "foo": "foo"}), ("snapshot", self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - kwargs = { - "name": "new_snapshot", - } - self.snapshots_mock.update.assert_called_with( - self.snapshot.id, **kwargs + self.assertIsNone(result) + self.volume_sdk_client.update_snapshot.assert_called_with( + self.snapshot.id, name="new_snapshot" ) - self.snapshots_mock.set_metadata.assert_called_with( - self.snapshot.id, new_property + self.volume_sdk_client.set_snapshot_metadata.assert_called_with( + self.snapshot.id, x="y", foo="foo" ) - self.assertIsNone(result) def test_snapshot_set_with_no_property(self): arglist = [ @@ -568,14 +572,17 @@ def test_snapshot_set_with_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.assertNotCalled(self.snapshots_mock.set_metadata) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] - ) + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) def test_snapshot_set_with_no_property_and_property(self): arglist = [ @@ -586,22 +593,26 @@ def test_snapshot_set_with_no_property_and_property(self): ] verifylist = [ ("no_property", True), - ("property", {"foo_1": "bar_1"}), + ("properties", {"foo_1": "bar_1"}), ("snapshot", self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] + + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False ) - self.snapshots_mock.set_metadata.assert_called_once_with( - self.snapshot.id, {"foo_1": "bar_1"} + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) + self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with( + self.snapshot.id, + foo_1="bar_1", ) - self.assertIsNone(result) def test_snapshot_set_state_to_error(self): arglist = ["--state", "error", self.snapshot.id] @@ -610,30 +621,32 @@ def test_snapshot_set_state_to_error(self): result = self.cmd.take_action(parsed_args) - self.snapshots_mock.reset_state.assert_called_with( + self.assertIsNone(result) + self.volume_sdk_client.reset_snapshot_status.assert_called_with( self.snapshot.id, "error" ) - self.assertIsNone(result) def test_volume_set_state_failed(self): - self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) arglist = ['--state', 'error', self.snapshot.id] verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.snapshots_mock.reset_state.assert_called_once_with( + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( self.snapshot.id, 'error' ) def test_volume_set_name_and_state_failed(self): - self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) arglist = [ '--state', 'error', @@ -646,43 +659,39 @@ def test_volume_set_name_and_state_failed(self): ("name", "new_snapshot"), ('snapshot', self.snapshot.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - kwargs = { - "name": "new_snapshot", - } - self.snapshots_mock.update.assert_called_once_with( - self.snapshot.id, **kwargs + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.update_snapshot.assert_called_once_with( + self.snapshot.id, name="new_snapshot" ) - self.snapshots_mock.reset_state.assert_called_once_with( + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( self.snapshot.id, 'error' ) -class TestVolumeSnapshotShow(TestVolumeSnapshot): - columns = ( - 'created_at', - 'description', - 'id', - 'name', - 'properties', - 'size', - 'status', - 'volume_id', - ) - +class TestVolumeSnapshotShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshot = volume_fakes.create_one_snapshot() - + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + + self.columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) self.data = ( self.snapshot.created_at, self.snapshot.description, @@ -694,8 +703,8 @@ def setUp(self): self.snapshot.volume_id, ) - self.snapshots_mock.get.return_value = self.snapshot - # Get the command object to test + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None) def test_snapshot_show(self): @@ -704,21 +713,22 @@ def test_snapshot_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_with(self.snapshot.id) + self.volume_sdk_client.find_snapshot.assert_called_with( + self.snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) -class TestVolumeSnapshotUnset(TestVolumeSnapshot): - snapshot = volume_fakes.create_one_snapshot() - +class TestVolumeSnapshotUnset(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get.return_value = self.snapshot - self.snapshots_mock.delete_metadata.return_value = None - # Get the command object to mock + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.delete_snapshot_metadata.return_value = None + self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None) def test_snapshot_unset(self): @@ -728,7 +738,7 @@ def test_snapshot_unset(self): self.snapshot.id, ] verifylist = [ - ("property", ["foo"]), + ("properties", ["foo"]), ("snapshot", self.snapshot.id), ] @@ -736,7 +746,7 @@ def test_snapshot_unset(self): result = self.cmd.take_action(parsed_args) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] - ) self.assertIsNone(result) + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py index 1885a1f5e0..2677ddc10a 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py @@ -241,7 +241,7 @@ def test_delete_multiple_transfers_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 volume transfer requests failed ' 'to delete', + '1 of 2 volume transfer requests failed to delete', str(e), ) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 208cd7622d..eb5c170f63 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -129,14 +129,8 @@ def setUp(self): # avoid circular imports by defining this manually rather than using # openstackclient.tests.unit.compute.v2.fakes.FakeClientMixin - # TODO(stephenfin): Rename to 'compute_client' once all commands are - # migrated to SDK - self.app.client_manager.sdk_connection.compute = mock.Mock( - _compute_proxy.Proxy - ) - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) + self.app.client_manager.compute = mock.Mock(_compute_proxy.Proxy) + self.compute_client = self.app.client_manager.compute # avoid circular imports by defining this manually rather than using # openstackclient.tests.unit.image.v2.fakes.FakeClientMixin diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py b/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py index 58394e18b9..9f27197c56 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py @@ -10,9 +10,10 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# import ddt +from openstack.block_storage.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit import utils as tests_utils @@ -20,24 +21,17 @@ from openstackclient.volume.v3 import block_storage_log_level as service -class TestService(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestBlockStorageLogLevelList(TestService): - service_log = volume_fakes.create_service_log_level_entry() - +class TestBlockStorageLogLevelList(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.get_log_levels.return_value = [self.service_log] + self.log_level = sdk_fakes.generate_fake_resource( + _service.LogLevel, binary='cinder-scheduler' + ) + self.volume_sdk_client.get_service_log_levels.return_value = [ + self.log_level + ] - # Get the command object to test self.cmd = service.BlockStorageLogLevelList(self.app, None) def test_block_storage_log_level_list(self): @@ -45,16 +39,16 @@ def test_block_storage_log_level_list(self): arglist = [ '--host', - self.service_log.host, + self.log_level.host, '--service', - self.service_log.binary, + self.log_level.binary, '--log-prefix', - self.service_log.prefix, + 'cinder.', ] verifylist = [ - ('host', self.service_log.host), - ('service', self.service_log.binary), - ('log_prefix', self.service_log.prefix), + ('host', self.log_level.host), + ('service', self.log_level.binary), + ('log_prefix', 'cinder.'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -66,40 +60,35 @@ def test_block_storage_log_level_list(self): 'Prefix', 'Level', ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( + datalist = tuple( ( - self.service_log.binary, - self.service_log.host, - self.service_log.prefix, - self.service_log.level, - ), + self.log_level.binary, + self.log_level.host, + prefix, + level, + ) + for prefix, level in self.log_level.levels.values() ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - # checking if proper call was made to get log level of services - self.service_mock.get_log_levels.assert_called_with( - server=self.service_log.host, - binary=self.service_log.binary, - prefix=self.service_log.prefix, + self.volume_sdk_client.get_service_log_levels.assert_called_with( + server=self.log_level.host, + binary=self.log_level.binary, + prefix='cinder.', ) def test_block_storage_log_level_list_pre_332(self): arglist = [ '--host', - self.service_log.host, + self.log_level.host, '--service', 'cinder-api', '--log-prefix', 'cinder_test.api.common', ] verifylist = [ - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'cinder-api'), ('log_prefix', 'cinder_test.api.common'), ] @@ -117,14 +106,14 @@ def test_block_storage_log_level_list_invalid_service_name(self): arglist = [ '--host', - self.service_log.host, + self.log_level.host, '--service', 'nova-api', '--log-prefix', 'cinder_test.api.common', ] verifylist = [ - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'nova-api'), ('log_prefix', 'cinder_test.api.common'), ] @@ -139,13 +128,15 @@ def test_block_storage_log_level_list_invalid_service_name(self): @ddt.ddt -class TestBlockStorageLogLevelSet(TestService): - service_log = volume_fakes.create_service_log_level_entry() - +class TestBlockStorageLogLevelSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - # Get the command object to test + self.log_level = sdk_fakes.generate_fake_resource( + _service.LogLevel, binary='cinder-api' + ) + self.volume_sdk_client.set_service_log_levels.return_value = None + self.cmd = service.BlockStorageLogLevelSet(self.app, None) def test_block_storage_log_level_set(self): @@ -154,45 +145,45 @@ def test_block_storage_log_level_set(self): arglist = [ 'ERROR', '--host', - self.service_log.host, + self.log_level.host, '--service', - self.service_log.binary, + self.log_level.binary, '--log-prefix', - self.service_log.prefix, + 'cinder.api.common', ] verifylist = [ ('level', 'ERROR'), - ('host', self.service_log.host), - ('service', self.service_log.binary), - ('log_prefix', self.service_log.prefix), + ('host', self.log_level.host), + ('service', self.log_level.binary), + ('log_prefix', 'cinder.api.common'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + ret = self.cmd.take_action(parsed_args) - # checking if proper call was made to set log level of services - self.service_mock.set_log_levels.assert_called_with( + self.assertIsNone(ret) + self.volume_sdk_client.set_service_log_levels.assert_called_with( level='ERROR', - server=self.service_log.host, - binary=self.service_log.binary, - prefix=self.service_log.prefix, + server=self.log_level.host, + binary=self.log_level.binary, + prefix='cinder.api.common', ) def test_block_storage_log_level_set_pre_332(self): arglist = [ 'ERROR', '--host', - self.service_log.host, + self.log_level.host, '--service', 'cinder-api', '--log-prefix', - 'cinder_test.api.common', + 'cinder.api.common', ] verifylist = [ ('level', 'ERROR'), - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'cinder-api'), - ('log_prefix', 'cinder_test.api.common'), + ('log_prefix', 'cinder.api.common'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -209,7 +200,7 @@ def test_block_storage_log_level_set_invalid_service_name(self): arglist = [ 'ERROR', '--host', - self.service_log.host, + self.log_level.host, '--service', 'nova-api', '--log-prefix', @@ -217,7 +208,7 @@ def test_block_storage_log_level_set_invalid_service_name(self): ] verifylist = [ ('level', 'ERROR'), - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'nova-api'), ('log_prefix', 'cinder.api.common'), ] @@ -237,7 +228,7 @@ def test_block_storage_log_level_set_log_level(self, log_level): arglist = [ log_level, '--host', - self.service_log.host, + self.log_level.host, '--service', 'cinder-api', '--log-prefix', @@ -245,7 +236,7 @@ def test_block_storage_log_level_set_log_level(self, log_level): ] verifylist = [ ('level', log_level.upper()), - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'cinder-api'), ('log_prefix', 'cinder.api.common'), ] @@ -263,10 +254,9 @@ def test_block_storage_log_level_set_log_level(self, log_level): self.cmd.take_action(parsed_args) - # checking if proper call was made to set log level of services - self.service_mock.set_log_levels.assert_called_with( + self.volume_sdk_client.set_service_log_levels.assert_called_with( level=log_level.upper(), - server=self.service_log.host, - binary=self.service_log.binary, - prefix=self.service_log.prefix, + server=self.log_level.host, + binary=self.log_level.binary, + prefix='cinder.api.common', ) diff --git a/openstackclient/tests/unit/volume/v3/test_service.py b/openstackclient/tests/unit/volume/v3/test_service.py index 3333595513..53027fcb58 100644 --- a/openstackclient/tests/unit/volume/v3/test_service.py +++ b/openstackclient/tests/unit/volume/v3/test_service.py @@ -12,108 +12,83 @@ # under the License. # -from cinderclient import api_versions +from unittest import mock + +from openstack.block_storage.v3 import service as _service +from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import service -class TestService(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() - +class TestServiceList(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.list.return_value = [self.services] + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.volume_sdk_client.services.return_value = [self.service] - # Get the command object to test self.cmd = service.ListService(self.app, None) def test_service_list(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ] 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) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', 'Status', 'State', 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - def test_service_list_with_long_option(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, '--long', ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -121,55 +96,43 @@ def test_service_list_with_long_option(self): 'State', 'Updated At', 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) def test_service_list_with_cluster(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') - cluster = {'cluster': 'fake-cluster'} - cluster_service = volume_fakes.create_one_service(attrs=cluster) - self.service_mock.list.return_value = [cluster_service] + self.set_volume_api_version('3.7') arglist = [ '--host', - cluster_service.host, + self.service.host, '--service', - cluster_service.binary, + self.service.binary, ] verifylist = [ - ('host', cluster_service.host), - ('service', cluster_service.binary), + ('host', self.service.host), + ('service', self.service.binary), ] 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) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -177,60 +140,43 @@ def test_service_list_with_cluster(self): 'State', 'Updated At', 'Cluster', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - cluster_service.binary, - cluster_service.host, - cluster_service.zone, - cluster_service.status, - cluster_service.state, - cluster_service.updated_at, - cluster_service.cluster, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.cluster, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - cluster_service.host, - cluster_service.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(cluster_service.disabled_reason, tuple(data)) - def test_service_list_with_backend_state(self): - self.volume_client.api_version = api_versions.APIVersion('3.49') - backend_state = {'cluster': 'fake-cluster', 'backend_state': 'up'} - backend_service = volume_fakes.create_one_service(attrs=backend_state) - self.service_mock.list.return_value = [backend_service] + self.set_volume_api_version('3.49') arglist = [ '--host', - backend_service.host, + self.service.host, '--service', - backend_service.binary, + self.service.binary, ] verifylist = [ - ('host', backend_service.host), - ('service', backend_service.binary), + ('host', self.service.host), + ('service', self.service.binary), ] 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) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -239,33 +185,167 @@ def test_service_list_with_backend_state(self): 'Updated At', 'Cluster', 'Backend State', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - backend_service.binary, - backend_service.host, - backend_service.zone, - backend_service.status, - backend_service.state, - backend_service.updated_at, - backend_service.cluster, - backend_service.backend_state, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.cluster, + self.service.backend_state, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, + ) + + +class TestServiceSet(volume_fakes.TestVolume): + def setUp(self): + super().setUp() - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - backend_service.host, - backend_service.binary, + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.service.enable = mock.Mock(autospec=True) + self.service.disable = mock.Mock(autospec=True) + self.volume_sdk_client.find_service.return_value = self.service + + self.cmd = service.SetService(self.app, None) + + def test_service_set_nothing(self): + arglist = [ + self.service.host, + self.service.binary, + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.service.enable.assert_not_called() + self.service.disable.assert_not_called() + self.assertIsNone(result) + + def test_service_set_enable(self): + arglist = [ + '--enable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service.enable.assert_called_with(self.volume_sdk_client) + self.service.disable.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable(self): + arglist = [ + '--disable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=None ) + self.assertIsNone(result) + + def test_service_set_disable_with_reason(self): + reason = 'earthquake' + arglist = [ + '--disable', + '--disable-reason', + reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=reason + ) + self.assertIsNone(result) + + def test_service_set_only_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--disable-reason', + reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual( + "Cannot specify option --disable-reason without " + "--disable specified.", + str(e), + ) + + def test_service_set_enable_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--enable', + '--disable-reason', + reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(backend_service.disabled_reason, tuple(data)) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual( + "Cannot specify option --disable-reason without " + "--disable specified.", + str(e), + ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 076de3ac3b..33dcfe5a47 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -13,15 +13,19 @@ import copy from unittest import mock +import uuid +from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import block_storage_summary as _summary from openstack.block_storage.v3 import snapshot as _snapshot from openstack.block_storage.v3 import volume as _volume +from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v3 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 import utils as test_utils @@ -29,55 +33,83 @@ from openstackclient.volume.v3 import volume -# TODO(stephenfin): Combine these two test classes -class TestVolumeCreateLegacy(volume_fakes.TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() - +class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', + 'backup_id', 'bootable', + 'cluster_name', + 'consistencygroup_id', + 'consumes_quota', + 'created_at', 'description', + 'encrypted', + 'encryption_key_id', + 'group_id', 'id', + 'multiattach', 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', 'properties', + 'provider_id', + 'replication_status', + 'service_uuid', + 'shared_targets', 'size', 'snapshot_id', + 'source_volid', 'status', 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', + 'volume_type_id', ) def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - self.consistencygroups_mock = self.volume_client.consistencygroups - self.consistencygroups_mock.reset_mock() - - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - - self.new_volume = volume_fakes.create_one_volume() - self.volumes_mock.create.return_value = self.new_volume + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.create_volume.return_value = self.volume self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.description, - self.new_volume.id, - self.new_volume.name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, + self.volume.attachments, + self.volume.availability_zone, + self.volume.backup_id, + self.volume.is_bootable, + self.volume.cluster_name, + self.volume.consistency_group_id, + self.volume.consumes_quota, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.encryption_key_id, + self.volume.group_id, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + format_columns.DictColumn(self.volume.metadata), + self.volume.provider_id, + self.volume.replication_status, + self.volume.service_uuid, + self.volume.shared_targets, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, + self.volume.volume_type_id, ) # Get the command object to test @@ -86,87 +118,88 @@ def setUp(self): def test_volume_create_min_options(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, name=None, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_options(self): - consistency_group = volume_fakes.create_one_consistency_group() - self.consistencygroups_mock.get.return_value = consistency_group + consistency_group_id = 'cg123' arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--description', - self.new_volume.description, + self.volume.description, '--type', - self.new_volume.volume_type, + self.volume.volume_type, '--availability-zone', - self.new_volume.availability_zone, + self.volume.availability_zone, '--consistency-group', - consistency_group.id, + consistency_group_id, '--hint', 'k=v', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('consistency_group', consistency_group.id), + ('size', self.volume.size), + ('description', self.volume.description), + ('type', self.volume.volume_type), + ('availability_zone', self.volume.availability_zone), + ('consistency_group', consistency_group_id), ('hint', {'k': 'v'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + with mock.patch.object( + volume_v3, + 'find_consistency_group', + return_value={'id': consistency_group_id}, + ) as mock_find_cg: + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, - description=self.new_volume.description, - volume_type=self.new_volume.volume_type, - availability_zone=self.new_volume.availability_zone, + name=self.volume.name, + description=self.volume.description, + volume_type=self.volume.volume_type, + availability_zone=self.volume.availability_zone, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=consistency_group.id, + image_id=None, + source_volume_id=None, + consistency_group_id=consistency_group_id, scheduler_hints={'k': 'v'}, backup_id=None, ) + mock_find_cg.assert_called_once_with( + self.volume_sdk_client, consistency_group_id + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_properties(self): arglist = [ @@ -175,38 +208,35 @@ def test_volume_create_properties(self): '--property', 'Beta=b', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata={'Alpha': 'a', 'Beta': 'b'}, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_image_id(self): image = image_fakes.create_one_image() @@ -216,38 +246,35 @@ def test_volume_create_image_id(self): '--image', image.id, '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, + image_id=image.id, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_image_name(self): image = image_fakes.create_one_image() @@ -257,177 +284,173 @@ def test_volume_create_image_name(self): '--image', image.name, '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, + image_id=image.id, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_snapshot(self): - snapshot = volume_fakes.create_one_snapshot() - self.new_volume.snapshot_id = snapshot.id + snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = snapshot + arglist = [ '--snapshot', - self.new_volume.snapshot_id, - self.new_volume.name, + snapshot.id, + self.volume.name, ] verifylist = [ - ('snapshot', self.new_volume.snapshot_id), - ('name', self.new_volume.name), + ('snapshot', snapshot.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.snapshots_mock.get.return_value = snapshot - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( + self.volume_sdk_client.create_volume.assert_called_with( size=snapshot.size, snapshot_id=snapshot.id, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_backup(self): self.set_volume_api_version('3.47') - backup = volume_fakes.create_one_backup() - self.new_volume.backup_id = backup.id + backup = sdk_fakes.generate_fake_resource(_backup.Backup) + self.volume_sdk_client.find_backup.return_value = backup + arglist = [ '--backup', - self.new_volume.backup_id, - self.new_volume.name, + backup.id, + self.volume.name, ] verifylist = [ - ('backup', self.new_volume.backup_id), - ('name', self.new_volume.name), + ('backup', backup.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.backups_mock.get.return_value = backup - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( + self.volume_sdk_client.create_volume.assert_called_with( size=backup.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=backup.id, ) + self.volume_sdk_client.find_backup.assert_called_once_with( + backup.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_backup_pre_v347(self): - backup = volume_fakes.create_one_backup() - self.new_volume.backup_id = backup.id + backup = sdk_fakes.generate_fake_resource(_backup.Backup) + self.volume_sdk_client.find_backup.return_value = backup + arglist = [ '--backup', - self.new_volume.backup_id, - self.new_volume.name, + backup.id, + self.volume.name, ] verifylist = [ - ('backup', self.new_volume.backup_id), - ('name', self.new_volume.name), + ('backup', backup.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.backups_mock.get.return_value = backup - exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn("--os-volume-api-version 3.47 or greater", str(exc)) + self.volume_sdk_client.create_volume.assert_not_called() + def test_volume_create_with_source_volume(self): - source_vol = "source_vol" + source_volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = source_volume + arglist = [ '--source', - self.new_volume.id, - source_vol, + source_volume.id, + self.volume.name, ] verifylist = [ - ('source', self.new_volume.id), - ('name', source_vol), + ('source', source_volume.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.volumes_mock.get.return_value = self.new_volume - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=source_volume.size, snapshot_id=None, - name=source_vol, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=self.new_volume.id, - consistencygroup_id=None, + image_id=None, + source_volume_id=source_volume.id, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) + self.volume_sdk_client.find_volume.assert_called_once_with( + source_volume.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly(self, mock_wait): @@ -435,200 +458,191 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): arglist = [ '--non-bootable', '--read-write', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', False), - ('read_write', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, False ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, False ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly_fail( self, mock_wait, mock_error ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() + self.volume_sdk_client.set_volume_bootable_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + self.volume_sdk_client.set_volume_readonly.side_effect = ( + sdk_exceptions.NotFoundException('foo') ) arglist = [ '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True + ) + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True + ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) + self.assertEqual(self.datalist, data) @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=False) def test_volume_create_non_available_with_readonly( - self, - mock_wait, - mock_error, + self, mock_wait, mock_error ): arglist = [ '--non-bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_without_size(self): arglist = [ - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -645,15 +659,15 @@ def test_volume_create_with_multi_source(self): '--snapshot', 'source_snapshot', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', 'source_image'), ('source', 'source_volume'), ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] self.assertRaises( @@ -672,7 +686,7 @@ def test_volume_create_hints(self): """ arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--hint', 'k=v', '--hint', @@ -687,10 +701,10 @@ def test_volume_create_hints(self): 'local_to_instance=v6', '--hint', 'different_host=v7', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ( 'hint', { @@ -700,26 +714,23 @@ def test_volume_create_hints(self): 'different_host': ['v5', 'v7'], }, ), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints={ 'k': 'v2', 'same_host': ['v3', 'v4'], @@ -730,102 +741,22 @@ def test_volume_create_hints(self): ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - -class TestVolumeCreate(volume_fakes.TestVolume): - columns = ( - 'attachments', - 'availability_zone', - 'consistency_group_id', - 'created_at', - 'description', - 'extended_replication_status', - 'group_id', - 'host', - 'id', - 'image_id', - 'is_bootable', - 'is_encrypted', - 'is_multiattach', - 'location', - 'metadata', - 'migration_id', - 'migration_status', - 'name', - 'project_id', - 'provider_id', - 'replication_driver_data', - 'replication_status', - 'scheduler_hints', - 'size', - 'snapshot_id', - 'source_volume_id', - 'status', - 'updated_at', - 'user_id', - 'volume_image_metadata', - 'volume_type', - ) - - def setUp(self): - super().setUp() - - self.new_volume = sdk_fakes.generate_fake_resource( - _volume.Volume, **{'size': 1} - ) - - self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.consistency_group_id, - self.new_volume.created_at, - self.new_volume.description, - self.new_volume.extended_replication_status, - self.new_volume.group_id, - self.new_volume.host, - self.new_volume.id, - self.new_volume.image_id, - self.new_volume.is_bootable, - self.new_volume.is_encrypted, - self.new_volume.is_multiattach, - self.new_volume.location, - self.new_volume.metadata, - self.new_volume.migration_id, - self.new_volume.migration_status, - self.new_volume.name, - self.new_volume.project_id, - self.new_volume.provider_id, - self.new_volume.replication_driver_data, - self.new_volume.replication_status, - self.new_volume.scheduler_hints, - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.source_volume_id, - self.new_volume.status, - self.new_volume.updated_at, - self.new_volume.user_id, - self.new_volume.volume_image_metadata, - self.new_volume.volume_type, - ) - - # Get the command object to test - self.cmd = volume.CreateVolume(self.app, None) + self.assertEqual(self.datalist, data) def test_volume_create_remote_source(self): - self.volume_sdk_client.manage_volume.return_value = self.new_volume + self.volume_sdk_client.manage_volume.return_value = self.volume arglist = [ '--remote-source', 'key=val', '--host', 'fake_host', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), ('host', 'fake_host'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -838,13 +769,13 @@ def test_volume_create_remote_source(self): description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, + metadata=parsed_args.properties, bootable=parsed_args.bootable, cluster=getattr(parsed_args, 'cluster', None), ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_remote_source_pre_v316(self): self.set_volume_api_version('3.15') @@ -853,12 +784,12 @@ def test_volume_create_remote_source_pre_v316(self): 'key=val', '--cluster', 'fake_cluster', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), ('cluster', 'fake_cluster'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -878,13 +809,13 @@ def test_volume_create_remote_source_host_and_cluster(self): 'fake_host', '--cluster', 'fake_cluster', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), ('host', 'fake_host'), ('cluster', 'fake_cluster'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -899,11 +830,11 @@ def test_volume_create_remote_source_no_host_or_cluster(self): arglist = [ '--remote-source', 'key=val', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -917,15 +848,15 @@ def test_volume_create_remote_source_no_host_or_cluster(self): def test_volume_create_remote_source_size(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--remote-source', 'key=val', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ('remote_source', {'key': 'val'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -941,15 +872,15 @@ def test_volume_create_remote_source_size(self): def test_volume_create_host_no_remote_source(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--host', 'fake_host', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ('host', 'fake_host'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -962,16 +893,17 @@ def test_volume_create_host_no_remote_source(self): ) -class TestVolumeDeleteLegacy(volume_fakes.TestVolume): +class TestVolumeDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.volumes_mock.delete.return_value = None - self.volumes = volume_fakes.create_volumes(count=1) - self.volumes_mock.get = volume_fakes.get_volumes(self.volumes, 0) + self.volumes = list(sdk_fakes.generate_fake_resources(_volume.Volume)) + self.volume_sdk_client.find_volume.side_effect = self.volumes + self.volume_sdk_client.delete_volume.return_value = None + self.volume_sdk_client.unmanage_volume.return_value = None # Get the command object to mock self.cmd = volume.DeleteVolume(self.app, None) @@ -986,11 +918,14 @@ def test_volume_delete_one_volume(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - self.volumes[0].id, cascade=False + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=False ) - self.assertIsNone(result) def test_volume_delete_multi_volumes(self): arglist = [v.id for v in self.volumes] @@ -1002,12 +937,21 @@ def test_volume_delete_multi_volumes(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - calls = [mock.call(v.id, cascade=False) for v in self.volumes] - self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_has_calls( + [mock.call(v.id, ignore_missing=False) for v in self.volumes] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [mock.call(v.id, cascade=False, force=False) for v in self.volumes] + ) + def test_volume_delete_multi_volumes_with_exception(self): + self.volume_sdk_client.find_volume.side_effect = [ + self.volumes[0], + sdk_exceptions.NotFoundException(), + ] + arglist = [ self.volumes[0].id, 'unexist_volume', @@ -1015,27 +959,28 @@ def test_volume_delete_multi_volumes_with_exception(self): verifylist = [ ('force', False), ('purge', False), - ('volumes', arglist), + ('volumes', [self.volumes[0].id, 'unexist_volume']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [self.volumes[0], exceptions.CommandError] - with mock.patch.object( - utils, 'find_resource', side_effect=find_mock_result - ) as find_mock: - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual('1 of 2 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, self.volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with( - self.volumes[0].id, cascade=False - ) + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 volumes failed to delete.', str(exc)) + + self.volume_sdk_client.find_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, ignore_missing=False), + mock.call('unexist_volume', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, cascade=False, force=False), + ] + ) def test_volume_delete_with_purge(self): arglist = [ @@ -1050,11 +995,14 @@ def test_volume_delete_with_purge(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - self.volumes[0].id, cascade=True + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=True, force=False ) - self.assertIsNone(result) def test_volume_delete_with_force(self): arglist = [ @@ -1069,49 +1017,38 @@ def test_volume_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with( - self.volumes[0].id - ) self.assertIsNone(result) - -class TestVolumeDelete(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.volume_sdk_client.unmanage_volume.return_value = None - - # Get the command object to mock - self.cmd = volume.DeleteVolume(self.app, None) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=True + ) def test_volume_delete_remote(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) - self.volumes_mock.get.return_value = vol - - arglist = ['--remote', vol.id] + arglist = ['--remote', self.volumes[0].id] verifylist = [ ("remote", True), ("force", False), ("purge", False), - ("volumes", [vol.id]), + ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.volume_sdk_client.unmanage_volume.assert_called_once_with(vol.id) self.assertIsNone(result) - def test_volume_delete_multi_volumes_remote(self): - volumes = sdk_fakes.generate_fake_resources( - _volume.Volume, count=3, attrs={'size': 1} + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_called_once_with( + self.volumes[0].id ) - arglist = ['--remote'] - arglist += [v.id for v in volumes] + def test_volume_delete_multi_volumes_remote(self): + arglist = ['--remote'] + [v.id for v in self.volumes] verifylist = [ ('remote', True), ('force', False), @@ -1121,24 +1058,27 @@ def test_volume_delete_multi_volumes_remote(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - calls = [mock.call(v.id) for v in volumes] - self.volume_sdk_client.unmanage_volume.assert_has_calls(calls) self.assertIsNone(result) - def test_volume_delete_remote_with_purge(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + self.volume_sdk_client.find_volume.assert_has_calls( + [mock.call(v.id, ignore_missing=False) for v in self.volumes] + ) + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_has_calls( + [mock.call(v.id) for v in self.volumes] + ) + def test_volume_delete_remote_with_purge(self): arglist = [ '--remote', '--purge', - vol.id, + self.volumes[0].id, ] verifylist = [ ('remote', True), ('force', False), ('purge', True), - ('volumes', [vol.id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1151,19 +1091,21 @@ def test_volume_delete_remote_with_purge(self): str(exc), ) - def test_volume_delete_remote_with_force(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_not_called() + def test_volume_delete_remote_with_force(self): arglist = [ '--remote', '--force', - vol.id, + self.volumes[0].id, ] verifylist = [ ('remote', True), ('force', True), ('purge', False), - ('volumes', [vol.id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1176,6 +1118,10 @@ def test_volume_delete_remote_with_force(self): str(exc), ) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_not_called() + class TestVolumeList(volume_fakes.TestVolume): project = identity_fakes.FakeProject.create_one_project() @@ -1231,6 +1177,7 @@ def test_volume_list_no_options(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1274,6 +1221,7 @@ def test_volume_list_project(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1320,6 +1268,7 @@ def test_volume_list_project_domain(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1363,6 +1312,7 @@ def test_volume_list_user(self): 'user_id': self.user.id, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1408,6 +1358,7 @@ def test_volume_list_user_domain(self): 'user_id': self.user.id, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1451,6 +1402,7 @@ def test_volume_list_name(self): 'user_id': None, 'name': self.mock_volume.name, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1494,6 +1446,7 @@ def test_volume_list_status(self): 'user_id': None, 'name': None, 'status': self.mock_volume.status, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1536,6 +1489,7 @@ def test_volume_list_all_projects(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1579,6 +1533,7 @@ def test_volume_list_long(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1652,6 +1607,7 @@ def test_volume_list_with_marker_and_limit(self): 'user_id': None, 'name': None, 'all_tenants': False, + 'metadata': None, }, ) self.assertCountEqual(datalist, tuple(data)) @@ -1696,6 +1652,7 @@ def test_volume_list_backward_compatibility(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1711,71 +1668,79 @@ def test_volume_list_backward_compatibility(self): class TestVolumeMigrate(volume_fakes.TestVolume): - _volume = volume_fakes.create_one_volume() - def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.migrate_volume.return_value = None - self.volumes_mock.get.return_value = self._volume - self.volumes_mock.migrate_volume.return_value = None - # Get the command object to test self.cmd = volume.MigrateVolume(self.app, None) def test_volume_migrate(self): arglist = [ "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("volume", self.volume.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", False, False - ) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=False, + lock_volume=False, + ) + def test_volume_migrate_with_option(self): arglist = [ "--force-host-copy", "--lock-volume", "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", True), ("lock_volume", True), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("volume", self.volume.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self._volume.id) - self.volumes_mock.migrate_volume.assert_called_once_with( - self._volume.id, "host@backend-name#pool", True, True - ) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=True, + lock_volume=True, + ) + def test_volume_migrate_without_host(self): arglist = [ - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), - ("volume", self._volume.id), + ("volume", self.volume.id), ] self.assertRaises( @@ -1786,6 +1751,9 @@ def test_volume_migrate_without_host(self): verifylist, ) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.migrate_volume.assert_not_called() + class TestVolumeSet(volume_fakes.TestVolume): volume_type = volume_fakes.create_one_volume_type() @@ -1815,16 +1783,16 @@ def test_volume_set_property(self): self.new_volume.id, ] verifylist = [ - ('property', {'a': 'b', 'c': 'd'}), + ('properties', {'a': 'b', 'c': 'd'}), + ('read_only', None), + ('bootable', None), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.volumes_mock.set_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) def test_volume_set_image_property(self): @@ -1836,10 +1804,10 @@ def test_volume_set_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), + ('read_only', None), + ('bootable', None), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1847,15 +1815,15 @@ def test_volume_set_image_property(self): # returns nothing self.cmd.take_action(parsed_args) self.volumes_mock.set_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) def test_volume_set_state(self): arglist = ['--state', 'error', self.new_volume.id] verifylist = [ - ('read_only', False), - ('read_write', False), ('state', 'error'), + ('read_only', None), + ('bootable', None), ('volume', self.new_volume.id), ] @@ -1919,36 +1887,40 @@ def test_volume_set_detached(self): def test_volume_set_bootable(self): arglist = [ - ['--bootable', self.new_volume.id], - ['--non-bootable', self.new_volume.id], + '--bootable', + self.new_volume.id, ] verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self.new_volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self.new_volume.id), - ], + ('bootable', True), + ('volume', self.new_volume.id), ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, verifylist[index][0][1] - ) + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) + + def test_volume_set_non_bootable(self): + arglist = [ + '--non-bootable', + self.new_volume.id, + ] + verifylist = [ + ('bootable', False), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) def test_volume_set_readonly(self): arglist = ['--read-only', self.new_volume.id] verifylist = [ ('read_only', True), - ('read_write', False), ('volume', self.new_volume.id), ] @@ -1964,7 +1936,6 @@ def test_volume_set_read_write(self): arglist = ['--read-write', self.new_volume.id] verifylist = [ ('read_only', False), - ('read_write', True), ('volume', self.new_volume.id), ] @@ -2027,7 +1998,7 @@ def test_volume_set_with_only_retype_policy(self, mock_warning): result = self.cmd.take_action(parsed_args) self.volumes_mock.retype.assert_not_called() mock_warning.assert_called_with( - "'--retype-policy' option will " "not work without '--type' option" + "'--retype-policy' option will not work without '--type' option" ) self.assertIsNone(result) @@ -2036,41 +2007,93 @@ class TestVolumeShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + + self.columns = ( + 'attachments', + 'availability_zone', + 'backup_id', + 'bootable', + 'cluster_name', + 'consistencygroup_id', + 'consumes_quota', + 'created_at', + 'description', + 'encrypted', + 'encryption_key_id', + 'group_id', + 'id', + 'multiattach', + 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', + 'properties', + 'provider_id', + 'replication_status', + 'service_uuid', + 'shared_targets', + 'size', + 'snapshot_id', + 'source_volid', + 'status', + 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', + 'volume_type_id', + ) + self.data = ( + self.volume.attachments, + self.volume.availability_zone, + self.volume.backup_id, + self.volume.is_bootable, + self.volume.cluster_name, + self.volume.consistency_group_id, + self.volume.consumes_quota, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.encryption_key_id, + self.volume.group_id, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + format_columns.DictColumn(self.volume.metadata), + self.volume.provider_id, + self.volume.replication_status, + self.volume.service_uuid, + self.volume.shared_targets, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, + self.volume.volume_type_id, + ) - self._volume = volume_fakes.create_one_volume() - self.volumes_mock.get.return_value = self._volume - # Get the command object to test self.cmd = volume.ShowVolume(self.app, None) def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] + arglist = [self.volume.id] + verifylist = [("volume", self.volume.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - self.assertEqual( - tuple(sorted(self._volume.keys())), - columns, - ) - self.assertTupleEqual( - ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.description, - self._volume.id, - self._volume.name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ), - data, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False ) @@ -2100,7 +2123,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_set, arglist, verifylist) @@ -2116,7 +2139,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist_unset = [ - ('image_property', ['Alpha']), + ('image_properties', ['Alpha']), ('volume', self.new_volume.id), ] parsed_args_unset = self.check_parser( @@ -2128,7 +2151,7 @@ def test_volume_unset_image_property(self): self.cmd_unset.take_action(parsed_args_unset) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args_unset.image_property + self.new_volume.id, parsed_args_unset.image_properties ) def test_volume_unset_image_property_fail(self): @@ -2143,8 +2166,8 @@ def test_volume_unset_image_property_fail(self): self.new_volume.id, ] verifylist = [ - ('image_property', ['Alpha']), - ('property', ['Beta']), + ('image_properties', ['Alpha']), + ('properties', ['Beta']), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_unset, arglist, verifylist) @@ -2157,10 +2180,10 @@ def test_volume_unset_image_property_fail(self): 'One or more of the unset operations failed', str(e) ) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) self.volumes_mock.delete_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) @@ -2316,29 +2339,45 @@ def test_volume_revert_to_snapshot(self): class TestColumns(volume_fakes.TestVolume): def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] - col = volume.AttachmentsColumn(_volume.attachments, {}) + col = volume.AttachmentsColumn(vol.attachments, {}) self.assertEqual( f'Attached to {server_id} on {device} ', col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] fake_server = mock.Mock() fake_server.name = 'fake-server-name' server_cache = {server_id: fake_server} - col = volume.AttachmentsColumn(_volume.attachments, server_cache) + col = volume.AttachmentsColumn(vol.attachments, server_cache) self.assertEqual( 'Attached to {} on {} '.format('fake-server-name', device), col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index aca2e3399a..b7838e034a 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -28,7 +28,7 @@ def setUp(self): class TestVolumeAttachmentCreate(TestVolumeAttachment): volume = volume_fakes.create_one_volume() - server = compute_fakes.create_one_sdk_server() + server = compute_fakes.create_one_server() volume_attachment = volume_fakes.create_one_volume_attachment( attrs={'instance': server.id, 'volume_id': volume.id}, ) @@ -61,7 +61,7 @@ def setUp(self): self.volume_sdk_client.create_attachment.return_value = ( self.volume_attachment.to_dict() ) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None) @@ -92,7 +92,7 @@ def test_volume_attachment_create(self): self.volume_sdk_client.find_volume.assert_called_once_with( self.volume.id, ignore_missing=False ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.volume_sdk_client.create_attachment.assert_called_once_with( @@ -159,7 +159,7 @@ def test_volume_attachment_create_with_connect(self): self.volume_sdk_client.find_volume.assert_called_once_with( self.volume.id, ignore_missing=False ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.volume_sdk_client.create_attachment.assert_called_once_with( diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index d4e7e79248..86bde785f6 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -11,91 +11,84 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest.mock import call - +from unittest import mock + +from openstack.block_storage.v3 import backup as _backup +from openstack.block_storage.v3 import snapshot as _snapshot +from openstack.block_storage.v3 import volume as _volume +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import volume_backup -class TestBackupLegacy(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - class TestBackupCreate(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - snapshot = volume_fakes.create_one_snapshot() - new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id} - ) - columns = ( 'id', 'name', 'volume_id', ) - data = ( - new_backup.id, - new_backup.name, - new_backup.volume_id, - ) def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) self.volume_sdk_client.find_snapshot.return_value = self.snapshot - self.volume_sdk_client.create_backup.return_value = self.new_backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, + volume_id=self.volume.id, + snapshot_id=self.snapshot.id, + ) + self.volume_sdk_client.create_backup.return_value = self.backup + + self.data = ( + self.backup.id, + self.backup.name, + self.backup.volume_id, + ) - # Get the command object to test self.cmd = volume_backup.CreateVolumeBackup(self.app, None) def test_backup_create(self): arglist = [ "--name", - self.new_backup.name, + self.backup.name, "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, + self.backup.container, "--force", "--incremental", "--snapshot", - self.new_backup.snapshot_id, - self.new_backup.volume_id, + self.backup.snapshot_id, + self.backup.volume_id, ] verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), + ("name", self.backup.name), + ("description", self.backup.description), + ("container", self.backup.container), ("force", True), ("incremental", True), - ("snapshot", self.new_backup.snapshot_id), - ("volume", self.new_backup.volume_id), + ("snapshot", self.backup.snapshot_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, - name=self.new_backup.name, - description=self.new_backup.description, + volume_id=self.backup.volume_id, + container=self.backup.container, + name=self.backup.name, + description=self.backup.description, force=True, is_incremental=True, - snapshot_id=self.new_backup.snapshot_id, + snapshot_id=self.backup.snapshot_id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -108,18 +101,18 @@ def test_backup_create_with_properties(self): "foo=bar", "--property", "wow=much-cool", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("properties", {"foo": "bar", "wow": "much-cool"}), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, + volume_id=self.backup.volume_id, container=None, name=None, description=None, @@ -138,11 +131,11 @@ def test_backup_create_with_properties_pre_v343(self): "foo=bar", "--property", "wow=much-cool", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("properties", {"foo": "bar", "wow": "much-cool"}), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -157,18 +150,18 @@ def test_backup_create_with_availability_zone(self): arglist = [ "--availability-zone", "my-az", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("availability_zone", "my-az"), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, + volume_id=self.backup.volume_id, container=None, name=None, description=None, @@ -185,11 +178,11 @@ def test_backup_create_with_availability_zone_pre_v351(self): arglist = [ "--availability-zone", "my-az", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("availability_zone", "my-az"), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -201,25 +194,25 @@ def test_backup_create_with_availability_zone_pre_v351(self): def test_backup_create_without_name(self): arglist = [ "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, - self.new_backup.volume_id, + self.backup.container, + self.backup.volume_id, ] verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), + ("description", self.backup.description), + ("container", self.backup.container), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, + volume_id=self.backup.volume_id, + container=self.backup.container, name=None, - description=self.new_backup.description, + description=self.backup.description, force=False, is_incremental=False, ) @@ -228,17 +221,13 @@ def test_backup_create_without_name(self): class TestBackupDelete(volume_fakes.TestVolume): - backups = volume_fakes.create_backups(count=2) - def setUp(self): super().setUp() - self.volume_sdk_client.find_backup = volume_fakes.get_backups( - self.backups - ) + self.backups = list(sdk_fakes.generate_fake_resources(_backup.Backup)) + self.volume_sdk_client.find_backup.side_effect = self.backups self.volume_sdk_client.delete_backup.return_value = None - # Get the command object to mock self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) def test_backup_delete(self): @@ -281,7 +270,7 @@ def test_delete_multiple_backups(self): calls = [] for b in self.backups: - calls.append(call(b.id, ignore_missing=False, force=False)) + calls.append(mock.call(b.id, ignore_missing=False, force=False)) self.volume_sdk_client.delete_backup.assert_has_calls(calls) self.assertIsNone(result) @@ -321,11 +310,6 @@ def test_delete_multiple_backups_with_exception(self): class TestBackupList(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.name}, count=3 - ) - columns = ( 'ID', 'Name', @@ -333,6 +317,7 @@ class TestBackupList(volume_fakes.TestVolume): 'Status', 'Size', 'Incremental', + 'Created At', ) columns_long = columns + ( 'Availability Zone', @@ -340,43 +325,51 @@ class TestBackupList(volume_fakes.TestVolume): 'Container', ) - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.volumes.return_value = [self.volume] + self.backups = list( + sdk_fakes.generate_fake_resources( + _backup.Backup, + attrs={'volume_id': self.volume.id}, + ) + ) self.volume_sdk_client.backups.return_value = self.backups - self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.find_backup.return_value = self.backups[0] - # Get the command to test + self.data = [] + for b in self.backups: + self.data.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + ) + ) + self.data_long = [] + for b in self.backups: + self.data_long.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + b.availability_zone, + volume_backup.VolumeIdColumn(b.volume_id), + b.container, + ) + ) + self.cmd = volume_backup.ListVolumeBackup(self.app, None) def test_backup_list_without_options(self): @@ -389,6 +382,7 @@ def test_backup_list_without_options(self): ("marker", None), ("limit", None), ('all_projects', False), + ("project", None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -403,11 +397,14 @@ def test_backup_list_without_options(self): all_tenants=False, marker=None, limit=None, + project_id=None, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) def test_backup_list_with_options(self): + project = sdk_fakes.generate_fake_resource(_project.Project) + self.identity_sdk_client.find_project.return_value = project arglist = [ "--long", "--name", @@ -421,6 +418,8 @@ def test_backup_list_with_options(self): "--all-projects", "--limit", "3", + "--project", + project.id, ] verifylist = [ ("long", True), @@ -430,6 +429,7 @@ def test_backup_list_with_options(self): ("marker", self.backups[0].id), ('all_projects', True), ("limit", 3), + ("project", project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -448,29 +448,41 @@ def test_backup_list_with_options(self): all_tenants=True, marker=self.backups[0].id, limit=3, + project_id=project.id, ) self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, list(data)) class TestBackupRestore(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, + columns = ( + "id", + "volume_id", + "volume_name", ) def setUp(self): super().setUp() - self.volume_sdk_client.find_backup.return_value = self.backup + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume - self.volume_sdk_client.restore_backup.return_value = ( - volume_fakes.create_one_volume( - {'id': self.volume['id']}, - ) + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, volume_id=self.volume.id + ) + self.volume_sdk_client.find_backup.return_value = self.backup + self.volume_sdk_client.create_backup.return_value = self.backup + self.volume_sdk_client.restore_backup.return_value = { + 'id': self.backup['id'], + 'volume_id': self.volume['id'], + 'volume_name': self.volume['name'], + } + + self.data = ( + self.backup.id, + self.volume.id, + self.volume.name, ) - # Get the command object to mock self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) def test_backup_restore(self): @@ -484,13 +496,15 @@ def test_backup_restore(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume(self): self.volume_sdk_client.find_volume.side_effect = ( @@ -506,13 +520,15 @@ def test_backup_restore_with_volume(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=self.backup.volume_id, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_force(self): arglist = [ @@ -527,13 +543,15 @@ def test_backup_restore_with_volume_force(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=self.volume.id, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_existing(self): arglist = [ @@ -553,17 +571,15 @@ def test_backup_restore_with_volume_existing(self): ) -class TestBackupSet(TestBackupLegacy): - backup = volume_fakes.create_one_backup( - attrs={'metadata': {'wow': 'cool'}}, - ) - +class TestBackupSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock.get.return_value = self.backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, metadata={'wow': 'cool'} + ) + self.volume_sdk_client.find_backup.return_value = self.backup - # Get the command object to test self.cmd = volume_backup.SetVolumeBackup(self.app, None) def test_backup_set_name(self): @@ -580,14 +596,16 @@ def test_backup_set_name(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns nothing result = self.cmd.take_action(parsed_args) - self.backups_mock.update.assert_called_once_with( - self.backup.id, **{'name': 'new_name'} - ) self.assertIsNone(result) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, name='new_name' + ) + def test_backup_set_name_pre_v39(self): self.set_volume_api_version('3.8') @@ -623,13 +641,14 @@ def test_backup_set_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = {'description': 'new_description'} - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, description='new_description' ) - self.assertIsNone(result) def test_backup_set_description_pre_v39(self): self.set_volume_api_version('3.8') @@ -658,26 +677,34 @@ def test_backup_set_state(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.backups_mock.reset_state.assert_called_once_with( - self.backup.id, 'error' - ) self.assertIsNone(result) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' + ) + def test_backup_set_state_failed(self): - self.backups_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_backup_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + arglist = ['--state', 'error', self.backup.id] verifylist = [('state', 'error'), ('backup', self.backup.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.backups_mock.reset_state.assert_called_with( - self.backup.id, 'error' + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertEqual('One or more of the set operations failed', str(exc)) + + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' ) def test_backup_set_no_property(self): @@ -694,15 +721,14 @@ def test_backup_set_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = { - 'metadata': {}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, metadata={} ) - self.assertIsNone(result) def test_backup_set_no_property_pre_v343(self): self.set_volume_api_version('3.42') @@ -737,15 +763,14 @@ def test_backup_set_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = { - 'metadata': {'wow': 'cool', 'foo': 'bar'}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, metadata={'wow': 'cool', 'foo': 'bar'} ) - self.assertIsNone(result) def test_backup_set_property_pre_v343(self): self.set_volume_api_version('3.42') @@ -767,17 +792,16 @@ def test_backup_set_property_pre_v343(self): self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) -class TestBackupUnset(TestBackupLegacy): - backup = volume_fakes.create_one_backup( - attrs={'metadata': {'foo': 'bar'}}, - ) - +class TestBackupUnset(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock.get.return_value = self.backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, metadata={'foo': 'bar', 'wow': 'cool'} + ) + self.volume_sdk_client.find_backup.return_value = self.backup + self.volume_sdk_client.delete_backup_metadata.return_value = None - # Get the command object to test self.cmd = volume_backup.UnsetVolumeBackup(self.app, None) def test_backup_unset_property(self): @@ -795,15 +819,14 @@ def test_backup_unset_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = { - 'metadata': {}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.delete_backup_metadata.assert_called_once_with( + self.backup, keys=['wow'] ) - self.assertIsNone(result) def test_backup_unset_property_pre_v343(self): self.set_volume_api_version('3.42') @@ -826,8 +849,6 @@ def test_backup_unset_property_pre_v343(self): class TestBackupShow(volume_fakes.TestVolume): - backup = volume_fakes.create_one_backup() - columns = ( "availability_zone", "container", @@ -850,34 +871,36 @@ class TestBackupShow(volume_fakes.TestVolume): "user_id", "volume_id", ) - data = ( - backup.availability_zone, - backup.container, - backup.created_at, - backup.data_timestamp, - backup.description, - backup.encryption_key_id, - backup.fail_reason, - backup.has_dependent_backups, - backup.id, - backup.is_incremental, - backup.metadata, - backup.name, - backup.object_count, - backup.project_id, - backup.size, - backup.snapshot_id, - backup.status, - backup.updated_at, - backup.user_id, - backup.volume_id, - ) def setUp(self): super().setUp() - self.volume_sdk_client.get_backup.return_value = self.backup - # Get the command object to test + self.backup = sdk_fakes.generate_fake_resource(_backup.Backup) + self.volume_sdk_client.find_backup.return_value = self.backup + + self.data = ( + self.backup.availability_zone, + self.backup.container, + self.backup.created_at, + self.backup.data_timestamp, + self.backup.description, + self.backup.encryption_key_id, + self.backup.fail_reason, + self.backup.has_dependent_backups, + self.backup.id, + self.backup.is_incremental, + self.backup.metadata, + self.backup.name, + self.backup.object_count, + self.backup.project_id, + self.backup.size, + self.backup.snapshot_id, + self.backup.status, + self.backup.updated_at, + self.backup.user_id, + self.backup.volume_id, + ) + self.cmd = volume_backup.ShowVolumeBackup(self.app, None) def test_backup_show(self): @@ -886,7 +909,9 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_sdk_client.get_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py new file mode 100644 index 0000000000..85613603de --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -0,0 +1,795 @@ +# +# 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 openstack.block_storage.v3 import snapshot as _snapshot +from openstack.block_storage.v3 import volume as _volume +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes +from osc_lib.cli import format_columns +from osc_lib import exceptions + +from openstackclient.tests.unit.identity.v3 import fakes as project_fakes +from openstackclient.tests.unit import utils as test_utils +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes +from openstackclient.volume.v3 import volume_snapshot + + +class TestVolumeSnapshotCreate(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, volume_id=self.volume.id + ) + self.volume_sdk_client.create_snapshot.return_value = self.snapshot + self.volume_sdk_client.manage_snapshot.return_value = self.snapshot + + self.columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) + self.data = ( + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + format_columns.DictColumn(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, + ) + + self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) + + def test_snapshot_create(self): + arglist = [ + "--volume", + self.snapshot.volume_id, + "--description", + self.snapshot.description, + "--force", + '--property', + 'Alpha=a', + '--property', + 'Beta=b', + self.snapshot.name, + ] + verifylist = [ + ("volume", self.snapshot.volume_id), + ("description", self.snapshot.description), + ("force", True), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), + ("snapshot_name", self.snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, + force=True, + name=self.snapshot.name, + description=self.snapshot.description, + metadata={'Alpha': 'a', 'Beta': 'b'}, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_snapshot_create_without_name(self): + arglist = [ + "--volume", + self.snapshot.volume_id, + ] + verifylist = [ + ("volume", self.snapshot.volume_id), + ] + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + def test_snapshot_create_without_volume(self): + arglist = [ + "--description", + self.snapshot.description, + "--force", + self.snapshot.name, + ] + verifylist = [ + ("description", self.snapshot.description), + ("force", True), + ("snapshot_name", self.snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.name, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, + force=True, + name=self.snapshot.name, + description=self.snapshot.description, + metadata=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_snapshot_create_with_remote_source(self): + arglist = [ + '--remote-source', + 'source-name=test_source_name', + '--remote-source', + 'source-id=test_source_id', + '--volume', + self.snapshot.volume_id, + self.snapshot.name, + ] + ref_dict = { + 'source-name': 'test_source_name', + 'source-id': 'test_source_id', + } + verifylist = [ + ('remote_source', ref_dict), + ('volume', self.snapshot.volume_id), + ("snapshot_name", self.snapshot.name), + ] + 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.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.manage_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, + ref=ref_dict, + name=self.snapshot.name, + description=None, + metadata=None, + ) + self.volume_sdk_client.create_snapshot.assert_not_called() + + +class TestVolumeSnapshotDelete(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.snapshots = list( + sdk_fakes.generate_fake_resources(_snapshot.Snapshot) + ) + self.volume_sdk_client.find_snapshot.side_effect = self.snapshots + self.volume_sdk_client.delete_snapshot.return_value = None + self.volume_sdk_client.unmanage_snapshot.return_value = None + + self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None) + + def test_snapshot_delete(self): + arglist = [self.snapshots[0].id] + verifylist = [("snapshots", [self.snapshots[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=False + ) + + def test_snapshot_delete_with_force(self): + arglist = ['--force', self.snapshots[0].id] + verifylist = [('force', True), ("snapshots", [self.snapshots[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=True + ) + + def test_delete_multiple_snapshots(self): + arglist = [] + for s in self.snapshots: + arglist.append(s.id) + verifylist = [ + ('snapshots', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.volume_sdk_client.find_snapshot.assert_has_calls( + [mock.call(x.id, ignore_missing=False) for x in self.snapshots] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [mock.call(x.id, force=False) for x in self.snapshots] + ) + + def test_delete_multiple_snapshots_with_exception(self): + self.volume_sdk_client.find_snapshot.side_effect = [ + self.snapshots[0], + sdk_exceptions.NotFoundException(), + ] + + arglist = [ + self.snapshots[0].id, + 'unexist_snapshot', + ] + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 snapshots failed to delete.', str(exc)) + + self.volume_sdk_client.find_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, ignore_missing=False), + mock.call('unexist_snapshot', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, force=False), + ] + ) + + def test_snapshot_delete_remote(self): + arglist = ['--remote', self.snapshots[0].id] + verifylist = [('remote', True), ("snapshots", [self.snapshots[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.volume_sdk_client.unmanage_snapshot.assert_called_with( + self.snapshots[0].id + ) + + def test_snapshot_delete_with_remote_force(self): + arglist = ['--remote', '--force', self.snapshots[0].id] + verifylist = [ + ('remote', True), + ('force', True), + ("snapshots", [self.snapshots[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + "The --force option is not supported with the --remote parameter.", + str(exc), + ) + + def test_delete_multiple_snapshots_remote(self): + arglist = ['--remote'] + for s in self.snapshots: + arglist.append(s.id) + verifylist = [('remote', True), ('snapshots', arglist[1:])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.volume_sdk_client.unmanage_snapshot.assert_has_calls( + [mock.call(s.id) for s in self.snapshots] + ) + + +class TestVolumeSnapshotList(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.snapshots = list( + sdk_fakes.generate_fake_resources( + _snapshot.Snapshot, attrs={'volume_id': self.volume.name} + ) + ) + self.project = project_fakes.FakeProject.create_one_project() + self.volume_sdk_client.volumes.return_value = [self.volume] + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.snapshots.return_value = self.snapshots + self.project_mock = self.identity_client.projects + self.project_mock.get.return_value = self.project + + self.columns = ("ID", "Name", "Description", "Status", "Size") + self.columns_long = self.columns + ( + "Created At", + "Volume", + "Properties", + ) + + self.data = [] + self.data_long = [] + for s in self.snapshots: + self.data.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + ) + ) + self.data_long.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + s.created_at, + volume_snapshot.VolumeIdColumn( + s.volume_id, volume_cache={self.volume.id: self.volume} + ), + format_columns.DictColumn(s.metadata), + ) + ) + + self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None) + + def test_snapshot_list_without_options(self): + arglist = [] + verifylist = [('all_projects', False), ('long', False)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.snapshots.assert_called_once_with( + limit=None, + marker=None, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_with_options(self): + arglist = [ + "--long", + "--limit", + "2", + "--project", + self.project.id, + "--marker", + self.snapshots[0].id, + ] + verifylist = [ + ("long", True), + ("limit", 2), + ("project", self.project.id), + ("marker", self.snapshots[0].id), + ('all_projects', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.snapshots.assert_called_once_with( + limit=2, + marker=self.snapshots[0].id, + all_projects=True, + project_id=self.project.id, + name=None, + status=None, + volume_id=None, + ) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_snapshot_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [('long', False), ('all_projects', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.snapshots.assert_called_once_with( + limit=None, + marker=None, + all_projects=True, + name=None, + status=None, + project_id=None, + volume_id=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_name_option(self): + arglist = [ + '--name', + self.snapshots[0].name, + ] + verifylist = [ + ('all_projects', False), + ('long', False), + ('name', self.snapshots[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.snapshots.assert_called_once_with( + limit=None, + marker=None, + all_projects=False, + name=self.snapshots[0].name, + status=None, + project_id=None, + volume_id=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_status_option(self): + arglist = [ + '--status', + 'available', + ] + verifylist = [ + ('all_projects', False), + ('long', False), + ('status', 'available'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.snapshots.assert_called_once_with( + limit=None, + marker=None, + all_projects=False, + name=None, + status='available', + project_id=None, + volume_id=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_volumeid_option(self): + arglist = [ + '--volume', + self.volume.id, + ] + verifylist = [ + ('all_projects', False), + ('long', False), + ('volume', self.volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.snapshots.assert_called_once_with( + limit=None, + marker=None, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=self.volume.id, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_negative_limit(self): + arglist = [ + "--limit", + "-2", + ] + verifylist = [ + ("limit", -2), + ] + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + +class TestVolumeSnapshotSet(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, metadata={'foo': 'bar'} + ) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.set_snapshot_metadata.return_value = None + self.volume_sdk_client.update_snapshot.return_value = None + # Get the command object to mock + self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None) + + def test_snapshot_set_no_option(self): + arglist = [ + self.snapshot.id, + ] + verifylist = [ + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() + + def test_snapshot_set_name_and_property(self): + arglist = [ + "--name", + "new_snapshot", + "--property", + "x=y", + "--property", + "foo=foo", + self.snapshot.id, + ] + verifylist = [ + ("name", "new_snapshot"), + ("properties", {"x": "y", "foo": "foo"}), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.volume_sdk_client.update_snapshot.assert_called_with( + self.snapshot.id, name="new_snapshot" + ) + self.volume_sdk_client.set_snapshot_metadata.assert_called_with( + self.snapshot.id, x="y", foo="foo" + ) + + def test_snapshot_set_with_no_property(self): + arglist = [ + "--no-property", + self.snapshot.id, + ] + verifylist = [ + ("no_property", True), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) + + def test_snapshot_set_with_no_property_and_property(self): + arglist = [ + "--no-property", + "--property", + "foo_1=bar_1", + self.snapshot.id, + ] + verifylist = [ + ("no_property", True), + ("properties", {"foo_1": "bar_1"}), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) + self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with( + self.snapshot.id, + foo_1="bar_1", + ) + + def test_snapshot_set_state_to_error(self): + arglist = ["--state", "error", self.snapshot.id] + verifylist = [("state", "error"), ("snapshot", self.snapshot.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.volume_sdk_client.reset_snapshot_status.assert_called_with( + self.snapshot.id, "error" + ) + + def test_volume_set_state_failed(self): + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) + arglist = ['--state', 'error', self.snapshot.id] + verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( + self.snapshot.id, 'error' + ) + + def test_volume_set_name_and_state_failed(self): + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) + arglist = [ + '--state', + 'error', + "--name", + "new_snapshot", + self.snapshot.id, + ] + verifylist = [ + ('state', 'error'), + ("name", "new_snapshot"), + ('snapshot', self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.update_snapshot.assert_called_once_with( + self.snapshot.id, name="new_snapshot" + ) + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( + self.snapshot.id, 'error' + ) + + +class TestVolumeSnapshotShow(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + + self.columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) + self.data = ( + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + format_columns.DictColumn(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, + ) + + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + + self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None) + + def test_snapshot_show(self): + arglist = [self.snapshot.id] + verifylist = [("snapshot", self.snapshot.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_sdk_client.find_snapshot.assert_called_with( + self.snapshot.id, ignore_missing=False + ) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + +class TestVolumeSnapshotUnset(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.delete_snapshot_metadata.return_value = None + + self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None) + + def test_snapshot_unset(self): + arglist = [ + "--property", + "foo", + self.snapshot.id, + ] + verifylist = [ + ("properties", ["foo"]), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py index 1871bde73d..ffe59db659 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py @@ -284,7 +284,7 @@ def test_delete_multiple_transfers_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 volume transfer requests failed ' 'to delete', + '1 of 2 volume transfer requests failed to delete', str(e), ) diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index 4615184e70..dbef055fac 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -20,16 +20,14 @@ from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '3' API_VERSION_OPTION = 'os_volume_api_version' -API_NAME = "volume" +API_NAME = 'volume' API_VERSIONS = { - "1": "cinderclient.v1.client.Client", - "2": "cinderclient.v2.client.Client", - "3": "cinderclient.v3.client.Client", + '2': 'cinderclient.v2.client.Client', + '3': 'cinderclient.v3.client.Client', } # Save the microversion if in use @@ -45,11 +43,6 @@ def make_client(instance): from cinderclient.v3 import volume_snapshots from cinderclient.v3 import volumes - # Check whether the available cinderclient supports v1 or v2 - try: - from cinderclient.v1 import services # noqa - except Exception: - del API_VERSIONS['1'] try: from cinderclient.v2 import services # noqa except Exception: @@ -93,7 +86,7 @@ def make_client(instance): region_name=instance.region_name, endpoint_override=endpoint_override, api_version=version, - **kwargs + **kwargs, ) return client @@ -105,9 +98,7 @@ def build_option_parser(parser): '--os-volume-api-version', metavar='', default=utils.env('OS_VOLUME_API_VERSION'), - help=_( - 'Volume API version, default=%s ' '(Env: OS_VOLUME_API_VERSION)' - ) + help=_('Volume API version, default=%s (Env: OS_VOLUME_API_VERSION)') % DEFAULT_API_VERSION, ) return parser @@ -129,21 +120,18 @@ def check_api_version(check_version): global _volume_api_version - # Copy some logic from novaclient 3.3.0 for basic version detection - # NOTE(dtroyer): This is only enough to resume operations using API - # version 3.0 or any valid version supplied by the user. _volume_api_version = api_versions.get_api_version(check_version) # Bypass X.latest format microversion if not _volume_api_version.is_latest(): - if _volume_api_version > api_versions.APIVersion("3.0"): + if _volume_api_version > api_versions.APIVersion('3.0'): if not _volume_api_version.matches( api_versions.MIN_VERSION, api_versions.MAX_VERSION, ): - msg = _("versions supported by client: %(min)s - %(max)s") % { - "min": api_versions.MIN_VERSION, - "max": api_versions.MAX_VERSION, + msg = _('versions supported by client: %(min)s - %(max)s') % { + 'min': api_versions.MIN_VERSION, + 'max': api_versions.MAX_VERSION, } raise exceptions.CommandError(msg) diff --git a/openstackclient/volume/v1/__init__.py b/openstackclient/volume/v1/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py deleted file mode 100644 index 74938392e4..0000000000 --- a/openstackclient/volume/v1/qos_specs.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright 2015 iWeb Technologies Inc. -# -# 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 v1 QoS action implementations""" - -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AssociateQos(command.Command): - _description = _("Associate a QoS specification to a volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to associate the QoS (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - - volume_client.qos_specs.associate(qos_spec.id, volume_type.id) - - -class CreateQos(command.ShowOne): - _description = _("Create new QoS specification") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('New QoS specification name'), - ) - consumer_choices = ['front-end', 'back-end', 'both'] - parser.add_argument( - '--consumer', - metavar='', - choices=consumer_choices, - default='both', - help=( - _( - 'Consumer of the QoS. Valid consumers: %s ' - "(defaults to 'both')" - ) - % utils.format_list(consumer_choices) - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a QoS specification property ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - specs = {} - specs.update({'consumer': parsed_args.consumer}) - - if parsed_args.property: - specs.update(parsed_args.property) - - qos_spec = volume_client.qos_specs.create(parsed_args.name, specs) - qos_spec._info.update( - { - 'properties': format_columns.DictColumn( - qos_spec._info.pop('specs') - ) - } - ) - return zip(*sorted(qos_spec._info.items())) - - -class DeleteQos(command.Command): - _description = _("Delete QoS specification") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_specs', - metavar='', - nargs="+", - help=_('QoS specification(s) to delete (name or ID)'), - ) - parser.add_argument( - '--force', - action='store_true', - default=False, - help=_("Allow to delete in-use QoS specification(s)"), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.qos_specs: - try: - qos_spec = utils.find_resource(volume_client.qos_specs, i) - volume_client.qos_specs.delete(qos_spec.id, parsed_args.force) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete QoS specification with " - "name or ID '%(qos)s': %(e)s" - ), - {'qos': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.qos_specs) - msg = _( - "%(result)s of %(total)s QoS specifications failed" - " to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class DisassociateQos(command.Command): - _description = _("Disassociate a QoS specification from a volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - volume_type_group = parser.add_mutually_exclusive_group() - volume_type_group.add_argument( - '--volume-type', - metavar='', - help=_('Volume type to disassociate the QoS from (name or ID)'), - ) - volume_type_group.add_argument( - '--all', - action='store_true', - default=False, - help=_('Disassociate the QoS from every volume type'), - ) - - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - if parsed_args.volume_type: - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - volume_client.qos_specs.disassociate(qos_spec.id, volume_type.id) - elif parsed_args.all: - volume_client.qos_specs.disassociate_all(qos_spec.id) - - -class ListQos(command.Lister): - _description = _("List QoS specifications") - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_specs_list = volume_client.qos_specs.list() - - for qos in qos_specs_list: - try: - qos_associations = volume_client.qos_specs.get_associations( - qos, - ) - if qos_associations: - associations = [ - association.name for association in qos_associations - ] - qos._info.update({'associations': associations}) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - qos._info.update({'associations': None}) - else: - raise - - display_columns = ( - 'ID', - 'Name', - 'Consumer', - 'Associations', - 'Properties', - ) - columns = ('ID', 'Name', 'Consumer', 'Associations', 'Specs') - return ( - display_columns, - ( - utils.get_dict_properties( - s._info, - columns, - formatters={ - 'Specs': format_columns.DictColumn, - 'Associations': format_columns.ListColumn, - }, - ) - for s in qos_specs_list - ), - ) - - -class SetQos(command.Command): - _description = _("Set QoS specification properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - '--no-property', - dest='no_property', - action='store_true', - help=_( - 'Remove all properties from ' - '(specify both --no-property and --property to remove the ' - 'current properties before setting new properties)' - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Property to add or modify for this QoS specification ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - result = 0 - if parsed_args.no_property: - try: - key_list = list(qos_spec._info['specs'].keys()) - volume_client.qos_specs.unset_keys(qos_spec.id, key_list) - except Exception as e: - LOG.error(_("Failed to clean qos properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.qos_specs.set_keys( - qos_spec.id, - parsed_args.property, - ) - except Exception as e: - LOG.error(_("Failed to set qos property: %s"), e) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowQos(command.ShowOne): - _description = _("Display QoS specification details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - qos_associations = volume_client.qos_specs.get_associations(qos_spec) - if qos_associations: - associations = [ - association.name for association in qos_associations - ] - qos_spec._info.update( - {'associations': format_columns.ListColumn(associations)} - ) - qos_spec._info.update( - { - 'properties': format_columns.DictColumn( - qos_spec._info.pop('specs') - ) - } - ) - - return zip(*sorted(qos_spec._info.items())) - - -class UnsetQos(command.Command): - _description = _("Unset QoS specification properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Property to remove from the QoS specification. ' - '(repeat option to unset multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - if parsed_args.property: - volume_client.qos_specs.unset_keys( - qos_spec.id, parsed_args.property - ) diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py deleted file mode 100644 index 2a33fc0b9a..0000000000 --- a/openstackclient/volume/v1/service.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Service action implementations""" - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -class ListService(command.Lister): - _description = _("List service command") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - "--host", - metavar="", - help=_("List services on specified host (name only)"), - ) - parser.add_argument( - "--service", - metavar="", - help=_("List only specified service (name only)"), - ) - parser.add_argument( - "--long", - action="store_true", - default=False, - help=_("List additional fields in output"), - ) - return parser - - def take_action(self, parsed_args): - service_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] - - data = service_client.services.list( - parsed_args.host, parsed_args.service - ) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) - - -class SetService(command.Command): - _description = _("Set volume service properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument("host", metavar="", help=_("Name of host")) - parser.add_argument( - "service", - metavar="", - help=_("Name of service (Binary name)"), - ) - enabled_group = parser.add_mutually_exclusive_group() - enabled_group.add_argument( - "--enable", action="store_true", help=_("Enable volume service") - ) - enabled_group.add_argument( - "--disable", action="store_true", help=_("Disable volume service") - ) - parser.add_argument( - "--disable-reason", - metavar="", - help=_( - "Reason for disabling the service " - "(should be used with --disable option)" - ), - ) - return parser - - def take_action(self, parsed_args): - if parsed_args.disable_reason and not parsed_args.disable: - msg = _( - "Cannot specify option --disable-reason without " - "--disable specified." - ) - raise exceptions.CommandError(msg) - - service_client = self.app.client_manager.volume - if parsed_args.enable: - service_client.services.enable( - parsed_args.host, parsed_args.service - ) - if parsed_args.disable: - if parsed_args.disable_reason: - service_client.services.disable_log_reason( - parsed_args.host, - parsed_args.service, - parsed_args.disable_reason, - ) - else: - service_client.services.disable( - parsed_args.host, parsed_args.service - ) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py deleted file mode 100644 index c60625ee91..0000000000 --- a/openstackclient/volume/v1/volume.py +++ /dev/null @@ -1,734 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 Volume action implementations""" - -import argparse -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.common import pagination -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AttachmentsColumn(cliff_columns.FormattableColumn): - """Formattable column for attachments column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes server_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(AttachmentsColumn, server_cache)``. - """ - - def __init__(self, value, server_cache=None): - super().__init__(value) - self._server_cache = server_cache or {} - - def human_readable(self): - """Return a formatted string of a volume's attached instances - - :rtype: a string of formatted instances - """ - - msg = '' - for attachment in self._value: - server = attachment['server_id'] - if server in self._server_cache.keys(): - server = self._server_cache[server].name - device = attachment['device'] - msg += f'Attached to {server} on {device} ' - return msg - - -def _check_size_arg(args): - """Check whether --size option is required or not. - - Require size parameter only in case when snapshot or source - volume is not specified. - """ - - if (args.snapshot or args.source) is None and args.size is None: - msg = _( - "--size is a required option if snapshot " - "or source volume is not specified." - ) - raise exceptions.CommandError(msg) - - -class CreateVolume(command.ShowOne): - _description = _("Create new volume") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('Volume name'), - ) - parser.add_argument( - '--size', - metavar='', - type=int, - help=_( - "Volume size in GB (Required unless --snapshot or " - "--source is specified)" - ), - ) - parser.add_argument( - '--type', - metavar='', - help=_("Set the type of volume"), - ) - source_group = parser.add_mutually_exclusive_group() - source_group.add_argument( - '--image', - metavar='', - help=_('Use as source of volume (name or ID)'), - ) - source_group.add_argument( - '--snapshot', - metavar='', - help=_('Use as source of volume (name or ID)'), - ) - source_group.add_argument( - '--snapshot-id', - metavar='', - help=argparse.SUPPRESS, - ) - source_group.add_argument( - '--source', - metavar='', - help=_('Volume to clone (name or ID)'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Volume description'), - ) - parser.add_argument( - '--user', - metavar='', - help=_('Specify an alternate user (name or ID)'), - ) - parser.add_argument( - '--project', - metavar='', - help=_('Specify an alternate project (name or ID)'), - ) - parser.add_argument( - '--availability-zone', - metavar='', - help=_('Create volume in '), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume ' - '(repeat option to set multiple properties)' - ), - ) - bootable_group = parser.add_mutually_exclusive_group() - bootable_group.add_argument( - "--bootable", - action="store_true", - help=_("Mark volume as bootable"), - ) - bootable_group.add_argument( - "--non-bootable", - action="store_true", - help=_("Mark volume as non-bootable (default)"), - ) - readonly_group = parser.add_mutually_exclusive_group() - readonly_group.add_argument( - "--read-only", - action="store_true", - help=_("Set volume to read-only access mode"), - ) - readonly_group.add_argument( - "--read-write", - action="store_true", - help=_("Set volume to read-write access mode (default)"), - ) - - return parser - - def take_action(self, parsed_args): - _check_size_arg(parsed_args) - identity_client = self.app.client_manager.identity - image_client = self.app.client_manager.image - volume_client = self.app.client_manager.volume - - source_volume = None - if parsed_args.source: - source_volume = utils.find_resource( - volume_client.volumes, - parsed_args.source, - ).id - - project = None - if parsed_args.project: - project = utils.find_resource( - identity_client.tenants, - parsed_args.project, - ).id - - user = None - if parsed_args.user: - user = utils.find_resource( - identity_client.users, - parsed_args.user, - ).id - - image = None - if parsed_args.image: - image = image_client.find_image( - parsed_args.image, - ignore_missing=False, - ).id - - snapshot = parsed_args.snapshot or parsed_args.snapshot_id - - volume = volume_client.volumes.create( - parsed_args.size, - snapshot, - source_volume, - parsed_args.name, - parsed_args.description, - parsed_args.type, - user, - project, - parsed_args.availability_zone, - parsed_args.property, - image, - ) - - if parsed_args.bootable or parsed_args.non_bootable: - try: - if utils.wait_for_status( - volume_client.volumes.get, - volume.id, - success_status=['available'], - error_status=['error'], - sleep_time=1, - ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable - ) - else: - msg = _( - "Volume status is not available for setting boot " - "state" - ) - raise exceptions.CommandError(msg) - except Exception as e: - LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: - try: - if utils.wait_for_status( - volume_client.volumes.get, - volume.id, - success_status=['available'], - error_status=['error'], - sleep_time=1, - ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only - ) - else: - msg = _( - "Volume status is not available for setting it" - "read only." - ) - raise exceptions.CommandError(msg) - except Exception as e: - LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), - e, - ) - - # Map 'metadata' column to 'properties' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - # Replace "display_name" by "name", keep consistent in v1 and v2 - if 'display_name' in volume._info: - volume._info.update({'name': volume._info.pop('display_name')}) - volume_info = utils.backward_compat_col_showone( - volume._info, parsed_args.columns, {'display_name': 'name'} - ) - - return zip(*sorted(volume_info.items())) - - -class DeleteVolume(command.Command): - _description = _("Delete volume(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volumes', - metavar='', - nargs="+", - help=_('Volume(s) to delete (name or ID)'), - ) - parser.add_argument( - '--force', - action='store_true', - default=False, - help=_( - 'Attempt forced removal of volume(s), regardless of state ' - '(defaults to False)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.volumes: - try: - volume_obj = utils.find_resource(volume_client.volumes, i) - if parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) - else: - volume_client.volumes.delete(volume_obj.id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume with " - "name or ID '%(volume)s': %(e)s" - ), - {'volume': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed " "to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolume(command.Lister): - _description = _("List volumes") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--name', - metavar='', - help=_('Filter results by volume name'), - ) - parser.add_argument( - '--status', - metavar='', - help=_('Filter results by status'), - ) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - pagination.add_offset_pagination_option_to_parser(parser) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = ( - 'ID', - 'Display Name', - 'Status', - 'Size', - 'Volume Type', - 'Bootable', - 'Attachments', - 'Metadata', - ) - column_headers = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Type', - 'Bootable', - 'Attached to', - 'Properties', - ) - else: - columns = ( - 'ID', - 'Display Name', - 'Status', - 'Size', - 'Attachments', - ) - column_headers = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Attached to', - ) - - # Cache the server list - server_cache = {} - try: - compute_client = self.app.client_manager.sdk_connection.compute - for s in compute_client.servers(): - server_cache[s.id] = s - except Exception: - # Just forget it if there's any trouble - pass # nosec: B110 - AttachmentsColumnWithCache = functools.partial( - AttachmentsColumn, server_cache=server_cache - ) - - search_opts = { - 'all_tenants': parsed_args.all_projects, - 'display_name': parsed_args.name, - 'status': parsed_args.status, - } - - if parsed_args.offset: - search_opts['offset'] = parsed_args.offset - - data = volume_client.volumes.list( - search_opts=search_opts, - limit=parsed_args.limit, - ) - column_headers = utils.backward_compat_col_lister( - column_headers, parsed_args.columns, {'Display Name': 'Name'} - ) - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={ - 'Metadata': format_columns.DictColumn, - 'Attachments': AttachmentsColumnWithCache, - }, - ) - for s in data - ), - ) - - -class MigrateVolume(command.Command): - _description = _("Migrate volume to a new host") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar="", - help=_("Volume to migrate (name or ID)"), - ) - parser.add_argument( - '--host', - metavar="", - required=True, - help=_( - "Destination host (takes the form: host@backend-name#pool)" - ), - ) - parser.add_argument( - '--force-host-copy', - action="store_true", - help=_( - "Enable generic host-based force-migration, " - "which bypasses driver optimizations" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - volume_client.volumes.migrate_volume( - volume.id, - parsed_args.host, - parsed_args.force_host_copy, - ) - - -class SetVolume(command.Command): - _description = _("Set volume properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to modify (name or ID)'), - ) - parser.add_argument( - '--name', - metavar='', - help=_('New volume name'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('New volume description'), - ) - parser.add_argument( - '--size', - metavar='', - type=int, - help=_('Extend volume size in GB'), - ) - parser.add_argument( - "--no-property", - dest="no_property", - action="store_true", - help=_( - "Remove all properties from " - "(specify both --no-property and --property to " - "remove the current properties before setting " - "new properties.)" - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume ' - '(repeat option to set multiple properties)' - ), - ) - bootable_group = parser.add_mutually_exclusive_group() - bootable_group.add_argument( - "--bootable", - action="store_true", - help=_("Mark volume as bootable"), - ) - bootable_group.add_argument( - "--non-bootable", - action="store_true", - help=_("Mark volume as non-bootable"), - ) - readonly_group = parser.add_mutually_exclusive_group() - readonly_group.add_argument( - "--read-only", - action="store_true", - help=_("Set volume to read-only access mode"), - ) - readonly_group.add_argument( - "--read-write", - action="store_true", - help=_("Set volume to read-write access mode"), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - - result = 0 - if parsed_args.size: - try: - if volume.status != 'available': - msg = ( - _( - "Volume is in %s state, it must be available " - "before size can be extended" - ) - % volume.status - ) - raise exceptions.CommandError(msg) - if parsed_args.size <= volume.size: - msg = ( - _("New size must be greater than %s GB") % volume.size - ) - raise exceptions.CommandError(msg) - volume_client.volumes.extend(volume.id, parsed_args.size) - except Exception as e: - LOG.error(_("Failed to set volume size: %s"), e) - result += 1 - - if parsed_args.no_property: - try: - volume_client.volumes.delete_metadata( - volume.id, volume.metadata.keys() - ) - except Exception as e: - LOG.error(_("Failed to clean volume properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.volumes.set_metadata( - volume.id, parsed_args.property - ) - except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) - result += 1 - if parsed_args.bootable or parsed_args.non_bootable: - try: - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable - ) - except Exception as e: - LOG.error(_("Failed to set volume bootable property: %s"), e) - result += 1 - if parsed_args.read_only or parsed_args.read_write: - try: - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only - ) - except Exception as e: - LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), - e, - ) - result += 1 - kwargs = {} - if parsed_args.name: - kwargs['display_name'] = parsed_args.name - if parsed_args.description: - kwargs['display_description'] = parsed_args.description - if kwargs: - try: - volume_client.volumes.update(volume.id, **kwargs) - except Exception as e: - LOG.error( - _( - "Failed to update volume display name " - "or display description: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the " "set operations failed") - ) - - -class ShowVolume(command.ShowOne): - _description = _("Show volume details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - # Map 'metadata' column to 'properties' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - if 'os-vol-tenant-attr:tenant_id' in volume._info: - volume._info.update( - { - 'project_id': volume._info.pop( - 'os-vol-tenant-attr:tenant_id' - ) - } - ) - # Replace "display_name" by "name", keep consistent in v1 and v2 - if 'display_name' in volume._info: - volume._info.update({'name': volume._info.pop('display_name')}) - - volume_info = utils.backward_compat_col_showone( - volume._info, parsed_args.columns, {'display_name': 'name'} - ) - - return zip(*sorted(volume_info.items())) - - -class UnsetVolume(command.Command): - _description = _("Unset volume properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Remove a property from volume ' - '(repeat option to remove multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - - if parsed_args.property: - volume_client.volumes.delete_metadata( - volume.id, - parsed_args.property, - ) diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py deleted file mode 100644 index 99d5838099..0000000000 --- a/openstackclient/volume/v1/volume_backup.py +++ /dev/null @@ -1,302 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 Backup action implementations""" - -import copy -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class VolumeIdColumn(cliff_columns.FormattableColumn): - """Formattable column for volume ID column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes volume_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(VolumeIdColumn, volume_cache)``. - """ - - def __init__(self, value, volume_cache=None): - super().__init__(value) - self._volume_cache = volume_cache or {} - - def human_readable(self): - """Return a volume name if available - - :rtype: either the volume ID or name - """ - volume_id = self._value - volume = volume_id - if volume_id in self._volume_cache.keys(): - volume = self._volume_cache[volume_id].display_name - return volume - - -class CreateVolumeBackup(command.ShowOne): - _description = _("Create new volume backup") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to backup (name or ID)'), - ) - parser.add_argument( - '--container', - metavar='', - required=False, - help=_('Optional backup container name'), - ) - parser.add_argument( - '--name', - metavar='', - help=_('Name of the backup'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the backup'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - backup = volume_client.backups.create( - volume_id, - parsed_args.container, - parsed_args.name, - parsed_args.description, - ) - - backup._info.pop('links') - return zip(*sorted(backup._info.items())) - - -class DeleteVolumeBackup(command.Command): - _description = _("Delete volume backup(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backups', - metavar='', - nargs="+", - help=_('Backup(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.backups: - try: - backup_id = utils.find_resource(volume_client.backups, i).id - volume_client.backups.delete(backup_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete backup with " - "name or ID '%(backup)s': %(e)s" - ), - {'backup': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.backups) - msg = _("%(result)s of %(total)s backups failed " "to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolumeBackup(command.Lister): - _description = _("List volume backups") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - "--name", - metavar="", - help=_("Filters results by the backup name"), - ) - parser.add_argument( - "--status", - metavar="", - choices=[ - 'creating', - 'available', - 'deleting', - 'error', - 'restoring', - 'error_restoring', - ], - help=_( - "Filters results by the backup status " - "('creating', 'available', 'deleting', " - "'error', 'restoring' or 'error_restoring')" - ), - ) - parser.add_argument( - "--volume", - metavar="", - help=_( - "Filters results by the volume which they " - "backup (name or ID)" - ), - ) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', - 'Availability Zone', - 'Volume ID', - 'Container', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - else: - columns = ['ID', 'Name', 'Description', 'Status', 'Size'] - column_headers = columns - - # Cache the volume list - volume_cache = {} - try: - for s in volume_client.volumes.list(): - volume_cache[s.id] = s - except Exception: - # Just forget it if there's any trouble - pass # nosec: B110 - VolumeIdColumnWithCache = functools.partial( - VolumeIdColumn, volume_cache=volume_cache - ) - - filter_volume_id = None - if parsed_args.volume: - filter_volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - search_opts = { - 'name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': filter_volume_id, - 'all_tenants': parsed_args.all_projects, - } - data = volume_client.backups.list( - search_opts=search_opts, - ) - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={'Volume ID': VolumeIdColumnWithCache}, - ) - for s in data - ), - ) - - -class RestoreVolumeBackup(command.Command): - _description = _("Restore volume backup") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to restore (name or ID)'), - ) - parser.add_argument( - 'volume', - metavar='', - 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, - ) - 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): - _description = _("Display volume backup details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to display (name or ID)'), - ) - 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) - backup._info.pop('links') - return zip(*sorted(backup._info.items())) diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py deleted file mode 100644 index 18d8c3d621..0000000000 --- a/openstackclient/volume/v1/volume_snapshot.py +++ /dev/null @@ -1,433 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 Snapshot action implementations""" - -import copy -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class VolumeIdColumn(cliff_columns.FormattableColumn): - """Formattable column for volume ID column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes volume_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(VolumeIdColumn, volume_cache)``. - """ - - def __init__(self, value, volume_cache=None): - super().__init__(value) - self._volume_cache = volume_cache or {} - - def human_readable(self): - """Return a volume name if available - - :rtype: either the volume ID or name - """ - volume_id = self._value - volume = volume_id - if volume_id in self._volume_cache.keys(): - volume = self._volume_cache[volume_id].display_name - return volume - - -class CreateVolumeSnapshot(command.ShowOne): - _description = _("Create new volume snapshot") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot_name', - metavar='', - help=_('Name of the new snapshot'), - ) - parser.add_argument( - '--volume', - metavar='', - help=_( - 'Volume to snapshot (name or ID) ' - '(default is )' - ), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the snapshot'), - ) - parser.add_argument( - '--force', - dest='force', - action='store_true', - default=False, - help=_( - 'Create a snapshot attached to an instance. ' - 'Default is False' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = parsed_args.volume - if not parsed_args.volume: - volume = parsed_args.snapshot_name - volume_id = utils.find_resource(volume_client.volumes, volume).id - snapshot = volume_client.volume_snapshots.create( - volume_id, - parsed_args.force, - parsed_args.snapshot_name, - parsed_args.description, - ) - - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - - return zip(*sorted(snapshot._info.items())) - - -class DeleteVolumeSnapshot(command.Command): - _description = _("Delete volume snapshot(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshots', - metavar='', - nargs="+", - help=_('Snapshot(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.snapshots: - try: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, i - ).id - volume_client.volume_snapshots.delete(snapshot_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete snapshot with " - "name or ID '%(snapshot)s': %(e)s" - ), - {'snapshot': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.snapshots) - msg = _( - "%(result)s of %(total)s snapshots failed " "to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class ListVolumeSnapshot(command.Lister): - _description = _("List volume snapshots") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - '--name', - metavar='', - default=None, - help=_('Filters results by a name.'), - ) - parser.add_argument( - '--status', - metavar='', - choices=[ - 'available', - 'error', - 'creating', - 'deleting', - 'error_deleting', - ], - help=_( - "Filters results by a status. " - "('available', 'error', 'creating', 'deleting'" - " or 'error_deleting')" - ), - ) - parser.add_argument( - '--volume', - metavar='', - default=None, - help=_('Filters results by a volume (name or ID).'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - 'ID', - 'Display Name', - 'Display Description', - 'Status', - 'Size', - 'Created At', - 'Volume ID', - 'Metadata', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - column_headers[7] = 'Properties' - else: - columns = [ - 'ID', - 'Display Name', - 'Display Description', - 'Status', - 'Size', - ] - column_headers = copy.deepcopy(columns) - - # Always update Name and Description - column_headers[1] = 'Name' - column_headers[2] = 'Description' - - # Cache the volume list - volume_cache = {} - try: - for s in volume_client.volumes.list(): - volume_cache[s.id] = s - except Exception: - # Just forget it if there's any trouble - pass # nosec: B110 - VolumeIdColumnWithCache = functools.partial( - VolumeIdColumn, volume_cache=volume_cache - ) - - volume_id = None - if parsed_args.volume: - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - - search_opts = { - 'all_tenants': parsed_args.all_projects, - 'display_name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': volume_id, - } - - data = volume_client.volume_snapshots.list(search_opts=search_opts) - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={ - 'Metadata': format_columns.DictColumn, - 'Volume ID': VolumeIdColumnWithCache, - }, - ) - for s in data - ), - ) - - -class SetVolumeSnapshot(command.Command): - _description = _("Set volume snapshot properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to modify (name or ID)'), - ) - parser.add_argument( - '--name', metavar='', help=_('New snapshot name') - ) - parser.add_argument( - '--description', - metavar='', - help=_('New snapshot description'), - ) - parser.add_argument( - "--no-property", - dest="no_property", - action="store_true", - help=_( - "Remove all properties from " - "(specify both --no-property and --property to " - "remove the current properties before setting " - "new properties.)" - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Property to add/change for this snapshot ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - result = 0 - if parsed_args.no_property: - try: - key_list = snapshot.metadata.keys() - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - list(key_list), - ) - except Exception as e: - LOG.error(_("Failed to clean snapshot properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.volume_snapshots.set_metadata( - snapshot.id, parsed_args.property - ) - except Exception as e: - LOG.error(_("Failed to set snapshot property: %s"), e) - result += 1 - - kwargs = {} - if parsed_args.name: - kwargs['display_name'] = parsed_args.name - if parsed_args.description: - kwargs['display_description'] = parsed_args.description - if kwargs: - try: - snapshot.update(**kwargs) - except Exception as e: - LOG.error( - _( - "Failed to update snapshot display name " - "or display description: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the " "set operations failed") - ) - - -class ShowVolumeSnapshot(command.ShowOne): - _description = _("Display volume snapshot details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - - return zip(*sorted(snapshot._info.items())) - - -class UnsetVolumeSnapshot(command.Command): - _description = _("Unset volume snapshot properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Property to remove from snapshot ' - '(repeat option to remove multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - if parsed_args.property: - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - parsed_args.property, - ) diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py deleted file mode 100644 index d82584dc79..0000000000 --- a/openstackclient/volume/v1/volume_transfer_request.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 transfer action implementations""" - -import logging - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AcceptTransferRequest(command.ShowOne): - _description = _("Accept volume transfer request.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - help=_('Volume transfer request to accept (ID only)'), - ) - parser.add_argument( - '--auth-key', - metavar="", - help=_('Volume transfer request authentication key'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - try: - transfer_request_id = utils.find_resource( - volume_client.transfers, parsed_args.transfer_request - ).id - except exceptions.CommandError: - # Non-admin users will fail to lookup name -> ID so we just - # move on and attempt with the user-supplied information - transfer_request_id = parsed_args.transfer_request - - if not parsed_args.auth_key: - msg = _("argument --auth-key is required") - raise exceptions.CommandError(msg) - - transfer_accept = volume_client.transfers.accept( - transfer_request_id, - parsed_args.auth_key, - ) - transfer_accept._info.pop("links", None) - - return zip(*sorted(transfer_accept._info.items())) - - -class CreateTransferRequest(command.ShowOne): - _description = _("Create volume transfer request.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--name', - metavar="", - help=_('New transfer request name (default to None)'), - ) - parser.add_argument( - 'volume', - metavar="", - help=_('Volume to transfer (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_id = utils.find_resource( - volume_client.volumes, - parsed_args.volume, - ).id - volume_transfer_request = volume_client.transfers.create( - volume_id, - parsed_args.name, - ) - volume_transfer_request._info.pop("links", None) - - return zip(*sorted(volume_transfer_request._info.items())) - - -class DeleteTransferRequest(command.Command): - _description = _("Delete volume transfer request(s).") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - nargs="+", - help=_('Volume transfer request(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for t in parsed_args.transfer_request: - try: - transfer_request_id = utils.find_resource( - volume_client.transfers, - t, - ).id - volume_client.transfers.delete(transfer_request_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume transfer request " - "with name or ID '%(transfer)s': %(e)s" - ) - % {'transfer': t, 'e': e} - ) - - if result > 0: - total = len(parsed_args.transfer_request) - msg = _( - "%(result)s of %(total)s volume transfer requests failed" - " to delete" - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class ListTransferRequest(command.Lister): - _description = _("Lists all volume transfer requests.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--all-projects', - dest='all_projects', - action="store_true", - default=False, - help=_('Include all projects (admin only)'), - ) - return parser - - def take_action(self, parsed_args): - columns = ['ID', 'Name', 'Volume ID'] - column_headers = ['ID', 'Name', 'Volume'] - - volume_client = self.app.client_manager.volume - - volume_transfer_result = volume_client.transfers.list( - detailed=True, - search_opts={'all_tenants': parsed_args.all_projects}, - ) - - return ( - column_headers, - ( - utils.get_item_properties(s, columns) - for s in volume_transfer_result - ), - ) - - -class ShowTransferRequest(command.ShowOne): - _description = _("Show volume transfer request details.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - help=_('Volume transfer request to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_transfer_request = utils.find_resource( - volume_client.transfers, - parsed_args.transfer_request, - ) - volume_transfer_request._info.pop("links", None) - - return zip(*sorted(volume_transfer_request._info.items())) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py deleted file mode 100644 index fed0a601fa..0000000000 --- a/openstackclient/volume/v1/volume_type.py +++ /dev/null @@ -1,520 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Volume v1 Type action implementations""" - -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class EncryptionInfoColumn(cliff_columns.FormattableColumn): - """Formattable column for encryption info column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes encryption_data as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(EncryptionInfoColumn encryption_data)``. - """ - - def __init__(self, value, encryption_data=None): - super().__init__(value) - self._encryption_data = encryption_data or {} - - def _get_encryption_info(self): - type_id = self._value - return self._encryption_data.get(type_id) - - def human_readable(self): - encryption_info = self._get_encryption_info() - if encryption_info: - return utils.format_dict(encryption_info) - else: - return '-' - - def machine_readable(self): - return self._get_encryption_info() - - -def _create_encryption_type(volume_client, volume_type, parsed_args): - if not parsed_args.encryption_provider: - msg = _( - "'--encryption-provider' should be specified while " - "creating a new encryption type" - ) - raise exceptions.CommandError(msg) - # set the default of control location while creating - control_location = 'front-end' - if parsed_args.encryption_control_location: - control_location = parsed_args.encryption_control_location - body = { - 'provider': parsed_args.encryption_provider, - 'cipher': parsed_args.encryption_cipher, - 'key_size': parsed_args.encryption_key_size, - 'control_location': control_location, - } - encryption = volume_client.volume_encryption_types.create( - volume_type, body - ) - return encryption - - -class CreateVolumeType(command.ShowOne): - _description = _("Create new volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('Volume type name'), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume type ' - '(repeat option to set multiple properties)' - ), - ) - # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. - parser.add_argument( - '--encryption-provider', - metavar='', - help=_( - 'Set the encryption provider format for ' - 'this volume type (e.g "luks" or "plain") (admin only) ' - '(This option is required when setting encryption type ' - 'of a volume. Consider using other encryption options ' - 'such as: "--encryption-cipher", "--encryption-key-size" ' - 'and "--encryption-control-location")' - ), - ) - parser.add_argument( - '--encryption-cipher', - metavar='', - help=_( - 'Set the encryption algorithm or mode for this ' - 'volume type (e.g "aes-xts-plain64") (admin only)' - ), - ) - parser.add_argument( - '--encryption-key-size', - metavar='', - type=int, - help=_( - 'Set the size of the encryption key of this ' - 'volume type (e.g "128" or "256") (admin only)' - ), - ) - parser.add_argument( - '--encryption-control-location', - metavar='', - choices=['front-end', 'back-end'], - help=_( - 'Set the notional service where the encryption is ' - 'performed ("front-end" or "back-end") (admin only) ' - '(The default value for this option is "front-end" ' - 'when setting encryption type of a volume. Consider ' - 'using other encryption options such as: ' - '"--encryption-cipher", "--encryption-key-size" and ' - '"--encryption-provider")' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = volume_client.volume_types.create(parsed_args.name) - volume_type._info.pop('extra_specs') - if parsed_args.property: - result = volume_type.set_keys(parsed_args.property) - volume_type._info.update( - {'properties': format_columns.DictColumn(result)} - ) - if ( - parsed_args.encryption_provider - or parsed_args.encryption_cipher - or parsed_args.encryption_key_size - or parsed_args.encryption_control_location - ): - try: - # create new encryption - encryption = _create_encryption_type( - volume_client, volume_type, parsed_args - ) - except Exception as e: - LOG.error( - _( - "Failed to set encryption information for this " - "volume type: %s" - ), - e, - ) - # add encryption info in result - encryption._info.pop("volume_type_id", None) - volume_type._info.update( - {'encryption': format_columns.DictColumn(encryption._info)} - ) - volume_type._info.pop("os-volume-type-access:is_public", None) - - return zip(*sorted(volume_type._info.items())) - - -class DeleteVolumeType(command.Command): - _description = _("Delete volume type(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_types', - metavar='', - nargs='+', - help=_('Volume type(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for volume_type in parsed_args.volume_types: - try: - vol_type = utils.find_resource( - volume_client.volume_types, volume_type - ) - - volume_client.volume_types.delete(vol_type) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume type with " - "name or ID '%(volume_type)s': %(e)s" - ) - % {'volume_type': volume_type, 'e': e} - ) - - if result > 0: - total = len(parsed_args.volume_types) - msg = _( - "%(result)s of %(total)s volume types failed " "to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class ListVolumeType(command.Lister): - _description = _("List volume types") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Display encryption information for each volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - if parsed_args.long: - columns = ['ID', 'Name', 'Is Public', 'Extra Specs'] - column_headers = ['ID', 'Name', 'Is Public', 'Properties'] - else: - columns = ['ID', 'Name', 'Is Public'] - column_headers = ['ID', 'Name', 'Is Public'] - data = volume_client.volume_types.list() - - formatters = {'Extra Specs': format_columns.DictColumn} - - if parsed_args.encryption_type: - encryption = {} - for d in volume_client.volume_encryption_types.list(): - volume_type_id = d._info['volume_type_id'] - # remove some redundant information - del_key = [ - 'deleted', - 'created_at', - 'updated_at', - 'deleted_at', - 'volume_type_id', - ] - for key in del_key: - d._info.pop(key, None) - # save the encryption information with their volume type ID - encryption[volume_type_id] = d._info - # We need to get volume type ID, then show encryption - # information according to the ID, so use "id" to keep - # difference to the real "ID" column. - columns += ['id'] - column_headers += ['Encryption'] - - _EncryptionInfoColumn = functools.partial( - EncryptionInfoColumn, encryption_data=encryption - ) - formatters['id'] = _EncryptionInfoColumn - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters=formatters, - ) - for s in data - ), - ) - - -class SetVolumeType(command.Command): - _description = _("Set volume type properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume type ' - '(repeat option to set multiple properties)' - ), - ) - # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. - parser.add_argument( - '--encryption-provider', - metavar='', - help=_( - 'Set the encryption provider format for ' - 'this volume type (e.g "luks" or "plain") (admin only) ' - '(This option is required when setting encryption type ' - 'of a volume. Consider using other encryption options ' - 'such as: "--encryption-cipher", "--encryption-key-size" ' - 'and "--encryption-control-location")' - ), - ) - parser.add_argument( - '--encryption-cipher', - metavar='', - help=_( - 'Set the encryption algorithm or mode for this ' - 'volume type (e.g "aes-xts-plain64") (admin only)' - ), - ) - parser.add_argument( - '--encryption-key-size', - metavar='', - type=int, - help=_( - 'Set the size of the encryption key of this ' - 'volume type (e.g "128" or "256") (admin only)' - ), - ) - parser.add_argument( - '--encryption-control-location', - metavar='', - choices=['front-end', 'back-end'], - help=_( - 'Set the notional service where the encryption is ' - 'performed ("front-end" or "back-end") (admin only) ' - '(The default value for this option is "front-end" ' - 'when setting encryption type of a volume. Consider ' - 'using other encryption options such as: ' - '"--encryption-cipher", "--encryption-key-size" and ' - '"--encryption-provider")' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - - result = 0 - if parsed_args.property: - try: - volume_type.set_keys(parsed_args.property) - except Exception as e: - LOG.error(_("Failed to set volume type property: %s"), e) - result += 1 - - if ( - parsed_args.encryption_provider - or parsed_args.encryption_cipher - or parsed_args.encryption_key_size - or parsed_args.encryption_control_location - ): - try: - _create_encryption_type( - volume_client, volume_type, parsed_args - ) - except Exception as e: - LOG.error( - _( - "Failed to set encryption information for this " - "volume type: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") - ) - - -class ShowVolumeType(command.ShowOne): - _description = _("Display volume type details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - "volume_type", - metavar="", - help=_("Volume type to display (name or ID)"), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Display encryption information of this volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - properties = format_columns.DictColumn( - volume_type._info.pop('extra_specs') - ) - volume_type._info.update({'properties': properties}) - if parsed_args.encryption_type: - # show encryption type information for this volume type - try: - encryption = volume_client.volume_encryption_types.get( - volume_type.id - ) - encryption._info.pop("volume_type_id", None) - volume_type._info.update( - {'encryption': format_columns.DictColumn(encryption._info)} - ) - except Exception as e: - LOG.error( - _( - "Failed to display the encryption information " - "of this volume type: %s" - ), - e, - ) - volume_type._info.pop("os-volume-type-access:is_public", None) - return zip(*sorted(volume_type._info.items())) - - -class UnsetVolumeType(command.Command): - _description = _("Unset volume type properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Remove a property from this volume type ' - '(repeat option to remove multiple properties)' - ), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Remove the encryption type for this volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, - parsed_args.volume_type, - ) - - result = 0 - if parsed_args.property: - try: - volume_type.unset_keys(parsed_args.property) - except Exception as e: - LOG.error(_("Failed to unset volume type property: %s"), e) - result += 1 - if parsed_args.encryption_type: - try: - volume_client.volume_encryption_types.delete(volume_type) - except Exception as e: - LOG.error( - _( - "Failed to remove the encryption type for this " - "volume type: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") - ) diff --git a/openstackclient/volume/v2/backup_record.py b/openstackclient/volume/v2/backup_record.py index 98f7c7171a..93492f87f7 100644 --- a/openstackclient/volume/v2/backup_record.py +++ b/openstackclient/volume/v2/backup_record.py @@ -16,9 +16,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py index ee17acc53e..4910bb129e 100644 --- a/openstackclient/volume/v2/consistency_group.py +++ b/openstackclient/volume/v2/consistency_group.py @@ -18,10 +18,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -38,10 +38,7 @@ def _find_volumes(parsed_args_volumes, volume_client): except Exception as e: result += 1 LOG.error( - _( - "Failed to find volume with " - "name or ID '%(volume)s':%(e)s" - ) + _("Failed to find volume with name or ID '%(volume)s':%(e)s") % {'volume': volume, 'e': e} ) @@ -76,7 +73,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) LOG.error( - _("%(result)s of %(total)s volumes failed " "to add.") + _("%(result)s of %(total)s volumes failed to add.") % {'result': result, 'total': total} ) @@ -236,8 +233,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.consistency_groups) msg = _( - "%(result)s of %(total)s consistency groups failed " - "to delete." + "%(result)s of %(total)s consistency groups failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -321,7 +317,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) LOG.error( - _("%(result)s of %(total)s volumes failed " "to remove.") + _("%(result)s of %(total)s volumes failed to remove.") % {'result': result, 'total': total} ) diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py index 220d4f7f0b..23c3f1034d 100644 --- a/openstackclient/volume/v2/consistency_group_snapshot.py +++ b/openstackclient/volume/v2/consistency_group_snapshot.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -123,8 +123,7 @@ def get_parser(self, prog_name): '--all-projects', action="store_true", help=_( - 'Show detail for all projects (admin only) ' - '(defaults to False)' + 'Show detail for all projects (admin only) (defaults to False)' ), ) parser.add_argument( diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index fa6cec4949..39aa99eb42 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -19,10 +19,10 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -153,8 +153,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.qos_specs) msg = _( - "%(result)s of %(total)s QoS specifications failed" - " to delete." + "%(result)s of %(total)s QoS specifications failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index 2a33fc0b9a..7777e7e638 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -14,10 +14,10 @@ """Service action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -45,33 +45,34 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + + columns: tuple[str, ...] = ( + "binary", + "host", + "availability_zone", + "status", + "state", + "updated_at", + ) + column_names: tuple[str, ...] = ( + "Binary", + "Host", + "Zone", + "Status", + "State", + "Updated At", + ) if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] - - data = service_client.services.list( - parsed_args.host, parsed_args.service + columns += ("disabled_reason",) + column_names += ("Disabled Reason",) + + data = volume_client.services( + host=parsed_args.host, binary=parsed_args.service ) return ( - columns, + column_names, ( utils.get_item_properties( s, @@ -87,7 +88,11 @@ class SetService(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("host", metavar="", help=_("Name of host")) + parser.add_argument( + "host", + metavar="", + help=_("Name of host"), + ) parser.add_argument( "service", metavar="", @@ -118,19 +123,17 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + + service = volume_client.find_service( + parsed_args.service, ignore_missing=False, host=parsed_args.host + ) + if parsed_args.enable: - service_client.services.enable( - parsed_args.host, parsed_args.service - ) + service.enable(volume_client) + if parsed_args.disable: - if parsed_args.disable_reason: - service_client.services.disable_log_reason( - parsed_args.host, - parsed_args.service, - parsed_args.disable_reason, - ) - else: - service_client.services.disable( - parsed_args.host, parsed_args.service - ) + service.disable( + volume_client, + reason=parsed_args.disable_reason, + ) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index b5a4a1ffc9..61cce04f7b 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -18,15 +18,18 @@ import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v2 +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -58,7 +61,7 @@ def __call__(self, parser, namespace, values, option_string=None): ) -class AttachmentsColumn(cliff_columns.FormattableColumn): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[ty.Any]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the @@ -89,6 +92,47 @@ def human_readable(self): return msg +def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'OS-SCH-HNT:scheduler_hints', + 'imageRef', + # unnecessary columns + 'links', + } + optional_columns = { + # only present if part of a consistency group + 'consistencygroup_id', + # only present if there are image properties associated + 'volume_image_metadata', + } + + info = volume.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + if key in optional_columns: + if info[key] is None: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + 'type': data.pop('volume_type'), + } + ) + + return data + + class CreateVolume(command.ShowOne): _description = _("Create new volume") @@ -107,7 +151,7 @@ def _check_size_arg(args): ) raise exceptions.CommandError(msg) - def _get_parser(self, prog_name): + def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( "name", @@ -169,6 +213,7 @@ def _get_parser(self, prog_name): "--property", metavar="", action=parseractions.KeyValueAction, + dest="properties", help=_( "Set a property to this volume " "(repeat option to set multiple properties)" @@ -189,54 +234,58 @@ def _get_parser(self, prog_name): bootable_group.add_argument( "--bootable", action="store_true", + dest="bootable", + default=None, help=_("Mark volume as bootable"), ) bootable_group.add_argument( "--non-bootable", - action="store_true", + action="store_false", + dest="bootable", + default=None, help=_("Mark volume as non-bootable (default)"), ) readonly_group = parser.add_mutually_exclusive_group() readonly_group.add_argument( "--read-only", action="store_true", + dest="read_only", + default=None, help=_("Set volume to read-only access mode"), ) readonly_group.add_argument( "--read-write", - action="store_true", + action="store_false", + dest="read_only", + default=None, help=_("Set volume to read-write access mode (default)"), ) - return parser, source_group - - def get_parser(self, prog_name): - parser, _ = self._get_parser(prog_name) return parser def take_action(self, parsed_args): - CreateVolume._check_size_arg(parsed_args) + self._check_size_arg(parsed_args) # size is validated in the above call to # _check_size_arg where we check that size # should be passed if we are not creating a # volume from snapshot or source volume size = parsed_args.size - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume image_client = self.app.client_manager.image source_volume = None if parsed_args.source: - source_volume_obj = utils.find_resource( - volume_client.volumes, parsed_args.source + source_volume_obj = volume_client.find_volume( + parsed_args.source, ignore_missing=False ) source_volume = source_volume_obj.id size = max(size or 0, source_volume_obj.size) consistency_group = None if parsed_args.consistency_group: - consistency_group = utils.find_resource( - volume_client.consistencygroups, parsed_args.consistency_group - ).id + consistency_group = volume_v2.find_consistency_group( + volume_client, parsed_args.consistency_group + )['id'] image = None if parsed_args.image: @@ -246,8 +295,8 @@ def take_action(self, parsed_args): snapshot = None if parsed_args.snapshot: - snapshot_obj = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + snapshot_obj = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) snapshot = snapshot_obj.id # Cinder requires a value for size when creating a volume @@ -258,51 +307,51 @@ def take_action(self, parsed_args): # snapshot size. size = max(size or 0, snapshot_obj.size) - volume = volume_client.volumes.create( + volume = volume_client.create_volume( size=size, snapshot_id=snapshot, name=parsed_args.name, description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, - imageRef=image, - source_volid=source_volume, - consistencygroup_id=consistency_group, + metadata=parsed_args.properties, + image_id=image, + source_volume_id=source_volume, + consistency_group_id=consistency_group, scheduler_hints=parsed_args.hint, ) - if parsed_args.bootable or parsed_args.non_bootable: + if parsed_args.bootable is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable + volume_client.set_volume_bootable_status( + volume, parsed_args.bootable ) else: msg = _( - "Volume status is not available for setting boot " - "state" + "Volume status is not available for setting boot state" ) raise exceptions.CommandError(msg) except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only + volume_client.set_volume_readonly( + volume, parsed_args.read_only ) else: msg = _( @@ -312,24 +361,12 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) - # Remove key links from being displayed - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - } - ) - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class DeleteVolume(command.Command): @@ -356,25 +393,25 @@ def get_parser(self, prog_name): "--purge", action="store_true", help=_( - "Remove any snapshots along with volume(s) " - "(defaults to False)" + "Remove any snapshots along with volume(s) (defaults to False)" ), ) return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume result = 0 - for i in parsed_args.volumes: + for volume in parsed_args.volumes: try: - volume_obj = utils.find_resource(volume_client.volumes, i) - if parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) - else: - volume_client.volumes.delete( - volume_obj.id, cascade=parsed_args.purge - ) + volume_obj = volume_client.find_volume( + volume, ignore_missing=False + ) + volume_client.delete_volume( + volume_obj.id, + force=parsed_args.force, + cascade=parsed_args.purge, + ) except Exception as e: result += 1 LOG.error( @@ -382,12 +419,12 @@ def take_action(self, parsed_args): "Failed to delete volume with " "name or ID '%(volume)s': %(e)s" ), - {'volume': i, 'e': e}, + {'volume': volume, 'e': e}, ) if result > 0: total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed " "to delete.") % { + msg = _("%(result)s of %(total)s volumes failed to delete.") % { 'result': result, 'total': total, } @@ -508,12 +545,12 @@ def take_action(self, parsed_args): server_cache = {} if do_server_list: try: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for s in compute_client.servers(): server_cache[s.id] = s - except sdk_exceptions.SDKException: + except sdk_exceptions.SDKException: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass AttachmentsColumnWithCache = functools.partial( AttachmentsColumn, server_cache=server_cache ) @@ -576,13 +613,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - volume_client.volumes.migrate_volume( + volume_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False + ) + volume_client.migrate_volume( volume.id, - parsed_args.host, - parsed_args.force_host_copy, - parsed_args.lock_volume, + host=parsed_args.host, + force_host_copy=parsed_args.force_host_copy, + lock_volume=parsed_args.lock_volume, ) @@ -627,6 +666,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest="properties", help=_( 'Set a property on this volume ' '(repeat option to set multiple properties)' @@ -636,6 +676,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action=parseractions.KeyValueAction, + dest="image_properties", help=_( 'Set an image property on this volume ' '(repeat option to set multiple image properties)' @@ -712,22 +753,30 @@ def get_parser(self, prog_name): bootable_group.add_argument( "--bootable", action="store_true", + dest="bootable", + default=None, help=_("Mark volume as bootable"), ) bootable_group.add_argument( "--non-bootable", - action="store_true", + action="store_false", + dest="bootable", + default=None, help=_("Mark volume as non-bootable"), ) readonly_group = parser.add_mutually_exclusive_group() readonly_group.add_argument( "--read-only", action="store_true", + dest="read_only", + default=None, help=_("Set volume to read-only access mode"), ) readonly_group.add_argument( "--read-write", - action="store_true", + action="store_false", + dest="read_only", + default=None, help=_("Set volume to read-write access mode"), ) return parser @@ -776,28 +825,31 @@ def take_action(self, parsed_args): LOG.error(_("Failed to clean volume properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.set_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) + LOG.error(_("Failed to set volume properties: %s"), e) result += 1 - if parsed_args.image_property: + + if parsed_args.image_properties: try: volume_client.volumes.set_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to set image property: %s"), e) + LOG.error(_("Failed to set image properties: %s"), e) result += 1 + if parsed_args.state: try: volume_client.volumes.reset_state(volume.id, parsed_args.state) except Exception as e: LOG.error(_("Failed to set volume state: %s"), e) result += 1 + if parsed_args.attached: try: volume_client.volumes.reset_state( @@ -806,6 +858,7 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 + if parsed_args.detached: try: volume_client.volumes.reset_state( @@ -814,7 +867,8 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 - if parsed_args.bootable or parsed_args.non_bootable: + + if parsed_args.bootable is not None: try: volume_client.volumes.set_bootable( volume.id, parsed_args.bootable @@ -822,20 +876,19 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) result += 1 - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: volume_client.volumes.update_readonly_flag( volume.id, parsed_args.read_only ) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) result += 1 + policy = parsed_args.migration_policy or parsed_args.retype_policy if parsed_args.type: # get the migration policy @@ -885,7 +938,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) @@ -902,24 +955,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - - # Special mapping for columns to make the output easier to read: - # 'metadata' --> 'properties' - # 'volume_type' --> 'type' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, + volume_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ) - # Remove key links from being displayed - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class UnsetVolume(command.Command): @@ -936,6 +978,7 @@ def get_parser(self, prog_name): '--property', metavar='', action='append', + dest='properties', help=_( 'Remove a property from volume ' '(repeat option to remove multiple properties)' @@ -945,6 +988,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action='append', + dest='image_properties', help=_( 'Remove an image property from volume ' '(repeat option to remove multiple image properties)' @@ -957,25 +1001,25 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) result = 0 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.delete_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to unset volume property: %s"), e) + LOG.error(_("Failed to unset volume properties: %s"), e) result += 1 - if parsed_args.image_property: + if parsed_args.image_properties: try: volume_client.volumes.delete_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to unset image property: %s"), e) + LOG.error(_("Failed to unset image properties: %s"), e) result += 1 if result > 0: raise exceptions.CommandError( - _("One or more of the " "unset operations failed") + _("One or more of the unset operations failed") ) diff --git a/openstackclient/volume/v2/volume_backend.py b/openstackclient/volume/v2/volume_backend.py index 2fbbed64a3..e51e37bb9f 100644 --- a/openstackclient/volume/v2/volume_backend.py +++ b/openstackclient/volume/v2/volume_backend.py @@ -15,9 +15,9 @@ """Storage backend action implementations""" from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 0ed0e76228..7dbe92c962 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -18,17 +18,17 @@ import logging from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -118,7 +118,7 @@ def take_action(self, parsed_args): ignore_missing=False, ).id - columns = ( + columns: tuple[str, ...] = ( "id", "name", "volume_id", @@ -240,21 +240,23 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'description', 'status', 'size', 'is_incremental', + 'created_at', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Description', 'Status', 'Size', 'Incremental', + 'Created At', ) if parsed_args.long: columns += ('availability_zone', 'volume_id', 'container') @@ -265,9 +267,9 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache @@ -343,8 +345,7 @@ def get_parser(self, prog_name): "--force", action="store_true", help=_( - "Restore the backup to an existing volume " - "(default to False)" + "Restore the backup to an existing volume (default to False)" ), ) return parser @@ -357,6 +358,12 @@ def take_action(self, parsed_args): ignore_missing=False, ) + columns: tuple[str, ...] = ( + 'id', + 'volume_id', + 'volume_name', + ) + volume_name = None volume_id = None try: @@ -376,12 +383,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg % parsed_args.volume) - return volume_client.restore_backup( + restore = volume_client.restore_backup( backup.id, volume_id=volume_id, name=volume_name, ) + data = utils.get_dict_properties(restore, columns) + return (columns, data) + class SetVolumeBackup(command.Command): _description = _("Set volume backup properties") @@ -407,13 +417,19 @@ def get_parser(self, prog_name): 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) + volume_client = self.app.client_manager.sdk_connection.volume + + backup = volume_client.find_backup( + parsed_args.backup, + ignore_missing=False, + ) result = 0 if parsed_args.state: try: - volume_client.backups.reset_state(backup.id, parsed_args.state) + volume_client.reset_backup_status( + backup, status=parsed_args.state + ) except Exception as e: LOG.error(_("Failed to set backup state: %s"), e) result += 1 @@ -437,8 +453,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - backup = volume_client.get_backup(parsed_args.backup) - columns = ( + backup = volume_client.find_backup( + parsed_args.backup, ignore_missing=False + ) + columns: tuple[str, ...] = ( "availability_zone", "container", "created_at", diff --git a/openstackclient/volume/v2/volume_host.py b/openstackclient/volume/v2/volume_host.py index 2b0df0aa2e..44fd58a5cd 100644 --- a/openstackclient/volume/v2/volume_host.py +++ b/openstackclient/volume/v2/volume_host.py @@ -14,8 +14,7 @@ """Volume v2 host action implementations""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 3398f502a0..3b1dbbabf2 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -14,17 +14,18 @@ """Volume v2 snapshot action implementations""" -import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v2 import snapshot as _snapshot from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -33,7 +34,7 @@ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -60,6 +61,42 @@ def human_readable(self): return volume +def _format_snapshot(snapshot: _snapshot.Snapshot) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'consumes_quota', + 'force', + 'group_snapshot_id', + # ignored columns + 'os-extended-snapshot-attributes:progress', + 'os-extended-snapshot-attributes:project_id', + 'updated_at', + 'user_id', + # unnecessary columns + 'links', + } + + info = snapshot.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + } + ) + + return data + + class CreateVolumeSnapshot(command.ShowOne): _description = _("Create new volume snapshot") @@ -74,8 +111,7 @@ def get_parser(self, prog_name): "--volume", metavar="", help=_( - "Volume to snapshot (name or ID) " - "(default is )" + "Volume to snapshot (name or ID) (default is )" ), ) parser.add_argument( @@ -88,14 +124,14 @@ def get_parser(self, prog_name): action="store_true", default=False, help=_( - "Create a snapshot attached to an instance. " - "Default is False" + "Create a snapshot attached to an instance. Default is False" ), ) parser.add_argument( "--property", metavar="", action=parseractions.KeyValueAction, + dest="properties", help=_( "Set a property to this snapshot " "(repeat option to set multiple properties)" @@ -115,11 +151,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + volume = parsed_args.volume if not parsed_args.volume: volume = parsed_args.snapshot_name - volume_id = utils.find_resource(volume_client.volumes, volume).id + volume_id = volume_client.find_volume(volume, ignore_missing=False).id + if parsed_args.remote_source: # Create a new snapshot from an existing remote snapshot source if parsed_args.force: @@ -129,30 +167,26 @@ def take_action(self, parsed_args): "volume snapshot" ) LOG.warning(msg) - snapshot = volume_client.volume_snapshots.manage( + + snapshot = volume_client.manage_snapshot( volume_id=volume_id, ref=parsed_args.remote_source, name=parsed_args.snapshot_name, description=parsed_args.description, - metadata=parsed_args.property, + metadata=parsed_args.properties, ) else: # create a new snapshot from scratch - snapshot = volume_client.volume_snapshots.create( - volume_id, + snapshot = volume_client.create_snapshot( + volume_id=volume_id, force=parsed_args.force, name=parsed_args.snapshot_name, description=parsed_args.description, - metadata=parsed_args.property, + metadata=parsed_args.properties, ) - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - return zip(*sorted(snapshot._info.items())) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) class DeleteVolumeSnapshot(command.Command): @@ -177,16 +211,16 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume result = 0 - for i in parsed_args.snapshots: + for snapshot in parsed_args.snapshots: try: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, i + snapshot_id = volume_client.find_snapshot( + snapshot, ignore_missing=False ).id - volume_client.volume_snapshots.delete( - snapshot_id, parsed_args.force + volume_client.delete_snapshot( + snapshot_id, force=parsed_args.force ) except Exception as e: result += 1 @@ -195,14 +229,15 @@ def take_action(self, parsed_args): "Failed to delete snapshot with " "name or ID '%(snapshot)s': %(e)s" ) - % {'snapshot': i, 'e': e} + % {'snapshot': snapshot, 'e': e} ) if result > 0: total = len(parsed_args.snapshots) - msg = _( - "%(result)s of %(total)s snapshots failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s snapshots failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -261,43 +296,51 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume identity_client = self.app.client_manager.identity + columns: tuple[str, ...] = ( + 'id', + 'name', + 'description', + 'status', + 'size', + ) + column_headers: tuple[str, ...] = ( + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + ) if parsed_args.long: - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', + columns += ( + 'created_at', + 'volume_id', + 'metadata', + ) + column_headers += ( 'Created At', - 'Volume ID', - 'Metadata', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - column_headers[7] = 'Properties' - else: - columns = ['ID', 'Name', 'Description', 'Status', 'Size'] - column_headers = copy.deepcopy(columns) + 'Volume', + 'Properties', + ) # Cache the volume list volume_cache = {} try: - for s in volume_client.volumes.list(): + for s in volume_client.volumes(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) volume_id = None if parsed_args.volume: - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume + volume_id = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ).id project_id = None @@ -313,18 +356,14 @@ def take_action(self, parsed_args): True if parsed_args.project else parsed_args.all_projects ) - search_opts = { - 'all_tenants': all_projects, - 'project_id': project_id, - 'name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': volume_id, - } - - data = volume_client.volume_snapshots.list( - search_opts=search_opts, + data = volume_client.snapshots( marker=parsed_args.marker, limit=parsed_args.limit, + all_projects=all_projects, + project_id=project_id, + name=parsed_args.name, + status=parsed_args.status, + volume_id=volume_id, ) return ( column_headers, @@ -333,8 +372,8 @@ def take_action(self, parsed_args): s, columns, formatters={ - 'Metadata': format_columns.DictColumn, - 'Volume ID': _VolumeIdColumn, + 'metadata': format_columns.DictColumn, + 'volume_id': _VolumeIdColumn, }, ) for s in data @@ -375,6 +414,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Property to add/change for this snapshot ' '(repeat option to set multiple properties)' @@ -401,27 +441,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) result = 0 if parsed_args.no_property: try: - key_list = snapshot.metadata.keys() - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - list(key_list), + volume_client.delete_snapshot_metadata( + snapshot.id, keys=list(snapshot.metadata) ) except Exception as e: LOG.error(_("Failed to clean snapshot properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: - volume_client.volume_snapshots.set_metadata( - snapshot.id, parsed_args.property + volume_client.set_snapshot_metadata( + snapshot.id, **parsed_args.properties ) except Exception as e: LOG.error(_("Failed to set snapshot property: %s"), e) @@ -429,7 +468,7 @@ def take_action(self, parsed_args): if parsed_args.state: try: - volume_client.volume_snapshots.reset_state( + volume_client.reset_snapshot_status( snapshot.id, parsed_args.state ) except Exception as e: @@ -443,17 +482,17 @@ def take_action(self, parsed_args): kwargs['description'] = parsed_args.description if kwargs: try: - volume_client.volume_snapshots.update(snapshot.id, **kwargs) + volume_client.update_snapshot(snapshot.id, **kwargs) except Exception as e: LOG.error( - _("Failed to update snapshot name " "or description: %s"), + _("Failed to update snapshot name or description: %s"), e, ) result += 1 if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) @@ -470,18 +509,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) - return zip(*sorted(snapshot._info.items())) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) class UnsetVolumeSnapshot(command.Command): @@ -499,6 +534,7 @@ def get_parser(self, prog_name): metavar='', action='append', default=[], + dest='properties', help=_( 'Property to remove from snapshot ' '(repeat option to remove multiple properties)' @@ -507,13 +543,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) - if parsed_args.property: - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - parsed_args.property, + if parsed_args.properties: + volume_client.delete_snapshot_metadata( + snapshot.id, keys=parsed_args.properties ) diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index 18094b6262..dcdc527625 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -85,8 +85,6 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - kwargs = {} - volume_id = utils.find_resource( volume_client.volumes, parsed_args.volume, @@ -94,7 +92,6 @@ def take_action(self, parsed_args): volume_transfer_request = volume_client.transfers.create( volume_id, parsed_args.name, - **kwargs, ) volume_transfer_request._info.pop("links", None) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index f4540f4e85..e7b90af95a 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -16,14 +16,15 @@ import functools import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -31,7 +32,7 @@ LOG = logging.getLogger(__name__) -class EncryptionInfoColumn(cliff_columns.FormattableColumn): +class EncryptionInfoColumn(cliff_columns.FormattableColumn[ty.Any]): """Formattable column for encryption info column. Unlike the parent FormattableColumn class, the initializer of the @@ -171,7 +172,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -181,7 +183,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -272,8 +275,7 @@ def take_action(self, parsed_args): ) except Exception as e: msg = _( - "Failed to add project %(project)s access to " - "type: %(e)s" + "Failed to add project %(project)s access to type: %(e)s" ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) @@ -363,7 +365,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volume_types) msg = _( - "%(result)s of %(total)s volume types failed " "to delete." + "%(result)s of %(total)s volume types failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -535,7 +537,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -545,7 +548,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -553,8 +557,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Set volume type access to project (name or ID) ' - '(admin only)' + 'Set volume type access to project (name or ID) (admin only)' ), ) public_group = parser.add_mutually_exclusive_group() @@ -646,17 +649,12 @@ def take_action(self, parsed_args): volume_client.volume_types.update(volume_type.id, **kwargs) except Exception as e: LOG.error( - _( - "Failed to update volume type name or" - " description: %s" - ), + _("Failed to update volume type name or description: %s"), e, ) result += 1 - properties = {} - - properties = {} + properties: dict[str, str] = {} if parsed_args.properties: properties.update(parsed_args.properties) if parsed_args.multiattach: @@ -690,7 +688,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _("Failed to set volume type access to " "project: %s"), e + _("Failed to set volume type access to project: %s"), e ) result += 1 @@ -714,7 +712,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -822,8 +820,7 @@ def get_parser(self, prog_name): "--encryption-type", action="store_true", help=_( - "Remove the encryption type for this volume type " - "(admin only)" + "Remove the encryption type for this volume type (admin only)" ), ) return parser @@ -859,10 +856,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to remove volume type access from " - "project: %s" - ), + _("Failed to remove volume type access from project: %s"), e, ) result += 1 @@ -881,5 +875,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) diff --git a/openstackclient/volume/v3/block_storage_cleanup.py b/openstackclient/volume/v3/block_storage_cleanup.py index 361440c994..5208504a35 100644 --- a/openstackclient/volume/v3/block_storage_cleanup.py +++ b/openstackclient/volume/v3/block_storage_cleanup.py @@ -11,9 +11,9 @@ # under the License. from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_cluster.py b/openstackclient/volume/v3/block_storage_cluster.py index 58bcae67cb..d99ec52b0a 100644 --- a/openstackclient/volume/v3/block_storage_cluster.py +++ b/openstackclient/volume/v3/block_storage_cluster.py @@ -11,21 +11,21 @@ # 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 import command from openstackclient.i18n import _ def _format_cluster(cluster, detailed=False): - columns = ( + columns: tuple[str, ...] = ( 'name', 'binary', 'state', 'status', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Name', 'Binary', 'State', @@ -147,7 +147,7 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - columns = ('Name', 'Binary', 'State', 'Status') + columns: tuple[str, ...] = ('Name', 'Binary', 'State', 'Status') if parsed_args.long: columns += ( 'Num Hosts', diff --git a/openstackclient/volume/v3/block_storage_log_level.py b/openstackclient/volume/v3/block_storage_log_level.py index 878cf4087a..2e2fdc5138 100644 --- a/openstackclient/volume/v3/block_storage_log_level.py +++ b/openstackclient/volume/v3/block_storage_log_level.py @@ -14,11 +14,10 @@ """Block Storage Service action implementations""" -from cinderclient import api_versions -from osc_lib.command import command +from openstack import utils as sdk_utils from osc_lib import exceptions -from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -33,7 +32,7 @@ def get_parser(self, prog_name): parser.add_argument( "--host", metavar="", - default="", + default=None, help=_( "List block storage service log level of specified host " "(name only)" @@ -42,9 +41,9 @@ def get_parser(self, prog_name): parser.add_argument( "--service", metavar="", - default="", + default=None, choices=( - '', + None, '*', 'cinder-api', 'cinder-volume', @@ -59,13 +58,13 @@ def get_parser(self, prog_name): parser.add_argument( "--log-prefix", metavar="", - default="", + default=None, help="Prefix for the log, e.g. 'sqlalchemy'", ) return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume columns = [ "Binary", "Host", @@ -73,29 +72,24 @@ def take_action(self, parsed_args): "Level", ] - if service_client.api_version < api_versions.APIVersion('3.32'): + if not sdk_utils.supports_microversion(volume_client, '3.32'): msg = _( "--os-volume-api-version 3.32 or greater is required to " "support the 'block storage log level list' command" ) raise exceptions.CommandError(msg) - data = service_client.services.get_log_levels( + data = [] + for entry in volume_client.get_service_log_levels( binary=parsed_args.service, server=parsed_args.host, prefix=parsed_args.log_prefix, - ) + ): + entry_levels = sorted(entry.levels.items(), key=lambda x: x[0]) + for prefix, level in entry_levels: + data.append((entry.binary, entry.host, prefix, level)) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) + return (columns, data) class BlockStorageLogLevelSet(command.Command): @@ -111,12 +105,12 @@ def get_parser(self, prog_name): metavar="", choices=('INFO', 'WARNING', 'ERROR', 'DEBUG'), type=str.upper, - help=_("Desired log level."), + help=_("Desired log level"), ) parser.add_argument( "--host", metavar="", - default="", + default=None, help=_( "Set block storage service log level of specified host " "(name only)" @@ -125,9 +119,9 @@ def get_parser(self, prog_name): parser.add_argument( "--service", metavar="", - default="", + default=None, choices=( - '', + None, '*', 'cinder-api', 'cinder-volume', @@ -142,22 +136,22 @@ def get_parser(self, prog_name): parser.add_argument( "--log-prefix", metavar="", - default="", + default=None, help="Prefix for the log, e.g. 'sqlalchemy'", ) return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume - if service_client.api_version < api_versions.APIVersion('3.32'): + if not sdk_utils.supports_microversion(volume_client, '3.32'): msg = _( "--os-volume-api-version 3.32 or greater is required to " "support the 'block storage log level set' command" ) raise exceptions.CommandError(msg) - service_client.services.set_log_levels( + volume_client.set_service_log_levels( level=parsed_args.level, binary=parsed_args.service, server=parsed_args.host, diff --git a/openstackclient/volume/v3/block_storage_manage.py b/openstackclient/volume/v3/block_storage_manage.py index 5b76b53cb6..78756385cc 100644 --- a/openstackclient/volume/v3/block_storage_manage.py +++ b/openstackclient/volume/v3/block_storage_manage.py @@ -16,10 +16,10 @@ import argparse from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -317,9 +317,7 @@ def take_action(self, parsed_args): # if the user requested e.g. '--detailed false' then they # should simply stop requesting this since the default has # changed - msg = _( - "The --detailed option has been deprecated. " "Unset it." - ) + msg = _("The --detailed option has been deprecated. Unset it.") self.log.warning(msg) columns = [ diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py index 494d9cbe16..fc564386e2 100644 --- a/openstackclient/volume/v3/block_storage_resource_filter.py +++ b/openstackclient/volume/v3/block_storage_resource_filter.py @@ -14,10 +14,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index 900aeab2d8..eecd8e0d06 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -14,47 +14,72 @@ """Service action implementations""" -from cinderclient import api_versions +from openstack import utils as sdk_utils +from osc_lib import exceptions from osc_lib import utils -from openstackclient.volume.v2 import service as service_v2 +from openstackclient import command +from openstackclient.i18n import _ -class ListService(service_v2.ListService): +class ListService(command.Lister): + _description = _("List service command") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--host", + metavar="", + help=_("List services on specified host (name only)"), + ) + parser.add_argument( + "--service", + metavar="", + help=_("List only specified service (name only)"), + ) + parser.add_argument( + "--long", + action="store_true", + default=False, + help=_("List additional fields in output"), + ) + return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + + columns: tuple[str, ...] = ( + "binary", + "host", + "availability_zone", + "status", + "state", + "updated_at", + ) + column_names: tuple[str, ...] = ( + "Binary", + "Host", + "Zone", + "Status", + "State", + "Updated At", + ) + if sdk_utils.supports_microversion(volume_client, '3.7'): + columns += ("cluster",) + column_names += ("Cluster",) + if sdk_utils.supports_microversion(volume_client, '3.49'): + columns += ("backend_state",) + column_names += ("Backend State",) if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] - - if service_client.api_version >= api_versions.APIVersion('3.7'): - columns.append("Cluster") - if service_client.api_version >= api_versions.APIVersion('3.49'): - columns.append("Backend State") - - data = service_client.services.list( - parsed_args.host, parsed_args.service + columns += ("disabled_reason",) + column_names += ("Disabled Reason",) + + data = volume_client.services( + host=parsed_args.host, binary=parsed_args.service ) return ( - columns, + column_names, ( utils.get_item_properties( s, @@ -63,3 +88,59 @@ def take_action(self, parsed_args): for s in data ), ) + + +class SetService(command.Command): + _description = _("Set volume service properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "host", + metavar="", + help=_("Name of host"), + ) + parser.add_argument( + "service", + metavar="", + help=_("Name of service (Binary name)"), + ) + enabled_group = parser.add_mutually_exclusive_group() + enabled_group.add_argument( + "--enable", action="store_true", help=_("Enable volume service") + ) + enabled_group.add_argument( + "--disable", action="store_true", help=_("Disable volume service") + ) + parser.add_argument( + "--disable-reason", + metavar="", + help=_( + "Reason for disabling the service " + "(should be used with --disable option)" + ), + ) + return parser + + def take_action(self, parsed_args): + if parsed_args.disable_reason and not parsed_args.disable: + msg = _( + "Cannot specify option --disable-reason without " + "--disable specified." + ) + raise exceptions.CommandError(msg) + + volume_client = self.app.client_manager.sdk_connection.volume + + service = volume_client.find_service( + parsed_args.service, ignore_missing=False, host=parsed_args.host + ) + + if parsed_args.enable: + service.enable(volume_client) + + if parsed_args.disable: + service.disable( + volume_client, + reason=parsed_args.disable_reason, + ) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 3bb3cdaed5..50ea77fb5a 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -18,20 +18,22 @@ import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions 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 from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v3 +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common -from openstackclient.volume.v2 import volume as volume_v2 LOG = logging.getLogger(__name__) @@ -60,7 +62,7 @@ def __call__(self, parser, namespace, values, option_string=None): ) -class AttachmentsColumn(cliff_columns.FormattableColumn): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[ty.Any]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the @@ -91,7 +93,53 @@ def human_readable(self): return msg -class CreateVolume(volume_v2.CreateVolume): +def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'OS-SCH-HNT:scheduler_hints', + 'imageRef', + # removed columns + 'os-volume-replication:driver_data', + 'os-volume-replication:extended_status', + # unnecessary columns + 'links', + } + optional_columns = { + # only present if part of a consistency group + 'consistencygroup_id', + # only present if the volume is encrypted + 'encryption_key_id', + # only present if there are image properties associated + 'volume_image_metadata', + } + + info = volume.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + if key in optional_columns: + if info[key] is None: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + 'type': data.pop('volume_type'), + } + ) + + return data + + +class CreateVolume(command.ShowOne): _description = _("Create new volume") @staticmethod @@ -117,8 +165,48 @@ def _check_size_arg(args): raise exceptions.CommandError(msg) def get_parser(self, prog_name): - parser, source_group = self._get_parser(prog_name) - + parser = super().get_parser(prog_name) + parser.add_argument( + "name", + metavar="", + nargs="?", + help=_("Volume name"), + ) + parser.add_argument( + "--size", + metavar="", + type=int, + help=_( + "Volume size in GB (required unless --snapshot or " + "--source specified)" + ), + ) + parser.add_argument( + "--type", + metavar="", + help=_("Set the type of volume"), + ) + source_group = parser.add_mutually_exclusive_group() + source_group.add_argument( + "--image", + metavar="", + help=_("Use as source of volume (name or ID)"), + ) + source_group.add_argument( + "--snapshot", + metavar="", + help=_("Use as source of volume (name or ID)"), + ) + source_group.add_argument( + "--source", + metavar="", + help=_("Volume to clone (name or ID)"), + ) + source_group.add_argument( + "--source-replicated", + metavar="", + help=argparse.SUPPRESS, + ) source_group.add_argument( "--backup", metavar="", @@ -138,6 +226,72 @@ def get_parser(self, prog_name): "--remote-source source-id=test_id')" ), ) + parser.add_argument( + "--description", + metavar="", + help=_("Volume description"), + ) + parser.add_argument( + "--availability-zone", + metavar="", + help=_("Create volume in "), + ) + parser.add_argument( + "--consistency-group", + metavar="consistency-group>", + help=_("Consistency group where the new volume belongs to"), + ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + dest="properties", + help=_( + "Set a property to this volume " + "(repeat option to set multiple properties)" + ), + ) + parser.add_argument( + "--hint", + metavar="", + action=KeyValueHintAction, + help=_( + "Arbitrary scheduler hint key-value pairs to help creating " + "a volume. Repeat the option to set multiple hints. " + "'same_host' and 'different_host' get values appended when " + "repeated, all other keys take the last given value" + ), + ) + bootable_group = parser.add_mutually_exclusive_group() + bootable_group.add_argument( + "--bootable", + action="store_true", + dest="bootable", + default=None, + help=_("Mark volume as bootable"), + ) + bootable_group.add_argument( + "--non-bootable", + action="store_false", + dest="bootable", + default=None, + help=_("Mark volume as non-bootable (default)"), + ) + readonly_group = parser.add_mutually_exclusive_group() + readonly_group.add_argument( + "--read-only", + action="store_true", + dest="read_only", + default=None, + help=_("Set volume to read-only access mode"), + ) + readonly_group.add_argument( + "--read-write", + action="store_false", + dest="read_only", + default=None, + help=_("Set volume to read-write access mode (default)"), + ) parser.add_argument( "--host", metavar="", @@ -154,21 +308,20 @@ def get_parser(self, prog_name): "Cinder cluster on which the existing volume resides; " "takes the form: cluster@backend-name#pool. This is only " "used along with the --remote-source option. " - "(supported by --os-volume-api-version 3.16 or above)", + "(supported by --os-volume-api-version 3.16 or above)" ), ) return parser def take_action(self, parsed_args): - CreateVolume._check_size_arg(parsed_args) + self._check_size_arg(parsed_args) # size is validated in the above call to # _check_size_arg where we check that size # should be passed if we are not creating a # volume from snapshot, backup or source volume size = parsed_args.size - volume_client_sdk = self.app.client_manager.sdk_connection.volume - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume image_client = self.app.client_manager.image if ( @@ -180,8 +333,8 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - if parsed_args.backup and not ( - volume_client.api_version.matches('3.47') + if parsed_args.backup and not sdk_utils.supports_microversion( + volume_client, '3.47' ): msg = _( "--os-volume-api-version 3.47 or greater is required " @@ -194,8 +347,7 @@ def take_action(self, parsed_args): parsed_args.size or parsed_args.consistency_group or parsed_args.hint - or parsed_args.read_only - or parsed_args.read_write + or parsed_args.read_only is not None ): msg = _( "The --size, --consistency-group, --hint, --read-only " @@ -204,9 +356,7 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) if parsed_args.cluster: - if not sdk_utils.supports_microversion( - volume_client_sdk, '3.16' - ): + if not sdk_utils.supports_microversion(volume_client, '3.16'): msg = _( "--os-volume-api-version 3.16 or greater is required " "to support the cluster parameter." @@ -224,7 +374,7 @@ def take_action(self, parsed_args): "manage a volume." ) raise exceptions.CommandError(msg) - volume = volume_client_sdk.manage_volume( + volume = volume_client.manage_volume( host=parsed_args.host, cluster=parsed_args.cluster, ref=parsed_args.remote_source, @@ -232,24 +382,25 @@ def take_action(self, parsed_args): description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, + metadata=parsed_args.properties, bootable=parsed_args.bootable, ) - return zip(*sorted(volume.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) source_volume = None if parsed_args.source: - source_volume_obj = utils.find_resource( - volume_client.volumes, parsed_args.source + source_volume_obj = volume_client.find_volume( + parsed_args.source, ignore_missing=False ) source_volume = source_volume_obj.id size = max(size or 0, source_volume_obj.size) consistency_group = None if parsed_args.consistency_group: - consistency_group = utils.find_resource( - volume_client.consistencygroups, parsed_args.consistency_group - ).id + consistency_group = volume_v3.find_consistency_group( + volume_client, parsed_args.consistency_group + )['id'] image = None if parsed_args.image: @@ -259,8 +410,8 @@ def take_action(self, parsed_args): snapshot = None if parsed_args.snapshot: - snapshot_obj = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + snapshot_obj = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) snapshot = snapshot_obj.id # Cinder requires a value for size when creating a volume @@ -273,59 +424,59 @@ def take_action(self, parsed_args): backup = None if parsed_args.backup: - backup_obj = utils.find_resource( - volume_client.backups, parsed_args.backup + backup_obj = volume_client.find_backup( + parsed_args.backup, ignore_missing=False ) backup = backup_obj.id # As above size = max(size or 0, backup_obj.size) - volume = volume_client.volumes.create( + volume = volume_client.create_volume( size=size, snapshot_id=snapshot, name=parsed_args.name, description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, - imageRef=image, - source_volid=source_volume, - consistencygroup_id=consistency_group, + metadata=parsed_args.properties, + image_id=image, + source_volume_id=source_volume, + consistency_group_id=consistency_group, scheduler_hints=parsed_args.hint, backup_id=backup, ) - if parsed_args.bootable or parsed_args.non_bootable: + if parsed_args.bootable is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable + volume_client.set_volume_bootable_status( + volume, parsed_args.bootable ) else: msg = _( - "Volume status is not available for setting boot " - "state" + "Volume status is not available for setting boot state" ) raise exceptions.CommandError(msg) except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only + volume_client.set_volume_readonly( + volume, parsed_args.read_only ) else: msg = _( @@ -335,31 +486,41 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) - # Remove key links from being displayed - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - } - ) - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) -class DeleteVolume(volume_v2.DeleteVolume): +class DeleteVolume(command.Command): _description = _("Delete volume(s)") def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + "volumes", + metavar="", + nargs="+", + help=_("Volume(s) to delete (name or ID)"), + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--force", + action="store_true", + help=_( + "Attempt forced removal of volume(s), regardless of state " + "(defaults to False)" + ), + ) + group.add_argument( + "--purge", + action="store_true", + help=_( + "Remove any snapshots along with volume(s) (defaults to False)" + ), + ) parser.add_argument( '--remote', action='store_true', @@ -368,8 +529,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_client_sdk = self.app.client_manager.sdk_connection.volume + volume_client = self.app.client_manager.sdk_connection.volume result = 0 if parsed_args.remote and (parsed_args.force or parsed_args.purge): @@ -379,16 +539,18 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - for i in parsed_args.volumes: + for volume in parsed_args.volumes: try: - volume_obj = utils.find_resource(volume_client.volumes, i) + volume_obj = volume_client.find_volume( + volume, ignore_missing=False + ) if parsed_args.remote: - volume_client_sdk.unmanage_volume(volume_obj.id) - elif parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) + volume_client.unmanage_volume(volume_obj.id) else: - volume_client.volumes.delete( - volume_obj.id, cascade=parsed_args.purge + volume_client.delete_volume( + volume_obj.id, + force=parsed_args.force, + cascade=parsed_args.purge, ) except Exception as e: result += 1 @@ -397,12 +559,12 @@ def take_action(self, parsed_args): "Failed to delete volume with " "name or ID '%(volume)s': %(e)s" ), - {'volume': i, 'e': e}, + {'volume': volume, 'e': e}, ) if result > 0: total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed " "to delete.") % { + msg = _("%(result)s of %(total)s volumes failed to delete.") % { 'result': result, 'total': total, } @@ -436,6 +598,16 @@ def get_parser(self, prog_name): metavar='', help=_('Filter results by status'), ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Filter by a property on the volume list ' + '(repeat option to filter by multiple properties) ' + ), + ) parser.add_argument( '--all-projects', action='store_true', @@ -504,6 +676,7 @@ def take_action(self, parsed_args): 'user_id': user_id, 'name': parsed_args.name, 'status': parsed_args.status, + 'metadata': parsed_args.properties, } data = volume_client.volumes.list( @@ -523,12 +696,12 @@ def take_action(self, parsed_args): server_cache = {} if do_server_list: try: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for s in compute_client.servers(): server_cache[s.id] = s - except sdk_exceptions.SDKException: + except sdk_exceptions.SDKException: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass AttachmentsColumnWithCache = functools.partial( AttachmentsColumn, server_cache=server_cache ) @@ -588,16 +761,19 @@ def get_parser(self, prog_name): "(possibly by another operation)" ), ) + # TODO(stephenfin): Add --cluster argument return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - volume_client.volumes.migrate_volume( + volume_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False + ) + volume_client.migrate_volume( volume.id, - parsed_args.host, - parsed_args.force_host_copy, - parsed_args.lock_volume, + host=parsed_args.host, + force_host_copy=parsed_args.force_host_copy, + lock_volume=parsed_args.lock_volume, ) @@ -642,6 +818,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Set a property on this volume ' '(repeat option to set multiple properties)' @@ -651,6 +828,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action=parseractions.KeyValueAction, + dest='image_properties', help=_( 'Set an image property on this volume ' '(repeat option to set multiple image properties)' @@ -727,22 +905,30 @@ def get_parser(self, prog_name): bootable_group.add_argument( "--bootable", action="store_true", + dest="bootable", + default=None, help=_("Mark volume as bootable"), ) bootable_group.add_argument( "--non-bootable", - action="store_true", + action="store_false", + dest="bootable", + default=None, help=_("Mark volume as non-bootable"), ) readonly_group = parser.add_mutually_exclusive_group() readonly_group.add_argument( "--read-only", action="store_true", + dest="read_only", + default=None, help=_("Set volume to read-only access mode"), ) readonly_group.add_argument( "--read-write", - action="store_true", + action="store_false", + dest="read_only", + default=None, help=_("Set volume to read-write access mode"), ) return parser @@ -768,18 +954,24 @@ def take_action(self, parsed_args): _("New size must be greater than %s GB") % volume.size ) raise exceptions.CommandError(msg) - if ( - volume.status != 'available' - and not volume_client.api_version.matches('3.42') - ): + if volume.status not in ('available', 'in-use'): msg = ( _( "Volume is in %s state, it must be available " - "before size can be extended" + "or in-use before size can be extended." ) % volume.status ) raise exceptions.CommandError(msg) + if ( + volume.status == 'in-use' + and not volume_client.api_version.matches('3.42') + ): + msg = _( + "--os-volume-api-version 3.42 or greater is " + "required to extend in-use volumes." + ) + raise exceptions.CommandError(msg) volume_client.volumes.extend(volume.id, parsed_args.size) except Exception as e: LOG.error(_("Failed to set volume size: %s"), e) @@ -794,28 +986,31 @@ def take_action(self, parsed_args): LOG.error(_("Failed to clean volume properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.set_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) + LOG.error(_("Failed to set volume properties: %s"), e) result += 1 - if parsed_args.image_property: + + if parsed_args.image_properties: try: volume_client.volumes.set_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to set image property: %s"), e) + LOG.error(_("Failed to set image properties: %s"), e) result += 1 + if parsed_args.state: try: volume_client.volumes.reset_state(volume.id, parsed_args.state) except Exception as e: LOG.error(_("Failed to set volume state: %s"), e) result += 1 + if parsed_args.attached: try: volume_client.volumes.reset_state( @@ -824,6 +1019,7 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 + if parsed_args.detached: try: volume_client.volumes.reset_state( @@ -832,7 +1028,8 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 - if parsed_args.bootable or parsed_args.non_bootable: + + if parsed_args.bootable is not None: try: volume_client.volumes.set_bootable( volume.id, parsed_args.bootable @@ -840,20 +1037,19 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) result += 1 - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: volume_client.volumes.update_readonly_flag( volume.id, parsed_args.read_only ) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) result += 1 + policy = parsed_args.migration_policy or parsed_args.retype_policy if parsed_args.type: # get the migration policy @@ -903,7 +1099,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) @@ -920,24 +1116,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - - # Special mapping for columns to make the output easier to read: - # 'metadata' --> 'properties' - # 'volume_type' --> 'type' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, + volume_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ) - # Remove key links from being displayed - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class UnsetVolume(command.Command): @@ -954,6 +1139,7 @@ def get_parser(self, prog_name): '--property', metavar='', action='append', + dest='properties', help=_( 'Remove a property from volume ' '(repeat option to remove multiple properties)' @@ -963,6 +1149,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action='append', + dest='image_properties', help=_( 'Remove an image property from volume ' '(repeat option to remove multiple image properties)' @@ -975,27 +1162,27 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) result = 0 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.delete_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to unset volume property: %s"), e) + LOG.error(_("Failed to unset volume properties: %s"), e) result += 1 - if parsed_args.image_property: + if parsed_args.image_properties: try: volume_client.volumes.delete_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to unset image property: %s"), e) + LOG.error(_("Failed to unset image properties: %s"), e) result += 1 if result > 0: raise exceptions.CommandError( - _("One or more of the " "unset operations failed") + _("One or more of the unset operations failed") ) diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index fe4765c7b4..3201da34bc 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -11,13 +11,15 @@ # under the License. import logging +import typing as ty from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command +from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -55,12 +57,12 @@ def _format_attachment(attachment): # VolumeAttachmentManager.create returns a dict while everything else # returns a VolumeAttachment object if isinstance(attachment, dict): - data = [] + data: tuple[ty.Any, ...] = () for column in columns: if column == 'connection_info': - data.append(format_columns.DictColumn(attachment[column])) + data += (format_columns.DictColumn(attachment[column]),) continue - data.append(attachment[column]) + data += (attachment[column],) else: data = utils.get_item_properties( attachment, @@ -171,7 +173,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( @@ -399,7 +401,7 @@ def get_parser(self, prog_name): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) parser.add_argument( @@ -457,7 +459,7 @@ def take_action(self, parsed_args): } # Update search option with `filters` # if AppendFilters.filters: - # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) + # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) # noqa: E501 # TODO(stephenfin): Implement sorting attachments = volume_client.attachments( diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 7052f608a4..df9a17eb03 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -18,21 +18,20 @@ import functools import logging -from cinderclient import api_versions from cliff import columns as cliff_columns from openstack import utils as sdk_utils from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -162,7 +161,7 @@ def take_action(self, parsed_args): kwargs['availability_zone'] = parsed_args.availability_zone - columns = ( + columns: tuple[str, ...] = ( "id", "name", "volume_id", @@ -237,6 +236,11 @@ class ListVolumeBackup(command.Lister): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + '--project', + metavar='', + help=_('Filter results by project (name or ID) (admin only)'), + ) parser.add_argument( "--long", action="store_true", @@ -297,22 +301,25 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume + identity_client = self.app.client_manager.sdk_connection.identity - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'description', 'status', 'size', 'is_incremental', + 'created_at', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Description', 'Status', 'Size', 'Incremental', + 'Created At', ) if parsed_args.long: columns += ('availability_zone', 'volume_id', 'container') @@ -323,14 +330,22 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) + all_tenants = parsed_args.all_projects + project_id = None + if parsed_args.project: + all_tenants = True + project_id = identity_client.find_project( + parsed_args.project, ignore_missing=False + ).id + filter_volume_id = None if parsed_args.volume: try: @@ -359,9 +374,10 @@ def take_action(self, parsed_args): name=parsed_args.name, status=parsed_args.status, volume_id=filter_volume_id, - all_tenants=parsed_args.all_projects, + all_tenants=all_tenants, marker=marker_backup_id, limit=parsed_args.limit, + project_id=project_id, ) return ( @@ -401,8 +417,7 @@ def get_parser(self, prog_name): "--force", action="store_true", help=_( - "Restore the backup to an existing volume " - "(default to False)" + "Restore the backup to an existing volume (default to False)" ), ) return parser @@ -410,6 +425,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume + columns: tuple[str, ...] = ( + 'id', + 'volume_id', + 'volume_name', + ) + backup = volume_client.find_backup( parsed_args.backup, ignore_missing=False, @@ -434,12 +455,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg % parsed_args.volume) - return volume_client.restore_backup( + restore = volume_client.restore_backup( backup.id, volume_id=volume_id, name=volume_name, ) + data = utils.get_dict_properties(restore, columns) + return (columns, data) + class SetVolumeBackup(command.Command): _description = _("Set volume backup properties") @@ -455,7 +479,7 @@ def get_parser(self, prog_name): '--name', metavar='', help=_( - 'New backup name' + 'New backup name ' '(supported by --os-volume-api-version 3.9 or above)' ), ) @@ -502,13 +526,19 @@ def get_parser(self, prog_name): 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) + volume_client = self.app.client_manager.sdk_connection.volume + + backup = volume_client.find_backup( + parsed_args.backup, + ignore_missing=False, + ) result = 0 if parsed_args.state: try: - volume_client.backups.reset_state(backup.id, parsed_args.state) + volume_client.reset_backup_status( + backup, status=parsed_args.state + ) except Exception as e: LOG.error(_("Failed to set backup state: %s"), e) result += 1 @@ -516,7 +546,7 @@ def take_action(self, parsed_args): kwargs = {} if parsed_args.name: - if volume_client.api_version < api_versions.APIVersion('3.9'): + if not sdk_utils.supports_microversion(volume_client, '3.9'): msg = _( '--os-volume-api-version 3.9 or greater is required to ' 'support the --name option' @@ -526,7 +556,7 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: - if volume_client.api_version < api_versions.APIVersion('3.9'): + if not sdk_utils.supports_microversion(volume_client, '3.9'): msg = _( '--os-volume-api-version 3.9 or greater is required to ' 'support the --description option' @@ -536,7 +566,7 @@ def take_action(self, parsed_args): kwargs['description'] = parsed_args.description if parsed_args.no_property: - if volume_client.api_version < api_versions.APIVersion('3.43'): + if not sdk_utils.supports_microversion(volume_client, '3.43'): msg = _( '--os-volume-api-version 3.43 or greater is required to ' 'support the --no-property option' @@ -544,14 +574,14 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.properties: - if volume_client.api_version < api_versions.APIVersion('3.43'): + if not sdk_utils.supports_microversion(volume_client, '3.43'): msg = _( '--os-volume-api-version 3.43 or greater is required to ' 'support the --property option' ) raise exceptions.CommandError(msg) - if volume_client.api_version >= api_versions.APIVersion('3.43'): + if sdk_utils.supports_microversion(volume_client, '3.43'): metadata = copy.deepcopy(backup.metadata) if parsed_args.no_property: @@ -562,7 +592,7 @@ def take_action(self, parsed_args): if kwargs: try: - volume_client.backups.update(backup.id, **kwargs) + volume_client.update_backup(backup, **kwargs) except Exception as e: LOG.error("Failed to update backup: %s", e) result += 1 @@ -598,16 +628,18 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume - if volume_client.api_version < api_versions.APIVersion('3.43'): + if not sdk_utils.supports_microversion(volume_client, '3.43'): msg = _( '--os-volume-api-version 3.43 or greater is required to ' 'support the --property option' ) raise exceptions.CommandError(msg) - backup = utils.find_resource(volume_client.backups, parsed_args.backup) + backup = volume_client.find_backup( + parsed_args.backup, ignore_missing=False + ) metadata = copy.deepcopy(backup.metadata) for key in parsed_args.properties: @@ -622,11 +654,7 @@ def take_action(self, parsed_args): del metadata[key] - kwargs = { - 'metadata': metadata, - } - - volume_client.backups.update(backup.id, **kwargs) + volume_client.delete_backup_metadata(backup, keys=list(metadata)) class ShowVolumeBackup(command.ShowOne): @@ -643,8 +671,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - backup = volume_client.get_backup(parsed_args.backup) - columns = ( + backup = volume_client.find_backup( + parsed_args.backup, ignore_missing=False + ) + columns: tuple[str, ...] = ( "availability_zone", "container", "created_at", diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index ba9368e013..1810feef56 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -13,10 +13,11 @@ import argparse from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command +from openstackclient.common import envvars from openstackclient.i18n import _ @@ -288,7 +289,7 @@ def get_parser(self, prog_name): default=False, help=_( 'Delete the volume group even if it contains volumes. ' - 'This will delete any remaining volumes in the group.', + 'This will delete any remaining volumes in the group.' ), ) return parser @@ -410,7 +411,7 @@ def get_parser(self, prog_name): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) # TODO(stephenfin): Add once we have an equivalent command for @@ -551,7 +552,7 @@ def take_action(self, parsed_args): parsed_args.group, ) - group = volume_client.groups.show(group.id, **kwargs) + group = volume_client.groups.get(group.id, **kwargs) if parsed_args.show_replication_targets: replication_targets = ( @@ -582,18 +583,14 @@ def get_parser(self, prog_name): action='store_true', dest='allow_attached_volume', default=False, - help=_( - 'Allow group with attached volumes to be failed over.', - ), + help=_('Allow group with attached volumes to be failed over.'), ) parser.add_argument( '--disallow-attached-volume', action='store_false', dest='allow_attached_volume', default=False, - help=_( - 'Disallow group with attached volumes to be failed over.', - ), + help=_('Disallow group with attached volumes to be failed over.'), ) parser.add_argument( '--secondary-backend-id', diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py index eabf700288..530b7d5d1a 100644 --- a/openstackclient/volume/v3/volume_group_snapshot.py +++ b/openstackclient/volume/v3/volume_group_snapshot.py @@ -13,10 +13,11 @@ import logging from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command +from openstackclient.common import envvars from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -145,7 +146,7 @@ def get_parser(self, prog_name): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) # TODO(stephenfin): Add once we have an equivalent command for diff --git a/openstackclient/volume/v3/volume_group_type.py b/openstackclient/volume/v3/volume_group_type.py index f1ef617409..bdedd25a19 100644 --- a/openstackclient/volume/v3/volume_group_type.py +++ b/openstackclient/volume/v3/volume_group_type.py @@ -15,10 +15,10 @@ from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/volume/v3/volume_message.py b/openstackclient/volume/v3/volume_message.py index 0fc724a586..b39c579469 100644 --- a/openstackclient/volume/v3/volume_message.py +++ b/openstackclient/volume/v3/volume_message.py @@ -17,10 +17,10 @@ import logging as LOG from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py new file mode 100644 index 0000000000..f89174c3da --- /dev/null +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -0,0 +1,573 @@ +# +# 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 snapshot action implementations""" + +import functools +import logging +import typing as ty + +from cliff import columns as cliff_columns +from openstack.block_storage.v3 import snapshot as _snapshot +from osc_lib.cli import format_columns +from osc_lib.cli import parseractions +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient import command +from openstackclient.common import pagination +from openstackclient.i18n import _ +from openstackclient.identity import common as identity_common + +LOG = logging.getLogger(__name__) + + +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): + """Formattable column for volume ID column. + + Unlike the parent FormattableColumn class, the initializer of the + class takes volume_cache as the second argument. + osc_lib.utils.get_item_properties instantiate cliff FormattableColumn + object with a single parameter "column value", so you need to pass + a partially initialized class like + ``functools.partial(VolumeIdColumn, volume_cache)``. + """ + + def __init__(self, value, volume_cache=None): + super().__init__(value) + self._volume_cache = volume_cache or {} + + def human_readable(self): + """Return a volume name if available + + :rtype: either the volume ID or name + """ + volume_id = self._value + volume = volume_id + if volume_id in self._volume_cache.keys(): + volume = self._volume_cache[volume_id].name + return volume + + +def _format_snapshot(snapshot: _snapshot.Snapshot) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'consumes_quota', + 'force', + 'group_snapshot_id', + # ignored columns + 'os-extended-snapshot-attributes:progress', + 'os-extended-snapshot-attributes:project_id', + 'updated_at', + 'user_id', + # unnecessary columns + 'links', + } + + info = snapshot.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + } + ) + + return data + + +class CreateVolumeSnapshot(command.ShowOne): + _description = _("Create new volume snapshot") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "snapshot_name", + metavar="", + help=_("Name of the new snapshot"), + ) + parser.add_argument( + "--volume", + metavar="", + help=_( + "Volume to snapshot (name or ID) (default is )" + ), + ) + parser.add_argument( + "--description", + metavar="", + help=_("Description of the snapshot"), + ) + parser.add_argument( + "--force", + action="store_true", + default=False, + help=_( + "Create a snapshot attached to an instance. Default is False" + ), + ) + parser.add_argument( + "--property", + metavar="", + dest='properties', + action=parseractions.KeyValueAction, + help=_( + "Set a property to this snapshot " + "(repeat option to set multiple properties)" + ), + ) + parser.add_argument( + "--remote-source", + metavar="", + action=parseractions.KeyValueAction, + 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'" + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + volume = parsed_args.volume + if not parsed_args.volume: + volume = parsed_args.snapshot_name + volume_id = volume_client.find_volume(volume, ignore_missing=False).id + + if parsed_args.remote_source: + # Create a new snapshot from an existing remote snapshot source + if parsed_args.force: + msg = _( + "'--force' option will not work when you create " + "new volume snapshot from an existing remote " + "volume snapshot" + ) + LOG.warning(msg) + + snapshot = volume_client.manage_snapshot( + volume_id=volume_id, + ref=parsed_args.remote_source, + name=parsed_args.snapshot_name, + description=parsed_args.description, + metadata=parsed_args.properties, + ) + else: + # Create a new snapshot from scratch + snapshot = volume_client.create_snapshot( + volume_id=volume_id, + force=parsed_args.force, + name=parsed_args.snapshot_name, + description=parsed_args.description, + metadata=parsed_args.properties, + ) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) + + +class DeleteVolumeSnapshot(command.Command): + _description = _("Delete volume snapshot(s)") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "snapshots", + metavar="", + nargs="+", + help=_("Snapshot(s) to delete (name or ID)"), + ) + parser.add_argument( + '--force', + action='store_true', + help=_( + "Attempt forced removal of snapshot(s), " + "regardless of state (defaults to False)" + ), + ) + parser.add_argument( + '--remote', + action='store_true', + help=_( + 'Unmanage the snapshot, removing it from the Block Storage ' + 'service management but not from the backend.' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + result = 0 + + if parsed_args.remote: + if parsed_args.force: + msg = _( + "The --force option is not supported with the " + "--remote parameter." + ) + raise exceptions.CommandError(msg) + + for snapshot in parsed_args.snapshots: + try: + snapshot_id = volume_client.find_snapshot( + snapshot, ignore_missing=False + ).id + if parsed_args.remote: + volume_client.unmanage_snapshot(snapshot_id) + else: + volume_client.delete_snapshot( + snapshot_id, force=parsed_args.force + ) + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete snapshot with " + "name or ID '%(snapshot)s': %(e)s" + ) + % {'snapshot': snapshot, 'e': e} + ) + + if result > 0: + total = len(parsed_args.snapshots) + msg = _("%(result)s of %(total)s snapshots failed to delete.") % { + 'result': result, + 'total': total, + } + raise exceptions.CommandError(msg) + + +class ListVolumeSnapshot(command.Lister): + _description = _("List volume snapshots") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + '--all-projects', + action='store_true', + default=False, + help=_('Include all projects (admin only)'), + ) + parser.add_argument( + '--project', + metavar='', + help=_('Filter results by project (name or ID) (admin only)'), + ) + identity_common.add_project_domain_option_to_parser(parser) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_('List additional fields in output'), + ) + parser.add_argument( + '--name', + metavar='', + default=None, + help=_('Filters results by a name.'), + ) + parser.add_argument( + '--status', + metavar='', + choices=[ + 'available', + 'error', + 'creating', + 'deleting', + 'error_deleting', + ], + help=_( + "Filters results by a status. " + "('available', 'error', 'creating', 'deleting'" + " or 'error_deleting')" + ), + ) + parser.add_argument( + '--volume', + metavar='', + default=None, + help=_('Filters results by a volume (name or ID).'), + ) + pagination.add_marker_pagination_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + identity_client = self.app.client_manager.identity + + columns: tuple[str, ...] = ( + 'id', + 'name', + 'description', + 'status', + 'size', + ) + column_headers: tuple[str, ...] = ( + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + ) + if parsed_args.long: + columns += ( + 'created_at', + 'volume_id', + 'metadata', + ) + column_headers += ( + 'Created At', + 'Volume', + 'Properties', + ) + + # Cache the volume list + volume_cache = {} + try: + for s in volume_client.volumes(): + volume_cache[s.id] = s + except Exception: # noqa: S110 + # Just forget it if there's any trouble + pass + _VolumeIdColumn = functools.partial( + VolumeIdColumn, volume_cache=volume_cache + ) + + volume_id = None + if parsed_args.volume: + volume_id = volume_client.find_volume( + parsed_args.volume, ignore_missing=False + ).id + + project_id = None + if parsed_args.project: + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + + # set value of 'all_tenants' when using project option + all_projects = ( + True if parsed_args.project else parsed_args.all_projects + ) + + data = volume_client.snapshots( + marker=parsed_args.marker, + limit=parsed_args.limit, + all_projects=all_projects, + project_id=project_id, + name=parsed_args.name, + status=parsed_args.status, + volume_id=volume_id, + ) + return ( + column_headers, + ( + utils.get_item_properties( + s, + columns, + formatters={ + 'metadata': format_columns.DictColumn, + 'volume_id': _VolumeIdColumn, + }, + ) + for s in data + ), + ) + + +class SetVolumeSnapshot(command.Command): + _description = _("Set volume snapshot properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'snapshot', + metavar='', + help=_('Snapshot to modify (name or ID)'), + ) + parser.add_argument( + '--name', metavar='', help=_('New snapshot name') + ) + parser.add_argument( + '--description', + metavar='', + help=_('New snapshot description'), + ) + parser.add_argument( + "--no-property", + dest="no_property", + action="store_true", + help=_( + "Remove all properties from " + "(specify both --no-property and --property to " + "remove the current properties before setting " + "new properties.)" + ), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Property to add/change for this snapshot ' + '(repeat option to set multiple properties)' + ), + ) + parser.add_argument( + '--state', + metavar='', + choices=[ + 'available', + 'error', + 'creating', + 'deleting', + 'error_deleting', + ], + help=_( + 'New snapshot state. ("available", "error", "creating", ' + '"deleting", or "error_deleting") (admin only) ' + '(This option simply changes the state of the snapshot ' + 'in the database with no regard to actual status, ' + 'exercise caution when using)' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False + ) + + result = 0 + if parsed_args.no_property: + try: + volume_client.delete_snapshot_metadata( + snapshot.id, keys=list(snapshot.metadata) + ) + except Exception as e: + LOG.error(_("Failed to clean snapshot properties: %s"), e) + result += 1 + + if parsed_args.properties: + try: + volume_client.set_snapshot_metadata( + snapshot.id, **parsed_args.properties + ) + except Exception as e: + LOG.error(_("Failed to set snapshot property: %s"), e) + result += 1 + + if parsed_args.state: + try: + volume_client.reset_snapshot_status( + snapshot.id, parsed_args.state + ) + except Exception as e: + LOG.error(_("Failed to set snapshot state: %s"), e) + result += 1 + + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + if parsed_args.description: + kwargs['description'] = parsed_args.description + if kwargs: + try: + volume_client.update_snapshot(snapshot.id, **kwargs) + except Exception as e: + LOG.error( + _("Failed to update snapshot name or description: %s"), + e, + ) + result += 1 + + if result > 0: + raise exceptions.CommandError( + _("One or more of the set operations failed") + ) + + +class ShowVolumeSnapshot(command.ShowOne): + _description = _("Display volume snapshot details") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "snapshot", + metavar="", + help=_("Snapshot to display (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False + ) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) + + +class UnsetVolumeSnapshot(command.Command): + _description = _("Unset volume snapshot properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'snapshot', + metavar='', + help=_('Snapshot to modify (name or ID)'), + ) + parser.add_argument( + '--property', + metavar='', + dest='properties', + action='append', + default=[], + help=_( + 'Property to remove from snapshot ' + '(repeat option to remove multiple properties)' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False + ) + + if parsed_args.properties: + volume_client.delete_snapshot_metadata( + snapshot.id, keys=parsed_args.properties + ) diff --git a/openstackclient/volume/v3/volume_transfer_request.py b/openstackclient/volume/v3/volume_transfer_request.py index b7add12280..afd4626038 100644 --- a/openstackclient/volume/v3/volume_transfer_request.py +++ b/openstackclient/volume/v3/volume_transfer_request.py @@ -17,10 +17,10 @@ import logging from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index 96d7553d4a..fbce2f2c9a 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -10,21 +10,21 @@ # 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 Type action implementations""" import functools import logging +import typing as ty from cinderclient import api_versions from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -32,7 +32,7 @@ LOG = logging.getLogger(__name__) -class EncryptionInfoColumn(cliff_columns.FormattableColumn): +class EncryptionInfoColumn(cliff_columns.FormattableColumn[ty.Any]): """Formattable column for encryption info column. Unlike the parent FormattableColumn class, the initializer of the @@ -172,7 +172,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -182,7 +183,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -273,8 +275,7 @@ def take_action(self, parsed_args): ) except Exception as e: msg = _( - "Failed to add project %(project)s access to " - "type: %(e)s" + "Failed to add project %(project)s access to type: %(e)s" ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) @@ -364,7 +365,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volume_types) msg = _( - "%(result)s of %(total)s volume types failed " "to delete." + "%(result)s of %(total)s volume types failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -448,7 +449,8 @@ def get_parser(self, prog_name): default=False, help=_( "List only volume types with replication enabled " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for " + "'--property replication_enabled= True') " "(supported by --os-volume-api-version 3.52 or above)" ), ) @@ -458,7 +460,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "List only volume types with this availability configured " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to filter on multiple availability zones)" ), ) @@ -617,7 +620,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -627,7 +631,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -635,8 +640,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Set volume type access to project (name or ID) ' - '(admin only)' + 'Set volume type access to project (name or ID) (admin only)' ), ) public_group = parser.add_mutually_exclusive_group() @@ -728,17 +732,12 @@ def take_action(self, parsed_args): volume_client.volume_types.update(volume_type.id, **kwargs) except Exception as e: LOG.error( - _( - "Failed to update volume type name or" - " description: %s" - ), + _("Failed to update volume type name or description: %s"), e, ) result += 1 - properties = {} - - properties = {} + properties: dict[str, str] = {} if parsed_args.properties: properties.update(parsed_args.properties) if parsed_args.multiattach: @@ -772,7 +771,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _("Failed to set volume type access to " "project: %s"), e + _("Failed to set volume type access to project: %s"), e ) result += 1 @@ -796,7 +795,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -904,8 +903,7 @@ def get_parser(self, prog_name): "--encryption-type", action="store_true", help=_( - "Remove the encryption type for this volume type " - "(admin only)" + "Remove the encryption type for this volume type (admin only)" ), ) return parser @@ -941,10 +939,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to remove volume type access from " - "project: %s" - ), + _("Failed to remove volume type access from project: %s"), e, ) result += 1 @@ -963,5 +958,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..37fb8d0c26 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,772 @@ +[build-system] +requires = ["pbr>=6.1.1"] +build-backend = "pbr.build" + +[project] +name = "python-openstackclient" +description = "OpenStack Command-line Client" +authors = [ + {name = "OpenStack", email = "openstack-discuss@lists.openstack.org"}, +] +readme = {file = "README.rst", content-type = "text/x-rst"} +license = {text = "Apache-2.0"} +dynamic = ["version", "dependencies"] +# dependencies = [ ] +requires-python = ">=3.10" +classifiers = [ + "Environment :: OpenStack", + "Intended Audience :: Information Technology", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] + +# [project.optional-dependencies] +# test = [ +# ] + +[project.urls] +Homepage = "https://docs.openstack.org/python-openstackclient/" +Repository = "https://opendev.org/openstack/python-openstackclient/" + +[project.scripts] +openstack = "openstackclient.shell:main" + +[project.entry-points."openstack.cli"] +command_list = "openstackclient.common.module:ListCommand" +module_list = "openstackclient.common.module:ListModule" + +[project.entry-points."openstack.cli.base"] +compute = "openstackclient.compute.client" +identity = "openstackclient.identity.client" +image = "openstackclient.image.client" +network = "openstackclient.network.client" +object_store = "openstackclient.object.client" +volume = "openstackclient.volume.client" + +[project.entry-points."openstack.common"] +availability_zone_list = "openstackclient.common.availability_zone:ListAvailabilityZone" +configuration_show = "openstackclient.common.configuration:ShowConfiguration" +extension_list = "openstackclient.common.extension:ListExtension" +extension_show = "openstackclient.common.extension:ShowExtension" +limits_show = "openstackclient.common.limits:ShowLimits" +project_cleanup = "openstackclient.common.project_cleanup:ProjectCleanup" +quota_list = "openstackclient.common.quota:ListQuota" +quota_set = "openstackclient.common.quota:SetQuota" +quota_show = "openstackclient.common.quota:ShowQuota" +quota_delete = "openstackclient.common.quota:DeleteQuota" +versions_show = "openstackclient.common.versions:ShowVersions" + +[project.entry-points."openstack.compute.v2"] +aggregate_add_host = "openstackclient.compute.v2.aggregate:AddAggregateHost" +aggregate_create = "openstackclient.compute.v2.aggregate:CreateAggregate" +aggregate_delete = "openstackclient.compute.v2.aggregate:DeleteAggregate" +aggregate_list = "openstackclient.compute.v2.aggregate:ListAggregate" +aggregate_remove_host = "openstackclient.compute.v2.aggregate:RemoveAggregateHost" +aggregate_set = "openstackclient.compute.v2.aggregate:SetAggregate" +aggregate_show = "openstackclient.compute.v2.aggregate:ShowAggregate" +aggregate_unset = "openstackclient.compute.v2.aggregate:UnsetAggregate" +aggregate_cache_image = "openstackclient.compute.v2.aggregate:CacheImageForAggregate" +compute_agent_create = "openstackclient.compute.v2.agent:CreateAgent" +compute_agent_delete = "openstackclient.compute.v2.agent:DeleteAgent" +compute_agent_list = "openstackclient.compute.v2.agent:ListAgent" +compute_agent_set = "openstackclient.compute.v2.agent:SetAgent" +compute_service_delete = "openstackclient.compute.v2.service:DeleteService" +compute_service_list = "openstackclient.compute.v2.service:ListService" +compute_service_set = "openstackclient.compute.v2.service:SetService" +console_log_show = "openstackclient.compute.v2.console:ShowConsoleLog" +console_url_show = "openstackclient.compute.v2.console:ShowConsoleURL" +console_connection_show = "openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation" +flavor_create = "openstackclient.compute.v2.flavor:CreateFlavor" +flavor_delete = "openstackclient.compute.v2.flavor:DeleteFlavor" +flavor_list = "openstackclient.compute.v2.flavor:ListFlavor" +flavor_show = "openstackclient.compute.v2.flavor:ShowFlavor" +flavor_set = "openstackclient.compute.v2.flavor:SetFlavor" +flavor_unset = "openstackclient.compute.v2.flavor:UnsetFlavor" +host_list = "openstackclient.compute.v2.host:ListHost" +host_set = "openstackclient.compute.v2.host:SetHost" +host_show = "openstackclient.compute.v2.host:ShowHost" +hypervisor_list = "openstackclient.compute.v2.hypervisor:ListHypervisor" +hypervisor_show = "openstackclient.compute.v2.hypervisor:ShowHypervisor" +hypervisor_stats_show = "openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats" +keypair_create = "openstackclient.compute.v2.keypair:CreateKeypair" +keypair_delete = "openstackclient.compute.v2.keypair:DeleteKeypair" +keypair_list = "openstackclient.compute.v2.keypair:ListKeypair" +keypair_show = "openstackclient.compute.v2.keypair:ShowKeypair" +server_add_fixed_ip = "openstackclient.compute.v2.server:AddFixedIP" +server_add_floating_ip = "openstackclient.compute.v2.server:AddFloatingIP" +server_add_port = "openstackclient.compute.v2.server:AddPort" +server_add_network = "openstackclient.compute.v2.server:AddNetwork" +server_add_security_group = "openstackclient.compute.v2.server:AddServerSecurityGroup" +server_add_volume = "openstackclient.compute.v2.server:AddServerVolume" +server_create = "openstackclient.compute.v2.server:CreateServer" +server_delete = "openstackclient.compute.v2.server:DeleteServer" +server_dump_create = "openstackclient.compute.v2.server:CreateServerDump" +server_evacuate = "openstackclient.compute.v2.server:EvacuateServer" +server_list = "openstackclient.compute.v2.server:ListServer" +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_confirm = "openstackclient.compute.v2.server:ConfirmMigration" +server_migration_revert = "openstackclient.compute.v2.server:RevertMigration" +server_pause = "openstackclient.compute.v2.server:PauseServer" +server_reboot = "openstackclient.compute.v2.server:RebootServer" +server_rebuild = "openstackclient.compute.v2.server:RebuildServer" +server_remove_fixed_ip = "openstackclient.compute.v2.server:RemoveFixedIP" +server_remove_floating_ip = "openstackclient.compute.v2.server:RemoveFloatingIP" +server_remove_port = "openstackclient.compute.v2.server:RemovePort" +server_remove_network = "openstackclient.compute.v2.server:RemoveNetwork" +server_remove_security_group = "openstackclient.compute.v2.server:RemoveServerSecurityGroup" +server_remove_volume = "openstackclient.compute.v2.server:RemoveServerVolume" +server_rescue = "openstackclient.compute.v2.server:RescueServer" +server_resize = "openstackclient.compute.v2.server:ResizeServer" +server_resize_confirm = "openstackclient.compute.v2.server:ResizeConfirm" +server_resize_revert = "openstackclient.compute.v2.server:ResizeRevert" +server_restore = "openstackclient.compute.v2.server:RestoreServer" +server_resume = "openstackclient.compute.v2.server:ResumeServer" +server_set = "openstackclient.compute.v2.server:SetServer" +server_shelve = "openstackclient.compute.v2.server:ShelveServer" +server_show = "openstackclient.compute.v2.server:ShowServer" +server_ssh = "openstackclient.compute.v2.server:SshServer" +server_start = "openstackclient.compute.v2.server:StartServer" +server_stop = "openstackclient.compute.v2.server:StopServer" +server_suspend = "openstackclient.compute.v2.server:SuspendServer" +server_unlock = "openstackclient.compute.v2.server:UnlockServer" +server_unpause = "openstackclient.compute.v2.server:UnpauseServer" +server_unrescue = "openstackclient.compute.v2.server:UnrescueServer" +server_unset = "openstackclient.compute.v2.server:UnsetServer" +server_unshelve = "openstackclient.compute.v2.server:UnshelveServer" +server_backup_create = "openstackclient.compute.v2.server_backup:CreateServerBackup" +server_event_list = "openstackclient.compute.v2.server_event:ListServerEvent" +server_event_show = "openstackclient.compute.v2.server_event:ShowServerEvent" +server_group_create = "openstackclient.compute.v2.server_group:CreateServerGroup" +server_group_delete = "openstackclient.compute.v2.server_group:DeleteServerGroup" +server_group_list = "openstackclient.compute.v2.server_group:ListServerGroup" +server_group_show = "openstackclient.compute.v2.server_group:ShowServerGroup" +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_set = "openstackclient.compute.v2.server_volume:SetServerVolume" +server_volume_update = "openstackclient.compute.v2.server_volume:UpdateServerVolume" +usage_list = "openstackclient.compute.v2.usage:ListUsage" +usage_show = "openstackclient.compute.v2.usage:ShowUsage" + +[project.entry-points."openstack.identity.v2"] +catalog_list = "openstackclient.identity.v2_0.catalog:ListCatalog" +catalog_show = "openstackclient.identity.v2_0.catalog:ShowCatalog" +ec2_credentials_create = "openstackclient.identity.v2_0.ec2creds:CreateEC2Creds" +ec2_credentials_delete = "openstackclient.identity.v2_0.ec2creds:DeleteEC2Creds" +ec2_credentials_list = "openstackclient.identity.v2_0.ec2creds:ListEC2Creds" +ec2_credentials_show = "openstackclient.identity.v2_0.ec2creds:ShowEC2Creds" +endpoint_create = "openstackclient.identity.v2_0.endpoint:CreateEndpoint" +endpoint_delete = "openstackclient.identity.v2_0.endpoint:DeleteEndpoint" +endpoint_list = "openstackclient.identity.v2_0.endpoint:ListEndpoint" +endpoint_show = "openstackclient.identity.v2_0.endpoint:ShowEndpoint" +project_create = "openstackclient.identity.v2_0.project:CreateProject" +project_delete = "openstackclient.identity.v2_0.project:DeleteProject" +project_list = "openstackclient.identity.v2_0.project:ListProject" +project_set = "openstackclient.identity.v2_0.project:SetProject" +project_show = "openstackclient.identity.v2_0.project:ShowProject" +project_unset = "openstackclient.identity.v2_0.project:UnsetProject" +role_add = "openstackclient.identity.v2_0.role:AddRole" +role_create = "openstackclient.identity.v2_0.role:CreateRole" +role_delete = "openstackclient.identity.v2_0.role:DeleteRole" +role_list = "openstackclient.identity.v2_0.role:ListRole" +role_remove = "openstackclient.identity.v2_0.role:RemoveRole" +role_show = "openstackclient.identity.v2_0.role:ShowRole" +role_assignment_list = "openstackclient.identity.v2_0.role_assignment:ListRoleAssignment" +service_create = "openstackclient.identity.v2_0.service:CreateService" +service_delete = "openstackclient.identity.v2_0.service:DeleteService" +service_list = "openstackclient.identity.v2_0.service:ListService" +service_show = "openstackclient.identity.v2_0.service:ShowService" +token_issue = "openstackclient.identity.v2_0.token:IssueToken" +token_revoke = "openstackclient.identity.v2_0.token:RevokeToken" +user_create = "openstackclient.identity.v2_0.user:CreateUser" +user_delete = "openstackclient.identity.v2_0.user:DeleteUser" +user_list = "openstackclient.identity.v2_0.user:ListUser" +user_set = "openstackclient.identity.v2_0.user:SetUser" +user_show = "openstackclient.identity.v2_0.user:ShowUser" + +[project.entry-points."openstack.identity.v3"] +access_token_create = "openstackclient.identity.v3.token:CreateAccessToken" +access_rule_delete = "openstackclient.identity.v3.access_rule:DeleteAccessRule" +access_rule_list = "openstackclient.identity.v3.access_rule:ListAccessRule" +access_rule_show = "openstackclient.identity.v3.access_rule:ShowAccessRule" +application_credential_create = "openstackclient.identity.v3.application_credential:CreateApplicationCredential" +application_credential_delete = "openstackclient.identity.v3.application_credential:DeleteApplicationCredential" +application_credential_list = "openstackclient.identity.v3.application_credential:ListApplicationCredential" +application_credential_show = "openstackclient.identity.v3.application_credential:ShowApplicationCredential" +catalog_list = "openstackclient.identity.v3.catalog:ListCatalog" +catalog_show = "openstackclient.identity.v3.catalog:ShowCatalog" +consumer_create = "openstackclient.identity.v3.consumer:CreateConsumer" +consumer_delete = "openstackclient.identity.v3.consumer:DeleteConsumer" +consumer_list = "openstackclient.identity.v3.consumer:ListConsumer" +consumer_set = "openstackclient.identity.v3.consumer:SetConsumer" +consumer_show = "openstackclient.identity.v3.consumer:ShowConsumer" +credential_create = "openstackclient.identity.v3.credential:CreateCredential" +credential_delete = "openstackclient.identity.v3.credential:DeleteCredential" +credential_list = "openstackclient.identity.v3.credential:ListCredential" +credential_set = "openstackclient.identity.v3.credential:SetCredential" +credential_show = "openstackclient.identity.v3.credential:ShowCredential" +domain_create = "openstackclient.identity.v3.domain:CreateDomain" +domain_delete = "openstackclient.identity.v3.domain:DeleteDomain" +domain_list = "openstackclient.identity.v3.domain:ListDomain" +domain_set = "openstackclient.identity.v3.domain:SetDomain" +domain_show = "openstackclient.identity.v3.domain:ShowDomain" +ec2_credentials_create = "openstackclient.identity.v3.ec2creds:CreateEC2Creds" +ec2_credentials_delete = "openstackclient.identity.v3.ec2creds:DeleteEC2Creds" +ec2_credentials_list = "openstackclient.identity.v3.ec2creds:ListEC2Creds" +ec2_credentials_show = "openstackclient.identity.v3.ec2creds:ShowEC2Creds" +endpoint_add_project = "openstackclient.identity.v3.endpoint:AddProjectToEndpoint" +endpoint_create = "openstackclient.identity.v3.endpoint:CreateEndpoint" +endpoint_delete = "openstackclient.identity.v3.endpoint:DeleteEndpoint" +endpoint_list = "openstackclient.identity.v3.endpoint:ListEndpoint" +endpoint_remove_project = "openstackclient.identity.v3.endpoint:RemoveProjectFromEndpoint" +endpoint_set = "openstackclient.identity.v3.endpoint:SetEndpoint" +endpoint_show = "openstackclient.identity.v3.endpoint:ShowEndpoint" +endpoint_group_add_project = "openstackclient.identity.v3.endpoint_group:AddProjectToEndpointGroup" +endpoint_group_create = "openstackclient.identity.v3.endpoint_group:CreateEndpointGroup" +endpoint_group_delete = "openstackclient.identity.v3.endpoint_group:DeleteEndpointGroup" +endpoint_group_list = "openstackclient.identity.v3.endpoint_group:ListEndpointGroup" +endpoint_group_remove_project = "openstackclient.identity.v3.endpoint_group:RemoveProjectFromEndpointGroup" +endpoint_group_set = "openstackclient.identity.v3.endpoint_group:SetEndpointGroup" +endpoint_group_show = "openstackclient.identity.v3.endpoint_group:ShowEndpointGroup" +federation_domain_list = "openstackclient.identity.v3.unscoped_saml:ListAccessibleDomains" +federation_project_list = "openstackclient.identity.v3.unscoped_saml:ListAccessibleProjects" +federation_protocol_create = "openstackclient.identity.v3.federation_protocol:CreateProtocol" +federation_protocol_delete = "openstackclient.identity.v3.federation_protocol:DeleteProtocol" +federation_protocol_list = "openstackclient.identity.v3.federation_protocol:ListProtocols" +federation_protocol_set = "openstackclient.identity.v3.federation_protocol:SetProtocol" +federation_protocol_show = "openstackclient.identity.v3.federation_protocol:ShowProtocol" +group_add_user = "openstackclient.identity.v3.group:AddUserToGroup" +group_contains_user = "openstackclient.identity.v3.group:CheckUserInGroup" +group_create = "openstackclient.identity.v3.group:CreateGroup" +group_delete = "openstackclient.identity.v3.group:DeleteGroup" +group_list = "openstackclient.identity.v3.group:ListGroup" +group_remove_user = "openstackclient.identity.v3.group:RemoveUserFromGroup" +group_set = "openstackclient.identity.v3.group:SetGroup" +group_show = "openstackclient.identity.v3.group:ShowGroup" +identity_provider_create = "openstackclient.identity.v3.identity_provider:CreateIdentityProvider" +identity_provider_delete = "openstackclient.identity.v3.identity_provider:DeleteIdentityProvider" +identity_provider_list = "openstackclient.identity.v3.identity_provider:ListIdentityProvider" +identity_provider_set = "openstackclient.identity.v3.identity_provider:SetIdentityProvider" +identity_provider_show = "openstackclient.identity.v3.identity_provider:ShowIdentityProvider" +implied_role_create = "openstackclient.identity.v3.implied_role:CreateImpliedRole" +implied_role_delete = "openstackclient.identity.v3.implied_role:DeleteImpliedRole" +implied_role_list = "openstackclient.identity.v3.implied_role:ListImpliedRole" +limit_create = "openstackclient.identity.v3.limit:CreateLimit" +limit_delete = "openstackclient.identity.v3.limit:DeleteLimit" +limit_list = "openstackclient.identity.v3.limit:ListLimit" +limit_set = "openstackclient.identity.v3.limit:SetLimit" +limit_show = "openstackclient.identity.v3.limit:ShowLimit" +mapping_create = "openstackclient.identity.v3.mapping:CreateMapping" +mapping_delete = "openstackclient.identity.v3.mapping:DeleteMapping" +mapping_list = "openstackclient.identity.v3.mapping:ListMapping" +mapping_set = "openstackclient.identity.v3.mapping:SetMapping" +mapping_show = "openstackclient.identity.v3.mapping:ShowMapping" +policy_create = "openstackclient.identity.v3.policy:CreatePolicy" +policy_delete = "openstackclient.identity.v3.policy:DeletePolicy" +policy_list = "openstackclient.identity.v3.policy:ListPolicy" +policy_set = "openstackclient.identity.v3.policy:SetPolicy" +policy_show = "openstackclient.identity.v3.policy:ShowPolicy" +project_create = "openstackclient.identity.v3.project:CreateProject" +project_delete = "openstackclient.identity.v3.project:DeleteProject" +project_list = "openstackclient.identity.v3.project:ListProject" +project_set = "openstackclient.identity.v3.project:SetProject" +project_show = "openstackclient.identity.v3.project:ShowProject" +region_create = "openstackclient.identity.v3.region:CreateRegion" +region_delete = "openstackclient.identity.v3.region:DeleteRegion" +region_list = "openstackclient.identity.v3.region:ListRegion" +region_set = "openstackclient.identity.v3.region:SetRegion" +region_show = "openstackclient.identity.v3.region:ShowRegion" +registered_limit_create = "openstackclient.identity.v3.registered_limit:CreateRegisteredLimit" +registered_limit_delete = "openstackclient.identity.v3.registered_limit:DeleteRegisteredLimit" +registered_limit_list = "openstackclient.identity.v3.registered_limit:ListRegisteredLimit" +registered_limit_set = "openstackclient.identity.v3.registered_limit:SetRegisteredLimit" +registered_limit_show = "openstackclient.identity.v3.registered_limit:ShowRegisteredLimit" +request_token_authorize = "openstackclient.identity.v3.token:AuthorizeRequestToken" +request_token_create = "openstackclient.identity.v3.token:CreateRequestToken" +role_add = "openstackclient.identity.v3.role:AddRole" +role_create = "openstackclient.identity.v3.role:CreateRole" +role_delete = "openstackclient.identity.v3.role:DeleteRole" +role_list = "openstackclient.identity.v3.role:ListRole" +role_remove = "openstackclient.identity.v3.role:RemoveRole" +role_show = "openstackclient.identity.v3.role:ShowRole" +role_set = "openstackclient.identity.v3.role:SetRole" +role_assignment_list = "openstackclient.identity.v3.role_assignment:ListRoleAssignment" +service_create = "openstackclient.identity.v3.service:CreateService" +service_delete = "openstackclient.identity.v3.service:DeleteService" +service_list = "openstackclient.identity.v3.service:ListService" +service_show = "openstackclient.identity.v3.service:ShowService" +service_set = "openstackclient.identity.v3.service:SetService" +service_provider_create = "openstackclient.identity.v3.service_provider:CreateServiceProvider" +service_provider_delete = "openstackclient.identity.v3.service_provider:DeleteServiceProvider" +service_provider_list = "openstackclient.identity.v3.service_provider:ListServiceProvider" +service_provider_set = "openstackclient.identity.v3.service_provider:SetServiceProvider" +service_provider_show = "openstackclient.identity.v3.service_provider:ShowServiceProvider" +token_issue = "openstackclient.identity.v3.token:IssueToken" +token_revoke = "openstackclient.identity.v3.token:RevokeToken" +trust_create = "openstackclient.identity.v3.trust:CreateTrust" +trust_delete = "openstackclient.identity.v3.trust:DeleteTrust" +trust_list = "openstackclient.identity.v3.trust:ListTrust" +trust_show = "openstackclient.identity.v3.trust:ShowTrust" +user_create = "openstackclient.identity.v3.user:CreateUser" +user_delete = "openstackclient.identity.v3.user:DeleteUser" +user_list = "openstackclient.identity.v3.user:ListUser" +user_set = "openstackclient.identity.v3.user:SetUser" +user_password_set = "openstackclient.identity.v3.user:SetPasswordUser" +user_show = "openstackclient.identity.v3.user:ShowUser" + +[project.entry-points."openstack.image.v1"] +image_create = "openstackclient.image.v1.image:CreateImage" +image_delete = "openstackclient.image.v1.image:DeleteImage" +image_list = "openstackclient.image.v1.image:ListImage" +image_save = "openstackclient.image.v1.image:SaveImage" +image_set = "openstackclient.image.v1.image:SetImage" +image_show = "openstackclient.image.v1.image:ShowImage" + +[project.entry-points."openstack.image.v2"] +image_add_project = "openstackclient.image.v2.image:AddProjectToImage" +image_create = "openstackclient.image.v2.image:CreateImage" +image_delete = "openstackclient.image.v2.image:DeleteImage" +image_list = "openstackclient.image.v2.image:ListImage" +image_member_list = "openstackclient.image.v2.image:ListImageProjects" +image_remove_project = "openstackclient.image.v2.image:RemoveProjectImage" +image_member_get = "openstackclient.image.v2.image:ShowProjectImage" +image_save = "openstackclient.image.v2.image:SaveImage" +image_show = "openstackclient.image.v2.image:ShowImage" +image_set = "openstackclient.image.v2.image:SetImage" +image_unset = "openstackclient.image.v2.image:UnsetImage" +image_stage = "openstackclient.image.v2.image:StageImage" +image_task_show = "openstackclient.image.v2.task:ShowTask" +image_task_list = "openstackclient.image.v2.task:ListTask" +image_import_info = "openstackclient.image.v2.info:ImportInfo" +image_import = "openstackclient.image.v2.image:ImportImage" +image_stores_list = "openstackclient.image.v2.image:StoresInfo" +image_metadef_namespace_create = "openstackclient.image.v2.metadef_namespaces:CreateMetadefNamespace" +image_metadef_namespace_delete = "openstackclient.image.v2.metadef_namespaces:DeleteMetadefNamespace" +image_metadef_namespace_list = "openstackclient.image.v2.metadef_namespaces:ListMetadefNamespace" +image_metadef_namespace_set = "openstackclient.image.v2.metadef_namespaces:SetMetadefNamespace" +image_metadef_namespace_show = "openstackclient.image.v2.metadef_namespaces:ShowMetadefNamespace" +image_metadef_object_create = "openstackclient.image.v2.metadef_objects:CreateMetadefObjects" +image_metadef_object_show = "openstackclient.image.v2.metadef_objects:ShowMetadefObjects" +image_metadef_object_list = "openstackclient.image.v2.metadef_objects:ListMetadefObjects" +image_metadef_object_delete = "openstackclient.image.v2.metadef_objects:DeleteMetadefObject" +image_metadef_object_update = "openstackclient.image.v2.metadef_objects:SetMetadefObject" +image_metadef_object_property_show = "openstackclient.image.v2.metadef_objects:ShowMetadefObjectProperty" +image_metadef_property_create = "openstackclient.image.v2.metadef_properties:CreateMetadefProperty" +image_metadef_property_delete = "openstackclient.image.v2.metadef_properties:DeleteMetadefProperty" +image_metadef_property_list = "openstackclient.image.v2.metadef_properties:ListMetadefProperties" +image_metadef_property_set = "openstackclient.image.v2.metadef_properties:SetMetadefProperty" +image_metadef_property_show = "openstackclient.image.v2.metadef_properties:ShowMetadefProperty" +image_metadef_resource_type_list = "openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes" +image_metadef_resource_type_association_create = "openstackclient.image.v2.metadef_resource_type_association:CreateMetadefResourceTypeAssociation" +image_metadef_resource_type_association_delete = "openstackclient.image.v2.metadef_resource_type_association:DeleteMetadefResourceTypeAssociation" +image_metadef_resource_type_association_list = "openstackclient.image.v2.metadef_resource_type_association:ListMetadefResourceTypeAssociations" +cached_image_list = "openstackclient.image.v2.cache:ListCachedImage" +cached_image_queue = "openstackclient.image.v2.cache:QueueCachedImage" +cached_image_delete = "openstackclient.image.v2.cache:DeleteCachedImage" +cached_image_clear = "openstackclient.image.v2.cache:ClearCachedImage" + +[project.entry-points."openstack.network.v2"] +address_group_create = "openstackclient.network.v2.address_group:CreateAddressGroup" +address_group_delete = "openstackclient.network.v2.address_group:DeleteAddressGroup" +address_group_list = "openstackclient.network.v2.address_group:ListAddressGroup" +address_group_set = "openstackclient.network.v2.address_group:SetAddressGroup" +address_group_show = "openstackclient.network.v2.address_group:ShowAddressGroup" +address_group_unset = "openstackclient.network.v2.address_group:UnsetAddressGroup" +address_scope_create = "openstackclient.network.v2.address_scope:CreateAddressScope" +address_scope_delete = "openstackclient.network.v2.address_scope:DeleteAddressScope" +address_scope_list = "openstackclient.network.v2.address_scope:ListAddressScope" +address_scope_set = "openstackclient.network.v2.address_scope:SetAddressScope" +address_scope_show = "openstackclient.network.v2.address_scope:ShowAddressScope" +floating_ip_create = "openstackclient.network.v2.floating_ip:CreateFloatingIP" +floating_ip_delete = "openstackclient.network.v2.floating_ip:DeleteFloatingIP" +floating_ip_list = "openstackclient.network.v2.floating_ip:ListFloatingIP" +floating_ip_set = "openstackclient.network.v2.floating_ip:SetFloatingIP" +floating_ip_show = "openstackclient.network.v2.floating_ip:ShowFloatingIP" +floating_ip_unset = "openstackclient.network.v2.floating_ip:UnsetFloatingIP" +floating_ip_pool_list = "openstackclient.network.v2.floating_ip_pool:ListFloatingIPPool" +floating_ip_port_forwarding_create = "openstackclient.network.v2.floating_ip_port_forwarding:CreateFloatingIPPortForwarding" +floating_ip_port_forwarding_delete = "openstackclient.network.v2.floating_ip_port_forwarding:DeleteFloatingIPPortForwarding" +floating_ip_port_forwarding_list = "openstackclient.network.v2.floating_ip_port_forwarding:ListFloatingIPPortForwarding" +floating_ip_port_forwarding_set = "openstackclient.network.v2.floating_ip_port_forwarding:SetFloatingIPPortForwarding" +floating_ip_port_forwarding_show = "openstackclient.network.v2.floating_ip_port_forwarding:ShowFloatingIPPortForwarding" +ip_availability_list = "openstackclient.network.v2.ip_availability:ListIPAvailability" +ip_availability_show = "openstackclient.network.v2.ip_availability:ShowIPAvailability" +local_ip_create = "openstackclient.network.v2.local_ip:CreateLocalIP" +local_ip_delete = "openstackclient.network.v2.local_ip:DeleteLocalIP" +local_ip_list = "openstackclient.network.v2.local_ip:ListLocalIP" +local_ip_set = "openstackclient.network.v2.local_ip:SetLocalIP" +local_ip_show = "openstackclient.network.v2.local_ip:ShowLocalIP" +local_ip_association_create = "openstackclient.network.v2.local_ip_association:CreateLocalIPAssociation" +local_ip_association_delete = "openstackclient.network.v2.local_ip_association:DeleteLocalIPAssociation" +local_ip_association_list = "openstackclient.network.v2.local_ip_association:ListLocalIPAssociation" +network_agent_add_network = "openstackclient.network.v2.network_agent:AddNetworkToAgent" +network_agent_add_router = "openstackclient.network.v2.network_agent:AddRouterToAgent" +network_agent_delete = "openstackclient.network.v2.network_agent:DeleteNetworkAgent" +network_agent_list = "openstackclient.network.v2.network_agent:ListNetworkAgent" +network_agent_remove_network = "openstackclient.network.v2.network_agent:RemoveNetworkFromAgent" +network_agent_remove_router = "openstackclient.network.v2.network_agent:RemoveRouterFromAgent" +network_agent_set = "openstackclient.network.v2.network_agent:SetNetworkAgent" +network_agent_show = "openstackclient.network.v2.network_agent:ShowNetworkAgent" +network_auto_allocated_topology_create = "openstackclient.network.v2.network_auto_allocated_topology:CreateAutoAllocatedTopology" +network_auto_allocated_topology_delete = "openstackclient.network.v2.network_auto_allocated_topology:DeleteAutoAllocatedTopology" +network_flavor_add_profile = "openstackclient.network.v2.network_flavor:AddNetworkFlavorToProfile" +network_flavor_create = "openstackclient.network.v2.network_flavor:CreateNetworkFlavor" +network_flavor_delete = "openstackclient.network.v2.network_flavor:DeleteNetworkFlavor" +network_flavor_list = "openstackclient.network.v2.network_flavor:ListNetworkFlavor" +network_flavor_remove_profile = "openstackclient.network.v2.network_flavor:RemoveNetworkFlavorFromProfile" +network_flavor_set = "openstackclient.network.v2.network_flavor:SetNetworkFlavor" +network_flavor_show = "openstackclient.network.v2.network_flavor:ShowNetworkFlavor" +network_flavor_profile_create = "openstackclient.network.v2.network_flavor_profile:CreateNetworkFlavorProfile" +network_flavor_profile_delete = "openstackclient.network.v2.network_flavor_profile:DeleteNetworkFlavorProfile" +network_flavor_profile_list = "openstackclient.network.v2.network_flavor_profile:ListNetworkFlavorProfile" +network_flavor_profile_set = "openstackclient.network.v2.network_flavor_profile:SetNetworkFlavorProfile" +network_flavor_profile_show = "openstackclient.network.v2.network_flavor_profile:ShowNetworkFlavorProfile" +network_create = "openstackclient.network.v2.network:CreateNetwork" +network_delete = "openstackclient.network.v2.network:DeleteNetwork" +network_list = "openstackclient.network.v2.network:ListNetwork" +network_set = "openstackclient.network.v2.network:SetNetwork" +network_show = "openstackclient.network.v2.network:ShowNetwork" +network_unset = "openstackclient.network.v2.network:UnsetNetwork" +network_l3_conntrack_helper_create = "openstackclient.network.v2.l3_conntrack_helper:CreateConntrackHelper" +network_l3_conntrack_helper_delete = "openstackclient.network.v2.l3_conntrack_helper:DeleteConntrackHelper" +network_l3_conntrack_helper_list = "openstackclient.network.v2.l3_conntrack_helper:ListConntrackHelper" +network_l3_conntrack_helper_set = "openstackclient.network.v2.l3_conntrack_helper:SetConntrackHelper" +network_l3_conntrack_helper_show = "openstackclient.network.v2.l3_conntrack_helper:ShowConntrackHelper" +network_meter_create = "openstackclient.network.v2.network_meter:CreateMeter" +network_meter_delete = "openstackclient.network.v2.network_meter:DeleteMeter" +network_meter_list = "openstackclient.network.v2.network_meter:ListMeter" +network_meter_show = "openstackclient.network.v2.network_meter:ShowMeter" +network_meter_rule_create = "openstackclient.network.v2.network_meter_rule:CreateMeterRule" +network_meter_rule_delete = "openstackclient.network.v2.network_meter_rule:DeleteMeterRule" +network_meter_rule_list = "openstackclient.network.v2.network_meter_rule:ListMeterRule" +network_meter_rule_show = "openstackclient.network.v2.network_meter_rule:ShowMeterRule" +network_qos_policy_create = "openstackclient.network.v2.network_qos_policy:CreateNetworkQosPolicy" +network_qos_policy_delete = "openstackclient.network.v2.network_qos_policy:DeleteNetworkQosPolicy" +network_qos_policy_list = "openstackclient.network.v2.network_qos_policy:ListNetworkQosPolicy" +network_qos_policy_set = "openstackclient.network.v2.network_qos_policy:SetNetworkQosPolicy" +network_qos_policy_show = "openstackclient.network.v2.network_qos_policy:ShowNetworkQosPolicy" +network_qos_rule_create = "openstackclient.network.v2.network_qos_rule:CreateNetworkQosRule" +network_qos_rule_delete = "openstackclient.network.v2.network_qos_rule:DeleteNetworkQosRule" +network_qos_rule_list = "openstackclient.network.v2.network_qos_rule:ListNetworkQosRule" +network_qos_rule_set = "openstackclient.network.v2.network_qos_rule:SetNetworkQosRule" +network_qos_rule_show = "openstackclient.network.v2.network_qos_rule:ShowNetworkQosRule" +network_qos_rule_type_list = "openstackclient.network.v2.network_qos_rule_type:ListNetworkQosRuleType" +network_qos_rule_type_show = "openstackclient.network.v2.network_qos_rule_type:ShowNetworkQosRuleType" +network_rbac_create = "openstackclient.network.v2.network_rbac:CreateNetworkRBAC" +network_rbac_delete = "openstackclient.network.v2.network_rbac:DeleteNetworkRBAC" +network_rbac_list = "openstackclient.network.v2.network_rbac:ListNetworkRBAC" +network_rbac_set = "openstackclient.network.v2.network_rbac:SetNetworkRBAC" +network_rbac_show = "openstackclient.network.v2.network_rbac:ShowNetworkRBAC" +network_segment_create = "openstackclient.network.v2.network_segment:CreateNetworkSegment" +network_segment_delete = "openstackclient.network.v2.network_segment:DeleteNetworkSegment" +network_segment_list = "openstackclient.network.v2.network_segment:ListNetworkSegment" +network_segment_set = "openstackclient.network.v2.network_segment:SetNetworkSegment" +network_segment_show = "openstackclient.network.v2.network_segment:ShowNetworkSegment" +network_segment_range_create = "openstackclient.network.v2.network_segment_range:CreateNetworkSegmentRange" +network_segment_range_delete = "openstackclient.network.v2.network_segment_range:DeleteNetworkSegmentRange" +network_segment_range_list = "openstackclient.network.v2.network_segment_range:ListNetworkSegmentRange" +network_segment_range_set = "openstackclient.network.v2.network_segment_range:SetNetworkSegmentRange" +network_segment_range_show = "openstackclient.network.v2.network_segment_range:ShowNetworkSegmentRange" +network_service_provider_list = "openstackclient.network.v2.network_service_provider:ListNetworkServiceProvider" +network_subport_list = "openstackclient.network.v2.network_trunk:ListNetworkSubport" +network_trunk_create = "openstackclient.network.v2.network_trunk:CreateNetworkTrunk" +network_trunk_delete = "openstackclient.network.v2.network_trunk:DeleteNetworkTrunk" +network_trunk_list = "openstackclient.network.v2.network_trunk:ListNetworkTrunk" +network_trunk_set = "openstackclient.network.v2.network_trunk:SetNetworkTrunk" +network_trunk_show = "openstackclient.network.v2.network_trunk:ShowNetworkTrunk" +network_trunk_unset = "openstackclient.network.v2.network_trunk:UnsetNetworkTrunk" +port_create = "openstackclient.network.v2.port:CreatePort" +port_delete = "openstackclient.network.v2.port:DeletePort" +port_list = "openstackclient.network.v2.port:ListPort" +port_set = "openstackclient.network.v2.port:SetPort" +port_show = "openstackclient.network.v2.port:ShowPort" +port_unset = "openstackclient.network.v2.port:UnsetPort" +router_add_gateway = "openstackclient.network.v2.router:AddGatewayToRouter" +router_add_port = "openstackclient.network.v2.router:AddPortToRouter" +router_add_route = "openstackclient.network.v2.router:AddExtraRoutesToRouter" +router_add_subnet = "openstackclient.network.v2.router:AddSubnetToRouter" +router_create = "openstackclient.network.v2.router:CreateRouter" +router_delete = "openstackclient.network.v2.router:DeleteRouter" +router_list = "openstackclient.network.v2.router:ListRouter" +router_remove_gateway = "openstackclient.network.v2.router:RemoveGatewayFromRouter" +router_remove_port = "openstackclient.network.v2.router:RemovePortFromRouter" +router_remove_route = "openstackclient.network.v2.router:RemoveExtraRoutesFromRouter" +router_remove_subnet = "openstackclient.network.v2.router:RemoveSubnetFromRouter" +router_set = "openstackclient.network.v2.router:SetRouter" +router_show = "openstackclient.network.v2.router:ShowRouter" +router_unset = "openstackclient.network.v2.router:UnsetRouter" +router_ndp_proxy_create = "openstackclient.network.v2.ndp_proxy:CreateNDPProxy" +router_ndp_proxy_delete = "openstackclient.network.v2.ndp_proxy:DeleteNDPProxy" +router_ndp_proxy_list = "openstackclient.network.v2.ndp_proxy:ListNDPProxy" +router_ndp_proxy_set = "openstackclient.network.v2.ndp_proxy:SetNDPProxy" +router_ndp_proxy_show = "openstackclient.network.v2.ndp_proxy:ShowNDPProxy" +security_group_create = "openstackclient.network.v2.security_group:CreateSecurityGroup" +security_group_delete = "openstackclient.network.v2.security_group:DeleteSecurityGroup" +security_group_list = "openstackclient.network.v2.security_group:ListSecurityGroup" +security_group_set = "openstackclient.network.v2.security_group:SetSecurityGroup" +security_group_show = "openstackclient.network.v2.security_group:ShowSecurityGroup" +security_group_unset = "openstackclient.network.v2.security_group:UnsetSecurityGroup" +security_group_rule_create = "openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule" +security_group_rule_delete = "openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule" +security_group_rule_list = "openstackclient.network.v2.security_group_rule:ListSecurityGroupRule" +security_group_rule_show = "openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule" +default_security_group_rule_create = "openstackclient.network.v2.default_security_group_rule:CreateDefaultSecurityGroupRule" +default_security_group_rule_delete = "openstackclient.network.v2.default_security_group_rule:DeleteDefaultSecurityGroupRule" +default_security_group_rule_list = "openstackclient.network.v2.default_security_group_rule:ListDefaultSecurityGroupRule" +default_security_group_rule_show = "openstackclient.network.v2.default_security_group_rule:ShowDefaultSecurityGroupRule" +subnet_create = "openstackclient.network.v2.subnet:CreateSubnet" +subnet_delete = "openstackclient.network.v2.subnet:DeleteSubnet" +subnet_list = "openstackclient.network.v2.subnet:ListSubnet" +subnet_set = "openstackclient.network.v2.subnet:SetSubnet" +subnet_show = "openstackclient.network.v2.subnet:ShowSubnet" +subnet_unset = "openstackclient.network.v2.subnet:UnsetSubnet" +subnet_pool_create = "openstackclient.network.v2.subnet_pool:CreateSubnetPool" +subnet_pool_delete = "openstackclient.network.v2.subnet_pool:DeleteSubnetPool" +subnet_pool_list = "openstackclient.network.v2.subnet_pool:ListSubnetPool" +subnet_pool_set = "openstackclient.network.v2.subnet_pool:SetSubnetPool" +subnet_pool_show = "openstackclient.network.v2.subnet_pool:ShowSubnetPool" +subnet_pool_unset = "openstackclient.network.v2.subnet_pool:UnsetSubnetPool" + +# Tap-as-a-Service +tap_flow_create = "openstackclient.network.v2.taas.tap_flow:CreateTapFlow" +tap_flow_delete = "openstackclient.network.v2.taas.tap_flow:DeleteTapFlow" +tap_flow_list = "openstackclient.network.v2.taas.tap_flow:ListTapFlow" +tap_flow_show = "openstackclient.network.v2.taas.tap_flow:ShowTapFlow" +tap_flow_update = "openstackclient.network.v2.taas.tap_flow:UpdateTapFlow" +tap_mirror_create = "openstackclient.network.v2.taas.tap_mirror:CreateTapMirror" +tap_mirror_delete = "openstackclient.network.v2.taas.tap_mirror:DeleteTapMirror" +tap_mirror_list = "openstackclient.network.v2.taas.tap_mirror:ListTapMirror" +tap_mirror_show = "openstackclient.network.v2.taas.tap_mirror:ShowTapMirror" +tap_mirror_update = "openstackclient.network.v2.taas.tap_mirror:UpdateTapMirror" +tap_service_create = "openstackclient.network.v2.taas.tap_service:CreateTapService" +tap_service_delete = "openstackclient.network.v2.taas.tap_service:DeleteTapService" +tap_service_list = "openstackclient.network.v2.taas.tap_service:ListTapService" +tap_service_show = "openstackclient.network.v2.taas.tap_service:ShowTapService" +tap_service_update = "openstackclient.network.v2.taas.tap_service:UpdateTapService" + +[project.entry-points."openstack.object_store.v1"] +object_store_account_set = "openstackclient.object.v1.account:SetAccount" +object_store_account_show = "openstackclient.object.v1.account:ShowAccount" +object_store_account_unset = "openstackclient.object.v1.account:UnsetAccount" +container_create = "openstackclient.object.v1.container:CreateContainer" +container_delete = "openstackclient.object.v1.container:DeleteContainer" +container_list = "openstackclient.object.v1.container:ListContainer" +container_save = "openstackclient.object.v1.container:SaveContainer" +container_set = "openstackclient.object.v1.container:SetContainer" +container_show = "openstackclient.object.v1.container:ShowContainer" +container_unset = "openstackclient.object.v1.container:UnsetContainer" +object_create = "openstackclient.object.v1.object:CreateObject" +object_delete = "openstackclient.object.v1.object:DeleteObject" +object_list = "openstackclient.object.v1.object:ListObject" +object_save = "openstackclient.object.v1.object:SaveObject" +object_set = "openstackclient.object.v1.object:SetObject" +object_show = "openstackclient.object.v1.object:ShowObject" +object_unset = "openstackclient.object.v1.object:UnsetObject" + +[project.entry-points."openstack.volume.v2"] +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" +consistency_group_list = "openstackclient.volume.v2.consistency_group:ListConsistencyGroup" +consistency_group_remove_volume = "openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup" +consistency_group_set = "openstackclient.volume.v2.consistency_group:SetConsistencyGroup" +consistency_group_show = "openstackclient.volume.v2.consistency_group:ShowConsistencyGroup" +consistency_group_snapshot_create = "openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot" +consistency_group_snapshot_delete = "openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot" +consistency_group_snapshot_list = "openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot" +consistency_group_snapshot_show = "openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot" +volume_create = "openstackclient.volume.v2.volume:CreateVolume" +volume_delete = "openstackclient.volume.v2.volume:DeleteVolume" +volume_list = "openstackclient.volume.v2.volume:ListVolume" +volume_migrate = "openstackclient.volume.v2.volume:MigrateVolume" +volume_set = "openstackclient.volume.v2.volume:SetVolume" +volume_show = "openstackclient.volume.v2.volume:ShowVolume" +volume_unset = "openstackclient.volume.v2.volume:UnsetVolume" +volume_backup_create = "openstackclient.volume.v2.volume_backup:CreateVolumeBackup" +volume_backup_delete = "openstackclient.volume.v2.volume_backup:DeleteVolumeBackup" +volume_backup_list = "openstackclient.volume.v2.volume_backup:ListVolumeBackup" +volume_backup_restore = "openstackclient.volume.v2.volume_backup:RestoreVolumeBackup" +volume_backup_set = "openstackclient.volume.v2.volume_backup:SetVolumeBackup" +volume_backup_show = "openstackclient.volume.v2.volume_backup:ShowVolumeBackup" +volume_backup_record_export = "openstackclient.volume.v2.backup_record:ExportBackupRecord" +volume_backup_record_import = "openstackclient.volume.v2.backup_record:ImportBackupRecord" +volume_backend_capability_show = "openstackclient.volume.v2.volume_backend:ShowCapability" +volume_backend_pool_list = "openstackclient.volume.v2.volume_backend:ListPool" +volume_host_failover = "openstackclient.volume.v2.volume_host:FailoverVolumeHost" +volume_host_set = "openstackclient.volume.v2.volume_host:SetVolumeHost" +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" +volume_snapshot_set = "openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot" +volume_snapshot_show = "openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot" +volume_snapshot_unset = "openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot" +volume_type_create = "openstackclient.volume.v2.volume_type:CreateVolumeType" +volume_type_delete = "openstackclient.volume.v2.volume_type:DeleteVolumeType" +volume_type_list = "openstackclient.volume.v2.volume_type:ListVolumeType" +volume_type_set = "openstackclient.volume.v2.volume_type:SetVolumeType" +volume_type_show = "openstackclient.volume.v2.volume_type:ShowVolumeType" +volume_type_unset = "openstackclient.volume.v2.volume_type:UnsetVolumeType" +volume_qos_associate = "openstackclient.volume.v2.qos_specs:AssociateQos" +volume_qos_create = "openstackclient.volume.v2.qos_specs:CreateQos" +volume_qos_delete = "openstackclient.volume.v2.qos_specs:DeleteQos" +volume_qos_disassociate = "openstackclient.volume.v2.qos_specs:DisassociateQos" +volume_qos_list = "openstackclient.volume.v2.qos_specs:ListQos" +volume_qos_set = "openstackclient.volume.v2.qos_specs:SetQos" +volume_qos_show = "openstackclient.volume.v2.qos_specs:ShowQos" +volume_qos_unset = "openstackclient.volume.v2.qos_specs:UnsetQos" +volume_service_list = "openstackclient.volume.v2.service:ListService" +volume_service_set = "openstackclient.volume.v2.service:SetService" +volume_transfer_request_accept = "openstackclient.volume.v2.volume_transfer_request:AcceptTransferRequest" +volume_transfer_request_create = "openstackclient.volume.v2.volume_transfer_request:CreateTransferRequest" +volume_transfer_request_delete = "openstackclient.volume.v2.volume_transfer_request:DeleteTransferRequest" +volume_transfer_request_list = "openstackclient.volume.v2.volume_transfer_request:ListTransferRequest" +volume_transfer_request_show = "openstackclient.volume.v2.volume_transfer_request:ShowTransferRequest" + +[project.entry-points."openstack.volume.v3"] +block_storage_log_level_list = "openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelList" +block_storage_log_level_set = "openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelSet" +block_storage_cleanup = "openstackclient.volume.v3.block_storage_cleanup:BlockStorageCleanup" +block_storage_volume_manageable_list = "openstackclient.volume.v3.block_storage_manage:BlockStorageManageVolumes" +block_storage_snapshot_manageable_list = "openstackclient.volume.v3.block_storage_manage:BlockStorageManageSnapshots" +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" +consistency_group_list = "openstackclient.volume.v2.consistency_group:ListConsistencyGroup" +consistency_group_remove_volume = "openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup" +consistency_group_set = "openstackclient.volume.v2.consistency_group:SetConsistencyGroup" +consistency_group_show = "openstackclient.volume.v2.consistency_group:ShowConsistencyGroup" +consistency_group_snapshot_create = "openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot" +consistency_group_snapshot_delete = "openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot" +consistency_group_snapshot_list = "openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot" +consistency_group_snapshot_show = "openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot" +volume_create = "openstackclient.volume.v3.volume:CreateVolume" +volume_delete = "openstackclient.volume.v3.volume:DeleteVolume" +volume_list = "openstackclient.volume.v3.volume:ListVolume" +volume_migrate = "openstackclient.volume.v3.volume:MigrateVolume" +volume_set = "openstackclient.volume.v3.volume:SetVolume" +volume_show = "openstackclient.volume.v3.volume:ShowVolume" +volume_unset = "openstackclient.volume.v3.volume:UnsetVolume" +volume_attachment_create = "openstackclient.volume.v3.volume_attachment:CreateVolumeAttachment" +volume_attachment_delete = "openstackclient.volume.v3.volume_attachment:DeleteVolumeAttachment" +volume_attachment_list = "openstackclient.volume.v3.volume_attachment:ListVolumeAttachment" +volume_attachment_complete = "openstackclient.volume.v3.volume_attachment:CompleteVolumeAttachment" +volume_attachment_set = "openstackclient.volume.v3.volume_attachment:SetVolumeAttachment" +volume_attachment_show = "openstackclient.volume.v3.volume_attachment:ShowVolumeAttachment" +volume_backup_create = "openstackclient.volume.v3.volume_backup:CreateVolumeBackup" +volume_backup_delete = "openstackclient.volume.v3.volume_backup:DeleteVolumeBackup" +volume_backup_list = "openstackclient.volume.v3.volume_backup:ListVolumeBackup" +volume_backup_restore = "openstackclient.volume.v3.volume_backup:RestoreVolumeBackup" +volume_backup_set = "openstackclient.volume.v3.volume_backup:SetVolumeBackup" +volume_backup_unset = "openstackclient.volume.v3.volume_backup:UnsetVolumeBackup" +volume_backup_show = "openstackclient.volume.v3.volume_backup:ShowVolumeBackup" +volume_backend_capability_show = "openstackclient.volume.v2.volume_backend:ShowCapability" +volume_backend_pool_list = "openstackclient.volume.v2.volume_backend:ListPool" +volume_backup_record_export = "openstackclient.volume.v2.backup_record:ExportBackupRecord" +volume_backup_record_import = "openstackclient.volume.v2.backup_record:ImportBackupRecord" +volume_group_create = "openstackclient.volume.v3.volume_group:CreateVolumeGroup" +volume_group_delete = "openstackclient.volume.v3.volume_group:DeleteVolumeGroup" +volume_group_list = "openstackclient.volume.v3.volume_group:ListVolumeGroup" +volume_group_failover = "openstackclient.volume.v3.volume_group:FailoverVolumeGroup" +volume_group_set = "openstackclient.volume.v3.volume_group:SetVolumeGroup" +volume_group_show = "openstackclient.volume.v3.volume_group:ShowVolumeGroup" +volume_group_snapshot_create = "openstackclient.volume.v3.volume_group_snapshot:CreateVolumeGroupSnapshot" +volume_group_snapshot_delete = "openstackclient.volume.v3.volume_group_snapshot:DeleteVolumeGroupSnapshot" +volume_group_snapshot_list = "openstackclient.volume.v3.volume_group_snapshot:ListVolumeGroupSnapshot" +volume_group_snapshot_show = "openstackclient.volume.v3.volume_group_snapshot:ShowVolumeGroupSnapshot" +volume_group_type_create = "openstackclient.volume.v3.volume_group_type:CreateVolumeGroupType" +volume_group_type_delete = "openstackclient.volume.v3.volume_group_type:DeleteVolumeGroupType" +volume_group_type_list = "openstackclient.volume.v3.volume_group_type:ListVolumeGroupType" +volume_group_type_set = "openstackclient.volume.v3.volume_group_type:SetVolumeGroupType" +volume_group_type_show = "openstackclient.volume.v3.volume_group_type:ShowVolumeGroupType" +volume_host_set = "openstackclient.volume.v2.volume_host:SetVolumeHost" +volume_message_delete = "openstackclient.volume.v3.volume_message:DeleteMessage" +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.v3.volume_snapshot:CreateVolumeSnapshot" +volume_snapshot_delete = "openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot" +volume_snapshot_list = "openstackclient.volume.v3.volume_snapshot:ListVolumeSnapshot" +volume_snapshot_set = "openstackclient.volume.v3.volume_snapshot:SetVolumeSnapshot" +volume_snapshot_show = "openstackclient.volume.v3.volume_snapshot:ShowVolumeSnapshot" +volume_snapshot_unset = "openstackclient.volume.v3.volume_snapshot:UnsetVolumeSnapshot" +volume_type_create = "openstackclient.volume.v3.volume_type:CreateVolumeType" +volume_type_delete = "openstackclient.volume.v3.volume_type:DeleteVolumeType" +volume_type_list = "openstackclient.volume.v3.volume_type:ListVolumeType" +volume_type_set = "openstackclient.volume.v3.volume_type:SetVolumeType" +volume_type_show = "openstackclient.volume.v3.volume_type:ShowVolumeType" +volume_type_unset = "openstackclient.volume.v3.volume_type:UnsetVolumeType" +volume_qos_associate = "openstackclient.volume.v2.qos_specs:AssociateQos" +volume_qos_create = "openstackclient.volume.v2.qos_specs:CreateQos" +volume_qos_delete = "openstackclient.volume.v2.qos_specs:DeleteQos" +volume_qos_disassociate = "openstackclient.volume.v2.qos_specs:DisassociateQos" +volume_qos_list = "openstackclient.volume.v2.qos_specs:ListQos" +volume_qos_set = "openstackclient.volume.v2.qos_specs:SetQos" +volume_qos_show = "openstackclient.volume.v2.qos_specs:ShowQos" +volume_qos_unset = "openstackclient.volume.v2.qos_specs:UnsetQos" +volume_service_list = "openstackclient.volume.v3.service:ListService" +volume_service_set = "openstackclient.volume.v3.service:SetService" +volume_transfer_request_accept = "openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest" +volume_transfer_request_create = "openstackclient.volume.v3.volume_transfer_request:CreateTransferRequest" +volume_transfer_request_delete = "openstackclient.volume.v3.volume_transfer_request:DeleteTransferRequest" +volume_transfer_request_list = "openstackclient.volume.v3.volume_transfer_request:ListTransferRequest" +volume_transfer_request_show = "openstackclient.volume.v3.volume_transfer_request:ShowTransferRequest" +volume_summary = "openstackclient.volume.v3.volume:VolumeSummary" +volume_revert = "openstackclient.volume.v3.volume:VolumeRevertToSnapshot" + +[tool.setuptools] +packages = [ + "openstackclient" +] + +[tool.mypy] +python_version = "3.10" +show_column_numbers = true +show_error_context = true +ignore_missing_imports = true +follow_imports = "normal" +incremental = true +check_untyped_defs = true +warn_unused_ignores = true +# keep this in-sync with 'mypy.exclude' in '.pre-commit-config.yaml' +exclude = ''' +(?x)( + doc + | examples + | hacking + | releasenotes + ) +''' + +[[tool.mypy.overrides]] +module = ["openstackclient.tests.unit.*"] +ignore_errors = true + +[tool.ruff] +line-length = 79 + +[tool.ruff.format] +quote-style = "preserve" +docstring-code-format = true + +[tool.ruff.lint] +select = ["E4", "E5", "E7", "E9", "F", "S", "UP"] + +[tool.ruff.lint.per-file-ignores] +"openstackclient/tests/*" = ["E501", "S"] diff --git a/releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml b/releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml new file mode 100644 index 0000000000..ffa9669540 --- /dev/null +++ b/releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add ``trusted`` attribute to the ``port create`` and ``port set`` commands. + It can be set to ``true`` with ``--trusted`` and to ``false`` with + ``--not-trusted`` CLI arguments passed to the ``port create`` and ``port + set`` commands`` diff --git a/releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml b/releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml new file mode 100644 index 0000000000..433dc87733 --- /dev/null +++ b/releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--chunk-size`` option to ``image save`` command to control the size + of bytes to read at one time. diff --git a/releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml b/releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml new file mode 100644 index 0000000000..7861a0c7a0 --- /dev/null +++ b/releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds operation which deletes all metadef object inside a namespace. diff --git a/releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml b/releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml new file mode 100644 index 0000000000..09aff5905c --- /dev/null +++ b/releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``image property delete`` command will now delete all properties in + the provided namespace if no property is provided. diff --git a/releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml b/releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml new file mode 100644 index 0000000000..7a528d7c9d --- /dev/null +++ b/releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml @@ -0,0 +1,4 @@ +--- +features: + - The ``os_hash_algo`` and ``os_hash_value image`` attributes are now shown + in the ``image list --long`` output. diff --git a/releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml b/releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml new file mode 100644 index 0000000000..762ea6dcaa --- /dev/null +++ b/releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--status`` option to ``port list`` command. + [Bug `1672680 `_] diff --git a/releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml b/releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml new file mode 100644 index 0000000000..1938f7121c --- /dev/null +++ b/releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``server add security group`` and ``server remove security group`` + commands now accept multiple security groups. diff --git a/releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml b/releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml new file mode 100644 index 0000000000..a0abd5e582 --- /dev/null +++ b/releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support for unmanaging snapshots with the + ``openstack snapshot delete --remote`` command. diff --git a/releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml b/releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml new file mode 100644 index 0000000000..a812ca6b73 --- /dev/null +++ b/releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add filters to search for enabled and disabled users and projects. diff --git a/releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml b/releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml new file mode 100644 index 0000000000..f89575b40c --- /dev/null +++ b/releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add ``qinq-vlan`` and ``no-qinq-vlan`` arguments to the ``network create`` + command. It will enable/disable QinQ feature for the created network. + This new argument is mutually exclusive with the ``transparent-vlan`` - only + one of them can be set to ``True`` for the network. diff --git a/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml b/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml new file mode 100644 index 0000000000..06bc7a4f02 --- /dev/null +++ b/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--project`` option to ``volume backup list`` command, + to allow filtering for projects when listing volume backups. diff --git a/releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml b/releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml new file mode 100644 index 0000000000..ab4b60025a --- /dev/null +++ b/releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add ``--property`` option to ``volume list`` command to filter volumes. diff --git a/releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml b/releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml new file mode 100644 index 0000000000..49e9e557d4 --- /dev/null +++ b/releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``aggregate list`` command will now include the UUIDs of the aggregates + when the cloud supports it. diff --git a/releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml b/releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml new file mode 100644 index 0000000000..ffd1ff90b6 --- /dev/null +++ b/releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Add ``--project`` and --project-domain`` options to the following network + commands: + + - ``openstack security group rule list`` + + [Bug `1648317 `_] + diff --git a/releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml b/releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml new file mode 100644 index 0000000000..5ac30b3733 --- /dev/null +++ b/releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The ``quota set`` and ``limits show`` commands will now check for the + ``block-storage`` and ``block-store`` service types along with ``volume``, + ``volumev2`` and ``volumev3``. + + [Bug `2084580 `_] diff --git a/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml b/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml new file mode 100644 index 0000000000..87fe689b18 --- /dev/null +++ b/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Running ``openstack application credential show`` on + a non-existent application credential does not + raise an exception. + + [Bug `2126565 `_] diff --git a/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml b/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml new file mode 100644 index 0000000000..818e29ae82 --- /dev/null +++ b/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix ``openstack quota show --usage`` to correctly display resource usage + and reservation. diff --git a/releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml b/releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml new file mode 100644 index 0000000000..5b03061efb --- /dev/null +++ b/releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add support for the new ``spice-direct`` console type, as well as the + exposing the ability for admins to lookup console connection information + via the new ``console connection show`` command. diff --git a/releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml b/releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml new file mode 100644 index 0000000000..b381e5a821 --- /dev/null +++ b/releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The ``openstack server set`` command has been extended with a new + parameter ``--auto-approve`` and the existing ``--state`` parameter + has been modified to require confirmation before resetting the state. diff --git a/releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml b/releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml new file mode 100644 index 0000000000..6d61b9e7aa --- /dev/null +++ b/releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Support for Python 3.8 has been dropped. diff --git a/releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml b/releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml new file mode 100644 index 0000000000..7d8ca21ffc --- /dev/null +++ b/releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Support for Python 3.9 has been dropped. diff --git a/releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml b/releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml new file mode 100644 index 0000000000..997075c272 --- /dev/null +++ b/releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``--network``, ``--port``, and ``--router`` options of the ``floating + ip list`` command can now be specified multiple times. diff --git a/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml b/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml new file mode 100644 index 0000000000..4cd755b3fe --- /dev/null +++ b/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + Fix a bug where using ``openstack image set --project + --accept `` incorrectly changed the image owner to the specified + project instead of only updating the member status. The ``--project`` + parameter when used with ``--accept``, ``--reject``, or ``--pending`` + should only identify which member's status to update, not change the + image ownership. + [Bug `2136795 `_] diff --git a/releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml b/releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml new file mode 100644 index 0000000000..2ee8f216d0 --- /dev/null +++ b/releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Fixed the output of ``volume backup restore`` command. diff --git a/releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml b/releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml new file mode 100644 index 0000000000..7ed9d72cc8 --- /dev/null +++ b/releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed the ``openstack volume backup show`` command + to show a backup by name. diff --git a/releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml b/releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml new file mode 100644 index 0000000000..96b56b8df6 --- /dev/null +++ b/releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml @@ -0,0 +1,6 @@ +--- +deprecations: + - | + The ``--id auto`` alias for the ``flavor create`` command is deprecated + for removal. Omit the option entirely to ensure the server creates the + ID for you. diff --git a/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml b/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml new file mode 100644 index 0000000000..7cd6acba0e --- /dev/null +++ b/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + [Bug `2136148 `_] Keystone allows + users to be created with no password but no value should be submitted for + the password instead of a ``null`` value. diff --git a/releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml b/releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml new file mode 100644 index 0000000000..9f6deb8d4e --- /dev/null +++ b/releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``access rule list`` + - ``access rule delete`` + - ``access rule show`` diff --git a/releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml b/releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml new file mode 100644 index 0000000000..e83001273f --- /dev/null +++ b/releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``credential create`` + - ``credential delete`` + - ``credential list`` + - ``credential set`` + - ``credential show`` diff --git a/releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml b/releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml new file mode 100644 index 0000000000..ce26909b4a --- /dev/null +++ b/releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``domain create`` + - ``domain delete`` + - ``domain list`` + - ``domain set`` + - ``domain show`` diff --git a/releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml b/releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml new file mode 100644 index 0000000000..ab715d1643 --- /dev/null +++ b/releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``endpoint create`` + - ``endpoint delete`` + - ``endpoint list`` + - ``endpoint show`` + - ``endpoint set`` diff --git a/releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml b/releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml new file mode 100644 index 0000000000..3cbb9b3dd1 --- /dev/null +++ b/releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Migrate ``group`` commands from keystoneclient to SDK. diff --git a/releasenotes/notes/migrate-limit-to-sdk-378037ec2b79e302.yaml b/releasenotes/notes/migrate-limit-to-sdk-378037ec2b79e302.yaml new file mode 100644 index 0000000000..d4a57329ef --- /dev/null +++ b/releasenotes/notes/migrate-limit-to-sdk-378037ec2b79e302.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Migrate ``limit`` commands from keystoneclient to SDK. +upgrade: + - | + Filtering in ``limit`` commands is now case sensitive. + - | + Specifying ``--region None`` is no longer supported for ``limit`` commands. diff --git a/releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml b/releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml new file mode 100644 index 0000000000..31a5e6d3e2 --- /dev/null +++ b/releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``region create`` + - ``region list`` + - ``region delete`` + - ``region set`` + - ``region show`` diff --git a/releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml b/releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml new file mode 100644 index 0000000000..d9914cf972 --- /dev/null +++ b/releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``service provider create`` + - ``service provider delete`` + - ``service provider set`` + - ``service provider list`` + - ``service provider show`` diff --git a/releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml b/releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml new file mode 100644 index 0000000000..e45218f312 --- /dev/null +++ b/releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml @@ -0,0 +1,9 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``trust create`` + - ``trust list`` + - ``trust delete`` + - ``trust show`` diff --git a/releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml b/releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml new file mode 100644 index 0000000000..7d9909fb0b --- /dev/null +++ b/releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Added four new network agent types to the list method filter: + ``ovn-controller``, ``ovn-controller-gateway``, ``ovn-metadata`` and + ``ovn-agent``. diff --git a/releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml b/releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml new file mode 100644 index 0000000000..e14baacdd3 --- /dev/null +++ b/releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added ``--device`` and ``--device-owner`` parameter to the + ``port unset`` command. diff --git a/releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml b/releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml new file mode 100644 index 0000000000..0e7b776207 --- /dev/null +++ b/releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--enable-uplink-status-propagation`` option and + ``--disable-uplink-status-propagation`` option to ``port update`` command. diff --git a/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml b/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml new file mode 100644 index 0000000000..0aa6929b07 --- /dev/null +++ b/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml @@ -0,0 +1,35 @@ +--- +upgrade: + - | + Support for the Block Storage (Cinder) v1 API has been officially removed + as it had been broken for some time. If you haven't noticed then you likely + don't need to do anything. However, in the unlikely event that your cloud + is using the Block Storage v1 API - or incorrectly advertises the Block + Storage v1 API - consider overriding the API version to use v2 as this + behaves very similarly. It may also be necessary to set an endpoint + override for the Block Storage API if your clouds service catalog is not + configured correctly. For example: + + .. code-block:: yaml + + example: + regions: + - name: regionOne + values: + block_storage_endpoint_override: 'https://blockstorage.api.cloud.example/' + volume_api_version: 2 + + If using a public cloud provider, there may also be a profile already + published that sets these. These are listed in the `Vendor Support`__ + doc. For example: + + .. code-block:: yaml + + example: + profile: rackspace + + Alternatively, consider use versions of OSC < 3.19 and python-cinderclient + < 5.0 (both Stein), since these were the last versions to fully support + Cinder v1. + + .. __: https://docs.openstack.org/openstacksdk/latest/user/config/vendor-support.html diff --git a/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml b/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml new file mode 100644 index 0000000000..67150ece9d --- /dev/null +++ b/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The router creation command now has the parameter ``--qos-policy``, that + allows to set a QoS policy for the provided external gateways (one or + many). It is mandatory to define an external gateway if the QoS policy is + set. diff --git a/releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml b/releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml new file mode 100644 index 0000000000..b2169d36e3 --- /dev/null +++ b/releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The ``server create`` command now supports a ``--no-security-group`` + option. When provided, no security groups will be associated with ports + created and attached to the server during server creation. This does not + affect pre-created ports. diff --git a/releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml b/releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml new file mode 100644 index 0000000000..88aa7143fa --- /dev/null +++ b/releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Listing volume backups now shows the created_at column when + volume v3 API is used. diff --git a/releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml b/releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml new file mode 100644 index 0000000000..e21e12dbbc --- /dev/null +++ b/releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The 'volume service set' command could not work due to a bad API call. + [Bug `2116969 `_] diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst index d1238479ba..2c9a36fae4 100644 --- a/releasenotes/source/2023.1.rst +++ b/releasenotes/source/2023.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2023.1 + :branch: unmaintained/2023.1 diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst index 4977a4f1a0..6896656be6 100644 --- a/releasenotes/source/2024.1.rst +++ b/releasenotes/source/2024.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2024.1 + :branch: unmaintained/2024.1 diff --git a/releasenotes/source/2024.2.rst b/releasenotes/source/2024.2.rst new file mode 100644 index 0000000000..aaebcbc8c3 --- /dev/null +++ b/releasenotes/source/2024.2.rst @@ -0,0 +1,6 @@ +=========================== +2024.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.2 diff --git a/releasenotes/source/2025.1.rst b/releasenotes/source/2025.1.rst new file mode 100644 index 0000000000..3add0e53aa --- /dev/null +++ b/releasenotes/source/2025.1.rst @@ -0,0 +1,6 @@ +=========================== +2025.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.1 diff --git a/releasenotes/source/2025.2.rst b/releasenotes/source/2025.2.rst new file mode 100644 index 0000000000..4dae18d869 --- /dev/null +++ b/releasenotes/source/2025.2.rst @@ -0,0 +1,6 @@ +=========================== +2025.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index e25b52decd..2b28cfb92e 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,9 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + 2025.2 + 2025.1 + 2024.2 2024.1 2023.2 2023.1 diff --git a/requirements.txt b/requirements.txt index 3c494d9811..fc31d78206 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,12 +5,12 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 -cliff>=3.5.0 # Apache-2.0 +cliff>=4.13.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=3.3.0 # Apache-2.0 +openstacksdk>=4.6.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0 python-cinderclient>=3.3.0 # Apache-2.0 -requests>=2.14.2 # Apache-2.0 +requests>=2.27.0 # Apache-2.0 stevedore>=2.0.1 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index d824afb15d..28a5b7809f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,865 +1,2 @@ [metadata] name = python-openstackclient -summary = OpenStack Command-line Client -description_file = - README.rst -author = OpenStack -author_email = openstack-discuss@lists.openstack.org -home_page = https://docs.openstack.org/python-openstackclient/latest/ -python_requires = >=3.8 -classifier = - Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - -[files] -packages = - openstackclient - -[entry_points] -console_scripts = - openstack = openstackclient.shell:main - -openstack.cli = - command_list = openstackclient.common.module:ListCommand - module_list = openstackclient.common.module:ListModule - -openstack.cli.base = - compute = openstackclient.compute.client - identity = openstackclient.identity.client - image = openstackclient.image.client - network = openstackclient.network.client - object_store = openstackclient.object.client - volume = openstackclient.volume.client - -openstack.common = - availability_zone_list = openstackclient.common.availability_zone:ListAvailabilityZone - configuration_show = openstackclient.common.configuration:ShowConfiguration - extension_list = openstackclient.common.extension:ListExtension - extension_show = openstackclient.common.extension:ShowExtension - limits_show = openstackclient.common.limits:ShowLimits - project_cleanup = openstackclient.common.project_cleanup:ProjectCleanup - quota_list = openstackclient.common.quota:ListQuota - quota_set = openstackclient.common.quota:SetQuota - quota_show = openstackclient.common.quota:ShowQuota - quota_delete = openstackclient.common.quota:DeleteQuota - versions_show = openstackclient.common.versions:ShowVersions - -openstack.compute.v2 = - compute_agent_create = openstackclient.compute.v2.agent:CreateAgent - compute_agent_delete = openstackclient.compute.v2.agent:DeleteAgent - compute_agent_list = openstackclient.compute.v2.agent:ListAgent - compute_agent_set = openstackclient.compute.v2.agent:SetAgent - - aggregate_add_host = openstackclient.compute.v2.aggregate:AddAggregateHost - aggregate_create = openstackclient.compute.v2.aggregate:CreateAggregate - aggregate_delete = openstackclient.compute.v2.aggregate:DeleteAggregate - aggregate_list = openstackclient.compute.v2.aggregate:ListAggregate - aggregate_remove_host = openstackclient.compute.v2.aggregate:RemoveAggregateHost - aggregate_set = openstackclient.compute.v2.aggregate:SetAggregate - aggregate_show = openstackclient.compute.v2.aggregate:ShowAggregate - aggregate_unset = openstackclient.compute.v2.aggregate:UnsetAggregate - aggregate_cache_image = openstackclient.compute.v2.aggregate:CacheImageForAggregate - - compute_service_delete = openstackclient.compute.v2.service:DeleteService - compute_service_list = openstackclient.compute.v2.service:ListService - compute_service_set = openstackclient.compute.v2.service:SetService - - console_log_show = openstackclient.compute.v2.console:ShowConsoleLog - console_url_show = openstackclient.compute.v2.console:ShowConsoleURL - - flavor_create = openstackclient.compute.v2.flavor:CreateFlavor - flavor_delete = openstackclient.compute.v2.flavor:DeleteFlavor - flavor_list = openstackclient.compute.v2.flavor:ListFlavor - flavor_show = openstackclient.compute.v2.flavor:ShowFlavor - flavor_set = openstackclient.compute.v2.flavor:SetFlavor - flavor_unset = openstackclient.compute.v2.flavor:UnsetFlavor - - host_list = openstackclient.compute.v2.host:ListHost - host_set = openstackclient.compute.v2.host:SetHost - host_show = openstackclient.compute.v2.host:ShowHost - - hypervisor_list = openstackclient.compute.v2.hypervisor:ListHypervisor - hypervisor_show = openstackclient.compute.v2.hypervisor:ShowHypervisor - - hypervisor_stats_show = openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats - - keypair_create = openstackclient.compute.v2.keypair:CreateKeypair - keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair - keypair_list = openstackclient.compute.v2.keypair:ListKeypair - keypair_show = openstackclient.compute.v2.keypair:ShowKeypair - - server_add_fixed_ip = openstackclient.compute.v2.server:AddFixedIP - server_add_floating_ip = openstackclient.compute.v2.server:AddFloatingIP - server_add_port = openstackclient.compute.v2.server:AddPort - server_add_network = openstackclient.compute.v2.server:AddNetwork - server_add_security_group = openstackclient.compute.v2.server:AddServerSecurityGroup - server_add_volume = openstackclient.compute.v2.server:AddServerVolume - server_create = openstackclient.compute.v2.server:CreateServer - server_delete = openstackclient.compute.v2.server:DeleteServer - server_dump_create = openstackclient.compute.v2.server:CreateServerDump - server_evacuate = openstackclient.compute.v2.server:EvacuateServer - server_list = openstackclient.compute.v2.server:ListServer - 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_confirm = openstackclient.compute.v2.server:ConfirmMigration - server_migration_revert = openstackclient.compute.v2.server:RevertMigration - server_pause = openstackclient.compute.v2.server:PauseServer - server_reboot = openstackclient.compute.v2.server:RebootServer - server_rebuild = openstackclient.compute.v2.server:RebuildServer - server_remove_fixed_ip = openstackclient.compute.v2.server:RemoveFixedIP - server_remove_floating_ip = openstackclient.compute.v2.server:RemoveFloatingIP - server_remove_port = openstackclient.compute.v2.server:RemovePort - server_remove_network = openstackclient.compute.v2.server:RemoveNetwork - server_remove_security_group = openstackclient.compute.v2.server:RemoveServerSecurityGroup - server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume - server_rescue = openstackclient.compute.v2.server:RescueServer - server_resize = openstackclient.compute.v2.server:ResizeServer - server_resize_confirm = openstackclient.compute.v2.server:ResizeConfirm - server_resize_revert = openstackclient.compute.v2.server:ResizeRevert - server_restore = openstackclient.compute.v2.server:RestoreServer - server_resume = openstackclient.compute.v2.server:ResumeServer - server_set = openstackclient.compute.v2.server:SetServer - server_shelve = openstackclient.compute.v2.server:ShelveServer - server_show = openstackclient.compute.v2.server:ShowServer - server_ssh = openstackclient.compute.v2.server:SshServer - server_start = openstackclient.compute.v2.server:StartServer - server_stop = openstackclient.compute.v2.server:StopServer - server_suspend = openstackclient.compute.v2.server:SuspendServer - server_unlock = openstackclient.compute.v2.server:UnlockServer - server_unpause = openstackclient.compute.v2.server:UnpauseServer - server_unrescue = openstackclient.compute.v2.server:UnrescueServer - server_unset = openstackclient.compute.v2.server:UnsetServer - server_unshelve = openstackclient.compute.v2.server:UnshelveServer - - server_backup_create = openstackclient.compute.v2.server_backup:CreateServerBackup - - server_event_list = openstackclient.compute.v2.server_event:ListServerEvent - server_event_show = openstackclient.compute.v2.server_event:ShowServerEvent - - server_group_create = openstackclient.compute.v2.server_group:CreateServerGroup - server_group_delete = openstackclient.compute.v2.server_group:DeleteServerGroup - server_group_list = openstackclient.compute.v2.server_group:ListServerGroup - server_group_show = openstackclient.compute.v2.server_group:ShowServerGroup - - 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_set = openstackclient.compute.v2.server_volume:SetServerVolume - server_volume_update = openstackclient.compute.v2.server_volume:UpdateServerVolume - - usage_list = openstackclient.compute.v2.usage:ListUsage - usage_show = openstackclient.compute.v2.usage:ShowUsage - -openstack.identity.v2 = - catalog_list = openstackclient.identity.v2_0.catalog:ListCatalog - catalog_show = openstackclient.identity.v2_0.catalog:ShowCatalog - - ec2_credentials_create = openstackclient.identity.v2_0.ec2creds:CreateEC2Creds - ec2_credentials_delete = openstackclient.identity.v2_0.ec2creds:DeleteEC2Creds - ec2_credentials_list = openstackclient.identity.v2_0.ec2creds:ListEC2Creds - ec2_credentials_show = openstackclient.identity.v2_0.ec2creds:ShowEC2Creds - - endpoint_create = openstackclient.identity.v2_0.endpoint:CreateEndpoint - endpoint_delete = openstackclient.identity.v2_0.endpoint:DeleteEndpoint - endpoint_list = openstackclient.identity.v2_0.endpoint:ListEndpoint - endpoint_show = openstackclient.identity.v2_0.endpoint:ShowEndpoint - - project_create = openstackclient.identity.v2_0.project:CreateProject - project_delete = openstackclient.identity.v2_0.project:DeleteProject - project_list = openstackclient.identity.v2_0.project:ListProject - project_set = openstackclient.identity.v2_0.project:SetProject - project_show = openstackclient.identity.v2_0.project:ShowProject - project_unset = openstackclient.identity.v2_0.project:UnsetProject - - role_add = openstackclient.identity.v2_0.role:AddRole - role_create = openstackclient.identity.v2_0.role:CreateRole - role_delete = openstackclient.identity.v2_0.role:DeleteRole - role_list = openstackclient.identity.v2_0.role:ListRole - role_remove = openstackclient.identity.v2_0.role:RemoveRole - role_show = openstackclient.identity.v2_0.role:ShowRole - role_assignment_list = openstackclient.identity.v2_0.role_assignment:ListRoleAssignment - - service_create = openstackclient.identity.v2_0.service:CreateService - service_delete = openstackclient.identity.v2_0.service:DeleteService - service_list = openstackclient.identity.v2_0.service:ListService - service_show = openstackclient.identity.v2_0.service:ShowService - - token_issue = openstackclient.identity.v2_0.token:IssueToken - token_revoke = openstackclient.identity.v2_0.token:RevokeToken - - user_create = openstackclient.identity.v2_0.user:CreateUser - user_delete = openstackclient.identity.v2_0.user:DeleteUser - user_list = openstackclient.identity.v2_0.user:ListUser - user_set = openstackclient.identity.v2_0.user:SetUser - user_show = openstackclient.identity.v2_0.user:ShowUser - -openstack.identity.v3 = - access_token_create = openstackclient.identity.v3.token:CreateAccessToken - - access_rule_delete = openstackclient.identity.v3.access_rule:DeleteAccessRule - access_rule_list = openstackclient.identity.v3.access_rule:ListAccessRule - access_rule_show = openstackclient.identity.v3.access_rule:ShowAccessRule - - application_credential_create = openstackclient.identity.v3.application_credential:CreateApplicationCredential - application_credential_delete = openstackclient.identity.v3.application_credential:DeleteApplicationCredential - application_credential_list = openstackclient.identity.v3.application_credential:ListApplicationCredential - application_credential_show = openstackclient.identity.v3.application_credential:ShowApplicationCredential - - catalog_list = openstackclient.identity.v3.catalog:ListCatalog - catalog_show = openstackclient.identity.v3.catalog:ShowCatalog - - consumer_create = openstackclient.identity.v3.consumer:CreateConsumer - consumer_delete = openstackclient.identity.v3.consumer:DeleteConsumer - consumer_list = openstackclient.identity.v3.consumer:ListConsumer - consumer_set = openstackclient.identity.v3.consumer:SetConsumer - consumer_show = openstackclient.identity.v3.consumer:ShowConsumer - - credential_create = openstackclient.identity.v3.credential:CreateCredential - credential_delete = openstackclient.identity.v3.credential:DeleteCredential - credential_list = openstackclient.identity.v3.credential:ListCredential - credential_set = openstackclient.identity.v3.credential:SetCredential - credential_show = openstackclient.identity.v3.credential:ShowCredential - - domain_create = openstackclient.identity.v3.domain:CreateDomain - domain_delete = openstackclient.identity.v3.domain:DeleteDomain - domain_list = openstackclient.identity.v3.domain:ListDomain - domain_set = openstackclient.identity.v3.domain:SetDomain - domain_show = openstackclient.identity.v3.domain:ShowDomain - - ec2_credentials_create = openstackclient.identity.v3.ec2creds:CreateEC2Creds - ec2_credentials_delete = openstackclient.identity.v3.ec2creds:DeleteEC2Creds - ec2_credentials_list = openstackclient.identity.v3.ec2creds:ListEC2Creds - ec2_credentials_show = openstackclient.identity.v3.ec2creds:ShowEC2Creds - - endpoint_add_project = openstackclient.identity.v3.endpoint:AddProjectToEndpoint - endpoint_create = openstackclient.identity.v3.endpoint:CreateEndpoint - endpoint_delete = openstackclient.identity.v3.endpoint:DeleteEndpoint - endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint - endpoint_remove_project = openstackclient.identity.v3.endpoint:RemoveProjectFromEndpoint - endpoint_set = openstackclient.identity.v3.endpoint:SetEndpoint - endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint - - endpoint_group_add_project = openstackclient.identity.v3.endpoint_group:AddProjectToEndpointGroup - endpoint_group_create = openstackclient.identity.v3.endpoint_group:CreateEndpointGroup - endpoint_group_delete = openstackclient.identity.v3.endpoint_group:DeleteEndpointGroup - endpoint_group_list = openstackclient.identity.v3.endpoint_group:ListEndpointGroup - endpoint_group_remove_project = openstackclient.identity.v3.endpoint_group:RemoveProjectFromEndpointGroup - endpoint_group_set = openstackclient.identity.v3.endpoint_group:SetEndpointGroup - endpoint_group_show = openstackclient.identity.v3.endpoint_group:ShowEndpointGroup - - federation_domain_list = openstackclient.identity.v3.unscoped_saml:ListAccessibleDomains - federation_project_list = openstackclient.identity.v3.unscoped_saml:ListAccessibleProjects - - federation_protocol_create = openstackclient.identity.v3.federation_protocol:CreateProtocol - federation_protocol_delete = openstackclient.identity.v3.federation_protocol:DeleteProtocol - federation_protocol_list = openstackclient.identity.v3.federation_protocol:ListProtocols - federation_protocol_set = openstackclient.identity.v3.federation_protocol:SetProtocol - federation_protocol_show = openstackclient.identity.v3.federation_protocol:ShowProtocol - - group_add_user = openstackclient.identity.v3.group:AddUserToGroup - group_contains_user = openstackclient.identity.v3.group:CheckUserInGroup - group_create = openstackclient.identity.v3.group:CreateGroup - group_delete = openstackclient.identity.v3.group:DeleteGroup - group_list = openstackclient.identity.v3.group:ListGroup - group_remove_user = openstackclient.identity.v3.group:RemoveUserFromGroup - group_set = openstackclient.identity.v3.group:SetGroup - group_show = openstackclient.identity.v3.group:ShowGroup - - identity_provider_create = openstackclient.identity.v3.identity_provider:CreateIdentityProvider - identity_provider_delete = openstackclient.identity.v3.identity_provider:DeleteIdentityProvider - identity_provider_list = openstackclient.identity.v3.identity_provider:ListIdentityProvider - identity_provider_set = openstackclient.identity.v3.identity_provider:SetIdentityProvider - identity_provider_show = openstackclient.identity.v3.identity_provider:ShowIdentityProvider - - implied_role_create = openstackclient.identity.v3.implied_role:CreateImpliedRole - implied_role_delete = openstackclient.identity.v3.implied_role:DeleteImpliedRole - implied_role_list = openstackclient.identity.v3.implied_role:ListImpliedRole - - limit_create = openstackclient.identity.v3.limit:CreateLimit - limit_delete = openstackclient.identity.v3.limit:DeleteLimit - limit_list = openstackclient.identity.v3.limit:ListLimit - limit_set = openstackclient.identity.v3.limit:SetLimit - limit_show = openstackclient.identity.v3.limit:ShowLimit - - mapping_create = openstackclient.identity.v3.mapping:CreateMapping - mapping_delete = openstackclient.identity.v3.mapping:DeleteMapping - mapping_list = openstackclient.identity.v3.mapping:ListMapping - mapping_set = openstackclient.identity.v3.mapping:SetMapping - mapping_show = openstackclient.identity.v3.mapping:ShowMapping - - policy_create = openstackclient.identity.v3.policy:CreatePolicy - policy_delete = openstackclient.identity.v3.policy:DeletePolicy - policy_list = openstackclient.identity.v3.policy:ListPolicy - policy_set = openstackclient.identity.v3.policy:SetPolicy - policy_show = openstackclient.identity.v3.policy:ShowPolicy - - project_create = openstackclient.identity.v3.project:CreateProject - project_delete = openstackclient.identity.v3.project:DeleteProject - project_list = openstackclient.identity.v3.project:ListProject - project_set = openstackclient.identity.v3.project:SetProject - project_show = openstackclient.identity.v3.project:ShowProject - - region_create = openstackclient.identity.v3.region:CreateRegion - region_delete = openstackclient.identity.v3.region:DeleteRegion - region_list = openstackclient.identity.v3.region:ListRegion - region_set = openstackclient.identity.v3.region:SetRegion - region_show = openstackclient.identity.v3.region:ShowRegion - - registered_limit_create = openstackclient.identity.v3.registered_limit:CreateRegisteredLimit - registered_limit_delete = openstackclient.identity.v3.registered_limit:DeleteRegisteredLimit - registered_limit_list = openstackclient.identity.v3.registered_limit:ListRegisteredLimit - registered_limit_set = openstackclient.identity.v3.registered_limit:SetRegisteredLimit - registered_limit_show = openstackclient.identity.v3.registered_limit:ShowRegisteredLimit - - request_token_authorize = openstackclient.identity.v3.token:AuthorizeRequestToken - request_token_create = openstackclient.identity.v3.token:CreateRequestToken - - role_add = openstackclient.identity.v3.role:AddRole - role_create = openstackclient.identity.v3.role:CreateRole - role_delete = openstackclient.identity.v3.role:DeleteRole - role_list = openstackclient.identity.v3.role:ListRole - role_remove = openstackclient.identity.v3.role:RemoveRole - role_show = openstackclient.identity.v3.role:ShowRole - role_set = openstackclient.identity.v3.role:SetRole - role_assignment_list = openstackclient.identity.v3.role_assignment:ListRoleAssignment - - service_create = openstackclient.identity.v3.service:CreateService - service_delete = openstackclient.identity.v3.service:DeleteService - service_list = openstackclient.identity.v3.service:ListService - service_show = openstackclient.identity.v3.service:ShowService - service_set = openstackclient.identity.v3.service:SetService - - service_provider_create = openstackclient.identity.v3.service_provider:CreateServiceProvider - service_provider_delete = openstackclient.identity.v3.service_provider:DeleteServiceProvider - service_provider_list = openstackclient.identity.v3.service_provider:ListServiceProvider - service_provider_set = openstackclient.identity.v3.service_provider:SetServiceProvider - service_provider_show = openstackclient.identity.v3.service_provider:ShowServiceProvider - - token_issue = openstackclient.identity.v3.token:IssueToken - token_revoke = openstackclient.identity.v3.token:RevokeToken - - trust_create = openstackclient.identity.v3.trust:CreateTrust - trust_delete = openstackclient.identity.v3.trust:DeleteTrust - trust_list = openstackclient.identity.v3.trust:ListTrust - trust_show = openstackclient.identity.v3.trust:ShowTrust - - user_create = openstackclient.identity.v3.user:CreateUser - user_delete = openstackclient.identity.v3.user:DeleteUser - user_list = openstackclient.identity.v3.user:ListUser - user_set = openstackclient.identity.v3.user:SetUser - user_password_set = openstackclient.identity.v3.user:SetPasswordUser - user_show = openstackclient.identity.v3.user:ShowUser - -openstack.image.v1 = - image_create = openstackclient.image.v1.image:CreateImage - image_delete = openstackclient.image.v1.image:DeleteImage - image_list = openstackclient.image.v1.image:ListImage - image_save = openstackclient.image.v1.image:SaveImage - image_set = openstackclient.image.v1.image:SetImage - image_show = openstackclient.image.v1.image:ShowImage - -openstack.image.v2 = - image_add_project = openstackclient.image.v2.image:AddProjectToImage - image_create = openstackclient.image.v2.image:CreateImage - image_delete = openstackclient.image.v2.image:DeleteImage - image_list = openstackclient.image.v2.image:ListImage - image_member_list = openstackclient.image.v2.image:ListImageProjects - image_remove_project = openstackclient.image.v2.image:RemoveProjectImage - image_member_get = openstackclient.image.v2.image:ShowProjectImage - image_save = openstackclient.image.v2.image:SaveImage - image_show = openstackclient.image.v2.image:ShowImage - image_set = openstackclient.image.v2.image:SetImage - image_unset = openstackclient.image.v2.image:UnsetImage - image_stage = openstackclient.image.v2.image:StageImage - image_task_show = openstackclient.image.v2.task:ShowTask - image_task_list = openstackclient.image.v2.task:ListTask - image_import_info = openstackclient.image.v2.info:ImportInfo - image_import = openstackclient.image.v2.image:ImportImage - image_stores_list = openstackclient.image.v2.image:StoresInfo - - image_metadef_namespace_create = openstackclient.image.v2.metadef_namespaces:CreateMetadefNamespace - image_metadef_namespace_delete = openstackclient.image.v2.metadef_namespaces:DeleteMetadefNamespace - image_metadef_namespace_list = openstackclient.image.v2.metadef_namespaces:ListMetadefNamespace - image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNamespace - image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNamespace - - image_metadef_object_create = openstackclient.image.v2.metadef_objects:CreateMetadefObjects - image_metadef_object_show = openstackclient.image.v2.metadef_objects:ShowMetadefObjects - image_metadef_object_list = openstackclient.image.v2.metadef_objects:ListMetadefObjects - image_metadef_object_delete = openstackclient.image.v2.metadef_objects:DeleteMetadefObject - image_metadef_object_update = openstackclient.image.v2.metadef_objects:SetMetadefObject - image_metadef_object_property_show = openstackclient.image.v2.metadef_objects:ShowMetadefObjectProperty - - image_metadef_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty - image_metadef_property_delete = openstackclient.image.v2.metadef_properties:DeleteMetadefProperty - image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties - image_metadef_property_set = openstackclient.image.v2.metadef_properties:SetMetadefProperty - image_metadef_property_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty - - image_metadef_resource_type_list = openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes - image_metadef_resource_type_association_create = openstackclient.image.v2.metadef_resource_type_association:CreateMetadefResourceTypeAssociation - image_metadef_resource_type_association_delete = openstackclient.image.v2.metadef_resource_type_association:DeleteMetadefResourceTypeAssociation - image_metadef_resource_type_association_list = openstackclient.image.v2.metadef_resource_type_association:ListMetadefResourceTypeAssociations - - cached_image_list = openstackclient.image.v2.cache:ListCachedImage - cached_image_queue = openstackclient.image.v2.cache:QueueCachedImage - cached_image_delete = openstackclient.image.v2.cache:DeleteCachedImage - cached_image_clear = openstackclient.image.v2.cache:ClearCachedImage - -openstack.network.v2 = - address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup - address_group_delete = openstackclient.network.v2.address_group:DeleteAddressGroup - address_group_list = openstackclient.network.v2.address_group:ListAddressGroup - address_group_set = openstackclient.network.v2.address_group:SetAddressGroup - address_group_show = openstackclient.network.v2.address_group:ShowAddressGroup - address_group_unset = openstackclient.network.v2.address_group:UnsetAddressGroup - - address_scope_create = openstackclient.network.v2.address_scope:CreateAddressScope - address_scope_delete = openstackclient.network.v2.address_scope:DeleteAddressScope - address_scope_list = openstackclient.network.v2.address_scope:ListAddressScope - address_scope_set = openstackclient.network.v2.address_scope:SetAddressScope - address_scope_show = openstackclient.network.v2.address_scope:ShowAddressScope - - floating_ip_create = openstackclient.network.v2.floating_ip:CreateFloatingIP - floating_ip_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP - floating_ip_list = openstackclient.network.v2.floating_ip:ListFloatingIP - floating_ip_set = openstackclient.network.v2.floating_ip:SetFloatingIP - floating_ip_show = openstackclient.network.v2.floating_ip:ShowFloatingIP - floating_ip_unset = openstackclient.network.v2.floating_ip:UnsetFloatingIP - - floating_ip_pool_list = openstackclient.network.v2.floating_ip_pool:ListFloatingIPPool - - floating_ip_port_forwarding_create = openstackclient.network.v2.floating_ip_port_forwarding:CreateFloatingIPPortForwarding - floating_ip_port_forwarding_delete = openstackclient.network.v2.floating_ip_port_forwarding:DeleteFloatingIPPortForwarding - floating_ip_port_forwarding_list = openstackclient.network.v2.floating_ip_port_forwarding:ListFloatingIPPortForwarding - floating_ip_port_forwarding_set = openstackclient.network.v2.floating_ip_port_forwarding:SetFloatingIPPortForwarding - floating_ip_port_forwarding_show = openstackclient.network.v2.floating_ip_port_forwarding:ShowFloatingIPPortForwarding - - ip_availability_list = openstackclient.network.v2.ip_availability:ListIPAvailability - ip_availability_show = openstackclient.network.v2.ip_availability:ShowIPAvailability - - local_ip_create = openstackclient.network.v2.local_ip:CreateLocalIP - local_ip_delete = openstackclient.network.v2.local_ip:DeleteLocalIP - local_ip_list = openstackclient.network.v2.local_ip:ListLocalIP - local_ip_set = openstackclient.network.v2.local_ip:SetLocalIP - local_ip_show = openstackclient.network.v2.local_ip:ShowLocalIP - - local_ip_association_create = openstackclient.network.v2.local_ip_association:CreateLocalIPAssociation - local_ip_association_delete = openstackclient.network.v2.local_ip_association:DeleteLocalIPAssociation - local_ip_association_list = openstackclient.network.v2.local_ip_association:ListLocalIPAssociation - - network_agent_add_network = openstackclient.network.v2.network_agent:AddNetworkToAgent - network_agent_add_router = openstackclient.network.v2.network_agent:AddRouterToAgent - network_agent_delete = openstackclient.network.v2.network_agent:DeleteNetworkAgent - network_agent_list = openstackclient.network.v2.network_agent:ListNetworkAgent - network_agent_remove_network = openstackclient.network.v2.network_agent:RemoveNetworkFromAgent - network_agent_remove_router = openstackclient.network.v2.network_agent:RemoveRouterFromAgent - network_agent_set = openstackclient.network.v2.network_agent:SetNetworkAgent - network_agent_show = openstackclient.network.v2.network_agent:ShowNetworkAgent - - network_auto_allocated_topology_create = openstackclient.network.v2.network_auto_allocated_topology:CreateAutoAllocatedTopology - network_auto_allocated_topology_delete = openstackclient.network.v2.network_auto_allocated_topology:DeleteAutoAllocatedTopology - - network_flavor_add_profile = openstackclient.network.v2.network_flavor:AddNetworkFlavorToProfile - network_flavor_create = openstackclient.network.v2.network_flavor:CreateNetworkFlavor - network_flavor_delete = openstackclient.network.v2.network_flavor:DeleteNetworkFlavor - network_flavor_list = openstackclient.network.v2.network_flavor:ListNetworkFlavor - network_flavor_remove_profile = openstackclient.network.v2.network_flavor:RemoveNetworkFlavorFromProfile - network_flavor_set = openstackclient.network.v2.network_flavor:SetNetworkFlavor - network_flavor_show = openstackclient.network.v2.network_flavor:ShowNetworkFlavor - - network_flavor_profile_create = openstackclient.network.v2.network_flavor_profile:CreateNetworkFlavorProfile - network_flavor_profile_delete = openstackclient.network.v2.network_flavor_profile:DeleteNetworkFlavorProfile - network_flavor_profile_list = openstackclient.network.v2.network_flavor_profile:ListNetworkFlavorProfile - network_flavor_profile_set = openstackclient.network.v2.network_flavor_profile:SetNetworkFlavorProfile - network_flavor_profile_show = openstackclient.network.v2.network_flavor_profile:ShowNetworkFlavorProfile - - network_create = openstackclient.network.v2.network:CreateNetwork - network_delete = openstackclient.network.v2.network:DeleteNetwork - network_list = openstackclient.network.v2.network:ListNetwork - network_set = openstackclient.network.v2.network:SetNetwork - network_show = openstackclient.network.v2.network:ShowNetwork - network_unset = openstackclient.network.v2.network:UnsetNetwork - - network_l3_conntrack_helper_create = openstackclient.network.v2.l3_conntrack_helper:CreateConntrackHelper - network_l3_conntrack_helper_delete = openstackclient.network.v2.l3_conntrack_helper:DeleteConntrackHelper - network_l3_conntrack_helper_list = openstackclient.network.v2.l3_conntrack_helper:ListConntrackHelper - network_l3_conntrack_helper_set = openstackclient.network.v2.l3_conntrack_helper:SetConntrackHelper - network_l3_conntrack_helper_show = openstackclient.network.v2.l3_conntrack_helper:ShowConntrackHelper - - network_meter_create = openstackclient.network.v2.network_meter:CreateMeter - network_meter_delete = openstackclient.network.v2.network_meter:DeleteMeter - network_meter_list = openstackclient.network.v2.network_meter:ListMeter - network_meter_show = openstackclient.network.v2.network_meter:ShowMeter - - network_meter_rule_create = openstackclient.network.v2.network_meter_rule:CreateMeterRule - network_meter_rule_delete = openstackclient.network.v2.network_meter_rule:DeleteMeterRule - network_meter_rule_list = openstackclient.network.v2.network_meter_rule:ListMeterRule - network_meter_rule_show = openstackclient.network.v2.network_meter_rule:ShowMeterRule - - network_qos_policy_create = openstackclient.network.v2.network_qos_policy:CreateNetworkQosPolicy - network_qos_policy_delete = openstackclient.network.v2.network_qos_policy:DeleteNetworkQosPolicy - network_qos_policy_list = openstackclient.network.v2.network_qos_policy:ListNetworkQosPolicy - network_qos_policy_set = openstackclient.network.v2.network_qos_policy:SetNetworkQosPolicy - network_qos_policy_show = openstackclient.network.v2.network_qos_policy:ShowNetworkQosPolicy - - network_qos_rule_create = openstackclient.network.v2.network_qos_rule:CreateNetworkQosRule - network_qos_rule_delete = openstackclient.network.v2.network_qos_rule:DeleteNetworkQosRule - network_qos_rule_list = openstackclient.network.v2.network_qos_rule:ListNetworkQosRule - network_qos_rule_set = openstackclient.network.v2.network_qos_rule:SetNetworkQosRule - network_qos_rule_show = openstackclient.network.v2.network_qos_rule:ShowNetworkQosRule - - network_qos_rule_type_list = openstackclient.network.v2.network_qos_rule_type:ListNetworkQosRuleType - network_qos_rule_type_show = openstackclient.network.v2.network_qos_rule_type:ShowNetworkQosRuleType - - network_rbac_create = openstackclient.network.v2.network_rbac:CreateNetworkRBAC - network_rbac_delete = openstackclient.network.v2.network_rbac:DeleteNetworkRBAC - network_rbac_list = openstackclient.network.v2.network_rbac:ListNetworkRBAC - network_rbac_set = openstackclient.network.v2.network_rbac:SetNetworkRBAC - network_rbac_show = openstackclient.network.v2.network_rbac:ShowNetworkRBAC - - network_segment_create = openstackclient.network.v2.network_segment:CreateNetworkSegment - network_segment_delete = openstackclient.network.v2.network_segment:DeleteNetworkSegment - network_segment_list = openstackclient.network.v2.network_segment:ListNetworkSegment - network_segment_set = openstackclient.network.v2.network_segment:SetNetworkSegment - network_segment_show = openstackclient.network.v2.network_segment:ShowNetworkSegment - - network_segment_range_create = openstackclient.network.v2.network_segment_range:CreateNetworkSegmentRange - network_segment_range_delete = openstackclient.network.v2.network_segment_range:DeleteNetworkSegmentRange - network_segment_range_list = openstackclient.network.v2.network_segment_range:ListNetworkSegmentRange - network_segment_range_set = openstackclient.network.v2.network_segment_range:SetNetworkSegmentRange - network_segment_range_show = openstackclient.network.v2.network_segment_range:ShowNetworkSegmentRange - - network_service_provider_list = openstackclient.network.v2.network_service_provider:ListNetworkServiceProvider - - network_subport_list = openstackclient.network.v2.network_trunk:ListNetworkSubport - network_trunk_create = openstackclient.network.v2.network_trunk:CreateNetworkTrunk - network_trunk_delete = openstackclient.network.v2.network_trunk:DeleteNetworkTrunk - network_trunk_list = openstackclient.network.v2.network_trunk:ListNetworkTrunk - network_trunk_set = openstackclient.network.v2.network_trunk:SetNetworkTrunk - network_trunk_show = openstackclient.network.v2.network_trunk:ShowNetworkTrunk - network_trunk_unset = openstackclient.network.v2.network_trunk:UnsetNetworkTrunk - - port_create = openstackclient.network.v2.port:CreatePort - port_delete = openstackclient.network.v2.port:DeletePort - port_list = openstackclient.network.v2.port:ListPort - port_set = openstackclient.network.v2.port:SetPort - port_show = openstackclient.network.v2.port:ShowPort - port_unset = openstackclient.network.v2.port:UnsetPort - - router_add_gateway = openstackclient.network.v2.router:AddGatewayToRouter - router_add_port = openstackclient.network.v2.router:AddPortToRouter - router_add_route = openstackclient.network.v2.router:AddExtraRoutesToRouter - router_add_subnet = openstackclient.network.v2.router:AddSubnetToRouter - router_create = openstackclient.network.v2.router:CreateRouter - router_delete = openstackclient.network.v2.router:DeleteRouter - router_list = openstackclient.network.v2.router:ListRouter - router_remove_gateway = openstackclient.network.v2.router:RemoveGatewayFromRouter - router_remove_port = openstackclient.network.v2.router:RemovePortFromRouter - router_remove_route = openstackclient.network.v2.router:RemoveExtraRoutesFromRouter - router_remove_subnet = openstackclient.network.v2.router:RemoveSubnetFromRouter - router_set = openstackclient.network.v2.router:SetRouter - router_show = openstackclient.network.v2.router:ShowRouter - router_unset = openstackclient.network.v2.router:UnsetRouter - - router_ndp_proxy_create = openstackclient.network.v2.ndp_proxy:CreateNDPProxy - router_ndp_proxy_delete = openstackclient.network.v2.ndp_proxy:DeleteNDPProxy - router_ndp_proxy_list = openstackclient.network.v2.ndp_proxy:ListNDPProxy - router_ndp_proxy_set = openstackclient.network.v2.ndp_proxy:SetNDPProxy - router_ndp_proxy_show = openstackclient.network.v2.ndp_proxy:ShowNDPProxy - - security_group_create = openstackclient.network.v2.security_group:CreateSecurityGroup - security_group_delete = openstackclient.network.v2.security_group:DeleteSecurityGroup - security_group_list = openstackclient.network.v2.security_group:ListSecurityGroup - security_group_set = openstackclient.network.v2.security_group:SetSecurityGroup - security_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup - security_group_unset = openstackclient.network.v2.security_group:UnsetSecurityGroup - - security_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule - security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule - security_group_rule_list = openstackclient.network.v2.security_group_rule:ListSecurityGroupRule - security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule - - default_security_group_rule_create = openstackclient.network.v2.default_security_group_rule:CreateDefaultSecurityGroupRule - default_security_group_rule_delete = openstackclient.network.v2.default_security_group_rule:DeleteDefaultSecurityGroupRule - default_security_group_rule_list = openstackclient.network.v2.default_security_group_rule:ListDefaultSecurityGroupRule - default_security_group_rule_show = openstackclient.network.v2.default_security_group_rule:ShowDefaultSecurityGroupRule - - subnet_create = openstackclient.network.v2.subnet:CreateSubnet - subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet - subnet_list = openstackclient.network.v2.subnet:ListSubnet - subnet_set = openstackclient.network.v2.subnet:SetSubnet - subnet_show = openstackclient.network.v2.subnet:ShowSubnet - subnet_unset = openstackclient.network.v2.subnet:UnsetSubnet - - subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool - subnet_pool_delete = openstackclient.network.v2.subnet_pool:DeleteSubnetPool - subnet_pool_list = openstackclient.network.v2.subnet_pool:ListSubnetPool - subnet_pool_set = openstackclient.network.v2.subnet_pool:SetSubnetPool - subnet_pool_show = openstackclient.network.v2.subnet_pool:ShowSubnetPool - subnet_pool_unset = openstackclient.network.v2.subnet_pool:UnsetSubnetPool - -openstack.object_store.v1 = - object_store_account_set = openstackclient.object.v1.account:SetAccount - object_store_account_show = openstackclient.object.v1.account:ShowAccount - object_store_account_unset = openstackclient.object.v1.account:UnsetAccount - container_create = openstackclient.object.v1.container:CreateContainer - container_delete = openstackclient.object.v1.container:DeleteContainer - container_list = openstackclient.object.v1.container:ListContainer - container_save = openstackclient.object.v1.container:SaveContainer - container_set = openstackclient.object.v1.container:SetContainer - container_show = openstackclient.object.v1.container:ShowContainer - container_unset = openstackclient.object.v1.container:UnsetContainer - object_create = openstackclient.object.v1.object:CreateObject - object_delete = openstackclient.object.v1.object:DeleteObject - object_list = openstackclient.object.v1.object:ListObject - object_save = openstackclient.object.v1.object:SaveObject - object_set = openstackclient.object.v1.object:SetObject - object_show = openstackclient.object.v1.object:ShowObject - object_unset = openstackclient.object.v1.object:UnsetObject - -openstack.volume.v1 = - volume_create = openstackclient.volume.v1.volume:CreateVolume - volume_delete = openstackclient.volume.v1.volume:DeleteVolume - volume_list = openstackclient.volume.v1.volume:ListVolume - volume_migrate = openstackclient.volume.v1.volume:MigrateVolume - volume_set = openstackclient.volume.v1.volume:SetVolume - volume_show = openstackclient.volume.v1.volume:ShowVolume - volume_unset = openstackclient.volume.v1.volume:UnsetVolume - - volume_backup_create = openstackclient.volume.v1.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v1.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v1.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v1.volume_backup:RestoreVolumeBackup - volume_backup_show = openstackclient.volume.v1.volume_backup:ShowVolumeBackup - - volume_snapshot_create = openstackclient.volume.v1.volume_snapshot:CreateVolumeSnapshot - volume_snapshot_delete = openstackclient.volume.v1.volume_snapshot:DeleteVolumeSnapshot - volume_snapshot_list = openstackclient.volume.v1.volume_snapshot:ListVolumeSnapshot - volume_snapshot_set = openstackclient.volume.v1.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v1.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v1.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v1.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v1.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v1.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v1.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v1.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v1.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v1.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v1.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v1.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v1.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v1.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v1.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v1.service:ListService - volume_service_set = openstackclient.volume.v1.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v1.volume_transfer_request:AcceptTransferRequest - volume_transfer_request_create = openstackclient.volume.v1.volume_transfer_request:CreateTransferRequest - volume_transfer_request_delete = openstackclient.volume.v1.volume_transfer_request:DeleteTransferRequest - volume_transfer_request_list = openstackclient.volume.v1.volume_transfer_request:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v1.volume_transfer_request:ShowTransferRequest - -openstack.volume.v2 = - 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 - consistency_group_list = openstackclient.volume.v2.consistency_group:ListConsistencyGroup - consistency_group_remove_volume = openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup - consistency_group_set = openstackclient.volume.v2.consistency_group:SetConsistencyGroup - consistency_group_show = openstackclient.volume.v2.consistency_group:ShowConsistencyGroup - - consistency_group_snapshot_create = openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot - consistency_group_snapshot_delete = openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot - consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot - consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot - - volume_create = openstackclient.volume.v2.volume:CreateVolume - volume_delete = openstackclient.volume.v2.volume:DeleteVolume - volume_list = openstackclient.volume.v2.volume:ListVolume - volume_migrate = openstackclient.volume.v2.volume:MigrateVolume - volume_set = openstackclient.volume.v2.volume:SetVolume - volume_show = openstackclient.volume.v2.volume:ShowVolume - volume_unset = openstackclient.volume.v2.volume:UnsetVolume - - volume_backup_create = openstackclient.volume.v2.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v2.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v2.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v2.volume_backup:RestoreVolumeBackup - volume_backup_set = openstackclient.volume.v2.volume_backup:SetVolumeBackup - volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup - - volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord - volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord - - volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability - volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool - - volume_host_failover = openstackclient.volume.v2.volume_host:FailoverVolumeHost - volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost - - 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 - volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v2.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v2.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v2.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v2.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v2.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v2.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v2.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v2.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v2.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v2.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v2.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v2.service:ListService - volume_service_set = openstackclient.volume.v2.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v2.volume_transfer_request:AcceptTransferRequest - volume_transfer_request_create = openstackclient.volume.v2.volume_transfer_request:CreateTransferRequest - volume_transfer_request_delete = openstackclient.volume.v2.volume_transfer_request:DeleteTransferRequest - volume_transfer_request_list = openstackclient.volume.v2.volume_transfer_request:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v2.volume_transfer_request:ShowTransferRequest - -openstack.volume.v3 = - 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 - consistency_group_list = openstackclient.volume.v2.consistency_group:ListConsistencyGroup - consistency_group_remove_volume = openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup - consistency_group_set = openstackclient.volume.v2.consistency_group:SetConsistencyGroup - consistency_group_show = openstackclient.volume.v2.consistency_group:ShowConsistencyGroup - - consistency_group_snapshot_create = openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot - consistency_group_snapshot_delete = openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot - consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot - consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot - - volume_create = openstackclient.volume.v3.volume:CreateVolume - volume_delete = openstackclient.volume.v3.volume:DeleteVolume - volume_list = openstackclient.volume.v3.volume:ListVolume - volume_migrate = openstackclient.volume.v3.volume:MigrateVolume - volume_set = openstackclient.volume.v3.volume:SetVolume - volume_show = openstackclient.volume.v3.volume:ShowVolume - volume_unset = openstackclient.volume.v3.volume:UnsetVolume - - volume_attachment_create = openstackclient.volume.v3.volume_attachment:CreateVolumeAttachment - volume_attachment_delete = openstackclient.volume.v3.volume_attachment:DeleteVolumeAttachment - volume_attachment_list = openstackclient.volume.v3.volume_attachment:ListVolumeAttachment - volume_attachment_complete = openstackclient.volume.v3.volume_attachment:CompleteVolumeAttachment - volume_attachment_set = openstackclient.volume.v3.volume_attachment:SetVolumeAttachment - volume_attachment_show = openstackclient.volume.v3.volume_attachment:ShowVolumeAttachment - - volume_backup_create = openstackclient.volume.v3.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v3.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v3.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v3.volume_backup:RestoreVolumeBackup - volume_backup_set = openstackclient.volume.v3.volume_backup:SetVolumeBackup - volume_backup_unset = openstackclient.volume.v3.volume_backup:UnsetVolumeBackup - volume_backup_show = openstackclient.volume.v3.volume_backup:ShowVolumeBackup - - volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability - volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool - - volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord - volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord - - volume_group_create = openstackclient.volume.v3.volume_group:CreateVolumeGroup - volume_group_delete = openstackclient.volume.v3.volume_group:DeleteVolumeGroup - volume_group_list = openstackclient.volume.v3.volume_group:ListVolumeGroup - volume_group_failover = openstackclient.volume.v3.volume_group:FailoverVolumeGroup - volume_group_set = openstackclient.volume.v3.volume_group:SetVolumeGroup - volume_group_show = openstackclient.volume.v3.volume_group:ShowVolumeGroup - - volume_group_snapshot_create = openstackclient.volume.v3.volume_group_snapshot:CreateVolumeGroupSnapshot - volume_group_snapshot_delete = openstackclient.volume.v3.volume_group_snapshot:DeleteVolumeGroupSnapshot - volume_group_snapshot_list = openstackclient.volume.v3.volume_group_snapshot:ListVolumeGroupSnapshot - volume_group_snapshot_show = openstackclient.volume.v3.volume_group_snapshot:ShowVolumeGroupSnapshot - - volume_group_type_create = openstackclient.volume.v3.volume_group_type:CreateVolumeGroupType - volume_group_type_delete = openstackclient.volume.v3.volume_group_type:DeleteVolumeGroupType - volume_group_type_list = openstackclient.volume.v3.volume_group_type:ListVolumeGroupType - volume_group_type_set = openstackclient.volume.v3.volume_group_type:SetVolumeGroupType - volume_group_type_show = openstackclient.volume.v3.volume_group_type:ShowVolumeGroupType - - volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost - - volume_message_delete = openstackclient.volume.v3.volume_message:DeleteMessage - 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 - volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v3.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v3.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v3.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v3.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v3.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v3.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v2.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v2.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v2.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v2.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v2.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v3.service:ListService - volume_service_set = openstackclient.volume.v2.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest - volume_transfer_request_create = openstackclient.volume.v3.volume_transfer_request:CreateTransferRequest - volume_transfer_request_delete = openstackclient.volume.v3.volume_transfer_request:DeleteTransferRequest - volume_transfer_request_list = openstackclient.volume.v3.volume_transfer_request:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v3.volume_transfer_request:ShowTransferRequest - - volume_summary = openstackclient.volume.v3.volume:VolumeSummary - volume_revert = openstackclient.volume.v3.volume:VolumeRevertToSnapshot - block_storage_log_level_list = openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelList - block_storage_log_level_set = openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelSet - block_storage_cleanup = openstackclient.volume.v3.block_storage_cleanup:BlockStorageCleanup - block_storage_volume_manageable_list = openstackclient.volume.v3.block_storage_manage:BlockStorageManageVolumes - block_storage_snapshot_manageable_list = openstackclient.volume.v3.block_storage_manage:BlockStorageManageSnapshots diff --git a/test-requirements.txt b/test-requirements.txt index 275c2ff114..c9c1b28ccd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,5 @@ coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD -oslotest>=3.2.0 # Apache-2.0 -requests>=2.14.2 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0 stestr>=1.0.0 # Apache-2.0 testtools>=2.2.0 # MIT diff --git a/tox.ini b/tox.ini index e7c84c5110..1988ec8236 100644 --- a/tox.ini +++ b/tox.ini @@ -44,7 +44,7 @@ commands = python -m pip freeze stestr run {posargs} -[testenv:functional{,-tips,-py38,-py39,-py310,-py311,-py312}] +[testenv:functional{,-tips,-py310,-py311,-py312,-py313,-py314}] description = Run functional tests. setenv = @@ -113,11 +113,17 @@ commands = [flake8] show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes -# E203 Black will put spaces after colons in list comprehensions -# E501 Black takes care of line length for us -# E704 Black will occasionally put multiple statements on one line +# We only enable the hacking (H) and openstackclient (O) checks +select = H,O # H301 Black will put commas after imports that can't fit on one line -# W503 and W504 are disabled since they're not very useful -ignore = E203, E501, E701, H301, W503, W504 +ignore = H301 import-order-style = pep8 application_import_names = openstackclient + +[flake8:local-plugins] +extension = + O400 = checks:assert_no_oslo + O401 = checks:assert_no_duplicated_setup + O402 = checks:assert_use_of_client_aliases + O403 = checks:assert_find_ignore_missing_kwargs +paths = ./hacking