python-openstackclient
Advanced tools
| ================== | ||
| console connection | ||
| ================== | ||
| Server console connection information | ||
| Compute v2 | ||
| .. autoprogram-cliff:: openstack.compute.v2 | ||
| :command: console connection show |
| # 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.command import command | ||
| from osc_lib import utils | ||
| from openstackclient.i18n import _ | ||
| def _get_console_connection_columns(item): | ||
| column_map: dict[str, str] = {} | ||
| hidden_columns = ['id', 'location', 'name'] | ||
| return utils.get_osc_show_columns_for_sdk_resource( | ||
| item, column_map, hidden_columns | ||
| ) | ||
| class ShowConsoleConnectionInformation(command.ShowOne): | ||
| _description = _("Show server's remote console connection information") | ||
| def get_parser(self, prog_name): | ||
| parser = super().get_parser(prog_name) | ||
| parser.add_argument( | ||
| 'token', | ||
| metavar='<token>', | ||
| help=_("Nova console token to lookup"), | ||
| ) | ||
| return parser | ||
| def take_action(self, parsed_args): | ||
| compute_client = self.app.client_manager.compute | ||
| data = compute_client.validate_console_auth_token(parsed_args.token) | ||
| display_columns, columns = _get_console_connection_columns(data) | ||
| data = utils.get_dict_properties(data, columns) | ||
| return (display_columns, data) |
| # 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) |
| --- | ||
| features: | ||
| - | | ||
| Add filters to search for enabled and disabled users and projects. |
| --- | ||
| 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. |
| --- | ||
| 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. |
| --- | ||
| upgrade: | ||
| - | | ||
| Support for Python 3.9 has been dropped. |
| --- | ||
| features: | ||
| - | | ||
| The ``--network``, ``--port``, and ``--router`` options of the ``floating | ||
| ip list`` command can now be specified multiple times. |
| --- | ||
| upgrade: | ||
| - | | ||
| The following commands have been migrated to SDK: | ||
| - ``domain create`` | ||
| - ``domain delete`` | ||
| - ``domain list`` | ||
| - ``domain set`` | ||
| - ``domain show`` |
| --- | ||
| upgrade: | ||
| - | | ||
| Migrate ``group`` commands from keystoneclient to SDK. |
@@ -18,3 +18,3 @@ --- | ||
| - repo: https://github.com/astral-sh/ruff-pre-commit | ||
| rev: v0.11.2 | ||
| rev: v0.11.8 | ||
| hooks: | ||
@@ -21,0 +21,0 @@ - id: ruff |
+10
-58
@@ -26,4 +26,4 @@ --- | ||
| - job: | ||
| name: osc-tox-py39-tips | ||
| parent: openstack-tox-py39 | ||
| name: osc-tox-py310-tips | ||
| parent: openstack-tox-py310 | ||
| description: | | ||
@@ -47,4 +47,4 @@ Run unit tests for OpenStackClient with master branch of important libs. | ||
| - job: | ||
| name: osc-tox-py312-tips | ||
| parent: openstack-tox-py312 | ||
| name: osc-tox-py313-tips | ||
| parent: openstack-tox-py313 | ||
| description: | | ||
@@ -142,18 +142,2 @@ Run unit tests for OpenStackClient with master branch of important libs. | ||
| - 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: | ||
@@ -168,35 +152,7 @@ name: osc-build-image | ||
| provides: osc-container-image | ||
| vars: &osc_image_vars | ||
| vars: | ||
| docker_images: | ||
| - context: . | ||
| repository: osclient/python-openstackclient | ||
| tags: [] | ||
| - job: | ||
| name: osc-upload-image | ||
| parent: opendev-upload-docker-image | ||
| description: Build Docker images and upload to Docker Hub. | ||
| allowed-projects: openstack/python-openstackclient | ||
| requires: | ||
| - python-builder-3.11-bookworm-container-image | ||
| - python-base-3.11-bookworm-container-image | ||
| provides: osc-container-image | ||
| secrets: | ||
| - name: docker_credentials | ||
| secret: osc-dockerhub | ||
| pass-to-parent: true | ||
| vars: *osc_image_vars | ||
| - job: | ||
| name: osc-promote-image | ||
| parent: opendev-promote-docker-image | ||
| allowed-projects: openstack/python-openstackclient | ||
| description: Promote previously uploaded Docker images. | ||
| secrets: | ||
| - name: docker_credentials | ||
| secret: osc-dockerhub | ||
| pass-to-parent: true | ||
| nodeset: | ||
| nodes: [] | ||
| vars: *osc_image_vars | ||
| - project-template: | ||
@@ -206,8 +162,8 @@ name: osc-tox-unit-tips | ||
| jobs: | ||
| - osc-tox-py39-tips | ||
| - osc-tox-py312-tips | ||
| - osc-tox-py310-tips | ||
| - osc-tox-py313-tips | ||
| gate: | ||
| jobs: | ||
| - osc-tox-py39-tips | ||
| - osc-tox-py312-tips | ||
| - osc-tox-py310-tips | ||
| - osc-tox-py313-tips | ||
@@ -234,6 +190,2 @@ - project: | ||
| jobs: | ||
| - osc-upload-image | ||
| - osc-functional-devstack | ||
| promote: | ||
| jobs: | ||
| - osc-promote-image |
+8
-0
@@ -0,1 +1,2 @@ | ||
| 0weng <oweng@osuosl.org> | ||
| Aaron Rosen <aaronorosen@gmail.com> | ||
@@ -70,2 +71,3 @@ Abhishek Chanda <abhishek@cloudscaling.com> | ||
| Cedric Brandily <zzelle@gmail.com> | ||
| Chaemin-Lim <antraxmin@naver.com> | ||
| Chaozhe.Chen <chaozhe.chen@easystack.cn> | ||
@@ -123,2 +125,3 @@ Chen <dstbtgagt@foxmail.com> | ||
| Douglas Mendizábal <dmendiza@redhat.com> | ||
| Douglas Viroel <viroel@gmail.com> | ||
| Dr. Jens Harbott <harbott@osism.tech> | ||
@@ -199,2 +202,3 @@ Einst Crazy <yu.changcai@99cloud.net> | ||
| Jan Hartkopf <jhartkopf@inovex.de> | ||
| Jan Ueberacker <jan.ueberacker@inovex.de> | ||
| Jas <singhj@us.ibm.com> | ||
@@ -285,2 +289,3 @@ Jaspreet Singh Rawel <jaspreetsinghrawel@gmail.com> | ||
| Michael McCune <msm@redhat.com> | ||
| Michael Still <mikal@stillhq.com> | ||
| Miguel Lavalle <miguel.lavalle@verizonmedia.com> | ||
@@ -472,2 +477,3 @@ Miguel Lavalle <mlavalle@redhat.com> | ||
| devMuscle <hongsbien@naver.com> | ||
| djp <dimsss0607@gmail.com> | ||
| dongwenshuai <dong.wenshuai@zte.com.cn> | ||
@@ -552,2 +558,3 @@ elajkat <lajos.katona@est.tech> | ||
| venkatamahesh <venkatamaheshkotha@gmail.com> | ||
| waf <ekrmdhsl7@gmail.com> | ||
| wanghong <w.wanghong@huawei.com> | ||
@@ -562,2 +569,3 @@ wangxiyuan <wangxiyuan@huawei.com> | ||
| wuyuting <wytdahu@gmail.com> | ||
| xfrnk2 <xfrnk2@gmail.com> | ||
| xiexs <xiexs@cn.fujitsu.com> | ||
@@ -564,0 +572,0 @@ yaeeee <rim0399@naver.com> |
+9
-2
@@ -16,3 +16,3 @@ # Copyright (c) 2020 Red Hat, Inc. | ||
| FROM docker.io/opendevorg/python-builder:3.11-bookworm as builder | ||
| FROM docker.io/opendevorg/python-builder:3.12-bookworm AS builder | ||
@@ -22,4 +22,11 @@ COPY . /tmp/src | ||
| 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 | ||
@@ -26,0 +33,0 @@ RUN /output/install-from-bindep |
@@ -49,1 +49,6 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| return parser | ||
| def check_api_version(check_version): | ||
| # SDK supports auto-negotiation for us: always return True | ||
| return True |
@@ -110,2 +110,9 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| 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', | ||
@@ -112,0 +119,0 @@ dest='url_type', |
@@ -303,2 +303,3 @@ # Copyright 2013 OpenStack Foundation | ||
| identity_client = self.app.client_manager.identity | ||
| identity_sdk_client = self.app.client_manager.sdk_connection.identity | ||
@@ -349,7 +350,13 @@ kwargs = {} | ||
| ).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)) | ||
@@ -356,0 +363,0 @@ elif parsed_args.user: |
@@ -85,3 +85,3 @@ # Copyright 2017 Huawei, Inc. All rights reserved. | ||
| def _get_server_event_columns(item, client): | ||
| hidden_columns = ['name', 'server_id', 'links', 'location'] | ||
| hidden_columns = ['name', 'server_id', 'links', 'location', 'finish_time'] | ||
@@ -88,0 +88,0 @@ if not sdk_utils.supports_microversion(client, '2.58'): |
@@ -192,2 +192,12 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| 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): | ||
@@ -208,2 +218,29 @@ if domain_name_or_id is None: | ||
| 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): | ||
@@ -234,2 +271,28 @@ if domain_name_or_id is None: | ||
| 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( | ||
@@ -275,2 +338,18 @@ identity_client_manager, name_or_id, resource_type, **kwargs | ||
| 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 get_immutable_options(parsed_args): | ||
@@ -277,0 +356,0 @@ options = {} |
@@ -272,5 +272,5 @@ # Copyright 2018 SUSE Linux GmbH | ||
| 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: | ||
@@ -277,0 +277,0 @@ conn = self.app.client_manager.sdk_connection |
@@ -20,3 +20,3 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| from keystoneauth1 import exceptions as ks_exc | ||
| from openstack import exceptions as sdk_exceptions | ||
| from osc_lib.command import command | ||
@@ -33,2 +33,27 @@ from osc_lib import exceptions | ||
| 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): | ||
@@ -52,3 +77,5 @@ _description = _("Create new domain") | ||
| '--enable', | ||
| dest='is_enabled', | ||
| action='store_true', | ||
| default=True, | ||
| help=_('Enable domain (default)'), | ||
@@ -58,3 +85,4 @@ ) | ||
| '--disable', | ||
| action='store_true', | ||
| dest='is_enabled', | ||
| action='store_false', | ||
| help=_('Disable domain'), | ||
@@ -71,22 +99,16 @@ ) | ||
| 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) | ||
| 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) | ||
| LOG.info(_('Returning existing domain %s'), domain.name) | ||
@@ -96,4 +118,3 @@ else: | ||
| domain._info.pop('links') | ||
| return zip(*sorted(domain._info.items())) | ||
| return _format_domain(domain) | ||
@@ -115,8 +136,8 @@ | ||
| 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: | ||
@@ -153,3 +174,3 @@ result += 1 | ||
| '--enabled', | ||
| dest='enabled', | ||
| dest='is_enabled', | ||
| action='store_true', | ||
@@ -164,9 +185,13 @@ help=_('The domains that are enabled will be returned'), | ||
| kwargs['name'] = parsed_args.name | ||
| if parsed_args.enabled: | ||
| kwargs['enabled'] = True | ||
| if parsed_args.is_enabled: | ||
| kwargs['is_enabled'] = True | ||
| columns = ('ID', 'Name', 'Enabled', 'Description') | ||
| data = self.app.client_manager.identity.domains.list(**kwargs) | ||
| columns = ('id', 'name', 'is_enabled', 'description') | ||
| column_headers = ('ID', 'Name', 'Enabled', 'Description') | ||
| data = self.app.client_manager.sdk_connection.identity.domains( | ||
| **kwargs | ||
| ) | ||
| return ( | ||
| columns, | ||
| column_headers, | ||
| ( | ||
@@ -206,3 +231,5 @@ utils.get_item_properties( | ||
| '--enable', | ||
| dest='is_enabled', | ||
| action='store_true', | ||
| default=None, | ||
| help=_('Enable domain'), | ||
@@ -212,3 +239,5 @@ ) | ||
| '--disable', | ||
| action='store_true', | ||
| dest='is_enabled', | ||
| action='store_false', | ||
| default=None, | ||
| help=_('Disable domain'), | ||
@@ -220,6 +249,4 @@ ) | ||
| 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) | ||
| kwargs = {} | ||
@@ -231,6 +258,4 @@ if parsed_args.name: | ||
| if parsed_args.enable: | ||
| kwargs['enabled'] = True | ||
| if parsed_args.disable: | ||
| kwargs['enabled'] = False | ||
| if parsed_args.is_enabled is not None: | ||
| kwargs['is_enabled'] = parsed_args.is_enabled | ||
@@ -241,3 +266,3 @@ options = common.get_immutable_options(parsed_args) | ||
| identity_client.domains.update(domain.id, **kwargs) | ||
| identity_client.update_domain(domain.id, **kwargs) | ||
@@ -258,11 +283,5 @@ | ||
| def take_action(self, parsed_args): | ||
| identity_client = self.app.client_manager.identity | ||
| identity_client = self.app.client_manager.sdk_connection.identity | ||
| domain = identity_client.find_domain(parsed_args.domain) | ||
| domain_str = common._get_token_resource( | ||
| identity_client, 'domain', parsed_args.domain | ||
| ) | ||
| domain = utils.find_resource(identity_client.domains, domain_str) | ||
| domain._info.pop('links') | ||
| return zip(*sorted(domain._info.items())) | ||
| return _format_domain(domain) |
@@ -20,3 +20,3 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| from keystoneauth1 import exceptions as ks_exc | ||
| from openstack import exceptions as sdk_exc | ||
| from osc_lib.command import command | ||
@@ -33,2 +33,21 @@ from osc_lib import exceptions | ||
| 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): | ||
@@ -58,7 +77,7 @@ _description = _("Add user to group") | ||
| 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 | ||
| ) | ||
@@ -68,6 +87,6 @@ result = 0 | ||
| 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: | ||
@@ -116,23 +135,26 @@ result += 1 | ||
| 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") % { | ||
@@ -143,2 +165,8 @@ 'user': parsed_args.user, | ||
| 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) | ||
@@ -174,19 +202,24 @@ | ||
| 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 | ||
| ) | ||
| else: | ||
| group = identity_client.find_group(parsed_args.name) | ||
| LOG.info(_('Returning existing group %s'), group.name) | ||
@@ -196,4 +229,3 @@ else: | ||
| group._info.pop('links') | ||
| return zip(*sorted(group._info.items())) | ||
| return _format_group(group) | ||
@@ -220,3 +252,3 @@ | ||
| def take_action(self, parsed_args): | ||
| identity_client = self.app.client_manager.identity | ||
| identity_client = self.app.client_manager.sdk_connection.identity | ||
@@ -226,6 +258,6 @@ errors = 0 | ||
| 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: | ||
@@ -275,16 +307,28 @@ errors += 1 | ||
| 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() | ||
@@ -295,6 +339,2 @@ # List groups | ||
| columns += ('Domain ID', 'Description') | ||
| data = identity_client.groups.list( | ||
| domain=domain, | ||
| user=user, | ||
| ) | ||
@@ -338,7 +378,7 @@ return ( | ||
| 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 | ||
| ) | ||
@@ -348,6 +388,6 @@ result = 0 | ||
| 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: | ||
@@ -404,4 +444,4 @@ result += 1 | ||
| 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 | ||
@@ -415,3 +455,3 @@ ) | ||
| identity_client.groups.update(group.id, **kwargs) | ||
| identity_client.update_group(group, **kwargs) | ||
@@ -437,11 +477,16 @@ | ||
| 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) |
@@ -243,2 +243,16 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| ) | ||
| 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')) | ||
@@ -281,2 +295,5 @@ return parser | ||
| if parsed_args.is_enabled is not None: | ||
| kwargs['is_enabled'] = parsed_args.is_enabled | ||
| tag.get_tag_filtering_args(parsed_args, kwargs) | ||
@@ -283,0 +300,0 @@ |
@@ -44,2 +44,3 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| 'password_expires_at', | ||
| 'options', | ||
| ) | ||
@@ -55,2 +56,3 @@ column_headers = ( | ||
| 'password_expires_at', | ||
| 'options', | ||
| ) | ||
@@ -294,3 +296,3 @@ return ( | ||
| if not parsed_args.password: | ||
| if not password: | ||
| LOG.warning( | ||
@@ -418,2 +420,20 @@ _( | ||
| ) | ||
| 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 | ||
@@ -438,2 +458,5 @@ | ||
| if parsed_args.is_enabled is not None: | ||
| enabled = parsed_args.is_enabled | ||
| if parsed_args.project: | ||
@@ -477,5 +500,11 @@ if domain is not None: | ||
| 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, | ||
| ) | ||
@@ -668,2 +697,4 @@ # Column handling | ||
| 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) | ||
@@ -711,3 +742,5 @@ # FIXME(gyee): there are two scenarios: | ||
| identity_client.update_user( | ||
| current_password=current_password, password=password | ||
| user=user_id, | ||
| current_password=current_password, | ||
| password=password, | ||
| ) | ||
@@ -714,0 +747,0 @@ |
@@ -49,1 +49,6 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| return parser | ||
| def check_api_version(check_version): | ||
| # SDK supports auto-negotiation for us: always return True | ||
| return True |
@@ -20,2 +20,3 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| from base64 import b64encode | ||
| import copy | ||
| import logging | ||
@@ -26,5 +27,5 @@ import os | ||
| 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 | ||
@@ -581,3 +582,3 @@ from osc_lib.cli import format_columns | ||
| def _take_action_volume(self, parsed_args): | ||
| volume_client = self.app.client_manager.volume | ||
| volume_client = self.app.client_manager.sdk_connection.volume | ||
@@ -613,5 +614,4 @@ unsupported_opts = { | ||
| source_volume = utils.find_resource( | ||
| volume_client.volumes, | ||
| parsed_args.volume, | ||
| source_volume = volume_client.find_volume( | ||
| parsed_args.volume, ignore_missing=False | ||
| ) | ||
@@ -622,3 +622,3 @@ kwargs: dict[str, ty.Any] = { | ||
| } | ||
| if volume_client.api_version < api_versions.APIVersion('3.1'): | ||
| if not sdk_utils.supports_microversion(volume_client, '3.1'): | ||
| if parsed_args.visibility or parsed_args.is_protected is not None: | ||
@@ -635,11 +635,11 @@ msg = _( | ||
| 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: | ||
@@ -646,0 +646,0 @@ info['volume_type'] = info['volume_type']['name'] |
@@ -30,8 +30,2 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| """Returns a network proxy""" | ||
| # NOTE(dtroyer): As of osc-lib 1.8.0 and OpenStackSDK 0.10.0 the | ||
| # old Profile interface and separate client creation | ||
| # for each API that uses the SDK is unnecessary. This | ||
| # callback remains as a remnant of the original plugin | ||
| # interface and to avoid the code churn of changing all | ||
| # of the existing references. | ||
| LOG.debug( | ||
@@ -38,0 +32,0 @@ 'Network client initialized using OpenStack SDK: %s', |
@@ -246,6 +246,9 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| metavar='<network>', | ||
| dest='networks', | ||
| action='append', | ||
| help=self.enhance_help_neutron( | ||
| _( | ||
| "List floating IP(s) according to " | ||
| "given network (name or ID)" | ||
| "List floating IP(s) according to given network " | ||
| "(name or ID) " | ||
| "(repeat option to fiter on multiple networks)" | ||
| ) | ||
@@ -257,4 +260,9 @@ ), | ||
| metavar='<port>', | ||
| dest='ports', | ||
| action='append', | ||
| help=self.enhance_help_neutron( | ||
| _("List floating IP(s) according to given port (name or ID)") | ||
| _( | ||
| "List floating IP(s) according to given port (name or ID) " | ||
| "(repeat option to fiter on multiple ports)" | ||
| ) | ||
| ), | ||
@@ -277,10 +285,2 @@ ) | ||
| parser.add_argument( | ||
| '--long', | ||
| action='store_true', | ||
| default=False, | ||
| help=self.enhance_help_neutron( | ||
| _("List additional fields in output") | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| '--status', | ||
@@ -301,4 +301,4 @@ metavar='<status>', | ||
| _( | ||
| "List floating IP(s) according to given project (name or " | ||
| "ID)" | ||
| "List floating IP(s) according to given project " | ||
| "(name or ID) " | ||
| ) | ||
@@ -311,4 +311,10 @@ ), | ||
| metavar='<router>', | ||
| dest='routers', | ||
| action='append', | ||
| help=self.enhance_help_neutron( | ||
| _("List floating IP(s) according to given router (name or ID)") | ||
| _( | ||
| "List floating IP(s) according to given router " | ||
| "(name or ID) " | ||
| "(repeat option to fiter on multiple routers)" | ||
| ) | ||
| ), | ||
@@ -319,2 +325,10 @@ ) | ||
| ) | ||
| parser.add_argument( | ||
| '--long', | ||
| action='store_true', | ||
| default=False, | ||
| help=self.enhance_help_neutron( | ||
| _("List additional fields in output") | ||
| ), | ||
| ) | ||
@@ -363,18 +377,29 @@ return parser | ||
| 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: | ||
@@ -387,8 +412,12 @@ project = identity_common.find_project( | ||
| 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) | ||
@@ -395,0 +424,0 @@ |
@@ -76,3 +76,3 @@ # Copyright (c) 2016, Intel Corporation. | ||
| def _get_columns(item): | ||
| hidden_columns = ['location', 'tenant_id'] | ||
| hidden_columns = ['location', 'name', 'tenant_id'] | ||
| return utils.get_osc_show_columns_for_sdk_resource( | ||
@@ -152,10 +152,2 @@ item, {}, hidden_columns | ||
| 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): | ||
@@ -362,6 +354,6 @@ rule_type = rule_type.replace('-', '_') | ||
| ) | ||
| 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), | ||
| ) | ||
@@ -368,0 +360,0 @@ |
@@ -79,3 +79,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| column_map['interfaces_info'] = 'interfaces_info' | ||
| invisible_columns = ['location'] | ||
| invisible_columns = ['location', 'tenant_id'] | ||
| if item.is_ha is None: | ||
@@ -82,0 +82,0 @@ invisible_columns.append('is_ha') |
@@ -33,3 +33,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| def _get_columns(item): | ||
| hidden_columns = ['location', 'tenant_id'] | ||
| hidden_columns = ['location', 'name', 'tenant_id', 'tags'] | ||
| return utils.get_osc_show_columns_for_sdk_resource( | ||
@@ -36,0 +36,0 @@ item, {}, hidden_columns |
@@ -92,5 +92,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| '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( | ||
@@ -190,3 +189,4 @@ item, column_map, hidden_columns | ||
| ) | ||
| 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( | ||
@@ -425,3 +425,4 @@ obj, property_columns, formatters=_formatters_compute | ||
| 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( | ||
@@ -428,0 +429,0 @@ obj, property_columns, formatters=_formatters_compute |
@@ -97,3 +97,3 @@ # Copyright 2012-2013 OpenStack Foundation | ||
| if mod_versions is not None and not isinstance( | ||
| mod_versions, (dict, tuple) | ||
| mod_versions, dict | tuple | ||
| ): | ||
@@ -100,0 +100,0 @@ raise TypeError( |
@@ -100,3 +100,7 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| if parse_output: | ||
| return json.loads(output) | ||
| try: | ||
| return json.loads(output) | ||
| except json.JSONDecodeError: | ||
| print(f'failed to decode: {output}') | ||
| raise | ||
| else: | ||
@@ -103,0 +107,0 @@ return output |
@@ -24,8 +24,14 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 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 | ||
@@ -38,6 +44,9 @@ def keypair_list(self, params=''): | ||
| 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: | ||
@@ -205,1 +214,28 @@ if not ignore_exceptions: | ||
| 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']) |
@@ -16,3 +16,2 @@ # Copyright 2013 Nebula Inc. | ||
| import copy | ||
| import random | ||
@@ -36,4 +35,3 @@ import re | ||
| 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 | ||
@@ -45,56 +43,2 @@ from openstackclient.tests.unit.network.v2 import fakes as network_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: | ||
@@ -127,6 +71,6 @@ def setUp(self): | ||
| class TestComputev2( | ||
| identity_fakes.FakeClientMixin, | ||
| network_fakes.FakeClientMixin, | ||
| image_fakes.FakeClientMixin, | ||
| volume_fakes.FakeClientMixin, | ||
| identity_fakes.FakeClientMixin, | ||
| FakeClientMixin, | ||
@@ -140,6 +84,4 @@ utils.TestCommand, | ||
| :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 | ||
| """ | ||
@@ -150,3 +92,3 @@ | ||
| # set default attributes. | ||
| agent_info = { | ||
| agent_attrs = { | ||
| 'agent_id': 'agent-id-' + uuid.uuid4().hex, | ||
@@ -161,7 +103,8 @@ 'os': 'agent-os-' + uuid.uuid4().hex, | ||
| 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 | ||
@@ -172,8 +115,5 @@ | ||
| :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 | ||
| """ | ||
@@ -191,3 +131,3 @@ agents = [] | ||
| :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 | ||
| """ | ||
@@ -224,6 +164,4 @@ attrs = attrs or {} | ||
| :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 | ||
| """ | ||
@@ -241,2 +179,4 @@ attrs = attrs or {} | ||
| assert not set(attrs) - set(security_group_attrs), 'unknown keys' | ||
| # Overwrite default attributes. | ||
@@ -250,8 +190,5 @@ security_group_attrs.update(attrs) | ||
| :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 | ||
| """ | ||
@@ -265,28 +202,7 @@ 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 | ||
| """ | ||
@@ -306,2 +222,4 @@ attrs = attrs or {} | ||
| assert not set(attrs) - set(security_group_rule_attrs), 'unknown keys' | ||
| # Overwrite default attributes. | ||
@@ -316,8 +234,5 @@ security_group_rule_attrs.update(attrs) | ||
| :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 | ||
| """ | ||
@@ -331,62 +246,7 @@ security_group_rules = [] | ||
| def create_one_server(attrs=None, methods=None): | ||
| """Create a fake server. | ||
| def create_one_server(attrs=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 | ||
| :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, | ||
| """ | ||
@@ -419,12 +279,12 @@ attrs = attrs or {} | ||
| 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)) | ||
@@ -434,20 +294,2 @@ 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): | ||
@@ -457,3 +299,4 @@ """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 | ||
| """ | ||
@@ -501,3 +344,3 @@ attrs = attrs or {} | ||
| :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 | ||
| """ | ||
@@ -535,3 +378,3 @@ attrs = attrs or {} | ||
| :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 | ||
| """ | ||
@@ -545,26 +388,7 @@ 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 | ||
| """ | ||
@@ -579,12 +403,10 @@ attrs = attrs or {} | ||
| assert not set(attrs) - set(flavor_access_info), 'unknown keys' | ||
| # Overwrite default attributes. | ||
| flavor_access_info.update(attrs) | ||
| flavor_access = fakes.FakeResource( | ||
| info=copy.deepcopy(flavor_access_info), loaded=True | ||
| ) | ||
| return flavor_access_info | ||
| return flavor_access | ||
| def create_one_availability_zone(attrs=None): | ||
@@ -594,4 +416,4 @@ """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 | ||
| """ | ||
@@ -643,8 +465,6 @@ attrs = attrs or {} | ||
| 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 | ||
| """ | ||
@@ -662,2 +482,4 @@ attrs = attrs or {} | ||
| assert not set(attrs) - set(floating_ip_attrs), 'unknown keys' | ||
| # Overwrite default attributes. | ||
@@ -670,10 +492,7 @@ floating_ip_attrs.update(attrs) | ||
| def create_floating_ips(attrs=None, count=2): | ||
| """Create multiple fake 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 FakeResource objects faking the 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 | ||
| """ | ||
@@ -686,28 +505,7 @@ 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 | ||
| """ | ||
@@ -722,2 +520,4 @@ if attrs is None: | ||
| assert not set(attrs) - set(floating_ip_pool_attrs), 'unknown keys' | ||
| # Overwrite default attributes. | ||
@@ -730,10 +530,7 @@ floating_ip_pool_attrs.update(attrs) | ||
| def create_floating_ip_pools(attrs=None, count=2): | ||
| """Create multiple fake 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 FakeResource objects faking the 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 | ||
| """ | ||
@@ -749,6 +546,4 @@ floating_ip_pools = [] | ||
| :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 | ||
| """ | ||
@@ -793,2 +588,4 @@ attrs = attrs or {} | ||
| assert not set(attrs) - set(network_attrs), 'unknown keys' | ||
| # Overwrite default attributes. | ||
@@ -803,8 +600,5 @@ network_attrs.update(attrs) | ||
| :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 | ||
| """ | ||
@@ -818,21 +612,2 @@ 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_limits(attrs=None): | ||
@@ -903,3 +678,3 @@ """Create a fake limits object.""" | ||
| :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 | ||
| """ | ||
@@ -940,3 +715,3 @@ attrs = attrs or {} | ||
| :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 | ||
| """ | ||
@@ -954,3 +729,4 @@ migrations = [] | ||
| :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 | ||
| """ | ||
@@ -1011,4 +787,4 @@ attrs = attrs or {} | ||
| :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 | ||
| """ | ||
@@ -1054,8 +830,8 @@ attrs = attrs or {} | ||
| 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 | ||
| """ | ||
@@ -1062,0 +838,0 @@ attrs = attrs or {} |
@@ -160,3 +160,3 @@ # Copyright 2016 Huawei, Inc. All rights reserved. | ||
| def test_console_url_show_with_spice(self): | ||
| def test_console_url_show_with_spice_html5(self): | ||
| arglist = [ | ||
@@ -178,2 +178,19 @@ '--spice', | ||
| 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): | ||
@@ -180,0 +197,0 @@ arglist = [ |
@@ -1055,3 +1055,3 @@ # Copyright 2015 Symantec Corporation | ||
| private_flavor.ephemeral, | ||
| [self.flavor_access.tenant_id], | ||
| [self.flavor_access['tenant_id']], | ||
| private_flavor.description, | ||
@@ -1058,0 +1058,0 @@ private_flavor.disk, |
@@ -21,2 +21,3 @@ # Copyright 2016 IBM | ||
| 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 | ||
@@ -533,9 +534,13 @@ from openstack.test import fakes as sdk_fakes | ||
| projects_mock = self.identity_client.tenants | ||
| projects_mock = self.identity_client.projects | ||
| projects_mock.reset_mock() | ||
| projects_mock.get.return_value = self._project | ||
| users_mock = self.identity_client.users | ||
| users_mock.reset_mock() | ||
| users_mock.list.return_value = [self._user] | ||
| 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] | ||
@@ -549,3 +554,5 @@ arglist = ['--project', self._project.name] | ||
| projects_mock.get.assert_called_with(self._project.name) | ||
| users_mock.list.assert_called_with(tenant_id=self._project.id) | ||
| role_assignments_mock.assert_called_with( | ||
| scope_project_id=self._project.id | ||
| ) | ||
| self.compute_client.keypairs.assert_called_with( | ||
@@ -552,0 +559,0 @@ user_id=self._user.id, |
@@ -25,18 +25,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| class TestServerBackup(compute_fakes.TestComputev2): | ||
| def setup_servers_mock(self, count): | ||
| servers = compute_fakes.create_sdk_servers( | ||
| count=count, | ||
| ) | ||
| self.compute_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 = ( | ||
@@ -68,31 +54,16 @@ 'id', | ||
| 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, | ||
| ] | ||
@@ -104,3 +75,3 @@ verifylist = [ | ||
| ('wait', False), | ||
| ('server', servers[0].id), | ||
| ('server', self.server.id), | ||
| ] | ||
@@ -115,4 +86,4 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.backup_server.assert_called_with( | ||
| servers[0].id, | ||
| servers[0].name, | ||
| self.server.id, | ||
| self.server.name, | ||
| '', | ||
@@ -122,9 +93,6 @@ 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 = [ | ||
@@ -137,3 +105,3 @@ '--name', | ||
| '2', | ||
| servers[0].id, | ||
| self.server.id, | ||
| ] | ||
@@ -144,3 +112,3 @@ verifylist = [ | ||
| ('rotate', 2), | ||
| ('server', servers[0].id), | ||
| ('server', self.server.id), | ||
| ] | ||
@@ -155,3 +123,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.backup_server.assert_called_with( | ||
| servers[0].id, | ||
| self.server.id, | ||
| 'image', | ||
@@ -162,12 +130,8 @@ 'daily', | ||
| 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 | ||
@@ -180,3 +144,3 @@ arglist = [ | ||
| '--wait', | ||
| servers[0].id, | ||
| self.server.id, | ||
| ] | ||
@@ -187,3 +151,3 @@ verifylist = [ | ||
| ('wait', True), | ||
| ('server', servers[0].id), | ||
| ('server', self.server.id), | ||
| ] | ||
@@ -199,3 +163,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.backup_server.assert_called_with( | ||
| servers[0].id, | ||
| self.server.id, | ||
| 'image', | ||
@@ -207,3 +171,3 @@ 'daily', | ||
| 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 | ||
| ) | ||
@@ -213,7 +177,4 @@ | ||
| 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], | ||
| side_effect=self.image, | ||
| ) | ||
@@ -227,3 +188,3 @@ | ||
| '--wait', | ||
| servers[0].id, | ||
| self.server.id, | ||
| ] | ||
@@ -234,3 +195,3 @@ verifylist = [ | ||
| ('wait', True), | ||
| ('server', servers[0].id), | ||
| ('server', self.server.id), | ||
| ] | ||
@@ -245,3 +206,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.backup_server.assert_called_with( | ||
| servers[0].id, | ||
| self.server.id, | ||
| 'image', | ||
@@ -253,6 +214,6 @@ 'daily', | ||
| 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) |
@@ -25,3 +25,3 @@ # Copyright 2017 Huawei, Inc. All rights reserved. | ||
| 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() | ||
@@ -370,3 +370,3 @@ | ||
| 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() | ||
@@ -373,0 +373,0 @@ columns = ( |
@@ -24,17 +24,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| class TestServerImage(compute_fakes.TestComputev2): | ||
| def setup_servers_mock(self, count): | ||
| servers = compute_fakes.create_sdk_servers( | ||
| count=count, | ||
| ) | ||
| self.compute_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 = ( | ||
@@ -66,37 +53,20 @@ 'id', | ||
| 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 | ||
| self.compute_client.create_server_image.return_value = self.image | ||
| # Get the command object to test | ||
| self.cmd = server_image.CreateServerImage(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) | ||
| self.compute_client.create_server_image = mock.Mock( | ||
| return_value=images[0], | ||
| ) | ||
| return images | ||
| def test_server_image_create_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 = [ | ||
| ('server', servers[0].id), | ||
| ('server', self.server.id), | ||
| ] | ||
@@ -111,14 +81,11 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.create_server_image.assert_called_with( | ||
| servers[0].id, | ||
| servers[0].name, | ||
| 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 = [ | ||
@@ -129,7 +96,7 @@ '--name', | ||
| 'key=value', | ||
| servers[0].id, | ||
| self.server.id, | ||
| ] | ||
| verifylist = [ | ||
| ('name', 'img-nam'), | ||
| ('server', servers[0].id), | ||
| ('server', self.server.id), | ||
| ('properties', {'key': 'value'}), | ||
@@ -145,3 +112,3 @@ ] | ||
| self.compute_client.create_server_image.assert_called_with( | ||
| servers[0].id, | ||
| self.server.id, | ||
| 'img-nam', | ||
@@ -151,17 +118,14 @@ {'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), | ||
| ] | ||
@@ -177,4 +141,4 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.create_server_image.assert_called_with( | ||
| servers[0].id, | ||
| servers[0].name, | ||
| self.server.id, | ||
| self.server.name, | ||
| None, | ||
@@ -184,3 +148,3 @@ ) | ||
| 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 | ||
| ) | ||
@@ -190,12 +154,9 @@ | ||
| 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), | ||
| ] | ||
@@ -210,4 +171,4 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.compute_client.create_server_image.assert_called_with( | ||
| servers[0].id, | ||
| servers[0].name, | ||
| self.server.id, | ||
| self.server.name, | ||
| None, | ||
@@ -217,6 +178,6 @@ ) | ||
| 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) |
@@ -55,3 +55,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| self.server = compute_fakes.create_one_sdk_server() | ||
| self.server = compute_fakes.create_one_server() | ||
| self.compute_client.find_server.return_value = self.server | ||
@@ -698,3 +698,3 @@ | ||
| self.server = compute_fakes.create_one_sdk_server() | ||
| self.server = compute_fakes.create_one_server() | ||
| self.compute_client.find_server.return_value = self.server | ||
@@ -902,3 +902,3 @@ | ||
| self.server = compute_fakes.create_one_sdk_server() | ||
| self.server = compute_fakes.create_one_server() | ||
@@ -1018,3 +1018,3 @@ # Return value for utils.find_resource for server. | ||
| self.server = compute_fakes.create_one_sdk_server() | ||
| self.server = compute_fakes.create_one_server() | ||
@@ -1021,0 +1021,0 @@ # Return value for utils.find_resource for server. |
@@ -27,2 +27,3 @@ # Copyright 2018 SUSE Linux GmbH | ||
| from openstack.identity.v3 import role as _role | ||
| from openstack.identity.v3 import user as _user | ||
| from openstack.test import fakes as sdk_fakes | ||
@@ -329,3 +330,28 @@ from openstackclient.identity.v3 import 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, | ||
| '', | ||
| self.application_credential.unrestricted, | ||
| self.application_credential.access_rules, | ||
| self.application_credential.expires_at, | ||
| ), | ||
| ) | ||
| # Get the command object to test | ||
@@ -344,7 +370,8 @@ self.cmd = application_credential.ListApplicationCredential( | ||
| # 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( | ||
@@ -354,26 +381,21 @@ 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)) | ||
@@ -380,0 +402,0 @@ |
@@ -13,2 +13,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| from openstack.identity.v3 import domain as _domain | ||
| from openstack.test import fakes as sdk_fakes | ||
| from openstackclient.identity.v3 import domain | ||
@@ -18,25 +20,22 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes | ||
| class TestDomain(identity_fakes.TestIdentityv3): | ||
| def setUp(self): | ||
| super().setUp() | ||
| class TestDomainCreate(identity_fakes.TestIdentityv3): | ||
| columns = ( | ||
| 'id', | ||
| 'name', | ||
| 'enabled', | ||
| 'description', | ||
| 'options', | ||
| ) | ||
| # 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') | ||
| 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, | ||
| ) | ||
@@ -66,5 +65,5 @@ | ||
| 'options': {}, | ||
| 'enabled': True, | ||
| 'is_enabled': True, | ||
| } | ||
| self.domains_mock.create.assert_called_with(**kwargs) | ||
| self.identity_sdk_client.create_domain.assert_called_with(**kwargs) | ||
@@ -96,5 +95,5 @@ self.assertEqual(self.columns, columns) | ||
| 'options': {}, | ||
| 'enabled': True, | ||
| 'is_enabled': True, | ||
| } | ||
| self.domains_mock.create.assert_called_with(**kwargs) | ||
| self.identity_sdk_client.create_domain.assert_called_with(**kwargs) | ||
@@ -110,3 +109,3 @@ self.assertEqual(self.columns, columns) | ||
| verifylist = [ | ||
| ('enable', True), | ||
| ('is_enabled', True), | ||
| ('name', self.domain.name), | ||
@@ -126,5 +125,5 @@ ] | ||
| 'options': {}, | ||
| 'enabled': True, | ||
| 'is_enabled': True, | ||
| } | ||
| self.domains_mock.create.assert_called_with(**kwargs) | ||
| self.identity_sdk_client.create_domain.assert_called_with(**kwargs) | ||
@@ -140,3 +139,3 @@ self.assertEqual(self.columns, columns) | ||
| verifylist = [ | ||
| ('disable', True), | ||
| ('is_enabled', False), | ||
| ('name', self.domain.name), | ||
@@ -156,5 +155,5 @@ ] | ||
| 'options': {}, | ||
| 'enabled': False, | ||
| 'is_enabled': False, | ||
| } | ||
| self.domains_mock.create.assert_called_with(**kwargs) | ||
| self.identity_sdk_client.create_domain.assert_called_with(**kwargs) | ||
@@ -185,5 +184,5 @@ self.assertEqual(self.columns, columns) | ||
| '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) | ||
@@ -214,5 +213,5 @@ self.assertEqual(self.columns, columns) | ||
| '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) | ||
@@ -223,4 +222,4 @@ self.assertEqual(self.columns, columns) | ||
| class TestDomainDelete(TestDomain): | ||
| domain = identity_fakes.FakeDomain.create_one_domain() | ||
| class TestDomainDelete(identity_fakes.TestIdentityv3): | ||
| domain = sdk_fakes.generate_fake_resource(_domain.Domain) | ||
@@ -231,4 +230,4 @@ def setUp(self): | ||
| # 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 | ||
@@ -249,3 +248,3 @@ # Get the command object to test | ||
| self.domains_mock.delete.assert_called_with( | ||
| self.identity_sdk_client.delete_domain.assert_called_with( | ||
| self.domain.id, | ||
@@ -256,4 +255,12 @@ ) | ||
| 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', | ||
| ) | ||
@@ -263,3 +270,11 @@ def setUp(self): | ||
| 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, | ||
| ), | ||
| ) | ||
@@ -278,15 +293,6 @@ # Get the command object to test | ||
| 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)) | ||
@@ -304,19 +310,10 @@ def test_domain_list_with_option_name(self): | ||
| 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) | ||
@@ -329,20 +326,11 @@ | ||
| 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) | ||
@@ -352,5 +340,5 @@ def setUp(self): | ||
| 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 | ||
@@ -372,3 +360,5 @@ # Get the command object to test | ||
| 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) | ||
@@ -394,3 +384,5 @@ | ||
| } | ||
| 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) | ||
@@ -416,3 +408,5 @@ | ||
| } | ||
| 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) | ||
@@ -426,3 +420,3 @@ | ||
| verifylist = [ | ||
| ('enable', True), | ||
| ('is_enabled', True), | ||
| ('domain', self.domain.id), | ||
@@ -436,5 +430,7 @@ ] | ||
| 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) | ||
@@ -448,3 +444,3 @@ | ||
| verifylist = [ | ||
| ('disable', True), | ||
| ('is_enabled', False), | ||
| ('domain', self.domain.id), | ||
@@ -458,5 +454,7 @@ ] | ||
| 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) | ||
@@ -481,3 +479,5 @@ | ||
| } | ||
| 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) | ||
@@ -502,12 +502,30 @@ | ||
| } | ||
| 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 | ||
@@ -532,15 +550,7 @@ self.cmd = domain.ShowDomain(self.app, None) | ||
| columns, data = self.cmd.take_action(parsed_args) | ||
| self.domains_mock.get.assert_called_with( | ||
| self.identity_sdk_client.find_domain.assert_called_with( | ||
| self.domain.id, | ||
| ) | ||
| 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) |
@@ -17,5 +17,8 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 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 | ||
@@ -26,33 +29,18 @@ from openstackclient.identity.v3 import group | ||
| 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() | ||
| self._group = sdk_fakes.generate_fake_resource(_group.Group) | ||
| self.users = tuple( | ||
| sdk_fakes.generate_fake_resources(_user.User, count=2) | ||
| ) | ||
| # Get a shortcut to the GroupManager Mock | ||
| self.groups_mock = self.identity_client.groups | ||
| self.groups_mock.reset_mock() | ||
| self.identity_sdk_client.find_group.return_value = self._group | ||
| self.identity_sdk_client.add_user_to_group.return_value = None | ||
| # 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.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.cmd = group.AddUserToGroup(self.app, None) | ||
| def test_group_add_user(self): | ||
| self.identity_sdk_client.find_user.return_value = self.users[0] | ||
| arglist = [ | ||
@@ -69,3 +57,3 @@ self._group.name, | ||
| 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 | ||
@@ -76,2 +64,6 @@ ) | ||
| def test_group_add_multi_users(self): | ||
| self.identity_sdk_client.find_user.side_effect = [ | ||
| self.users[0], | ||
| self.users[1], | ||
| ] | ||
| arglist = [ | ||
@@ -93,3 +85,3 @@ self._group.name, | ||
| ] | ||
| 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) | ||
@@ -99,4 +91,4 @@ | ||
| 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, | ||
@@ -120,17 +112,17 @@ ] | ||
| self.assertEqual(msg, str(e)) | ||
| msg = f"{self.users[0].name} not added to 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) | ||
@@ -150,3 +142,3 @@ | ||
| 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 | ||
@@ -157,6 +149,5 @@ ) | ||
| 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 = [ | ||
@@ -173,8 +164,8 @@ self.group.name, | ||
| 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) | ||
@@ -190,15 +181,11 @@ columns = ( | ||
| 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 | ||
@@ -208,2 +195,3 @@ self.cmd = group.CreateGroup(self.app, None) | ||
| def test_group_create(self): | ||
| self.identity_sdk_client.create_group.return_value = self.group | ||
| arglist = [ | ||
@@ -218,11 +206,18 @@ self.group.name, | ||
| 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 = [ | ||
@@ -232,9 +227,9 @@ '--domain', | ||
| '--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), | ||
| ] | ||
@@ -244,12 +239,21 @@ 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 = [ | ||
@@ -266,19 +270,65 @@ '--or-show', | ||
| 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 | ||
| ) | ||
| 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 | ||
| ) | ||
| 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 | ||
@@ -288,7 +338,8 @@ 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]), | ||
| ] | ||
@@ -298,12 +349,16 @@ 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 = [ | ||
@@ -316,20 +371,23 @@ ('groups', arglist), | ||
| 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]), | ||
| ] | ||
@@ -339,13 +397,21 @@ 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.groups_mock.delete.assert_called_once_with(self.groups[0].id) | ||
| self.identity_sdk_client.delete_group.assert_called_once_with( | ||
| self.group_with_domain.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', | ||
@@ -362,15 +428,25 @@ ] | ||
| except exceptions.CommandError as e: | ||
| self.assertEqual('1 of 2 groups failed to delete.', str(e)) | ||
| self.assertEqual('1 of 3 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.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) | ||
@@ -381,8 +457,2 @@ columns = ( | ||
| ) | ||
| datalist = ( | ||
| ( | ||
| group.id, | ||
| group.name, | ||
| ), | ||
| ) | ||
@@ -392,9 +462,13 @@ def setUp(self): | ||
| self.groups_mock.get.return_value = self.group | ||
| self.groups_mock.list.return_value = [self.group] | ||
| 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.domains_mock.get.return_value = self.domain | ||
| self.identity_sdk_client.find_user.return_value = self.user | ||
| self.identity_sdk_client.find_domain.return_value = self.domain | ||
| self.users_mock.get.return_value = self.user | ||
| # Get the command object to test | ||
@@ -404,2 +478,6 @@ 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 = [] | ||
@@ -414,14 +492,19 @@ verifylist = [] | ||
| # Set expected values | ||
| kwargs = { | ||
| 'domain': None, | ||
| 'user': None, | ||
| } | ||
| self.identity_sdk_client.groups.assert_called_with() | ||
| self.groups_mock.list.assert_called_with(**kwargs) | ||
| 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 = [ | ||
@@ -443,12 +526,13 @@ '--domain', | ||
| 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 = [ | ||
@@ -468,14 +552,49 @@ '--user', | ||
| 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 = [ | ||
@@ -494,11 +613,5 @@ '--long', | ||
| # 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', | ||
@@ -514,21 +627,29 @@ '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 = [ | ||
@@ -545,3 +666,3 @@ self._group.id, | ||
| 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 | ||
@@ -552,2 +673,6 @@ ) | ||
| def test_group_remove_multi_users(self): | ||
| self.identity_sdk_client.find_user.side_effect = [ | ||
| self.users[0], | ||
| self.users[1], | ||
| ] | ||
| arglist = [ | ||
@@ -569,3 +694,3 @@ self._group.name, | ||
| ] | ||
| 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) | ||
@@ -575,4 +700,4 @@ | ||
| 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, | ||
@@ -596,18 +721,20 @@ ] | ||
| self.assertEqual(msg, str(e)) | ||
| msg = f"{self.users[0].id} not removed from 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 | ||
@@ -617,2 +744,3 @@ self.cmd = group.SetGroup(self.app, None) | ||
| def test_group_set_nothing(self): | ||
| self.identity_sdk_client.update_group.return_value = self.group | ||
| arglist = [ | ||
@@ -627,6 +755,9 @@ self.group.id, | ||
| 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 = [ | ||
@@ -651,3 +782,3 @@ '--name', | ||
| } | ||
| self.groups_mock.update.assert_called_once_with( | ||
| self.identity_sdk_client.update_group.assert_called_once_with( | ||
| self.group.id, **kwargs | ||
@@ -658,13 +789,16 @@ ) | ||
| 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), | ||
| ] | ||
@@ -674,11 +808,15 @@ 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.groups_mock.update.assert_called_once_with(self.group.id) | ||
| self.identity_sdk_client.update_group.assert_called_once_with( | ||
| self.group_with_domain.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) | ||
@@ -694,14 +832,10 @@ columns = ( | ||
| 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 | ||
@@ -711,2 +845,3 @@ self.cmd = group.ShowGroup(self.app, None) | ||
| def test_group_show(self): | ||
| self.identity_sdk_client.find_group.return_value = self.group | ||
| arglist = [ | ||
@@ -721,18 +856,26 @@ self.group.id, | ||
| 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), | ||
| ] | ||
@@ -742,6 +885,14 @@ 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) |
@@ -944,3 +944,19 @@ # Copyright 2013 Nebula Inc. | ||
| 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): | ||
@@ -947,0 +963,0 @@ domain = identity_fakes.FakeDomain.create_one_domain() |
@@ -47,2 +47,3 @@ # Copyright 2013 Nebula Inc. | ||
| 'password_expires_at', | ||
| 'options', | ||
| ) | ||
@@ -67,2 +68,3 @@ | ||
| self.user.password_expires_at, | ||
| getattr(self.user, 'options', {}), | ||
| ) | ||
@@ -168,2 +170,49 @@ | ||
| def test_user_create_password_prompt_no_warning(self): | ||
| arglist = [ | ||
| '--password-prompt', | ||
| self.user.name, | ||
| ] | ||
| verifylist = [ | ||
| ('password', None), | ||
| ('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', | ||
| } | ||
| self.identity_sdk_client.create_user.assert_called_with(**kwargs) | ||
| self.assertEqual(self.columns, columns) | ||
| self.assertEqual(self.datalist, data) | ||
| def test_user_create_email(self): | ||
@@ -238,2 +287,3 @@ arglist = [ | ||
| self.user.password_expires_at, | ||
| getattr(self.user, 'options', {}), | ||
| ) | ||
@@ -286,2 +336,3 @@ self.assertEqual(datalist, data) | ||
| self.user.password_expires_at, | ||
| getattr(self.user, 'options', {}), | ||
| ) | ||
@@ -996,3 +1047,21 @@ self.assertEqual(datalist, 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)) | ||
| class TestUserSet(identity_fakes.TestIdentityv3): | ||
@@ -1695,7 +1764,10 @@ project = sdk_fakes.generate_fake_resource(_project.Project) | ||
| 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) | ||
@@ -1710,7 +1782,10 @@ def test_user_create_password_prompt(self): | ||
| 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) | ||
@@ -1733,7 +1808,10 @@ def test_user_password_change_no_prompt(self): | ||
| 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) | ||
@@ -1791,2 +1869,3 @@ | ||
| 'password_expires_at', | ||
| 'options', | ||
| ) | ||
@@ -1803,2 +1882,3 @@ self.assertEqual(collist, columns) | ||
| self.user.password_expires_at, | ||
| getattr(self.user, 'options', {}), | ||
| ) | ||
@@ -1805,0 +1885,0 @@ self.assertEqual(datalist, data) |
@@ -57,5 +57,5 @@ # Copyright 2013 Nebula Inc. | ||
| 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 | ||
@@ -216,4 +216,4 @@ # Get the command object to test | ||
| # 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 | ||
@@ -266,3 +266,2 @@ # Get the command object to test | ||
| self.image_client.images = mock.Mock() | ||
| self.image_client.images.side_effect = [ | ||
@@ -447,4 +446,4 @@ [self._image], | ||
| # 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 | ||
@@ -719,3 +718,3 @@ # Get the command object to test | ||
| self.image_client.find_image = mock.Mock(return_value=self._image) | ||
| self.image_client.find_image.return_value = self._image | ||
@@ -722,0 +721,0 @@ # Get the command object to test |
@@ -237,6 +237,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 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 = [ | ||
@@ -420,3 +418,3 @@ '--qos-policy', | ||
| ) | ||
| fake_router = network_fakes.FakeRouter.create_one_router( | ||
| fake_router = network_fakes.create_one_router( | ||
| { | ||
@@ -506,3 +504,3 @@ 'id': 'fake_router_id', | ||
| verifylist = [ | ||
| ('network', 'fake_network_id'), | ||
| ('networks', ['fake_network_id']), | ||
| ] | ||
@@ -515,3 +513,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| **{ | ||
| 'floating_network_id': 'fake_network_id', | ||
| 'floating_network_id': ['fake_network_id'], | ||
| } | ||
@@ -528,3 +526,3 @@ ) | ||
| verifylist = [ | ||
| ('port', 'fake_port_id'), | ||
| ('ports', ['fake_port_id']), | ||
| ] | ||
@@ -537,3 +535,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| **{ | ||
| 'port_id': 'fake_port_id', | ||
| 'port_id': ['fake_port_id'], | ||
| } | ||
@@ -671,3 +669,3 @@ ) | ||
| verifylist = [ | ||
| ('router', 'fake_router_id'), | ||
| ('routers', ['fake_router_id']), | ||
| ] | ||
@@ -680,3 +678,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| **{ | ||
| 'router_id': 'fake_router_id', | ||
| 'router_id': ['fake_router_id'], | ||
| } | ||
@@ -904,6 +902,4 @@ ) | ||
| 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 = [ | ||
@@ -934,6 +930,4 @@ "--qos-policy", | ||
| 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 = [ | ||
@@ -940,0 +934,0 @@ "--qos-policy", |
@@ -27,4 +27,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 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 | ||
@@ -31,0 +31,0 @@ |
@@ -33,5 +33,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| self.router = network_fakes.FakeRouter.create_one_router( | ||
| {'id': 'fake-router-id'} | ||
| ) | ||
| self.router = network_fakes.create_one_router({'id': 'fake-router-id'}) | ||
| self.network_client.find_router = mock.Mock(return_value=self.router) | ||
@@ -38,0 +36,0 @@ self.port = network_fakes.create_one_port() |
@@ -73,3 +73,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| class TestAddRouterAgent(TestNetworkAgent): | ||
| _router = network_fakes.FakeRouter.create_one_router() | ||
| _router = network_fakes.create_one_router() | ||
| _agent = network_fakes.create_one_network_agent() | ||
@@ -223,27 +223,21 @@ | ||
| 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.find_network.return_value = self._testnetwork | ||
| self.network_client.network_hosting_dhcp_agents.return_value = ( | ||
| self.network_agents | ||
| ) | ||
| self.network_client.network_hosting_dhcp_agents = mock.Mock( | ||
| return_value=self.network_agents | ||
| ) | ||
| self.network_client.get_agent = mock.Mock(return_value=_testagent) | ||
| self._testrouter = network_fakes.create_one_router() | ||
| self.network_client.find_router.return_value = self._testrouter | ||
| 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 | ||
| ) | ||
| # Get the command object to test | ||
@@ -328,6 +322,2 @@ self.cmd = network_agent.ListNetworkAgent(self.app, None) | ||
| attrs = { | ||
| self._testrouter, | ||
| } | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
@@ -337,3 +327,3 @@ columns, data = self.cmd.take_action(parsed_args) | ||
| self.network_client.routers_hosting_l3_agents.assert_called_once_with( | ||
| *attrs | ||
| self._testrouter | ||
| ) | ||
@@ -352,6 +342,2 @@ | ||
| attrs = { | ||
| self._testrouter, | ||
| } | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
@@ -361,3 +347,3 @@ columns, data = self.cmd.take_action(parsed_args) | ||
| self.network_client.routers_hosting_l3_agents.assert_called_once_with( | ||
| *attrs | ||
| self._testrouter | ||
| ) | ||
@@ -431,3 +417,3 @@ | ||
| class TestRemoveRouterAgent(TestNetworkAgent): | ||
| _router = network_fakes.FakeRouter.create_one_router() | ||
| _router = network_fakes.create_one_router() | ||
| _agent = network_fakes.create_one_network_agent() | ||
@@ -434,0 +420,0 @@ |
@@ -38,7 +38,6 @@ # Copyright (c) 2016, Intel Corporation. | ||
| # 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 = ( | ||
@@ -52,2 +51,3 @@ 'description', | ||
| 'shared', | ||
| 'tags', | ||
| ) | ||
@@ -62,3 +62,4 @@ | ||
| new_qos_policy.rules, | ||
| new_qos_policy.shared, | ||
| new_qos_policy.is_shared, | ||
| new_qos_policy.tags, | ||
| ) | ||
@@ -164,5 +165,3 @@ | ||
| # The address scope to delete. | ||
| _qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies( | ||
| count=2 | ||
| ) | ||
| _qos_policies = network_fakes.create_qos_policies(count=2) | ||
@@ -172,6 +171,4 @@ def setUp(self): | ||
| 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.find_qos_policy = network_fakes.get_qos_policies( | ||
| qos_policies=self._qos_policies | ||
| ) | ||
@@ -253,5 +250,3 @@ | ||
| # 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 = ( | ||
@@ -270,3 +265,3 @@ 'ID', | ||
| qos_policy.name, | ||
| qos_policy.shared, | ||
| qos_policy.is_shared, | ||
| qos_policy.is_default, | ||
@@ -355,3 +350,3 @@ qos_policy.project_id, | ||
| # The QoS policy to set. | ||
| _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() | ||
| _qos_policy = network_fakes.create_one_qos_policy() | ||
@@ -439,3 +434,3 @@ def setUp(self): | ||
| # The QoS policy to show. | ||
| _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() | ||
| _qos_policy = network_fakes.create_one_qos_policy() | ||
| columns = ( | ||
@@ -449,2 +444,3 @@ 'description', | ||
| 'shared', | ||
| 'tags', | ||
| ) | ||
@@ -458,3 +454,4 @@ data = ( | ||
| network_qos_policy.RulesColumn(_qos_policy.rules), | ||
| _qos_policy.shared, | ||
| _qos_policy.is_shared, | ||
| _qos_policy.tags, | ||
| ) | ||
@@ -461,0 +458,0 @@ |
@@ -31,6 +31,5 @@ # Copyright (c) 2016, Intel Corporation. | ||
| # 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] | ||
@@ -80,5 +79,4 @@ | ||
| # 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',) | ||
@@ -85,0 +83,0 @@ data = [] |
@@ -17,2 +17,3 @@ # Copyright (c) 2016, Intel Corporation. | ||
| from unittest import mock | ||
| import uuid | ||
@@ -58,5 +59,3 @@ from osc_lib import exceptions | ||
| super().setUp() | ||
| self.qos_policy = ( | ||
| network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() | ||
| ) | ||
| self.qos_policy = network_fakes.create_one_qos_policy() | ||
| self.network_client.find_qos_policy = mock.Mock( | ||
@@ -77,5 +76,3 @@ return_value=self.qos_policy | ||
| } | ||
| self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( | ||
| attrs | ||
| ) | ||
| self.new_rule = network_fakes.create_one_qos_rule(attrs) | ||
| self.columns = ( | ||
@@ -86,3 +83,2 @@ 'direction', | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -96,3 +92,2 @@ ) | ||
| self.new_rule.project_id, | ||
| self.new_rule.qos_policy_id, | ||
| self.new_rule.type, | ||
@@ -187,5 +182,3 @@ ) | ||
| } | ||
| self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( | ||
| attrs | ||
| ) | ||
| self.new_rule = network_fakes.create_one_qos_rule(attrs) | ||
| self.columns = ( | ||
@@ -196,3 +189,2 @@ 'direction', | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -206,3 +198,2 @@ ) | ||
| self.new_rule.project_id, | ||
| self.new_rule.qos_policy_id, | ||
| self.new_rule.type, | ||
@@ -297,5 +288,3 @@ ) | ||
| } | ||
| self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( | ||
| attrs | ||
| ) | ||
| self.new_rule = network_fakes.create_one_qos_rule(attrs) | ||
| self.columns = ( | ||
@@ -305,3 +294,2 @@ 'dscp_mark', | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -314,3 +302,2 @@ ) | ||
| self.new_rule.project_id, | ||
| self.new_rule.qos_policy_id, | ||
| self.new_rule.type, | ||
@@ -398,12 +385,9 @@ ) | ||
| } | ||
| 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', | ||
@@ -415,6 +399,5 @@ ) | ||
| 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, | ||
@@ -459,3 +442,3 @@ ) | ||
| rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( | ||
| rule = network_fakes.create_one_qos_rule( | ||
| { | ||
@@ -466,10 +449,9 @@ 'qos_policy_id': self.qos_policy.id, | ||
| ) | ||
| 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, | ||
@@ -503,3 +485,3 @@ ) | ||
| '--max-burst-kbits', | ||
| str(self.new_rule.max_burst_kbits), | ||
| str(self.new_rule.max_burst_kbps), | ||
| '--egress', | ||
@@ -512,3 +494,3 @@ self.new_rule.qos_policy_id, | ||
| ('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), | ||
@@ -525,3 +507,3 @@ ('qos_policy', self.new_rule.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, | ||
@@ -566,5 +548,3 @@ }, | ||
| } | ||
| 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] | ||
@@ -575,5 +555,3 @@ self.network_client.delete_qos_minimum_bandwidth_rule = mock.Mock( | ||
| 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) | ||
| ) | ||
@@ -635,5 +613,3 @@ | ||
| } | ||
| 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] | ||
@@ -644,5 +620,3 @@ self.network_client.delete_qos_minimum_packet_rate_rule = mock.Mock( | ||
| 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) | ||
| ) | ||
@@ -704,5 +678,3 @@ | ||
| } | ||
| 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] | ||
@@ -713,5 +685,3 @@ self.network_client.delete_qos_dscp_marking_rule = mock.Mock( | ||
| 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) | ||
| ) | ||
@@ -773,5 +743,3 @@ | ||
| } | ||
| 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] | ||
@@ -782,5 +750,3 @@ self.network_client.delete_qos_bandwidth_limit_rule = mock.Mock( | ||
| 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) | ||
| ) | ||
@@ -842,5 +808,3 @@ | ||
| } | ||
| 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] | ||
@@ -947,5 +911,3 @@ self.network_client.update_qos_minimum_bandwidth_rule = mock.Mock( | ||
| } | ||
| 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] | ||
@@ -1052,5 +1014,3 @@ self.network_client.update_qos_minimum_packet_rate_rule = mock.Mock( | ||
| } | ||
| 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] | ||
@@ -1156,17 +1116,10 @@ self.network_client.update_qos_dscp_marking_rule = mock.Mock( | ||
| } | ||
| 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.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_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.find_qos_policy.return_value = self.qos_policy | ||
| # Get the command object to test | ||
| self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) | ||
@@ -1236,3 +1189,3 @@ | ||
| 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 | ||
@@ -1242,9 +1195,9 @@ def _set_max_burst_kbits(self, max_burst_kbits=None): | ||
| 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, | ||
@@ -1254,3 +1207,3 @@ 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), | ||
@@ -1264,3 +1217,3 @@ ('id', self.new_rule.id), | ||
| attrs = { | ||
| 'max_burst_kbps': self.new_rule.max_burst_kbits, | ||
| 'max_burst_kbps': self.new_rule.max_burst_kbps, | ||
| } | ||
@@ -1334,39 +1287,32 @@ self.network_client.update_qos_bandwidth_limit_rule.assert_called_with( | ||
| 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 = ( | ||
@@ -1387,16 +1333,13 @@ 'ID', | ||
| ( | ||
| 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) | ||
@@ -1430,5 +1373,3 @@ | ||
| } | ||
| 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] | ||
@@ -1440,3 +1381,2 @@ self.columns = ( | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -1449,3 +1389,2 @@ ) | ||
| self.new_rule.project_id, | ||
| self.new_rule.qos_policy_id, | ||
| self.new_rule.type, | ||
@@ -1501,5 +1440,3 @@ ) | ||
| } | ||
| 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] | ||
@@ -1511,3 +1448,2 @@ self.columns = ( | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -1520,3 +1456,2 @@ ) | ||
| self.new_rule.project_id, | ||
| self.new_rule.qos_policy_id, | ||
| self.new_rule.type, | ||
@@ -1572,5 +1507,3 @@ ) | ||
| } | ||
| 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] | ||
@@ -1581,3 +1514,2 @@ self.columns = ( | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -1589,3 +1521,2 @@ ) | ||
| self.new_rule.project_id, | ||
| self.new_rule.qos_policy_id, | ||
| self.new_rule.type, | ||
@@ -1641,5 +1572,3 @@ ) | ||
| } | ||
| 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] | ||
@@ -1649,6 +1578,5 @@ self.columns = ( | ||
| 'id', | ||
| 'max_burst_kbits', | ||
| 'max_burst_kbps', | ||
| 'max_kbps', | ||
| 'project_id', | ||
| 'qos_policy_id', | ||
| 'type', | ||
@@ -1659,6 +1587,5 @@ ) | ||
| 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, | ||
@@ -1665,0 +1592,0 @@ ) |
@@ -37,4 +37,4 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 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() | ||
@@ -41,0 +41,0 @@ snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool() |
@@ -50,3 +50,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| ) | ||
| qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( | ||
| qos_policy = network_fakes.create_one_qos_policy( | ||
| attrs={'id': _network.qos_policy_id} | ||
@@ -626,3 +626,3 @@ ) | ||
| 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 | ||
@@ -972,3 +972,3 @@ self.network_client.dhcp_agent_hosting_networks = mock.Mock( | ||
| _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} | ||
@@ -1272,3 +1272,3 @@ ) | ||
| _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} | ||
@@ -1275,0 +1275,0 @@ ) |
@@ -39,7 +39,6 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| # 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', | ||
@@ -49,8 +48,11 @@ 'id', | ||
| 'project_id', | ||
| 'revision_number', | ||
| 'rules', | ||
| 'stateful', | ||
| 'tags', | ||
| 'updated_at', | ||
| ) | ||
| data = ( | ||
| _security_group.created_at, | ||
| _security_group.description, | ||
@@ -60,5 +62,7 @@ _security_group.id, | ||
| _security_group.project_id, | ||
| _security_group.revision_number, | ||
| security_group.NetworkSecurityGroupRulesColumn([]), | ||
| _security_group.stateful, | ||
| _security_group.tags, | ||
| _security_group.updated_at, | ||
| ) | ||
@@ -169,3 +173,3 @@ | ||
| self.assertEqual(self.columns, columns) | ||
| self.assertCountEqual(self.data, data) | ||
| self.assertEqual(self.data, data) | ||
@@ -181,3 +185,3 @@ def test_create_with_tags(self): | ||
| # The security groups to be deleted. | ||
| _security_groups = network_fakes.FakeSecurityGroup.create_security_groups() | ||
| _security_groups = network_fakes.create_security_groups() | ||
@@ -192,5 +196,3 @@ def setUp(self): | ||
| self.network_client.find_security_group = ( | ||
| network_fakes.FakeSecurityGroup.get_security_groups( | ||
| self._security_groups | ||
| ) | ||
| network_fakes.get_security_groups(self._security_groups) | ||
| ) | ||
@@ -273,5 +275,3 @@ | ||
| # 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) | ||
@@ -422,6 +422,4 @@ columns = ( | ||
| # 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']} | ||
| ) | ||
@@ -526,14 +524,11 @@ | ||
| # 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', | ||
@@ -543,8 +538,11 @@ 'id', | ||
| 'project_id', | ||
| 'revision_number', | ||
| 'rules', | ||
| 'stateful', | ||
| 'tags', | ||
| 'updated_at', | ||
| ) | ||
| data = ( | ||
| _security_group.created_at, | ||
| _security_group.description, | ||
@@ -554,7 +552,9 @@ _security_group.id, | ||
| _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, | ||
| ) | ||
@@ -597,6 +597,4 @@ | ||
| # 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']} | ||
| ) | ||
@@ -603,0 +601,0 @@ |
@@ -386,3 +386,2 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 'ip_protocol': 'tcp', | ||
| 'ethertype': 'IPv4', | ||
| 'from_port': 80, | ||
@@ -396,3 +395,2 @@ 'to_port': 80, | ||
| 'ip_protocol': 'icmp', | ||
| 'ethertype': 'IPv4', | ||
| 'from_port': -1, | ||
@@ -431,3 +429,3 @@ 'to_port': -1, | ||
| rule['ip_protocol'], | ||
| rule['ethertype'], | ||
| '', # ethertype is a neutron-only thing | ||
| rule['ip_range'], | ||
@@ -434,0 +432,0 @@ rule['port_range'], |
@@ -43,5 +43,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| # 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() | ||
@@ -52,2 +50,3 @@ # The address group to be used in security group rules | ||
| expected_columns = ( | ||
| 'created_at', | ||
| 'description', | ||
@@ -64,3 +63,5 @@ 'direction', | ||
| 'remote_ip_prefix', | ||
| 'revision_number', | ||
| 'security_group_id', | ||
| 'updated_at', | ||
| ) | ||
@@ -72,5 +73,3 @@ | ||
| self._security_group_rule = ( | ||
| network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( | ||
| attrs | ||
| ) | ||
| network_fakes.create_one_security_group_rule(attrs) | ||
| ) | ||
@@ -81,2 +80,3 @@ self.network_client.create_security_group_rule = mock.Mock( | ||
| self.expected_data = ( | ||
| self._security_group_rule.created_at, | ||
| self._security_group_rule.description, | ||
@@ -93,3 +93,5 @@ self._security_group_rule.direction, | ||
| 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, | ||
| ) | ||
@@ -972,7 +974,3 @@ | ||
| # 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) | ||
@@ -987,5 +985,3 @@ def setUp(self): | ||
| 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) | ||
| ) | ||
@@ -1068,29 +1064,23 @@ | ||
| # 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), | ||
| ] | ||
@@ -1276,7 +1266,6 @@ _security_group_rules = [ | ||
| # 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', | ||
@@ -1293,6 +1282,9 @@ 'direction', | ||
| 'remote_ip_prefix', | ||
| 'revision_number', | ||
| 'security_group_id', | ||
| 'updated_at', | ||
| ) | ||
| data = ( | ||
| _security_group_rule.created_at, | ||
| _security_group_rule.description, | ||
@@ -1309,3 +1301,5 @@ _security_group_rule.direction, | ||
| _security_group_rule.remote_ip_prefix, | ||
| _security_group_rule.revision_number, | ||
| _security_group_rule.security_group_id, | ||
| _security_group_rule.updated_at, | ||
| ) | ||
@@ -1312,0 +1306,0 @@ |
@@ -17,3 +17,2 @@ # | ||
| import random | ||
| import typing as ty | ||
| from unittest import mock | ||
@@ -96,3 +95,3 @@ import uuid | ||
| 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. | ||
@@ -99,0 +98,0 @@ |
@@ -15,2 +15,6 @@ # | ||
| 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 | ||
@@ -22,21 +26,9 @@ | ||
| 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() | ||
| self.service = sdk_fakes.generate_fake_resource(_service.Service) | ||
| self.volume_sdk_client.services.return_value = [self.service] | ||
| 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) | ||
@@ -47,18 +39,15 @@ | ||
| '--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', | ||
@@ -70,42 +59,31 @@ 'Host', | ||
| '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), | ||
@@ -115,8 +93,5 @@ ] | ||
| # 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', | ||
@@ -129,37 +104,30 @@ 'Host', | ||
| '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 | ||
@@ -180,5 +148,4 @@ self.cmd = service.SetService(self.app, None) | ||
| 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) | ||
@@ -201,7 +168,4 @@ | ||
| 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) | ||
@@ -224,7 +188,6 @@ | ||
| 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) | ||
@@ -251,4 +214,5 @@ | ||
| 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 | ||
| ) | ||
@@ -271,2 +235,3 @@ self.assertIsNone(result) | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| try: | ||
@@ -298,2 +263,3 @@ self.cmd.take_action(parsed_args) | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| try: | ||
@@ -300,0 +266,0 @@ self.cmd.take_action(parsed_args) |
@@ -16,2 +16,7 @@ # | ||
| 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 | ||
@@ -23,23 +28,3 @@ | ||
| 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 = ( | ||
@@ -50,7 +35,2 @@ 'id', | ||
| ) | ||
| data = ( | ||
| new_backup.id, | ||
| new_backup.name, | ||
| new_backup.volume_id, | ||
| ) | ||
@@ -60,7 +40,19 @@ def setUp(self): | ||
| 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 | ||
| # Get the command object to test | ||
| self.data = ( | ||
| self.backup.id, | ||
| self.backup.name, | ||
| self.backup.volume_id, | ||
| ) | ||
| self.cmd = volume_backup.CreateVolumeBackup(self.app, None) | ||
@@ -71,21 +63,21 @@ | ||
| "--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), | ||
| ] | ||
@@ -97,9 +89,9 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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, | ||
| ) | ||
@@ -112,11 +104,11 @@ self.assertEqual(self.columns, columns) | ||
| "--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), | ||
| ] | ||
@@ -128,6 +120,6 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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, | ||
@@ -141,13 +133,9 @@ is_incremental=False, | ||
| 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) | ||
@@ -235,7 +223,2 @@ | ||
| class TestBackupList(volume_fakes.TestVolume): | ||
| volume = volume_fakes.create_one_volume() | ||
| backups = volume_fakes.create_backups( | ||
| attrs={'volume_id': volume.name}, count=3 | ||
| ) | ||
| columns = ( | ||
@@ -256,41 +239,47 @@ 'ID', | ||
| data = [] | ||
| for b in backups: | ||
| data.append( | ||
| ( | ||
| b.id, | ||
| b.name, | ||
| b.description, | ||
| b.status, | ||
| b.size, | ||
| b.is_incremental, | ||
| b.created_at, | ||
| ) | ||
| ) | ||
| data_long = [] | ||
| for b in backups: | ||
| 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, | ||
| ) | ||
| ) | ||
| 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) | ||
@@ -373,7 +362,2 @@ | ||
| class TestBackupRestore(volume_fakes.TestVolume): | ||
| volume = volume_fakes.create_one_volume() | ||
| backup = volume_fakes.create_one_backup( | ||
| attrs={'volume_id': volume.id}, | ||
| ) | ||
| columns = ( | ||
@@ -385,13 +369,11 @@ "id", | ||
| data = ( | ||
| backup.id, | ||
| volume.id, | ||
| volume.name, | ||
| ) | ||
| 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.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.find_volume.return_value = self.volume | ||
| self.volume_sdk_client.restore_backup.return_value = { | ||
@@ -403,3 +385,8 @@ 'id': self.backup['id'], | ||
| # Get the command object to mock | ||
| self.data = ( | ||
| self.backup.id, | ||
| self.volume.id, | ||
| self.volume.name, | ||
| ) | ||
| self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) | ||
@@ -493,13 +480,11 @@ | ||
| 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) | ||
@@ -514,9 +499,16 @@ | ||
| 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] | ||
@@ -526,17 +518,16 @@ 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 = ( | ||
@@ -560,20 +551,2 @@ "availability_zone", | ||
| ) | ||
| 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, | ||
| ) | ||
@@ -583,4 +556,24 @@ def setUp(self): | ||
| self.backup = sdk_fakes.generate_fake_resource(_backup.Backup) | ||
| self.volume_sdk_client.find_backup.return_value = self.backup | ||
| # Get the command object to test | ||
| 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) | ||
@@ -587,0 +580,0 @@ |
@@ -16,5 +16,8 @@ # | ||
| 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 | ||
@@ -27,15 +30,3 @@ from openstackclient.tests.unit.identity.v3 import fakes as project_fakes | ||
| 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 = ( | ||
@@ -55,22 +46,21 @@ 'created_at', | ||
| 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.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.volumes_mock.get.return_value = self.volume | ||
| self.snapshots_mock.create.return_value = self.new_snapshot | ||
| self.snapshots_mock.manage.return_value = self.new_snapshot | ||
| # Get the command object to test | ||
| self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) | ||
@@ -81,5 +71,5 @@ | ||
| "--volume", | ||
| self.new_snapshot.volume_id, | ||
| self.snapshot.volume_id, | ||
| "--description", | ||
| self.new_snapshot.description, | ||
| self.snapshot.description, | ||
| "--force", | ||
@@ -90,10 +80,10 @@ '--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), | ||
| ] | ||
@@ -104,11 +94,14 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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) | ||
@@ -118,6 +111,6 @@ def test_snapshot_create_without_name(self): | ||
| "--volume", | ||
| self.new_snapshot.volume_id, | ||
| self.snapshot.volume_id, | ||
| ] | ||
| verifylist = [ | ||
| ("volume", self.new_snapshot.volume_id), | ||
| ("volume", self.snapshot.volume_id), | ||
| ] | ||
@@ -135,10 +128,10 @@ self.assertRaises( | ||
| "--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), | ||
| ] | ||
@@ -149,12 +142,14 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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) | ||
@@ -168,4 +163,4 @@ def test_snapshot_create_with_remote_source(self): | ||
| '--volume', | ||
| self.new_snapshot.volume_id, | ||
| self.new_snapshot.name, | ||
| self.snapshot.volume_id, | ||
| self.snapshot.name, | ||
| ] | ||
@@ -178,4 +173,4 @@ ref_dict = { | ||
| ('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), | ||
| ] | ||
@@ -186,24 +181,27 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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) | ||
@@ -217,7 +215,10 @@ | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_snapshot.assert_called_once_with( | ||
| self.snapshots[0].id, force=False | ||
| ) | ||
@@ -230,7 +231,10 @@ def test_snapshot_delete_with_force(self): | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_snapshot.assert_called_once_with( | ||
| self.snapshots[0].id, force=True | ||
| ) | ||
@@ -244,13 +248,20 @@ def test_delete_multiple_snapshots(self): | ||
| ] | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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) | ||
| 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 = [ | ||
@@ -266,69 +277,73 @@ self.snapshots[0].id, | ||
| 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)) | ||
| exc = self.assertRaises( | ||
| exceptions.CommandError, | ||
| self.cmd.take_action, | ||
| parsed_args, | ||
| ) | ||
| self.assertEqual('1 of 2 snapshots failed to delete.', str(exc)) | ||
| find_mock.assert_any_call( | ||
| self.snapshots_mock, self.snapshots[0].id | ||
| ) | ||
| find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') | ||
| 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), | ||
| ] | ||
| ) | ||
| self.assertEqual(2, find_mock.call_count) | ||
| self.snapshots_mock.delete.assert_called_once_with( | ||
| self.snapshots[0].id, False | ||
| ) | ||
| class TestVolumeSnapshotList(volume_fakes.TestVolume): | ||
| def setUp(self): | ||
| super().setUp() | ||
| 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 | ||
| ) | ||
| 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 | ||
| columns = ["ID", "Name", "Description", "Status", "Size"] | ||
| columns_long = columns + ["Created At", "Volume", "Properties"] | ||
| self.columns = ("ID", "Name", "Description", "Status", "Size") | ||
| self.columns_long = self.columns + ( | ||
| "Created At", | ||
| "Volume", | ||
| "Properties", | ||
| ) | ||
| data = [] | ||
| for s in snapshots: | ||
| data.append( | ||
| ( | ||
| s.id, | ||
| s.name, | ||
| s.description, | ||
| s.status, | ||
| s.size, | ||
| self.data = [] | ||
| self.data_long = [] | ||
| for s in self.snapshots: | ||
| self.data.append( | ||
| ( | ||
| s.id, | ||
| s.name, | ||
| s.description, | ||
| s.status, | ||
| s.size, | ||
| ) | ||
| ) | ||
| ) | ||
| data_long = [] | ||
| for s in snapshots: | ||
| data_long.append( | ||
| ( | ||
| s.id, | ||
| s.name, | ||
| s.description, | ||
| s.status, | ||
| s.size, | ||
| s.created_at, | ||
| volume_snapshot.VolumeIdColumn( | ||
| s.volume_id, volume_cache={volume.id: volume} | ||
| ), | ||
| format_columns.DictColumn(s.metadata), | ||
| 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), | ||
| ) | ||
| ) | ||
| ) | ||
| def setUp(self): | ||
| super().setUp() | ||
| self.volumes_mock.list.return_value = [self.volume] | ||
| self.volumes_mock.get.return_value = self.volume | ||
| self.project_mock.get.return_value = self.project | ||
| self.snapshots_mock.list.return_value = self.snapshots | ||
| # Get the command to test | ||
| self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None) | ||
@@ -343,12 +358,10 @@ | ||
| 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, | ||
| ) | ||
@@ -379,12 +392,10 @@ self.assertEqual(self.columns, columns) | ||
| 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, | ||
| ) | ||
@@ -403,12 +414,10 @@ self.assertEqual(self.columns_long, columns) | ||
| 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, | ||
| ) | ||
@@ -432,12 +441,10 @@ self.assertEqual(self.columns, columns) | ||
| 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, | ||
| ) | ||
@@ -450,3 +457,3 @@ self.assertEqual(self.columns, columns) | ||
| '--status', | ||
| self.snapshots[0].status, | ||
| 'available', | ||
| ] | ||
@@ -456,3 +463,3 @@ verifylist = [ | ||
| ('long', False), | ||
| ('status', self.snapshots[0].status), | ||
| ('status', 'available'), | ||
| ] | ||
@@ -463,12 +470,10 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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, | ||
| ) | ||
@@ -492,12 +497,10 @@ self.assertEqual(self.columns, columns) | ||
| 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, | ||
| ) | ||
@@ -524,12 +527,14 @@ self.assertEqual(self.columns, columns) | ||
| 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) | ||
@@ -547,7 +552,10 @@ | ||
| 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() | ||
@@ -564,6 +572,5 @@ def test_snapshot_set_name_and_property(self): | ||
| ] | ||
| new_property = {"x": "y", "foo": "foo"} | ||
| verifylist = [ | ||
| ("name", "new_snapshot"), | ||
| ("property", new_property), | ||
| ("properties", {"x": "y", "foo": "foo"}), | ||
| ("snapshot", self.snapshot.id), | ||
@@ -575,12 +582,9 @@ ] | ||
| 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) | ||
@@ -599,10 +603,13 @@ def test_snapshot_set_with_no_property(self): | ||
| 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.assertIsNone(result) | ||
| 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"] | ||
| ) | ||
@@ -618,3 +625,3 @@ def test_snapshot_set_with_no_property_and_property(self): | ||
| ("no_property", True), | ||
| ("property", {"foo_1": "bar_1"}), | ||
| ("properties", {"foo_1": "bar_1"}), | ||
| ("snapshot", self.snapshot.id), | ||
@@ -625,12 +632,16 @@ ] | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with( | ||
| self.snapshot.id, | ||
| foo_1="bar_1", | ||
| ) | ||
@@ -644,9 +655,11 @@ def test_snapshot_set_state_to_error(self): | ||
| 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] | ||
@@ -656,10 +669,8 @@ 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' | ||
@@ -669,3 +680,5 @@ ) | ||
| 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 = [ | ||
@@ -683,18 +696,15 @@ '--state', | ||
| ] | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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.snapshots_mock.reset_state.assert_called_once_with( | ||
| 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' | ||
@@ -704,19 +714,18 @@ ) | ||
| 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 = ( | ||
@@ -733,4 +742,4 @@ self.snapshot.created_at, | ||
| 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) | ||
@@ -744,3 +753,5 @@ | ||
| 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 | ||
| ) | ||
@@ -751,11 +762,10 @@ self.assertEqual(self.columns, columns) | ||
| 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) | ||
@@ -770,3 +780,3 @@ | ||
| verifylist = [ | ||
| ("property", ["foo"]), | ||
| ("properties", ["foo"]), | ||
| ("snapshot", self.snapshot.id), | ||
@@ -779,5 +789,5 @@ ] | ||
| 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"] | ||
| ) | ||
| self.assertIsNone(result) |
@@ -16,2 +16,5 @@ # | ||
| 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 | ||
@@ -50,9 +53,3 @@ from osc_lib import exceptions | ||
| 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): | ||
@@ -193,3 +190,3 @@ project = identity_fakes.FakeProject.create_one_project() | ||
| verifylist = [ | ||
| ('property', {'Alpha': 'a', 'Beta': 'b'}), | ||
| ('properties', {'Alpha': 'a', 'Beta': 'b'}), | ||
| ('size', self.new_volume.size), | ||
@@ -388,5 +385,3 @@ ('name', self.new_volume.name), | ||
| ('bootable', True), | ||
| ('non_bootable', False), | ||
| ('read_only', True), | ||
| ('read_write', False), | ||
| ('size', self.new_volume.size), | ||
@@ -434,5 +429,3 @@ ('name', self.new_volume.name), | ||
| ('bootable', False), | ||
| ('non_bootable', True), | ||
| ('read_only', False), | ||
| ('read_write', True), | ||
| ('size', self.new_volume.size), | ||
@@ -489,5 +482,3 @@ ('name', self.new_volume.name), | ||
| ('bootable', True), | ||
| ('non_bootable', False), | ||
| ('read_only', True), | ||
| ('read_write', False), | ||
| ('size', self.new_volume.size), | ||
@@ -541,5 +532,3 @@ ('name', self.new_volume.name), | ||
| ('bootable', False), | ||
| ('non_bootable', True), | ||
| ('read_only', True), | ||
| ('read_write', False), | ||
| ('size', self.new_volume.size), | ||
@@ -680,19 +669,18 @@ ('name', self.new_volume.name), | ||
| 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]), | ||
| ] | ||
@@ -702,12 +690,13 @@ 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_volume.assert_called_once_with( | ||
| self.volumes[0].id, cascade=False, force=False | ||
| ) | ||
| 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 = [ | ||
@@ -721,12 +710,19 @@ ('force', False), | ||
| 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', | ||
@@ -737,30 +733,29 @@ ] | ||
| ('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)) | ||
| exc = self.assertRaises( | ||
| exceptions.CommandError, | ||
| self.cmd.take_action, | ||
| parsed_args, | ||
| ) | ||
| self.assertEqual('1 of 2 volumes failed to delete.', str(exc)) | ||
| find_mock.assert_any_call(self.volumes_mock, volumes[0].id) | ||
| find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') | ||
| 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), | ||
| ] | ||
| ) | ||
| self.assertEqual(2, find_mock.call_count) | ||
| self.volumes_mock.delete.assert_called_once_with( | ||
| volumes[0].id, cascade=False | ||
| ) | ||
| def test_volume_delete_with_purge(self): | ||
| volumes = self.setup_volumes_mock(count=1) | ||
| arglist = [ | ||
| '--purge', | ||
| volumes[0].id, | ||
| self.volumes[0].id, | ||
| ] | ||
@@ -770,3 +765,3 @@ verifylist = [ | ||
| ('purge', True), | ||
| ('volumes', [volumes[0].id]), | ||
| ('volumes', [self.volumes[0].id]), | ||
| ] | ||
@@ -776,14 +771,15 @@ 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_volume.assert_called_once_with( | ||
| self.volumes[0].id, cascade=True, force=False | ||
| ) | ||
| def test_volume_delete_with_force(self): | ||
| volumes = self.setup_volumes_mock(count=1) | ||
| arglist = [ | ||
| '--force', | ||
| volumes[0].id, | ||
| self.volumes[0].id, | ||
| ] | ||
@@ -793,3 +789,3 @@ verifylist = [ | ||
| ('purge', False), | ||
| ('volumes', [volumes[0].id]), | ||
| ('volumes', [self.volumes[0].id]), | ||
| ] | ||
@@ -799,7 +795,12 @@ 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): | ||
@@ -1425,6 +1426,6 @@ project = identity_fakes.FakeProject.create_one_project() | ||
| 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), | ||
| ] | ||
@@ -1435,3 +1436,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.volumes_mock.set_metadata.assert_called_with( | ||
| self.new_volume.id, parsed_args.property | ||
| self.new_volume.id, parsed_args.properties | ||
| ) | ||
@@ -1448,6 +1449,6 @@ | ||
| 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), | ||
| ] | ||
@@ -1460,3 +1461,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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 | ||
| ) | ||
@@ -1467,4 +1468,3 @@ | ||
| verifylist = [ | ||
| ('read_only', False), | ||
| ('read_write', False), | ||
| ('read_only', None), | ||
| ('state', 'error'), | ||
@@ -1534,32 +1534,36 @@ ('volume', self.new_volume.id), | ||
| 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_readonly(self): | ||
| 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_read_only(self): | ||
| arglist = ['--read-only', self.new_volume.id] | ||
| verifylist = [ | ||
| ('read_only', True), | ||
| ('read_write', False), | ||
| ('volume', self.new_volume.id), | ||
@@ -1580,3 +1584,2 @@ ] | ||
| ('read_only', False), | ||
| ('read_write', True), | ||
| ('volume', self.new_volume.id), | ||
@@ -1711,3 +1714,3 @@ ] | ||
| verifylist = [ | ||
| ('image_property', {'Alpha': 'a', 'Beta': 'b'}), | ||
| ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), | ||
| ('volume', self.new_volume.id), | ||
@@ -1728,3 +1731,3 @@ ] | ||
| verifylist_unset = [ | ||
| ('image_property', ['Alpha']), | ||
| ('image_properties', ['Alpha']), | ||
| ('volume', self.new_volume.id), | ||
@@ -1741,3 +1744,3 @@ ] | ||
| 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 | ||
| ) | ||
@@ -1757,4 +1760,4 @@ | ||
| verifylist = [ | ||
| ('image_property', ['Alpha']), | ||
| ('property', ['Beta']), | ||
| ('image_properties', ['Alpha']), | ||
| ('properties', ['Beta']), | ||
| ('volume', self.new_volume.id), | ||
@@ -1772,6 +1775,6 @@ ] | ||
| 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 | ||
| ) | ||
@@ -1778,0 +1781,0 @@ |
@@ -13,5 +13,6 @@ # | ||
| # 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 | ||
@@ -24,20 +25,13 @@ | ||
| class TestService(volume_fakes.TestVolume): | ||
| class TestBlockStorageLogLevelList(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() | ||
| 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 | ||
| ] | ||
| class TestBlockStorageLogLevelList(TestService): | ||
| service_log = volume_fakes.create_service_log_level_entry() | ||
| def setUp(self): | ||
| super().setUp() | ||
| self.service_mock.get_log_levels.return_value = [self.service_log] | ||
| # Get the command object to test | ||
| self.cmd = service.BlockStorageLogLevelList(self.app, None) | ||
@@ -50,12 +44,12 @@ | ||
| '--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.'), | ||
| ] | ||
@@ -72,23 +66,18 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| ] | ||
| # 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.', | ||
| ) | ||
@@ -99,3 +88,3 @@ | ||
| '--host', | ||
| self.service_log.host, | ||
| self.log_level.host, | ||
| '--service', | ||
@@ -107,3 +96,3 @@ 'cinder-api', | ||
| verifylist = [ | ||
| ('host', self.service_log.host), | ||
| ('host', self.log_level.host), | ||
| ('service', 'cinder-api'), | ||
@@ -126,3 +115,3 @@ ('log_prefix', 'cinder_test.api.common'), | ||
| '--host', | ||
| self.service_log.host, | ||
| self.log_level.host, | ||
| '--service', | ||
@@ -134,3 +123,3 @@ 'nova-api', | ||
| verifylist = [ | ||
| ('host', self.service_log.host), | ||
| ('host', self.log_level.host), | ||
| ('service', 'nova-api'), | ||
@@ -150,9 +139,11 @@ ('log_prefix', 'cinder_test.api.common'), | ||
| @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) | ||
@@ -166,24 +157,24 @@ | ||
| '--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', | ||
| ) | ||
@@ -195,13 +186,13 @@ | ||
| '--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'), | ||
| ] | ||
@@ -223,3 +214,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| '--host', | ||
| self.service_log.host, | ||
| self.log_level.host, | ||
| '--service', | ||
@@ -232,3 +223,3 @@ 'nova-api', | ||
| ('level', 'ERROR'), | ||
| ('host', self.service_log.host), | ||
| ('host', self.log_level.host), | ||
| ('service', 'nova-api'), | ||
@@ -253,3 +244,3 @@ ('log_prefix', 'cinder.api.common'), | ||
| '--host', | ||
| self.service_log.host, | ||
| self.log_level.host, | ||
| '--service', | ||
@@ -262,3 +253,3 @@ 'cinder-api', | ||
| ('level', log_level.upper()), | ||
| ('host', self.service_log.host), | ||
| ('host', self.log_level.host), | ||
| ('service', 'cinder-api'), | ||
@@ -281,8 +272,7 @@ ('log_prefix', 'cinder.api.common'), | ||
| # 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', | ||
| ) |
@@ -15,4 +15,8 @@ # | ||
| 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 | ||
@@ -22,21 +26,9 @@ from openstackclient.volume.v3 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() | ||
| self.service = sdk_fakes.generate_fake_resource(_service.Service) | ||
| self.volume_sdk_client.services.return_value = [self.service] | ||
| 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) | ||
@@ -47,18 +39,15 @@ | ||
| '--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', | ||
@@ -70,42 +59,31 @@ 'Host', | ||
| '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), | ||
@@ -115,8 +93,5 @@ ] | ||
| # 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', | ||
@@ -129,51 +104,39 @@ 'Host', | ||
| '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', | ||
@@ -186,56 +149,39 @@ 'Host', | ||
| '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', | ||
@@ -249,31 +195,165 @@ 'Host', | ||
| '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, | ||
| ) | ||
| # checking if proper call was made to list services | ||
| self.service_mock.list.assert_called_with( | ||
| backend_service.host, | ||
| backend_service.binary, | ||
| class TestServiceSet(volume_fakes.TestVolume): | ||
| def setUp(self): | ||
| super().setUp() | ||
| 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) | ||
| # checking if prohibited columns are present in output | ||
| self.assertNotIn("Disabled Reason", columns) | ||
| self.assertNotIn(backend_service.disabled_reason, tuple(data)) | ||
| 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) | ||
| 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), | ||
| ) |
@@ -31,3 +31,3 @@ # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
| 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( | ||
@@ -34,0 +34,0 @@ attrs={'instance': server.id, 'volume_id': volume.id}, |
@@ -14,4 +14,9 @@ # | ||
| 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.test import fakes as sdk_fakes | ||
| from osc_lib import exceptions | ||
@@ -23,23 +28,3 @@ | ||
| 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 = ( | ||
@@ -50,7 +35,2 @@ 'id', | ||
| ) | ||
| data = ( | ||
| new_backup.id, | ||
| new_backup.name, | ||
| new_backup.volume_id, | ||
| ) | ||
@@ -60,7 +40,19 @@ def setUp(self): | ||
| 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 | ||
| # Get the command object to test | ||
| self.data = ( | ||
| self.backup.id, | ||
| self.backup.name, | ||
| self.backup.volume_id, | ||
| ) | ||
| self.cmd = volume_backup.CreateVolumeBackup(self.app, None) | ||
@@ -71,21 +63,21 @@ | ||
| "--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), | ||
| ] | ||
@@ -97,9 +89,9 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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, | ||
| ) | ||
@@ -117,7 +109,7 @@ self.assertEqual(self.columns, columns) | ||
| "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), | ||
| ] | ||
@@ -129,3 +121,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.volume_sdk_client.create_backup.assert_called_with( | ||
| volume_id=self.new_backup.volume_id, | ||
| volume_id=self.backup.volume_id, | ||
| container=None, | ||
@@ -149,7 +141,7 @@ name=None, | ||
| "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), | ||
| ] | ||
@@ -169,7 +161,7 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| "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), | ||
| ] | ||
@@ -181,3 +173,3 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| self.volume_sdk_client.create_backup.assert_called_with( | ||
| volume_id=self.new_backup.volume_id, | ||
| volume_id=self.backup.volume_id, | ||
| container=None, | ||
@@ -199,7 +191,7 @@ name=None, | ||
| "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), | ||
| ] | ||
@@ -216,11 +208,11 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| "--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), | ||
| ] | ||
@@ -232,6 +224,6 @@ parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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, | ||
@@ -245,13 +237,9 @@ is_incremental=False, | ||
| 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) | ||
@@ -299,3 +287,3 @@ | ||
| 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) | ||
@@ -340,7 +328,2 @@ self.assertIsNone(result) | ||
| class TestBackupList(volume_fakes.TestVolume): | ||
| volume = volume_fakes.create_one_volume() | ||
| backups = volume_fakes.create_backups( | ||
| attrs={'volume_id': volume.name}, count=3 | ||
| ) | ||
| columns = ( | ||
@@ -361,41 +344,47 @@ 'ID', | ||
| data = [] | ||
| for b in backups: | ||
| data.append( | ||
| ( | ||
| b.id, | ||
| b.name, | ||
| b.description, | ||
| b.status, | ||
| b.size, | ||
| b.is_incremental, | ||
| b.created_at, | ||
| ) | ||
| ) | ||
| data_long = [] | ||
| for b in backups: | ||
| 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, | ||
| ) | ||
| ) | ||
| 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) | ||
@@ -478,7 +467,2 @@ | ||
| class TestBackupRestore(volume_fakes.TestVolume): | ||
| volume = volume_fakes.create_one_volume() | ||
| backup = volume_fakes.create_one_backup( | ||
| attrs={'volume_id': volume.id}, | ||
| ) | ||
| columns = ( | ||
@@ -490,13 +474,12 @@ "id", | ||
| data = ( | ||
| backup.id, | ||
| volume.id, | ||
| volume.name, | ||
| ) | ||
| 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.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.find_volume.return_value = self.volume | ||
| self.volume_sdk_client.create_backup.return_value = self.backup | ||
| self.volume_sdk_client.restore_backup.return_value = { | ||
@@ -508,3 +491,8 @@ 'id': self.backup['id'], | ||
| # Get the command object to mock | ||
| self.data = ( | ||
| self.backup.id, | ||
| self.volume.id, | ||
| self.volume.name, | ||
| ) | ||
| self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) | ||
@@ -598,13 +586,11 @@ | ||
| 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) | ||
@@ -626,10 +612,12 @@ | ||
| # 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): | ||
@@ -670,9 +658,10 @@ self.set_volume_api_version('3.8') | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.update_backup.assert_called_once_with( | ||
| self.backup, description='new_description' | ||
| ) | ||
@@ -706,9 +695,16 @@ def test_backup_set_description_pre_v39(self): | ||
| 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] | ||
@@ -718,13 +714,14 @@ 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): | ||
@@ -744,11 +741,10 @@ self.set_volume_api_version('3.43') | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.update_backup.assert_called_once_with( | ||
| self.backup, metadata={} | ||
| ) | ||
@@ -788,11 +784,10 @@ def test_backup_set_no_property_pre_v343(self): | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.update_backup.assert_called_once_with( | ||
| self.backup, metadata={'wow': 'cool', 'foo': 'bar'} | ||
| ) | ||
@@ -819,13 +814,12 @@ def test_backup_set_property_pre_v343(self): | ||
| 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) | ||
@@ -848,11 +842,10 @@ | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_backup_metadata.assert_called_once_with( | ||
| self.backup, keys=['wow'] | ||
| ) | ||
@@ -880,4 +873,2 @@ def test_backup_unset_property_pre_v343(self): | ||
| class TestBackupShow(volume_fakes.TestVolume): | ||
| backup = volume_fakes.create_one_backup() | ||
| columns = ( | ||
@@ -905,24 +896,2 @@ "availability_zone", | ||
| ) | ||
| 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, | ||
| ) | ||
@@ -932,4 +901,28 @@ def setUp(self): | ||
| self.backup = sdk_fakes.generate_fake_resource(_backup.Backup) | ||
| self.volume_sdk_client.find_backup.return_value = self.backup | ||
| # Get the command object to test | ||
| 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) | ||
@@ -943,5 +936,7 @@ | ||
| columns, data = self.cmd.take_action(parsed_args) | ||
| self.volume_sdk_client.find_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) |
@@ -16,30 +16,181 @@ # | ||
| 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.tests.unit.volume.v2 import fakes as volume_fakes | ||
| from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes_v3 | ||
| 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 TestVolumeSnapshot(volume_fakes_v3.TestVolume): | ||
| class TestVolumeSnapshotCreate(volume_fakes.TestVolume): | ||
| def setUp(self): | ||
| super().setUp() | ||
| self.snapshots_mock = self.volume_client.volume_snapshots | ||
| self.snapshots_mock.reset_mock() | ||
| 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.volume_sdk_client.unmanage_snapshot.return_value = None | ||
| 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) | ||
| class TestVolumeSnapshotDelete(TestVolumeSnapshot): | ||
| snapshots = volume_fakes.create_snapshots(count=2) | ||
| 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_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 | ||
| self.volume_sdk_client.unmanage_snapshot.return_value = None | ||
| # Get the command object to mock | ||
| self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None) | ||
@@ -53,7 +204,10 @@ | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_snapshot.assert_called_once_with( | ||
| self.snapshots[0].id, force=False | ||
| ) | ||
@@ -66,7 +220,10 @@ def test_snapshot_delete_with_force(self): | ||
| 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.assertIsNone(result) | ||
| self.volume_sdk_client.delete_snapshot.assert_called_once_with( | ||
| self.snapshots[0].id, force=True | ||
| ) | ||
@@ -80,13 +237,20 @@ def test_delete_multiple_snapshots(self): | ||
| ] | ||
| parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||
| 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) | ||
| 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 = [ | ||
@@ -102,22 +266,21 @@ self.snapshots[0].id, | ||
| 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)) | ||
| exc = self.assertRaises( | ||
| exceptions.CommandError, | ||
| self.cmd.take_action, | ||
| parsed_args, | ||
| ) | ||
| self.assertEqual('1 of 2 snapshots failed to delete.', str(exc)) | ||
| find_mock.assert_any_call( | ||
| self.snapshots_mock, self.snapshots[0].id | ||
| ) | ||
| find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') | ||
| 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), | ||
| ] | ||
| ) | ||
| self.assertEqual(2, find_mock.call_count) | ||
| self.snapshots_mock.delete.assert_called_once_with( | ||
| self.snapshots[0].id, False | ||
| ) | ||
| def test_snapshot_delete_remote(self): | ||
@@ -129,2 +292,3 @@ arglist = ['--remote', self.snapshots[0].id] | ||
| result = self.cmd.take_action(parsed_args) | ||
| self.assertIsNone(result) | ||
@@ -134,3 +298,2 @@ self.volume_sdk_client.unmanage_snapshot.assert_called_with( | ||
| ) | ||
| self.assertIsNone(result) | ||
@@ -159,10 +322,482 @@ def test_snapshot_delete_with_remote_force(self): | ||
| 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) | ||
| calls = [] | ||
| for s in self.snapshots: | ||
| calls.append(mock.call(s.id)) | ||
| self.volume_sdk_client.unmanage_snapshot.assert_has_calls(calls) | ||
| 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"] | ||
| ) |
@@ -48,29 +48,30 @@ # | ||
| 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", | ||
| ] | ||
| columns += ("disabled_reason",) | ||
| column_names += ("Disabled Reason",) | ||
| data = service_client.services.list( | ||
| parsed_args.host, parsed_args.service | ||
| data = volume_client.services( | ||
| host=parsed_args.host, binary=parsed_args.service | ||
| ) | ||
| return ( | ||
| columns, | ||
| column_names, | ||
| ( | ||
@@ -91,4 +92,8 @@ utils.get_item_properties( | ||
| parser = super().get_parser(prog_name) | ||
| parser.add_argument("host", metavar="<host>", help=_("Name of host")) | ||
| parser.add_argument( | ||
| "host", | ||
| metavar="<host>", | ||
| help=_("Name of host"), | ||
| ) | ||
| parser.add_argument( | ||
| "service", | ||
@@ -123,17 +128,15 @@ metavar="<service>", | ||
| service_client = self.app.client_manager.volume | ||
| volume_client = self.app.client_manager.sdk_connection.volume | ||
| service = volume_client.find_service( | ||
| host=parsed_args.host, service=parsed_args.service | ||
| ) | ||
| if parsed_args.enable: | ||
| service_client.services.enable( | ||
| parsed_args.host, parsed_args.service | ||
| service.enable(volume_client) | ||
| if parsed_args.disable: | ||
| service.disable( | ||
| volume_client, | ||
| reason=parsed_args.disable_reason, | ||
| ) | ||
| 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 | ||
| ) |
@@ -420,9 +420,15 @@ # | ||
| 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: | ||
@@ -429,0 +435,0 @@ LOG.error(_("Failed to set backup state: %s"), e) |
@@ -17,7 +17,8 @@ # | ||
| 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 | ||
@@ -64,2 +65,38 @@ from osc_lib.cli import parseractions | ||
| 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): | ||
@@ -99,2 +136,3 @@ _description = _("Create new volume snapshot") | ||
| action=parseractions.KeyValueAction, | ||
| dest="properties", | ||
| help=_( | ||
@@ -119,7 +157,9 @@ "Set a property to this snapshot " | ||
| 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: | ||
@@ -134,3 +174,4 @@ # Create a new snapshot from an existing remote snapshot source | ||
| LOG.warning(msg) | ||
| snapshot = volume_client.volume_snapshots.manage( | ||
| snapshot = volume_client.manage_snapshot( | ||
| volume_id=volume_id, | ||
@@ -140,23 +181,18 @@ ref=parsed_args.remote_source, | ||
| 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): | ||
@@ -184,12 +220,12 @@ _description = _("Delete volume snapshot(s)") | ||
| 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 | ||
| ) | ||
@@ -203,3 +239,3 @@ except Exception as e: | ||
| ) | ||
| % {'snapshot': i, 'e': e} | ||
| % {'snapshot': snapshot, 'e': e} | ||
| ) | ||
@@ -271,22 +307,30 @@ | ||
| 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', | ||
| ) | ||
@@ -296,3 +340,3 @@ # Cache the volume list | ||
| try: | ||
| for s in volume_client.volumes.list(): | ||
| for s in volume_client.volumes(): | ||
| volume_cache[s.id] = s | ||
@@ -308,4 +352,4 @@ except Exception: # noqa: S110 | ||
| 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 | ||
@@ -326,14 +370,10 @@ | ||
| 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, | ||
| ) | ||
@@ -347,4 +387,4 @@ return ( | ||
| formatters={ | ||
| 'Metadata': format_columns.DictColumn, | ||
| 'Volume ID': _VolumeIdColumn, | ||
| 'metadata': format_columns.DictColumn, | ||
| 'volume_id': _VolumeIdColumn, | ||
| }, | ||
@@ -390,2 +430,3 @@ ) | ||
| action=parseractions.KeyValueAction, | ||
| dest='properties', | ||
| help=_( | ||
@@ -417,5 +458,6 @@ 'Property to add/change for this snapshot ' | ||
| 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 | ||
| ) | ||
@@ -426,6 +468,4 @@ | ||
| 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) | ||
| ) | ||
@@ -436,6 +476,6 @@ except Exception as e: | ||
| 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 | ||
| ) | ||
@@ -448,3 +488,3 @@ except Exception as e: | ||
| try: | ||
| volume_client.volume_snapshots.reset_state( | ||
| volume_client.reset_snapshot_status( | ||
| snapshot.id, parsed_args.state | ||
@@ -463,3 +503,3 @@ ) | ||
| try: | ||
| volume_client.volume_snapshots.update(snapshot.id, **kwargs) | ||
| volume_client.update_snapshot(snapshot.id, **kwargs) | ||
| except Exception as e: | ||
@@ -491,16 +531,12 @@ LOG.error( | ||
| 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 | ||
| ) | ||
| 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 UnsetVolumeSnapshot(command.Command): | ||
@@ -521,2 +557,3 @@ _description = _("Unset volume snapshot properties") | ||
| default=[], | ||
| dest='properties', | ||
| help=_( | ||
@@ -530,11 +567,11 @@ 'Property to remove from snapshot ' | ||
| 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 | ||
| ) |
@@ -110,3 +110,3 @@ # | ||
| def _get_parser(self, prog_name): | ||
| def get_parser(self, prog_name): | ||
| parser = super().get_parser(prog_name) | ||
@@ -173,2 +173,3 @@ parser.add_argument( | ||
| action=parseractions.KeyValueAction, | ||
| dest="properties", | ||
| help=_( | ||
@@ -194,2 +195,4 @@ "Set a property to this volume " | ||
| action="store_true", | ||
| dest="bootable", | ||
| default=None, | ||
| help=_("Mark volume as bootable"), | ||
@@ -199,3 +202,5 @@ ) | ||
| "--non-bootable", | ||
| action="store_true", | ||
| action="store_false", | ||
| dest="bootable", | ||
| default=None, | ||
| help=_("Mark volume as non-bootable (default)"), | ||
@@ -207,2 +212,4 @@ ) | ||
| action="store_true", | ||
| dest="read_only", | ||
| default=None, | ||
| help=_("Set volume to read-only access mode"), | ||
@@ -212,13 +219,11 @@ ) | ||
| "--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 | ||
@@ -274,3 +279,3 @@ # _check_size_arg where we check that size | ||
| availability_zone=parsed_args.availability_zone, | ||
| metadata=parsed_args.property, | ||
| metadata=parsed_args.properties, | ||
| imageRef=image, | ||
@@ -282,3 +287,3 @@ source_volid=source_volume, | ||
| if parsed_args.bootable or parsed_args.non_bootable: | ||
| if parsed_args.bootable is not None: | ||
| try: | ||
@@ -302,3 +307,4 @@ if utils.wait_for_status( | ||
| 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: | ||
@@ -370,14 +376,15 @@ if utils.wait_for_status( | ||
| 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: | ||
@@ -390,3 +397,3 @@ result += 1 | ||
| ), | ||
| {'volume': i, 'e': e}, | ||
| {'volume': volume, 'e': e}, | ||
| ) | ||
@@ -636,2 +643,3 @@ | ||
| action=parseractions.KeyValueAction, | ||
| dest="properties", | ||
| help=_( | ||
@@ -646,2 +654,3 @@ 'Set a property on this volume ' | ||
| action=parseractions.KeyValueAction, | ||
| dest="image_properties", | ||
| help=_( | ||
@@ -723,2 +732,4 @@ 'Set an image property on this volume ' | ||
| action="store_true", | ||
| dest="bootable", | ||
| default=None, | ||
| help=_("Mark volume as bootable"), | ||
@@ -728,3 +739,5 @@ ) | ||
| "--non-bootable", | ||
| action="store_true", | ||
| action="store_false", | ||
| dest="bootable", | ||
| default=None, | ||
| help=_("Mark volume as non-bootable"), | ||
@@ -736,2 +749,4 @@ ) | ||
| action="store_true", | ||
| dest="read_only", | ||
| default=None, | ||
| help=_("Set volume to read-only access mode"), | ||
@@ -741,3 +756,5 @@ ) | ||
| "--read-write", | ||
| action="store_true", | ||
| action="store_false", | ||
| dest="read_only", | ||
| default=None, | ||
| help=_("Set volume to read-write access mode"), | ||
@@ -791,18 +808,20 @@ ) | ||
| 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: | ||
@@ -814,2 +833,3 @@ try: | ||
| result += 1 | ||
| if parsed_args.attached: | ||
@@ -823,2 +843,3 @@ try: | ||
| result += 1 | ||
| if parsed_args.detached: | ||
@@ -832,3 +853,4 @@ try: | ||
| result += 1 | ||
| if parsed_args.bootable or parsed_args.non_bootable: | ||
| if parsed_args.bootable is not None: | ||
| try: | ||
@@ -841,3 +863,4 @@ volume_client.volumes.set_bootable( | ||
| result += 1 | ||
| if parsed_args.read_only or parsed_args.read_write: | ||
| if parsed_args.read_only is not None: | ||
| try: | ||
@@ -853,2 +876,3 @@ volume_client.volumes.update_readonly_flag( | ||
| result += 1 | ||
| policy = parsed_args.migration_policy or parsed_args.retype_policy | ||
@@ -954,2 +978,3 @@ if parsed_args.type: | ||
| action='append', | ||
| dest='properties', | ||
| help=_( | ||
@@ -964,2 +989,3 @@ 'Remove a property from volume ' | ||
| action='append', | ||
| dest='image_properties', | ||
| help=_( | ||
@@ -977,18 +1003,18 @@ 'Remove an image property from 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 | ||
@@ -995,0 +1021,0 @@ |
@@ -17,6 +17,5 @@ # | ||
| from cinderclient import api_versions | ||
| from openstack import utils as sdk_utils | ||
| from osc_lib.command import command | ||
| from osc_lib import exceptions | ||
| from osc_lib import utils | ||
@@ -37,3 +36,3 @@ from openstackclient.i18n import _ | ||
| metavar="<host>", | ||
| default="", | ||
| default=None, | ||
| help=_( | ||
@@ -47,5 +46,5 @@ "List block storage service log level of specified host " | ||
| metavar="<service>", | ||
| default="", | ||
| default=None, | ||
| choices=( | ||
| '', | ||
| None, | ||
| '*', | ||
@@ -65,3 +64,3 @@ 'cinder-api', | ||
| metavar="<log-prefix>", | ||
| default="", | ||
| default=None, | ||
| help="Prefix for the log, e.g. 'sqlalchemy'", | ||
@@ -72,3 +71,3 @@ ) | ||
| def take_action(self, parsed_args): | ||
| service_client = self.app.client_manager.volume | ||
| volume_client = self.app.client_manager.sdk_connection.volume | ||
| columns = [ | ||
@@ -81,3 +80,3 @@ "Binary", | ||
| if service_client.api_version < api_versions.APIVersion('3.32'): | ||
| if not sdk_utils.supports_microversion(volume_client, '3.32'): | ||
| msg = _( | ||
@@ -89,18 +88,13 @@ "--os-volume-api-version 3.32 or greater is required to " | ||
| 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) | ||
@@ -121,3 +115,3 @@ | ||
| type=str.upper, | ||
| help=_("Desired log level."), | ||
| help=_("Desired log level"), | ||
| ) | ||
@@ -127,3 +121,3 @@ parser.add_argument( | ||
| metavar="<host>", | ||
| default="", | ||
| default=None, | ||
| help=_( | ||
@@ -137,5 +131,5 @@ "Set block storage service log level of specified host " | ||
| metavar="<service>", | ||
| default="", | ||
| default=None, | ||
| choices=( | ||
| '', | ||
| None, | ||
| '*', | ||
@@ -155,3 +149,3 @@ 'cinder-api', | ||
| metavar="<log-prefix>", | ||
| default="", | ||
| default=None, | ||
| help="Prefix for the log, e.g. 'sqlalchemy'", | ||
@@ -162,5 +156,5 @@ ) | ||
| 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 = _( | ||
@@ -172,3 +166,3 @@ "--os-volume-api-version 3.32 or greater is required to " | ||
| service_client.services.set_log_levels( | ||
| volume_client.set_service_log_levels( | ||
| level=parsed_args.level, | ||
@@ -175,0 +169,0 @@ binary=parsed_args.service, |
@@ -17,13 +17,45 @@ # | ||
| from cinderclient import api_versions | ||
| 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.volume.v2 import service as service_v2 | ||
| 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="<host>", | ||
| help=_("List services on specified host (name only)"), | ||
| ) | ||
| parser.add_argument( | ||
| "--service", | ||
| metavar="<service>", | ||
| 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 = [ | ||
| columns: tuple[str, ...] = ( | ||
| "binary", | ||
| "host", | ||
| "availability_zone", | ||
| "status", | ||
| "state", | ||
| "updated_at", | ||
| ) | ||
| column_names: tuple[str, ...] = ( | ||
| "Binary", | ||
@@ -35,16 +67,19 @@ "Host", | ||
| "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") | ||
| 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.append("Disabled Reason") | ||
| columns += ("disabled_reason",) | ||
| column_names += ("Disabled Reason",) | ||
| data = service_client.services.list( | ||
| parsed_args.host, parsed_args.service | ||
| data = volume_client.services( | ||
| host=parsed_args.host, binary=parsed_args.service | ||
| ) | ||
| return ( | ||
| columns, | ||
| column_names, | ||
| ( | ||
@@ -58,1 +93,57 @@ utils.get_item_properties( | ||
| ) | ||
| 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="<host>", | ||
| help=_("Name of host"), | ||
| ) | ||
| parser.add_argument( | ||
| "service", | ||
| metavar="<service>", | ||
| 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="<reason>", | ||
| 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( | ||
| host=parsed_args.host, service=parsed_args.service | ||
| ) | ||
| if parsed_args.enable: | ||
| service.enable(volume_client) | ||
| if parsed_args.disable: | ||
| service.disable( | ||
| volume_client, | ||
| reason=parsed_args.disable_reason, | ||
| ) |
@@ -21,3 +21,2 @@ # | ||
| from cinderclient import api_versions | ||
| from cliff import columns as cliff_columns | ||
@@ -516,9 +515,15 @@ from openstack import utils as sdk_utils | ||
| 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: | ||
@@ -531,3 +536,3 @@ LOG.error(_("Failed to set backup state: %s"), e) | ||
| 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 = _( | ||
@@ -542,3 +547,3 @@ '--os-volume-api-version 3.9 or greater is required to ' | ||
| 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 = _( | ||
@@ -553,3 +558,3 @@ '--os-volume-api-version 3.9 or greater is required to ' | ||
| 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 = _( | ||
@@ -562,3 +567,3 @@ '--os-volume-api-version 3.43 or greater is required to ' | ||
| 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 = _( | ||
@@ -570,3 +575,3 @@ '--os-volume-api-version 3.43 or greater is required to ' | ||
| if volume_client.api_version >= api_versions.APIVersion('3.43'): | ||
| if sdk_utils.supports_microversion(volume_client, '3.43'): | ||
| metadata = copy.deepcopy(backup.metadata) | ||
@@ -582,3 +587,3 @@ | ||
| try: | ||
| volume_client.backups.update(backup.id, **kwargs) | ||
| volume_client.update_backup(backup, **kwargs) | ||
| except Exception as e: | ||
@@ -619,5 +624,5 @@ LOG.error("Failed to update backup: %s", e) | ||
| 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 = _( | ||
@@ -629,3 +634,5 @@ '--os-volume-api-version 3.43 or greater is required to ' | ||
| 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) | ||
@@ -645,9 +652,5 @@ | ||
| kwargs = { | ||
| 'metadata': metadata, | ||
| } | ||
| volume_client.delete_backup_metadata(backup, keys=list(metadata)) | ||
| volume_client.backups.update(backup.id, **kwargs) | ||
| class ShowVolumeBackup(command.ShowOne): | ||
@@ -667,3 +670,5 @@ _description = _("Display volume backup details") | ||
| volume_client = self.app.client_manager.sdk_connection.volume | ||
| backup = volume_client.find_backup(parsed_args.backup) | ||
| backup = volume_client.find_backup( | ||
| parsed_args.backup, ignore_missing=False | ||
| ) | ||
| columns: tuple[str, ...] = ( | ||
@@ -670,0 +675,0 @@ "availability_zone", |
@@ -17,4 +17,10 @@ # | ||
| 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.command import command | ||
@@ -24,3 +30,5 @@ from osc_lib import exceptions | ||
| from openstackclient.common import pagination | ||
| from openstackclient.i18n import _ | ||
| from openstackclient.identity import common as identity_common | ||
@@ -30,2 +38,157 @@ 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].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="<snapshot-name>", | ||
| help=_("Name of the new snapshot"), | ||
| ) | ||
| parser.add_argument( | ||
| "--volume", | ||
| metavar="<volume>", | ||
| help=_( | ||
| "Volume to snapshot (name or ID) (default is <snapshot-name>)" | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| "--description", | ||
| metavar="<description>", | ||
| help=_("Description of the snapshot"), | ||
| ) | ||
| parser.add_argument( | ||
| "--force", | ||
| action="store_true", | ||
| default=False, | ||
| help=_( | ||
| "Create a snapshot attached to an instance. Default is False" | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| "--property", | ||
| metavar="<key=value>", | ||
| dest='properties', | ||
| action=parseractions.KeyValueAction, | ||
| help=_( | ||
| "Set a property to this snapshot " | ||
| "(repeat option to set multiple properties)" | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| "--remote-source", | ||
| metavar="<key=value>", | ||
| 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): | ||
@@ -61,5 +224,3 @@ _description = _("Delete volume snapshot(s)") | ||
| 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 | ||
@@ -75,12 +236,12 @@ | ||
| 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 | ||
| if parsed_args.remote: | ||
| volume_client_sdk.unmanage_snapshot(snapshot_id) | ||
| volume_client.unmanage_snapshot(snapshot_id) | ||
| else: | ||
| volume_client.volume_snapshots.delete( | ||
| snapshot_id, parsed_args.force | ||
| volume_client.delete_snapshot( | ||
| snapshot_id, force=parsed_args.force | ||
| ) | ||
@@ -94,3 +255,3 @@ except Exception as e: | ||
| ) | ||
| % {'snapshot': i, 'e': e} | ||
| % {'snapshot': snapshot, 'e': e} | ||
| ) | ||
@@ -105,1 +266,315 @@ | ||
| 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='<project>', | ||
| 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='<name>', | ||
| default=None, | ||
| help=_('Filters results by a name.'), | ||
| ) | ||
| parser.add_argument( | ||
| '--status', | ||
| metavar='<status>', | ||
| 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='<volume>', | ||
| 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='<snapshot>', | ||
| help=_('Snapshot to modify (name or ID)'), | ||
| ) | ||
| parser.add_argument( | ||
| '--name', metavar='<name>', help=_('New snapshot name') | ||
| ) | ||
| parser.add_argument( | ||
| '--description', | ||
| metavar='<description>', | ||
| help=_('New snapshot description'), | ||
| ) | ||
| parser.add_argument( | ||
| "--no-property", | ||
| dest="no_property", | ||
| action="store_true", | ||
| help=_( | ||
| "Remove all properties from <snapshot> " | ||
| "(specify both --no-property and --property to " | ||
| "remove the current properties before setting " | ||
| "new properties.)" | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| '--property', | ||
| metavar='<key=value>', | ||
| action=parseractions.KeyValueAction, | ||
| dest='properties', | ||
| help=_( | ||
| 'Property to add/change for this snapshot ' | ||
| '(repeat option to set multiple properties)' | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| '--state', | ||
| metavar='<state>', | ||
| choices=[ | ||
| 'available', | ||
| 'error', | ||
| 'creating', | ||
| 'deleting', | ||
| 'error_deleting', | ||
| ], | ||
| help=_( | ||
| 'New snapshot state. ("available", "error", "creating", ' | ||
| '"deleting", or "error_deleting") (admin only) ' | ||
| '(This option simply changes the state of the snapshot ' | ||
| 'in the database with no regard to actual status, ' | ||
| 'exercise caution when using)' | ||
| ), | ||
| ) | ||
| return parser | ||
| def take_action(self, parsed_args): | ||
| 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="<snapshot>", | ||
| 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='<snapshot>', | ||
| help=_('Snapshot to modify (name or ID)'), | ||
| ) | ||
| parser.add_argument( | ||
| '--property', | ||
| metavar='<key>', | ||
| 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 | ||
| ) |
@@ -34,3 +34,2 @@ # | ||
| from openstackclient.identity import common as identity_common | ||
| from openstackclient.volume.v2 import volume as volume_v2 | ||
@@ -95,3 +94,3 @@ | ||
| class CreateVolume(volume_v2.CreateVolume): | ||
| class CreateVolume(command.ShowOne): | ||
| _description = _("Create new volume") | ||
@@ -122,5 +121,45 @@ | ||
| 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="<name>", | ||
| nargs="?", | ||
| help=_("Volume name"), | ||
| ) | ||
| parser.add_argument( | ||
| "--size", | ||
| metavar="<size>", | ||
| type=int, | ||
| help=_( | ||
| "Volume size in GB (required unless --snapshot or " | ||
| "--source specified)" | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| "--type", | ||
| metavar="<volume-type>", | ||
| help=_("Set the type of volume"), | ||
| ) | ||
| source_group = parser.add_mutually_exclusive_group() | ||
| source_group.add_argument( | ||
| "--image", | ||
| metavar="<image>", | ||
| help=_("Use <image> as source of volume (name or ID)"), | ||
| ) | ||
| source_group.add_argument( | ||
| "--snapshot", | ||
| metavar="<snapshot>", | ||
| help=_("Use <snapshot> as source of volume (name or ID)"), | ||
| ) | ||
| source_group.add_argument( | ||
| "--source", | ||
| metavar="<volume>", | ||
| help=_("Volume to clone (name or ID)"), | ||
| ) | ||
| source_group.add_argument( | ||
| "--source-replicated", | ||
| metavar="<replicated-volume>", | ||
| help=argparse.SUPPRESS, | ||
| ) | ||
| source_group.add_argument( | ||
| "--backup", | ||
@@ -145,2 +184,68 @@ metavar="<backup>", | ||
| parser.add_argument( | ||
| "--description", | ||
| metavar="<description>", | ||
| help=_("Volume description"), | ||
| ) | ||
| parser.add_argument( | ||
| "--availability-zone", | ||
| metavar="<availability-zone>", | ||
| help=_("Create volume in <availability-zone>"), | ||
| ) | ||
| parser.add_argument( | ||
| "--consistency-group", | ||
| metavar="consistency-group>", | ||
| help=_("Consistency group where the new volume belongs to"), | ||
| ) | ||
| parser.add_argument( | ||
| "--property", | ||
| metavar="<key=value>", | ||
| action=parseractions.KeyValueAction, | ||
| dest="properties", | ||
| help=_( | ||
| "Set a property to this volume " | ||
| "(repeat option to set multiple properties)" | ||
| ), | ||
| ) | ||
| parser.add_argument( | ||
| "--hint", | ||
| metavar="<key=value>", | ||
| 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", | ||
@@ -167,3 +272,3 @@ metavar="<host>", | ||
| 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 | ||
@@ -202,4 +307,3 @@ # _check_size_arg where we check that size | ||
| or parsed_args.hint | ||
| or parsed_args.read_only | ||
| or parsed_args.read_write | ||
| or parsed_args.read_only is not None | ||
| ): | ||
@@ -241,6 +345,20 @@ msg = _( | ||
| availability_zone=parsed_args.availability_zone, | ||
| metadata=parsed_args.property, | ||
| metadata=parsed_args.properties, | ||
| bootable=parsed_args.bootable, | ||
| ) | ||
| return zip(*sorted(volume.items())) | ||
| data = {} | ||
| for key, value in volume.to_dict().items(): | ||
| # FIXME(stephenfin): Stop ignoring these once we bump SDK | ||
| # https://review.opendev.org/c/openstack/openstacksdk/+/945836/ | ||
| if key in ( | ||
| 'cluster_name', | ||
| 'consumes_quota', | ||
| 'encryption_key_id', | ||
| 'service_uuid', | ||
| 'shared_targets', | ||
| 'volume_type_id', | ||
| ): | ||
| continue | ||
| data[key] = value | ||
| return zip(*sorted(data.items())) | ||
@@ -297,3 +415,3 @@ source_volume = None | ||
| availability_zone=parsed_args.availability_zone, | ||
| metadata=parsed_args.property, | ||
| metadata=parsed_args.properties, | ||
| imageRef=image, | ||
@@ -306,3 +424,3 @@ source_volid=source_volume, | ||
| if parsed_args.bootable or parsed_args.non_bootable: | ||
| if parsed_args.bootable is not None: | ||
| try: | ||
@@ -326,3 +444,4 @@ if utils.wait_for_status( | ||
| 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: | ||
@@ -364,3 +483,3 @@ if utils.wait_for_status( | ||
| class DeleteVolume(volume_v2.DeleteVolume): | ||
| class DeleteVolume(command.Command): | ||
| _description = _("Delete volume(s)") | ||
@@ -371,2 +490,24 @@ | ||
| parser.add_argument( | ||
| "volumes", | ||
| metavar="<volume>", | ||
| 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', | ||
@@ -379,4 +520,3 @@ action='store_true', | ||
| 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 | ||
@@ -391,12 +531,14 @@ | ||
| 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, | ||
| ) | ||
@@ -410,3 +552,3 @@ except Exception as e: | ||
| ), | ||
| {'volume': i, 'e': e}, | ||
| {'volume': volume, 'e': e}, | ||
| ) | ||
@@ -656,2 +798,3 @@ | ||
| action=parseractions.KeyValueAction, | ||
| dest='properties', | ||
| help=_( | ||
@@ -666,2 +809,3 @@ 'Set a property on this volume ' | ||
| action=parseractions.KeyValueAction, | ||
| dest='image_properties', | ||
| help=_( | ||
@@ -743,2 +887,4 @@ 'Set an image property on this volume ' | ||
| action="store_true", | ||
| dest="bootable", | ||
| default=None, | ||
| help=_("Mark volume as bootable"), | ||
@@ -748,3 +894,5 @@ ) | ||
| "--non-bootable", | ||
| action="store_true", | ||
| action="store_false", | ||
| dest="bootable", | ||
| default=None, | ||
| help=_("Mark volume as non-bootable"), | ||
@@ -756,2 +904,4 @@ ) | ||
| action="store_true", | ||
| dest="read_only", | ||
| default=None, | ||
| help=_("Set volume to read-only access mode"), | ||
@@ -761,3 +911,5 @@ ) | ||
| "--read-write", | ||
| action="store_true", | ||
| action="store_false", | ||
| dest="read_only", | ||
| default=None, | ||
| help=_("Set volume to read-write access mode"), | ||
@@ -820,18 +972,20 @@ ) | ||
| 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: | ||
@@ -843,2 +997,3 @@ try: | ||
| result += 1 | ||
| if parsed_args.attached: | ||
@@ -852,2 +1007,3 @@ try: | ||
| result += 1 | ||
| if parsed_args.detached: | ||
@@ -861,3 +1017,4 @@ try: | ||
| result += 1 | ||
| if parsed_args.bootable or parsed_args.non_bootable: | ||
| if parsed_args.bootable is not None: | ||
| try: | ||
@@ -870,3 +1027,4 @@ volume_client.volumes.set_bootable( | ||
| result += 1 | ||
| if parsed_args.read_only or parsed_args.read_write: | ||
| if parsed_args.read_only is not None: | ||
| try: | ||
@@ -882,2 +1040,3 @@ volume_client.volumes.update_readonly_flag( | ||
| result += 1 | ||
| policy = parsed_args.migration_policy or parsed_args.retype_policy | ||
@@ -983,2 +1142,3 @@ if parsed_args.type: | ||
| action='append', | ||
| dest='properties', | ||
| help=_( | ||
@@ -993,2 +1153,3 @@ 'Remove a property from volume ' | ||
| action='append', | ||
| dest='image_properties', | ||
| help=_( | ||
@@ -1006,18 +1167,18 @@ 'Remove an image property from 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 | ||
@@ -1024,0 +1185,0 @@ |
+150
-52
| Metadata-Version: 2.1 | ||
| Name: python-openstackclient | ||
| Version: 8.0.0 | ||
| Version: 8.1.0 | ||
| Summary: OpenStack Command-line Client | ||
@@ -15,7 +15,7 @@ Home-page: https://docs.openstack.org/python-openstackclient/latest/ | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3.9 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Programming Language :: Python :: 3.11 | ||
| Classifier: Programming Language :: Python :: 3.12 | ||
| Requires-Python: >=3.9 | ||
| Classifier: Programming Language :: Python :: 3.13 | ||
| Requires-Python: >=3.10 | ||
| Description-Content-Type: text/x-rst | ||
@@ -27,3 +27,3 @@ License-File: LICENSE | ||
| Requires-Dist: iso8601>=0.1.11 | ||
| Requires-Dist: openstacksdk>=3.3.0 | ||
| Requires-Dist: openstacksdk>=4.5.0 | ||
| Requires-Dist: osc-lib>=2.3.0 | ||
@@ -36,6 +36,2 @@ Requires-Dist: oslo.i18n>=3.15.3 | ||
| ======================== | ||
| Team and repository tags | ||
| ======================== | ||
| =============== | ||
@@ -49,5 +45,6 @@ OpenStackClient | ||
| 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. | ||
@@ -57,60 +54,88 @@ The primary goal is to provide a unified shell command structure and a common | ||
| * `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: | ||
| .. code-block:: shell | ||
| python3 -m pip install python-openstackclient | ||
| There are a few variants on getting help. A list of global options and supported | ||
| commands is shown with ``--help``:: | ||
| You can use ``--help`` or the ``help`` command to get a list of global options | ||
| and supported commands: | ||
| .. code-block:: shell | ||
| openstack --help | ||
| openstack help | ||
| There is also a ``help`` command that can be used to get help text for a specific | ||
| command:: | ||
| You can also get help for a specific command: | ||
| openstack help | ||
| .. 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): | ||
| .. code-block:: shell | ||
| python3 -m pip install python3-designateclient | ||
| A ``Dockerfile`` is provided for your convenience in the repository. You can | ||
| use this to build your own container images: | ||
| .. code-block:: shell | ||
| git clone https://opendev.org/openstack/python-openstackclient | ||
| cd python-openstackclient | ||
| python3 -m pip install -e . | ||
| podman build . -t example.com/myuser/openstackclient | ||
| For more information the available options and commands, refer to the `Users | ||
| Guide`__. | ||
| .. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html | ||
| Configuration | ||
| ============= | ||
| The CLI is configured via environment variables and command-line | ||
| options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. | ||
| 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: | ||
| Authentication using username/password is most commonly used: | ||
| * If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment | ||
| variable | ||
| * ``.`` (the current directory) | ||
| * ``$HOME/.config/openstack`` | ||
| * ``/etc/openstack`` | ||
| - For a local user, your configuration will look like the one below:: | ||
| The options you should set will depend on the configuration of your cloud and | ||
| the authentication mechanism(s) supported. For example, consider a cloud that | ||
| supports username/password authentication. Configuration for this cloud using a | ||
| ``clouds.yaml`` file would look like so: | ||
| .. code-block:: yaml | ||
| clouds: | ||
| my-cloud: | ||
| auth: | ||
| auth_url: '<url-to-openstack-identity>' | ||
| project_name: '<project-name>' | ||
| project_domain_name: '<project-domain-name>' | ||
| username: '<username>' | ||
| user_domain_name: '<user-domain-name>' | ||
| password: '<password>' # (optional) | ||
| region_name: '<region>' | ||
| The corresponding environment variables would look very similar: | ||
| .. code-block:: shell | ||
| export OS_AUTH_URL=<url-to-openstack-identity> | ||
| export OS_IDENTITY_API_VERSION=3 | ||
| export OS_REGION_NAME=<region> | ||
| export OS_PROJECT_NAME=<project-name> | ||
@@ -122,6 +147,9 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name> | ||
| The corresponding command-line options look very similar:: | ||
| Likewise, the corresponding command-line options would look very similar: | ||
| --os-auth-url <url> | ||
| --os-identity-api-version 3 | ||
| :: | ||
| openstack | ||
| --os-auth-url <url-to-openstack-identity> | ||
| --os-region <region> | ||
| --os-project-name <project-name> | ||
@@ -133,4 +161,37 @@ --os-project-domain-name <project-domain-name> | ||
| - For a federated user, your configuration will look the so:: | ||
| .. note:: | ||
| If a password is not provided above (in plaintext), you will be | ||
| interactively prompted to provide one securely. | ||
| Some clouds use federated authentication. If this is the case, your | ||
| configuration will be slightly more involved. For example, to configure | ||
| username/password authentication for a federated user using a ``clouds.yaml`` | ||
| file: | ||
| .. code-block:: yaml | ||
| clouds: | ||
| my-cloud: | ||
| auth: | ||
| auth_url: '<url-to-openstack-identity>' | ||
| project_name: '<project-name>' | ||
| project_domain_name: '<project-domain-name>' | ||
| username: '<username-in-idp>' | ||
| user_domain_name: '<user-domain-name>' | ||
| password: '<password-in-idp>' | ||
| identity_provider: '<the-desired-idp-in-keystone>' | ||
| client_id: '<the-client-id-configured-in-the-idp>' | ||
| client_secret: '<the-client-secret-configured-in-the-idp>' | ||
| openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>' | ||
| protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>' | ||
| access_token_type: '<the-access-token-type-used-by-your-idp>' | ||
| discovery_endpoint: '<the-well-known-endpoint-of-the-idp>' | ||
| auth_type: 'v3oidcpassword' | ||
| region_name: '<region>' | ||
| The corresponding environment variables would look very similar: | ||
| .. code-block:: shell | ||
| export OS_PROJECT_NAME=<project-name> | ||
@@ -140,3 +201,2 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name> | ||
| export OS_IDENTITY_API_VERSION=3 | ||
| export OS_AUTH_PLUGIN=openid | ||
| export OS_AUTH_TYPE=v3oidcpassword | ||
@@ -153,4 +213,6 @@ export OS_USERNAME=<username-in-idp> | ||
| The corresponding command-line options look very similar:: | ||
| Likewise, the corresponding command-line options would look very similar: | ||
| .. code-block:: shell | ||
| --os-project-name <project-name> | ||
@@ -172,4 +234,40 @@ --os-project-domain-name <project-domain-name> | ||
| 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 <https://bugs.launchpad.net/python-openstackclient>`_ | ||
| * `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_ | ||
| * `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_ | ||
| * `PyPi <https://pypi.org/project/python-openstackclient>`_ | ||
| * `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_ | ||
| * `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_ | ||
| * `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_ | ||
+2
-1
| [tool.mypy] | ||
| python_version = "3.9" | ||
| python_version = "3.10" | ||
| show_column_numbers = true | ||
@@ -25,2 +25,3 @@ show_error_context = true | ||
| line-length = 79 | ||
| target-version = "py310" | ||
@@ -27,0 +28,0 @@ [tool.ruff.format] |
@@ -46,2 +46,3 @@ [console_scripts] | ||
| compute_service_set = openstackclient.compute.v2.service:SetService | ||
| console_connection_show = openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation | ||
| console_log_show = openstackclient.compute.v2.console:ShowConsoleLog | ||
@@ -656,11 +657,11 @@ console_url_show = openstackclient.compute.v2.console:ShowConsoleURL | ||
| volume_service_list = openstackclient.volume.v3.service:ListService | ||
| volume_service_set = openstackclient.volume.v2.service:SetService | ||
| volume_service_set = openstackclient.volume.v3.service:SetService | ||
| volume_set = openstackclient.volume.v3.volume:SetVolume | ||
| volume_show = openstackclient.volume.v3.volume:ShowVolume | ||
| volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot | ||
| volume_snapshot_create = openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot | ||
| volume_snapshot_delete = openstackclient.volume.v3.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_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_summary = openstackclient.volume.v3.volume:VolumeSummary | ||
@@ -667,0 +668,0 @@ volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest |
@@ -1,1 +0,1 @@ | ||
| {"git_version": "30aa27b7", "is_release": true} | ||
| {"git_version": "88b59d89", "is_release": true} |
| Metadata-Version: 2.1 | ||
| Name: python-openstackclient | ||
| Version: 8.0.0 | ||
| Version: 8.1.0 | ||
| Summary: OpenStack Command-line Client | ||
@@ -15,7 +15,7 @@ Home-page: https://docs.openstack.org/python-openstackclient/latest/ | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3.9 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Programming Language :: Python :: 3.11 | ||
| Classifier: Programming Language :: Python :: 3.12 | ||
| Requires-Python: >=3.9 | ||
| Classifier: Programming Language :: Python :: 3.13 | ||
| Requires-Python: >=3.10 | ||
| Description-Content-Type: text/x-rst | ||
@@ -27,3 +27,3 @@ License-File: LICENSE | ||
| Requires-Dist: iso8601>=0.1.11 | ||
| Requires-Dist: openstacksdk>=3.3.0 | ||
| Requires-Dist: openstacksdk>=4.5.0 | ||
| Requires-Dist: osc-lib>=2.3.0 | ||
@@ -36,6 +36,2 @@ Requires-Dist: oslo.i18n>=3.15.3 | ||
| ======================== | ||
| Team and repository tags | ||
| ======================== | ||
| =============== | ||
@@ -49,5 +45,6 @@ OpenStackClient | ||
| 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. | ||
@@ -57,60 +54,88 @@ The primary goal is to provide a unified shell command structure and a common | ||
| * `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: | ||
| .. code-block:: shell | ||
| python3 -m pip install python-openstackclient | ||
| There are a few variants on getting help. A list of global options and supported | ||
| commands is shown with ``--help``:: | ||
| You can use ``--help`` or the ``help`` command to get a list of global options | ||
| and supported commands: | ||
| .. code-block:: shell | ||
| openstack --help | ||
| openstack help | ||
| There is also a ``help`` command that can be used to get help text for a specific | ||
| command:: | ||
| You can also get help for a specific command: | ||
| openstack help | ||
| .. 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): | ||
| .. code-block:: shell | ||
| python3 -m pip install python3-designateclient | ||
| A ``Dockerfile`` is provided for your convenience in the repository. You can | ||
| use this to build your own container images: | ||
| .. code-block:: shell | ||
| git clone https://opendev.org/openstack/python-openstackclient | ||
| cd python-openstackclient | ||
| python3 -m pip install -e . | ||
| podman build . -t example.com/myuser/openstackclient | ||
| For more information the available options and commands, refer to the `Users | ||
| Guide`__. | ||
| .. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html | ||
| Configuration | ||
| ============= | ||
| The CLI is configured via environment variables and command-line | ||
| options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. | ||
| 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: | ||
| Authentication using username/password is most commonly used: | ||
| * If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment | ||
| variable | ||
| * ``.`` (the current directory) | ||
| * ``$HOME/.config/openstack`` | ||
| * ``/etc/openstack`` | ||
| - For a local user, your configuration will look like the one below:: | ||
| The options you should set will depend on the configuration of your cloud and | ||
| the authentication mechanism(s) supported. For example, consider a cloud that | ||
| supports username/password authentication. Configuration for this cloud using a | ||
| ``clouds.yaml`` file would look like so: | ||
| .. code-block:: yaml | ||
| clouds: | ||
| my-cloud: | ||
| auth: | ||
| auth_url: '<url-to-openstack-identity>' | ||
| project_name: '<project-name>' | ||
| project_domain_name: '<project-domain-name>' | ||
| username: '<username>' | ||
| user_domain_name: '<user-domain-name>' | ||
| password: '<password>' # (optional) | ||
| region_name: '<region>' | ||
| The corresponding environment variables would look very similar: | ||
| .. code-block:: shell | ||
| export OS_AUTH_URL=<url-to-openstack-identity> | ||
| export OS_IDENTITY_API_VERSION=3 | ||
| export OS_REGION_NAME=<region> | ||
| export OS_PROJECT_NAME=<project-name> | ||
@@ -122,6 +147,9 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name> | ||
| The corresponding command-line options look very similar:: | ||
| Likewise, the corresponding command-line options would look very similar: | ||
| --os-auth-url <url> | ||
| --os-identity-api-version 3 | ||
| :: | ||
| openstack | ||
| --os-auth-url <url-to-openstack-identity> | ||
| --os-region <region> | ||
| --os-project-name <project-name> | ||
@@ -133,4 +161,37 @@ --os-project-domain-name <project-domain-name> | ||
| - For a federated user, your configuration will look the so:: | ||
| .. note:: | ||
| If a password is not provided above (in plaintext), you will be | ||
| interactively prompted to provide one securely. | ||
| Some clouds use federated authentication. If this is the case, your | ||
| configuration will be slightly more involved. For example, to configure | ||
| username/password authentication for a federated user using a ``clouds.yaml`` | ||
| file: | ||
| .. code-block:: yaml | ||
| clouds: | ||
| my-cloud: | ||
| auth: | ||
| auth_url: '<url-to-openstack-identity>' | ||
| project_name: '<project-name>' | ||
| project_domain_name: '<project-domain-name>' | ||
| username: '<username-in-idp>' | ||
| user_domain_name: '<user-domain-name>' | ||
| password: '<password-in-idp>' | ||
| identity_provider: '<the-desired-idp-in-keystone>' | ||
| client_id: '<the-client-id-configured-in-the-idp>' | ||
| client_secret: '<the-client-secret-configured-in-the-idp>' | ||
| openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>' | ||
| protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>' | ||
| access_token_type: '<the-access-token-type-used-by-your-idp>' | ||
| discovery_endpoint: '<the-well-known-endpoint-of-the-idp>' | ||
| auth_type: 'v3oidcpassword' | ||
| region_name: '<region>' | ||
| The corresponding environment variables would look very similar: | ||
| .. code-block:: shell | ||
| export OS_PROJECT_NAME=<project-name> | ||
@@ -140,3 +201,2 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name> | ||
| export OS_IDENTITY_API_VERSION=3 | ||
| export OS_AUTH_PLUGIN=openid | ||
| export OS_AUTH_TYPE=v3oidcpassword | ||
@@ -153,4 +213,6 @@ export OS_USERNAME=<username-in-idp> | ||
| The corresponding command-line options look very similar:: | ||
| Likewise, the corresponding command-line options would look very similar: | ||
| .. code-block:: shell | ||
| --os-project-name <project-name> | ||
@@ -172,4 +234,40 @@ --os-project-domain-name <project-domain-name> | ||
| 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 <https://bugs.launchpad.net/python-openstackclient>`_ | ||
| * `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_ | ||
| * `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_ | ||
| * `PyPi <https://pypi.org/project/python-openstackclient>`_ | ||
| * `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_ | ||
| * `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_ | ||
| * `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_ | ||
@@ -5,3 +5,3 @@ pbr!=2.1.0,>=2.0.0 | ||
| iso8601>=0.1.11 | ||
| openstacksdk>=3.3.0 | ||
| openstacksdk>=4.5.0 | ||
| osc-lib>=2.3.0 | ||
@@ -8,0 +8,0 @@ oslo.i18n>=3.15.3 |
+146
-48
@@ -1,5 +0,1 @@ | ||
| ======================== | ||
| Team and repository tags | ||
| ======================== | ||
| =============== | ||
@@ -13,5 +9,6 @@ OpenStackClient | ||
| 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. | ||
@@ -21,60 +18,88 @@ The primary goal is to provide a unified shell command structure and a common | ||
| * `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: | ||
| .. code-block:: shell | ||
| python3 -m pip install python-openstackclient | ||
| There are a few variants on getting help. A list of global options and supported | ||
| commands is shown with ``--help``:: | ||
| You can use ``--help`` or the ``help`` command to get a list of global options | ||
| and supported commands: | ||
| .. code-block:: shell | ||
| openstack --help | ||
| openstack help | ||
| There is also a ``help`` command that can be used to get help text for a specific | ||
| command:: | ||
| You can also get help for a specific command: | ||
| openstack help | ||
| .. 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): | ||
| .. code-block:: shell | ||
| python3 -m pip install python3-designateclient | ||
| A ``Dockerfile`` is provided for your convenience in the repository. You can | ||
| use this to build your own container images: | ||
| .. code-block:: shell | ||
| git clone https://opendev.org/openstack/python-openstackclient | ||
| cd python-openstackclient | ||
| python3 -m pip install -e . | ||
| podman build . -t example.com/myuser/openstackclient | ||
| For more information the available options and commands, refer to the `Users | ||
| Guide`__. | ||
| .. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html | ||
| Configuration | ||
| ============= | ||
| The CLI is configured via environment variables and command-line | ||
| options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. | ||
| 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: | ||
| Authentication using username/password is most commonly used: | ||
| * If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment | ||
| variable | ||
| * ``.`` (the current directory) | ||
| * ``$HOME/.config/openstack`` | ||
| * ``/etc/openstack`` | ||
| - For a local user, your configuration will look like the one below:: | ||
| The options you should set will depend on the configuration of your cloud and | ||
| the authentication mechanism(s) supported. For example, consider a cloud that | ||
| supports username/password authentication. Configuration for this cloud using a | ||
| ``clouds.yaml`` file would look like so: | ||
| .. code-block:: yaml | ||
| clouds: | ||
| my-cloud: | ||
| auth: | ||
| auth_url: '<url-to-openstack-identity>' | ||
| project_name: '<project-name>' | ||
| project_domain_name: '<project-domain-name>' | ||
| username: '<username>' | ||
| user_domain_name: '<user-domain-name>' | ||
| password: '<password>' # (optional) | ||
| region_name: '<region>' | ||
| The corresponding environment variables would look very similar: | ||
| .. code-block:: shell | ||
| export OS_AUTH_URL=<url-to-openstack-identity> | ||
| export OS_IDENTITY_API_VERSION=3 | ||
| export OS_REGION_NAME=<region> | ||
| export OS_PROJECT_NAME=<project-name> | ||
@@ -86,6 +111,9 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name> | ||
| The corresponding command-line options look very similar:: | ||
| Likewise, the corresponding command-line options would look very similar: | ||
| --os-auth-url <url> | ||
| --os-identity-api-version 3 | ||
| :: | ||
| openstack | ||
| --os-auth-url <url-to-openstack-identity> | ||
| --os-region <region> | ||
| --os-project-name <project-name> | ||
@@ -97,4 +125,37 @@ --os-project-domain-name <project-domain-name> | ||
| - For a federated user, your configuration will look the so:: | ||
| .. note:: | ||
| If a password is not provided above (in plaintext), you will be | ||
| interactively prompted to provide one securely. | ||
| Some clouds use federated authentication. If this is the case, your | ||
| configuration will be slightly more involved. For example, to configure | ||
| username/password authentication for a federated user using a ``clouds.yaml`` | ||
| file: | ||
| .. code-block:: yaml | ||
| clouds: | ||
| my-cloud: | ||
| auth: | ||
| auth_url: '<url-to-openstack-identity>' | ||
| project_name: '<project-name>' | ||
| project_domain_name: '<project-domain-name>' | ||
| username: '<username-in-idp>' | ||
| user_domain_name: '<user-domain-name>' | ||
| password: '<password-in-idp>' | ||
| identity_provider: '<the-desired-idp-in-keystone>' | ||
| client_id: '<the-client-id-configured-in-the-idp>' | ||
| client_secret: '<the-client-secret-configured-in-the-idp>' | ||
| openid_scope: '<the-scopes-of-desired-attributes-to-claim-from-idp>' | ||
| protocol: '<the-protocol-used-in-the-apache2-oidc-proxy>' | ||
| access_token_type: '<the-access-token-type-used-by-your-idp>' | ||
| discovery_endpoint: '<the-well-known-endpoint-of-the-idp>' | ||
| auth_type: 'v3oidcpassword' | ||
| region_name: '<region>' | ||
| The corresponding environment variables would look very similar: | ||
| .. code-block:: shell | ||
| export OS_PROJECT_NAME=<project-name> | ||
@@ -104,3 +165,2 @@ export OS_PROJECT_DOMAIN_NAME=<project-domain-name> | ||
| export OS_IDENTITY_API_VERSION=3 | ||
| export OS_AUTH_PLUGIN=openid | ||
| export OS_AUTH_TYPE=v3oidcpassword | ||
@@ -117,4 +177,6 @@ export OS_USERNAME=<username-in-idp> | ||
| The corresponding command-line options look very similar:: | ||
| Likewise, the corresponding command-line options would look very similar: | ||
| .. code-block:: shell | ||
| --os-project-name <project-name> | ||
@@ -136,3 +198,39 @@ --os-project-domain-name <project-domain-name> | ||
| 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 <https://bugs.launchpad.net/python-openstackclient>`_ | ||
| * `Code Review <https://review.opendev.org/#/q/status:open+project:openstack/openstacksdk,n,z>`_ | ||
| * `Documentation <https://docs.openstack.org/python-openstackclient/latest/>`_ | ||
| * `PyPi <https://pypi.org/project/python-openstackclient>`_ | ||
| * `Mailing list <https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/>`_ | ||
| * `Release Notes <https://docs.openstack.org/releasenotes/python-openstackclient>`_ | ||
| * `IRC (#openstack-sdks on OFTC (irc.oftc.net)) <irc://irc.oftc.net/openstack-sdks>`_ |
+1
-1
@@ -10,3 +10,3 @@ # Requirements lower bounds listed here are our best effort to keep them up to | ||
| iso8601>=0.1.11 # MIT | ||
| openstacksdk>=3.3.0 # Apache-2.0 | ||
| openstacksdk>=4.5.0 # Apache-2.0 | ||
| osc-lib>=2.3.0 # Apache-2.0 | ||
@@ -13,0 +13,0 @@ oslo.i18n>=3.15.3 # Apache-2.0 |
+10
-8
@@ -10,3 +10,3 @@ [metadata] | ||
| home_page = https://docs.openstack.org/python-openstackclient/latest/ | ||
| python_requires = >=3.9 | ||
| python_requires = >=3.10 | ||
| classifier = | ||
@@ -20,6 +20,6 @@ Environment :: OpenStack | ||
| Programming Language :: Python :: 3 | ||
| Programming Language :: Python :: 3.9 | ||
| Programming Language :: Python :: 3.10 | ||
| Programming Language :: Python :: 3.11 | ||
| Programming Language :: Python :: 3.12 | ||
| Programming Language :: Python :: 3.13 | ||
@@ -78,2 +78,4 @@ [files] | ||
| console_connection_show = openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation | ||
| flavor_create = openstackclient.compute.v2.flavor:CreateFlavor | ||
@@ -772,8 +774,8 @@ flavor_delete = openstackclient.compute.v2.flavor:DeleteFlavor | ||
| volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot | ||
| volume_snapshot_create = openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot | ||
| volume_snapshot_delete = openstackclient.volume.v3.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_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 | ||
@@ -797,3 +799,3 @@ volume_type_create = openstackclient.volume.v3.volume_type:CreateVolumeType | ||
| volume_service_list = openstackclient.volume.v3.service:ListService | ||
| volume_service_set = openstackclient.volume.v2.service:SetService | ||
| volume_service_set = openstackclient.volume.v3.service:SetService | ||
@@ -800,0 +802,0 @@ volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
6569848
1.2%1403
0.72%146496
1.2%