diff options
255 files changed, 6930 insertions, 3935 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 09a9172..718c80f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: mixed-line-ending args: ['--fix', 'lf'] exclude: '.*\.(svg)$' - - id: check-byte-order-marker + - id: fix-byte-order-marker - id: check-executables-have-shebangs - id: check-merge-conflict - id: debug-statements @@ -15,27 +15,14 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.15.1 hooks: - - id: ruff + - id: ruff-check args: ['--fix', '--unsafe-fixes'] - id: ruff-format - repo: https://opendev.org/openstack/hacking - rev: 7.0.0 + rev: 8.0.0 hooks: - id: hacking additional_dependencies: [] exclude: '^(doc|releasenotes)/.*$' - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 - hooks: - - id: mypy - additional_dependencies: - - types-requests - # keep this in-sync with '[mypy] exclude' in 'setup.cfg' - exclude: | - (?x)( - doc/.* - | examples/.* - | releasenotes/.* - ) @@ -173,6 +173,8 @@ - release-notes-jobs-python3 check: jobs: + - openstackclient-check-plugins: + voting: true - osc-build-image: voting: false - osc-functional-devstack @@ -8,4 +8,4 @@ libffi-dev [compile test platform:dpkg] libssl-dev [compile test platform:dpkg] python3-dev [compile test platform:dpkg] python3-devel [compile test platform:rpm] -libpcre3-dev [test platform:dpkg] +libpcre2-dev [test platform:dpkg] diff --git a/debian/changelog b/debian/changelog index b49ae48..7a19b8e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +python-openstackclient (9.0.0-1) experimental; urgency=medium + + * New upstream release. + * Fixed (build-)depends for this release. + * Removed patches applied upstream: + - Fix_openstack_quota_show_without_cinder.patch + - add-missing-backup_id-field.patch + + -- Thomas Goirand <zigo@debian.org> Fri, 27 Feb 2026 19:32:22 +0100 + python-openstackclient (8.2.0-9) unstable; urgency=medium * Correctly (build-)depends on python3-openstacksdk >= 4.7.2. diff --git a/debian/control b/debian/control index b828212..a09784a 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,7 @@ Build-Depends-Indep: python3-aodhclient <!nodoc>, python3-barbicanclient <!nodoc>, python3-cinderclient, - python3-cliff, + python3-cliff (>= 4.13.0), python3-cloudkittyclient <!nodoc>, python3-coverage, python3-cryptography, @@ -90,7 +90,7 @@ Package: python3-openstackclient Architecture: all Depends: python3-cinderclient, - python3-cliff, + python3-cliff (>= 4.13.0), python3-cryptography, python3-glanceclient, python3-iso8601, diff --git a/debian/patches/Fix_openstack_quota_show_without_cinder.patch b/debian/patches/Fix_openstack_quota_show_without_cinder.patch deleted file mode 100644 index 3e6eebf..0000000 --- a/debian/patches/Fix_openstack_quota_show_without_cinder.patch +++ /dev/null @@ -1,106 +0,0 @@ -Description: Fix openstack quota show without cinder - Per this Debian bug [1], 'openstack quota show --default' fails when - cinder is NOT installed. This is also true of other services. - . - [1] https://bugs.debian.org/1109288 -Author: Svein-Erik Skjelbred <svein-erik@skjelbred.com> -Date: Mon, 29 Sep 2025 14:59:03 +0200 -Change-Id: I361da44b9f1d09ba3a454632d41e2110a3815395 -Signed-off-by: Svein-Erik Skjelbred <svein-erik@skjelbred.com> -Signed-off-by: Thomas Goirand <zigo@debian.org> -Signed-off-by: Stephen Finucane <stephenfin@redhat.com> -Forwarded: https://review.opendev.org/c/openstack/python-openstackclient/+/962838 -Bug-Debian: https://bugs.debian.org/1109288 - -diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py -index f706a59..41c57e6 100644 ---- a/openstackclient/common/quota.py -+++ b/openstackclient/common/quota.py -@@ -746,21 +746,32 @@ - # values if the project or class does not exist. This is expected - # behavior. However, we have already checked for the presence of the - # project above so it shouldn't be an issue. -- if parsed_args.service in {'all', 'compute'}: -+ if parsed_args.service == 'compute' or ( -+ parsed_args.service == 'all' -+ and self.app.client_manager.is_compute_endpoint_enabled() -+ ): - compute_quota_info = get_compute_quotas( - self.app, - project, - detail=parsed_args.usage, - default=parsed_args.default, - ) -- if parsed_args.service in {'all', 'volume'}: -+ -+ if parsed_args.service == 'volume' or ( -+ parsed_args.service == 'all' -+ and self.app.client_manager.is_volume_endpoint_enabled() -+ ): - volume_quota_info = get_volume_quotas( - self.app, - project, - detail=parsed_args.usage, - default=parsed_args.default, - ) -- if parsed_args.service in {'all', 'network'}: -+ -+ if parsed_args.service == 'network' or ( -+ parsed_args.service == 'all' -+ and self.app.client_manager.is_network_endpoint_enabled() -+ ): - network_quota_info = get_network_quotas( - self.app, - project, -@@ -906,12 +917,18 @@ - ) - - # compute quotas -- if parsed_args.service in {'all', 'compute'}: -+ if parsed_args.service == 'compute' or ( -+ parsed_args.service == 'all' -+ and self.app.client_manager.is_compute_endpoint_enabled() -+ ): - compute_client = self.app.client_manager.compute - compute_client.revert_quota_set(project.id) - - # volume quotas -- if parsed_args.service in {'all', 'volume'}: -+ if parsed_args.service == 'volume' or ( -+ parsed_args.service == 'all' -+ and self.app.client_manager.is_volume_endpoint_enabled() -+ ): - volume_client = self.app.client_manager.sdk_connection.volume - volume_client.revert_quota_set(project.id) - -diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py -index 53df4da..a2418d0 100644 ---- a/openstackclient/tests/unit/common/test_quota.py -+++ b/openstackclient/tests/unit/common/test_quota.py -@@ -1041,6 +1041,26 @@ - ) - self.assertNotCalled(self.network_client.get_quota_default) - -+ def test_quota_show__missing_services(self): -+ self.app.client_manager.compute_endpoint_enabled = False -+ self.app.client_manager.volume_endpoint_enabled = False -+ self.app.client_manager.network_endpoint_enabled = False -+ -+ arglist = [ -+ self.projects[0].name, -+ ] -+ verifylist = [ -+ ('service', 'all'), -+ ('project', self.projects[0].name), -+ ] -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ -+ self.cmd.take_action(parsed_args) -+ -+ self.compute_client.get_quota_set.assert_not_called() -+ self.volume_sdk_client.get_quota_set.assert_not_called() -+ self.network_client.get_quota.assert_not_called() -+ - def test_quota_show__with_compute(self): - arglist = [ - '--compute', diff --git a/debian/patches/add-missing-backup_id-field.patch b/debian/patches/add-missing-backup_id-field.patch deleted file mode 100644 index 5efff0e..0000000 --- a/debian/patches/add-missing-backup_id-field.patch +++ /dev/null @@ -1,42 +0,0 @@ -Description: Add missing backup_id field -Author: Luan Utimura <luan.utimura@luizalabs.com> -Bug-Debian: https://bugs.debian.org/1123450 -Origin: upstream, https://review.opendev.org/c/openstack/python-openstackclient/+/971255 -Last-Update: 2025-12-23 - -Index: python-openstackclient/openstackclient/tests/unit/volume/v3/test_volume.py -=================================================================== ---- python-openstackclient.orig/openstackclient/tests/unit/volume/v3/test_volume.py -+++ python-openstackclient/openstackclient/tests/unit/volume/v3/test_volume.py -@@ -37,6 +37,7 @@ class TestVolumeCreate(volume_fakes.Test - columns = ( - 'attachments', - 'availability_zone', -+ 'backup_id', - 'bootable', - 'cluster_name', - 'consistencygroup_id', -@@ -78,6 +79,7 @@ class TestVolumeCreate(volume_fakes.Test - self.datalist = ( - self.volume.attachments, - self.volume.availability_zone, -+ self.volume.backup_id, - self.volume.is_bootable, - self.volume.cluster_name, - self.volume.consistency_group_id, -@@ -2011,6 +2013,7 @@ class TestVolumeShow(volume_fakes.TestVo - self.columns = ( - 'attachments', - 'availability_zone', -+ 'backup_id', - 'bootable', - 'cluster_name', - 'consistencygroup_id', -@@ -2045,6 +2048,7 @@ class TestVolumeShow(volume_fakes.TestVo - self.data = ( - self.volume.attachments, - self.volume.availability_zone, -+ self.volume.backup_id, - self.volume.is_bootable, - self.volume.cluster_name, - self.volume.consistency_group_id, diff --git a/debian/patches/series b/debian/patches/series index 27d569e..bb74ab0 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1 @@ package-all-files.patch -Fix_openstack_quota_show_without_cinder.patch -add-missing-backup_id-field.patch diff --git a/doc/requirements.txt b/doc/requirements.txt index 79e3ded..05a9bfa 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -4,7 +4,7 @@ sphinx>=2.0.0,!=2.1.0 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD # redirect tests in docs -whereto>=0.4.0 # Apache-2.0 +whereto>=0.5.0 # Apache-2.0 # Install these to generate sphinx autodocs aodhclient>=0.9.0 # Apache-2.0 diff --git a/examples/common.py b/examples/common.py index 650139e..8213f4b 100755 --- a/examples/common.py +++ b/examples/common.py @@ -264,7 +264,7 @@ def main(opts, run): if dump_stack_trace: _logger.error(traceback.format_exc(e)) else: - _logger.error('Exception raised: ' + str(e)) + _logger.error('Exception raised: %s', e) return 1 diff --git a/hacking/checks.py b/hacking/checks.py new file mode 100644 index 0000000..0eb485e --- /dev/null +++ b/hacking/checks.py @@ -0,0 +1,179 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import ast +import os +import re + +from hacking import core + +""" +Guidelines for writing new hacking checks + + - Use only for python-openstackclient specific tests. OpenStack general tests + should be submitted to the common 'hacking' module. + - Pick numbers in the range O4xx. Find the current test with the highest + allocated number and then pick the next value. +""" + + +@core.flake8ext +def assert_no_oslo(logical_line): + """Check for use of oslo libraries. + + O400 + """ + if re.match(r'(from|import) oslo_.*', logical_line): + yield (0, "0400: oslo libraries should not be used in SDK projects") + + +@core.flake8ext +def assert_no_duplicated_setup(logical_line, filename): + """Check for use of various unnecessary test duplications. + + O401 + """ + if os.path.join('openstackclient', 'tests', 'unit') not in filename: + return + + if re.match(r'self.app = .*\(self.app, self.namespace\)', logical_line): + yield ( + 0, + 'O401: It is not necessary to create dummy Namespace objects', + ) + + if os.path.basename(filename) != 'fakes.py': + if re.match( + r'self.[a-z_]+_client = self.app.client_manager.*', logical_line + ): + yield ( + 0, + "O401: Aliases for mocks of the service client are already " + "provided by the respective service's FakeClientMixin class", + ) + + if match := re.match( + r'self.app.client_manager.([a-z_]+) = mock.Mock', logical_line + ): + service = match.group(1) + if service == 'auth_ref': + return + yield ( + 0, + f"O401: client_manager.{service} mocks are already provided " + f"by the {service} service's FakeClientMixin class", + ) + + +@core.flake8ext +def assert_use_of_client_aliases(logical_line): + """Ensure we use $service_client instead of $sdk_connection.service. + + O402 + """ + # we should expand the list of services as we drop legacy clients + if match := re.match( + r'self\.app\.client_manager\.sdk_connnection\.(compute|network|image)', + logical_line, + ): + service = match.group(1) + yield (0, f"0402: prefer {service}_client to sdk_connection.{service}") + + if match := re.match( + r'(self\.app\.client_manager\.(compute|network|image)+\.[a-z_]+) = mock.Mock', # noqa: E501 + logical_line, + ): + yield ( + 0, + f"O402: {match.group(1)} is already a mock: there's no need to " + f"assign a new mock.Mock instance.", + ) + + if match := re.match( + r'(self\.(compute|network|image)_client\.[a-z_]+) = mock.Mock', + logical_line, + ): + yield ( + 0, + f"O402: {match.group(1)} is already a mock: there's no need to " + f"assign a new mock.Mock instance.", + ) + + +class SDKProxyFindChecker(ast.NodeVisitor): + """NodeVisitor to find ``*_client.find_*`` statements.""" + + def __init__(self): + self.error = False + + def visit_Call(self, node): + # No need to keep visiting the AST if we already found something. + if self.error: + return + + self.generic_visit(node) + + if not ( + isinstance(node.func, ast.Attribute) + and node.func.attr.startswith('find_') # and + # isinstance(node.func.value, ast.Attribute) and + # node.func.value.attr.endswith('_client') + ): + # print(f'skipping: got {node.func}') + return + + if not ( + ( + # handle calls like 'identity_client.find_project' + isinstance(node.func.value, ast.Name) + and node.func.value.id.endswith('client') + ) + or ( + # handle calls like 'self.app.client_manager.image.find_image' + isinstance(node.func.value, ast.Attribute) + and node.func.value.attr + in ('identity', 'network', 'image', 'compute') + ) + ): + return + + if not any(kw.arg == 'ignore_missing' for kw in node.keywords): + self.error = True + + +@core.flake8ext +def assert_find_ignore_missing_kwargs(logical_line, filename): + """Ensure ignore_missing is always used for ``find_*`` SDK proxy calls. + + Okay: self.compute_client.find_server(foo, ignore_missing=True) + Okay: self.image_client.find_server(foo, ignore_missing=False) + Okay: self.volume_client.volumes.find(name='foo') + O403: self.network_client.find_network(parsed_args.network) + O403: self.compute_client.find_flavor(flavor_id, get_extra_specs=True) + """ + if 'tests' in filename: + return + + checker = SDKProxyFindChecker() + try: + parsed_logical_line = ast.parse(logical_line) + except SyntaxError: + # let flake8 catch this itself + # https://github.com/PyCQA/flake8/issues/1948 + return + checker.visit(parsed_logical_line) + if checker.error: + yield ( + 0, + 'O403: Calls to find_* proxy methods must explicitly set ' + 'ignore_missing', + ) diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index fd941e4..933b01b 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -256,7 +256,10 @@ class APIv1(api.BaseAPI): # object's name in the container. object_name_str = name if name else object - full_url = f"{urllib.parse.quote(container)}/{urllib.parse.quote(object_name_str)}" + full_url = ( + f"{urllib.parse.quote(container)}/" + f"{urllib.parse.quote(object_name_str)}" + ) with open(object, 'rb') as f: response = self.create( full_url, diff --git a/openstackclient/command.py b/openstackclient/command.py new file mode 100644 index 0000000..124d755 --- /dev/null +++ b/openstackclient/command.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cliff import lister +from cliff import show +from osc_lib.command import command + +from openstackclient import shell + + +class Command(command.Command): + app: shell.OpenStackShell + + +class Lister(Command, lister.Lister): ... + + +class ShowOne(Command, show.ShowOne): ... diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index 61f77cc..6f5e4fd 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -17,9 +17,9 @@ import copy import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index a0455bf..51911aa 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -15,15 +15,25 @@ """Manage access to the clients, including authenticating when needed.""" +import argparse +from collections.abc import Callable import importlib import logging import sys import typing as ty +from osc_lib.cli import client_config from osc_lib import clientmanager from osc_lib import shell import stevedore +if ty.TYPE_CHECKING: + from keystoneauth1 import access as ksa_access + from openstack.compute.v2 import _proxy as compute_proxy + from openstack.image.v2 import _proxy as image_proxy + from openstack.network.v2 import _proxy as network_proxy + + from openstackclient.api import object_store_v1 LOG = logging.getLogger(__name__) @@ -40,11 +50,23 @@ class ClientManager(clientmanager.ClientManager): in osc-lib so we need to maintain a transition period. """ - # A simple incrementing version for the plugin to know what is available - PLUGIN_INTERFACE_VERSION = "2" - - # Let the commands set this - _auth_required = False + if ty.TYPE_CHECKING: + # we know this will be set by us and will not be nullable + auth_ref: ksa_access.AccessInfo + + # this is a hack to keep mypy happy: the actual attributes are set in + # get_plugin_modules below + # TODO(stephenfin): Change the types of identity and volume once we've + # migrated everything to SDK. Hopefully by then we'll have figured out + # how to statically distinguish between the v2 and v3 versions of both + # services... + # TODO(stephenfin): We also need to migrate object storage... + compute: compute_proxy.Proxy + identity: ty.Any + image: image_proxy.Proxy + network: network_proxy.Proxy + object_store: object_store_v1.APIv1 + volume: ty.Any def __init__( self, @@ -81,6 +103,12 @@ class ClientManager(clientmanager.ClientManager): self._auth_required and self._cli_options._openstack_config is not None ): + if not isinstance( + self._cli_options._openstack_config, client_config.OSC_Config + ): + # programmer error + raise TypeError('unexpected type for _openstack_config') + self._cli_options._openstack_config._pw_callback = ( shell.prompt_for_password ) @@ -107,6 +135,13 @@ class ClientManager(clientmanager.ClientManager): self._cli_options.config['auth_type'] = self._original_auth_type del self._cli_options.config['auth']['token'] del self._cli_options.config['auth']['endpoint'] + + if not isinstance( + self._cli_options._openstack_config, client_config.OSC_Config + ): + # programmer error + raise TypeError('unexpected type for _openstack_config') + self._cli_options._auth = ( self._cli_options._openstack_config.load_auth_plugin( self._cli_options.config, @@ -144,11 +179,25 @@ class ClientManager(clientmanager.ClientManager): # Plugin Support +ArgumentParserT = ty.TypeVar('ArgumentParserT', bound=argparse.ArgumentParser) + + +@ty.runtime_checkable # Optional: allows usage with isinstance() +class PluginModule(ty.Protocol): + DEFAULT_API_VERSION: str + API_VERSION_OPTION: str + API_NAME: str + API_VERSIONS: tuple[str] + + make_client: Callable[..., ty.Any] + build_option_parser: Callable[[ArgumentParserT], ArgumentParserT] + check_api_version: Callable[[str], bool] + def _on_load_failure_callback( manager: stevedore.ExtensionManager, ep: importlib.metadata.EntryPoint, - err: Exception, + err: BaseException, ) -> None: sys.stderr.write( f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n" @@ -158,36 +207,25 @@ def _on_load_failure_callback( def get_plugin_modules(group): """Find plugin entry points""" mod_list = [] + mgr: stevedore.ExtensionManager[PluginModule] mgr = stevedore.ExtensionManager( group, on_load_failure_callback=_on_load_failure_callback ) for ep in mgr: LOG.debug('Found plugin %s', ep.name) - # Different versions of stevedore use different - # implementations of EntryPoint from other libraries, which - # are not API-compatible. - try: - module_name = ep.entry_point.module_name - except AttributeError: - try: - module_name = ep.entry_point.module - except AttributeError: - module_name = ep.entry_point.value + module_name = ep.entry_point.module try: module = importlib.import_module(module_name) except Exception as err: sys.stderr.write( - f"WARNING: Failed to import plugin {ep.group}:{ep.name}: " - f"{err}.\n" + f"WARNING: Failed to import plugin " + f"{ep.module_name}:{ep.name}: {err}.\n" ) continue mod_list.append(module) - init_func = getattr(module, 'Initialize', None) - if init_func: - init_func('x') # Add the plugin to the ClientManager setattr( diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py index 418f7fd..4637ad2 100644 --- a/openstackclient/common/configuration.py +++ b/openstackclient/common/configuration.py @@ -14,8 +14,8 @@ """Configuration action implementations""" from keystoneauth1.loading import base -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ REDACTED = "<redacted>" diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index 42c2e9b..3f9b257 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -17,9 +17,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index 0c92805..6512e0f 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -17,9 +17,9 @@ import itertools -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index d4a5b95..0985107 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -17,9 +17,9 @@ import sys -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -48,7 +48,9 @@ class ListCommand(command.Lister): columns = ('Command Group', 'Commands') if parsed_args.group: - groups = (group for group in groups if parsed_args.group in group) + groups = sorted( + group for group in groups if parsed_args.group in group + ) commands = [] for group in groups: @@ -59,7 +61,7 @@ class ListCommand(command.Lister): # TODO(bapalm): Fix this when cliff properly supports # handling the detection rather than using the hard-code below. if parsed_args.formatter == 'table': - command_names = utils.format_list(command_names, "\n") + command_names = utils.format_list(command_names, "\n") # type: ignore commands.append((group, command_names)) diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 84a0735..444f23e 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -20,8 +20,8 @@ import queue import typing as ty from cliff.formatters import table -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -90,17 +90,19 @@ class ProjectCleanup(command.Command): return parser def take_action(self, parsed_args): - sdk = self.app.client_manager.sdk_connection + connection = self.app.client_manager.sdk_connection if parsed_args.auth_project: - project_connect = sdk + # is we've got a project already configured, use the connection + # as-is + pass elif parsed_args.project: - project = sdk.identity.find_project( + project = connection.identity.find_project( name_or_id=parsed_args.project, ignore_missing=False ) - project_connect = sdk.connect_as_project(project) + connection = connection.connect_as_project(project) - if project_connect: + if connection: status_queue: queue.Queue[ty.Any] = queue.Queue() parsed_args.max_width = int( os.environ.get('CLIFF_MAX_TERM_WIDTH', 0) @@ -120,7 +122,7 @@ class ProjectCleanup(command.Command): if parsed_args.updated_before: filters['updated_at'] = parsed_args.updated_before - project_connect.project_cleanup( + connection.project_cleanup( dry_run=True, status_queue=status_queue, filters=filters, @@ -150,7 +152,7 @@ class ProjectCleanup(command.Command): self.log.warning(_('Deleting resources')) - project_connect.project_cleanup( + connection.project_cleanup( dry_run=False, status_queue=status_queue, filters=filters, diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index f706a59..0e7edf4 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -21,10 +21,10 @@ import sys import typing as ty from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -272,7 +272,10 @@ class ListQuota(command.Lister): sdk_exceptions.NotFoundException, ) as exc: # Project not found, move on to next one - LOG.warning(f"Project {project_id} not found: {exc}") + LOG.warning( + 'Project %(project_id)s not found: %(exc)s', + {'project_id': project_id, 'exc': exc}, + ) continue project_result = _xform_get_quota( @@ -334,7 +337,10 @@ class ListQuota(command.Lister): sdk_exceptions.NotFoundException, ) as exc: # Project not found, move on to next one - LOG.warning(f"Project {project_id} not found: {exc}") + LOG.warning( + 'Project %(project_id)s not found: %(exc)s', + {'project_id': project_id, 'exc': exc}, + ) continue project_result = _xform_get_quota( @@ -389,7 +395,10 @@ class ListQuota(command.Lister): sdk_exceptions.ForbiddenException, ) as exc: # Project not found, move on to next one - LOG.warning(f"Project {project_id} not found: {exc}") + LOG.warning( + 'Project %(project_id)s not found: %(exc)s', + {'project_id': project_id, 'exc': exc}, + ) continue project_result = _xform_get_quota( @@ -746,21 +755,32 @@ and ``server-group-members`` output for a given quota class.""" # values if the project or class does not exist. This is expected # behavior. However, we have already checked for the presence of the # project above so it shouldn't be an issue. - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_quota_info = get_compute_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'volume'}: + + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_quota_info = get_volume_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'network'}: + + if parsed_args.service == 'network' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_network_endpoint_enabled() + ): network_quota_info = get_network_quotas( self.app, project, @@ -786,20 +806,25 @@ and ``server-group-members`` output for a given quota class.""" info.update(volume_quota_info) info.update(network_quota_info) - # Map the internal quota names to the external ones - # COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips, - # secgroup-rules and secgroups as dict value, so when - # neutron is enabled, quotas of these three resources - # in nova will be replaced by neutron's. - for k, v in itertools.chain( - COMPUTE_QUOTAS.items(), - NOVA_NETWORK_QUOTAS.items(), - VOLUME_QUOTAS.items(), - NETWORK_QUOTAS.items(), - ): - if not k == v and info.get(k) is not None: - info[v] = info[k] - info.pop(k) + def _normalize_names(section: dict) -> None: + # Map the internal quota names to the external ones + # COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips, + # secgroup-rules and secgroups as dict value, so when + # neutron is enabled, quotas of these three resources + # in nova will be replaced by neutron's. + for k, v in itertools.chain( + COMPUTE_QUOTAS.items(), + NOVA_NETWORK_QUOTAS.items(), + VOLUME_QUOTAS.items(), + NETWORK_QUOTAS.items(), + ): + if not k == v and section.get(k) is not None: + section[v] = section.pop(k) + + _normalize_names(info) + if parsed_args.usage: + _normalize_names(info["reservation"]) + _normalize_names(info["usage"]) # Remove the 'id' field since it's not very useful if 'id' in info: @@ -906,12 +931,18 @@ class DeleteQuota(command.Command): ) # compute quotas - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_client = self.app.client_manager.compute compute_client.revert_quota_set(project.id) # volume quotas - if parsed_args.service in {'all', 'volume'}: + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_client = self.app.client_manager.sdk_connection.volume volume_client.revert_quota_set(project.id) diff --git a/openstackclient/common/versions.py b/openstackclient/common/versions.py index 6622338..dfd84e0 100644 --- a/openstackclient/common/versions.py +++ b/openstackclient/common/versions.py @@ -14,8 +14,7 @@ """Versions Action Implementation""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 5bc6603..71b68d4 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index e649607..f8c3d96 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -19,20 +19,21 @@ import logging import typing as ty +from cliff import columns from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -_aggregate_formatters = { +_aggregate_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = { 'Hosts': format_columns.ListColumn, 'Metadata': format_columns.DictColumn, 'hosts': format_columns.ListColumn, @@ -438,15 +439,15 @@ class CacheImageForAggregate(command.Command): ) raise exceptions.CommandError(msg) + image_client = self.app.client_manager.sdk_connection.image + aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) images = [] for img in parsed_args.image: - image = self.app.client_manager.sdk_connection.image.find_image( - img, ignore_missing=False - ) + image = image_client.find_image(img, ignore_missing=False) images.append(image.id) compute_client.aggregate_precache_images(aggregate.id, images) diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index bcabcd2..cbcce4b 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -16,9 +16,9 @@ """Compute v2 Console action implementations""" from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -64,13 +64,15 @@ class ShowConsoleLog(command.Command): output = compute_client.get_server_console_output( server.id, length=parsed_args.lines ) - data = None + data: str | None = None if output: data = output.get('output', None) if data and data[-1] != '\n': data += '\n' - self.app.stdout.write(data) + + if data: + self.app.stdout.write(data) class ShowConsoleURL(command.ShowOne): diff --git a/openstackclient/compute/v2/console_connection.py b/openstackclient/compute/v2/console_connection.py index d90b2ce..97eb1a8 100644 --- a/openstackclient/compute/v2/console_connection.py +++ b/openstackclient/compute/v2/console_connection.py @@ -13,9 +13,9 @@ """Compute v2 Console auth token implementations.""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index c0e043e..de3a710 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -21,10 +21,10 @@ from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index aa1d1a5..5802367 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -16,9 +16,9 @@ """Host action implementations""" from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index 0a6cc53..9e1b265 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -21,10 +21,10 @@ import re from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py index 3b9a686..d151b41 100644 --- a/openstackclient/compute/v2/hypervisor_stats.py +++ b/openstackclient/compute/v2/hypervisor_stats.py @@ -13,9 +13,9 @@ """Hypervisor Stats action implementations""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 97dd04f..b774469 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -22,10 +22,10 @@ import os from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives import serialization from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index e14392a..a52657c 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -21,6 +21,7 @@ import getpass import json import logging import os +import typing as ty from cliff import columns as cliff_columns import iso8601 @@ -28,11 +29,11 @@ from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from openstackclient.api import compute_v2 +from openstackclient import command from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ @@ -44,7 +45,7 @@ LOG = logging.getLogger(__name__) IMAGE_STRING_FOR_BFV = 'N/A (booted from volume)' -class PowerStateColumn(cliff_columns.FormattableColumn): +class PowerStateColumn(cliff_columns.FormattableColumn[int]): """Generate a formatted string of a server's power state.""" power_states = [ @@ -65,7 +66,7 @@ class PowerStateColumn(cliff_columns.FormattableColumn): return 'N/A' -class AddressesColumn(cliff_columns.FormattableColumn): +class AddressesColumn(cliff_columns.FormattableColumn[ty.Any]): """Generate a formatted string of a server's addresses.""" def human_readable(self): @@ -86,7 +87,7 @@ class AddressesColumn(cliff_columns.FormattableColumn): } -class HostColumn(cliff_columns.FormattableColumn): +class HostColumn(cliff_columns.FormattableColumn[str | None]): """Generate a formatted string of a hostname.""" def human_readable(self): @@ -183,9 +184,17 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'updated_at': 'updated', 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', - 'pinned_availability_zone': 'pinned_availability_zone', - 'scheduler_hints': 'scheduler_hints', } + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if sdk_utils.supports_microversion(compute_client, '2.96'): + column_map['pinned_availability_zone'] = 'pinned_availability_zone' + + # NOTE(ratailor): microversion 2.100 introduces + # scheduler_hints support + if sdk_utils.supports_microversion(compute_client, '2.100'): + column_map['scheduler_hints'] = 'scheduler_hints' + # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates ignored_columns = { @@ -240,6 +249,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): if not sdk_utils.supports_microversion(compute_client, '2.100'): info.pop('scheduler_hints', None) + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if not sdk_utils.supports_microversion(compute_client, '2.96'): + info.pop('pinned_availability_zone', None) + # Convert the image blob to a name image_info = info.get('image', {}) if image_info and any(image_info.values()): @@ -326,10 +340,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info['OS-EXT-STS:power_state'] ) - if 'scheduler_hints' in info: - info['scheduler_hints'] = format_columns.DictListColumn( - info.pop('scheduler_hints', {}), - ) + if sdk_utils.supports_microversion(compute_client, '2.100'): + if 'scheduler_hints' in info: + info['scheduler_hints'] = format_columns.DictListColumn( + info.pop('scheduler_hints', {}), + ) return info @@ -2166,7 +2181,9 @@ class CreateServerDump(command.Command): def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for name_or_id in parsed_args.server: - server = compute_client.find_server(name_or_id) + server = compute_client.find_server( + name_or_id, ignore_missing=False + ) server.trigger_crash_dump(compute_client) @@ -2209,24 +2226,49 @@ class DeleteServer(command.Command): self.app.stdout.flush() compute_client = self.app.client_manager.compute + + deleted_servers = [] for server in parsed_args.server: - server_obj = compute_client.find_server( - server, - ignore_missing=False, - all_projects=parsed_args.all_projects, - ) + try: + server_obj = compute_client.find_server( + server, + ignore_missing=False, + all_projects=parsed_args.all_projects, + ) - compute_client.delete_server(server_obj, force=parsed_args.force) + compute_client.delete_server( + server_obj, force=parsed_args.force + ) + deleted_servers.append(server_obj) + except Exception as e: + LOG.error( + _( + "Failed to delete server with " + "name or ID '%(server)s': %(e)s" + ), + {'server': server, 'e': e}, + ) - if parsed_args.wait: + if parsed_args.wait: + for server_obj in deleted_servers: try: compute_client.wait_for_delete( server_obj, callback=_show_progress ) except sdk_exceptions.ResourceTimeout: msg = _('Error deleting server: %s') % server_obj.id + deleted_servers.remove(server_obj) raise exceptions.CommandError(msg) + fails = len(parsed_args.server) - len(deleted_servers) + if fails > 0: + total = len(parsed_args.server) + msg = _("%(fails)s of %(total)s servers failed to delete.") % { + 'fails': fails, + 'total': total, + } + raise exceptions.CommandError(msg) + class PercentAction(argparse.Action): def __init__( @@ -2838,18 +2880,21 @@ class ListServer(command.Lister): if parsed_args.long: columns += ( 'availability_zone', - 'pinned_availability_zone', 'hypervisor_hostname', 'metadata', - 'scheduler_hints', ) column_headers += ( 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', - 'Scheduler Hints', ) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) + + if sdk_utils.supports_microversion(compute_client, '2.100'): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) if parsed_args.all_projects: columns += ('project_id',) @@ -2887,10 +2932,11 @@ class ListServer(command.Lister): column_headers += ('Availability Zone',) if c in ( 'pinned_availability_zone', - "Pinned Availability Zone", + 'Pinned Availability Zone', ): - columns += ('Pinned Availability Zone',) - column_headers += ('Pinned Availability Zone',) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) if c in ('Host', "host"): columns += ('hypervisor_hostname',) column_headers += ('Host',) @@ -2901,8 +2947,11 @@ class ListServer(command.Lister): 'scheduler_hints', "Scheduler Hints", ): - columns += ('scheduler_hints',) - column_headers += ('Scheduler Hints',) + if sdk_utils.supports_microversion( + compute_client, '2.100' + ): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) # remove duplicates column_headers = tuple(dict.fromkeys(column_headers)) @@ -4218,7 +4267,7 @@ release the new server and restart the old one.""" metavar='<server>', help=_('Server (name or ID)'), ) - phase_group = parser.add_mutually_exclusive_group() + phase_group = parser.add_mutually_exclusive_group(required=True) phase_group.add_argument( '--flavor', metavar='<flavor>', @@ -4969,7 +5018,7 @@ class SshServer(command.Command): ) cmd = ' '.join(['ssh', ip_address] + args) - LOG.debug(f"ssh command: {cmd}") + LOG.debug('ssh command: %s', cmd) # we intentionally pass through user-provided arguments and run this in # the user's shell os.system(cmd) # noqa: S605 diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index b0395db..bb06f76 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -17,10 +17,10 @@ import importlib -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 3275df9..0e1e2b4 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -22,10 +22,10 @@ from cliff import columns import iso8601 from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index 7356748..b74c256 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -16,21 +16,23 @@ """Compute v2 Server Group action implementations""" import logging +import typing as ty +from cliff import columns from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -_formatters = { +_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = { 'member_ids': format_columns.ListColumn, 'policies': format_columns.ListColumn, 'rules': format_columns.DictColumn, diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index cb19173..26dbb4c 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -19,10 +19,10 @@ import importlib import logging from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index c356bd8..f2ea683 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -15,10 +15,10 @@ import uuid from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/compute/v2/server_volume.py b/openstackclient/compute/v2/server_volume.py index a27c63f..d92d137 100644 --- a/openstackclient/compute/v2/server_volume.py +++ b/openstackclient/compute/v2/server_volume.py @@ -15,10 +15,10 @@ """Compute v2 Server action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 3660ddd..b911835 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -18,10 +18,10 @@ import logging from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index d045c1e..3015a9e 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -15,19 +15,21 @@ """Usage action implementations""" +from collections.abc import Collection import datetime import functools +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ # TODO(stephenfin): This exists in a couple of places and should be moved to a # common module -class ProjectColumn(cliff_columns.FormattableColumn): +class ProjectColumn(cliff_columns.FormattableColumn[str]): """Formattable column for project column. Unlike the parent FormattableColumn class, the initializer of the class @@ -53,12 +55,12 @@ class ProjectColumn(cliff_columns.FormattableColumn): return project -class CountColumn(cliff_columns.FormattableColumn): +class CountColumn(cliff_columns.FormattableColumn[Collection[ty.Any]]): def human_readable(self): return len(self._value) if self._value is not None else None -class FloatColumn(cliff_columns.FormattableColumn): +class FloatColumn(cliff_columns.FormattableColumn[float]): def human_readable(self): return float(f"{self._value:.2f}") diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index c6e6cfc..f33c370 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -99,7 +99,8 @@ def find_service_sdk(identity_client, name_type_or_id): if next(services, None): msg = _( - "Multiple service matches found for '%(query)s', use an ID to be more specific." + "Multiple service matches found for '%(query)s', " + "use an ID to be more specific." ) % {"query": name_type_or_id} raise exceptions.CommandError(msg) @@ -255,6 +256,37 @@ def find_project(identity_client, name_or_id, domain_name_or_id=None): ) +def find_project_id_sdk( + identity_client, + name_or_id, + domain_name_or_id=None, + *, + validate_actor_existence=True, + validate_domain_actor_existence=None, +): + if domain_name_or_id is None: + return _find_sdk_id( + identity_client.find_project, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + ) + + if validate_domain_actor_existence is None: + validate_domain_actor_existence = validate_actor_existence + + domain_id = find_domain_id_sdk( + identity_client, + name_or_id=domain_name_or_id, + validate_actor_existence=validate_domain_actor_existence, + ) + return _find_sdk_id( + identity_client.find_project, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) + + def find_user(identity_client, name_or_id, domain_name_or_id=None): if domain_name_or_id is None: return _find_identity_resource( diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index ccac5d0..437cad2 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -14,19 +14,20 @@ """Identity v2 Service Catalog action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class EndpointsColumn(cliff_columns.FormattableColumn): +class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): if not self._value: return "" diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 25ee8a7..360a090 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -18,10 +18,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index db8efed..38f7f4e 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index a0c3c1e..bf19d7d 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -20,10 +20,10 @@ import logging from keystoneauth1 import exceptions as ks_exc from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -59,6 +59,7 @@ class CreateProject(command.ShowOne): parser.add_argument( '--property', metavar='<key=value>', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Add a property to <name> ' @@ -79,8 +80,8 @@ class CreateProject(command.ShowOne): if parsed_args.disable: enabled = False kwargs = {} - if parsed_args.property: - kwargs = parsed_args.property.copy() + if parsed_args.properties: + kwargs.update(parsed_args.properties) try: project = identity_client.tenants.create( @@ -105,7 +106,14 @@ class CreateProject(command.ShowOne): class DeleteProject(command.Command): - _description = _("Delete project(s)") + _description = _( + "Delete project(s). This command will remove specified " + "existing project(s) if an active user is authorized to do " + "this. If there are resources managed by other services " + "(for example, Nova, Neutron, Cinder) associated with " + "specified project(s), delete operation will proceed " + "regardless." + ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -223,6 +231,7 @@ class SetProject(command.Command): parser.add_argument( '--property', metavar='<key=value>', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Set a project property ' @@ -248,8 +257,8 @@ class SetProject(command.Command): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False - if parsed_args.property: - kwargs.update(parsed_args.property) + if parsed_args.properties: + kwargs.update(parsed_args.properties) if 'id' in kwargs: del kwargs['id'] if 'name' in kwargs: @@ -331,6 +340,7 @@ class UnsetProject(command.Command): parser.add_argument( '--property', metavar='<key>', + dest='properties', action='append', default=[], help=_( @@ -347,7 +357,7 @@ class UnsetProject(command.Command): parsed_args.project, ) kwargs = project._info - for key in parsed_args.property: + for key in parsed_args.properties: if key in kwargs: kwargs[key] = None identity_client.tenants.update(project.id, **kwargs) diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 1faab2e..e54c07a 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -18,10 +18,10 @@ import logging from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index 54d01b4..0aa800e 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -13,10 +13,10 @@ """Identity v2 Assignment action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ # noqa @@ -68,7 +68,7 @@ class ListRoleAssignment(command.Lister): parsed_args.user, ) elif parsed_args.authuser: - if auth_ref: + if auth_ref and auth_ref.user_id: user = utils.find_resource( identity_client.users, auth_ref.user_id ) @@ -80,7 +80,7 @@ class ListRoleAssignment(command.Lister): parsed_args.project, ) elif parsed_args.authproject: - if auth_ref: + if auth_ref and auth_ref.project_id: project = utils.find_resource( identity_client.projects, auth_ref.project_id ) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 978e926..a93a953 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -160,7 +160,9 @@ class ShowService(command.ShowOne): for service, service_endpoints in endpoints.items(): if service_endpoints: info = {"type": service} - info.update(service_endpoints[0]) + # FIXME(stephenfin): The return type for this in ksa is + # wrong + info.update(service_endpoints[0]) # type: ignore return zip(*sorted(info.items())) msg = _( diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py index fe9436d..ebb2269 100644 --- a/openstackclient/identity/v2_0/token.py +++ b/openstackclient/identity/v2_0/token.py @@ -15,9 +15,9 @@ """Identity v2 Token action implementations""" -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index c4cd52b..244b9e9 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -20,17 +20,17 @@ import logging from cliff import columns as cliff_columns from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class ProjectColumn(cliff_columns.FormattableColumn): +class ProjectColumn(cliff_columns.FormattableColumn[str]): """Formattable column for project column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index 367c649..1859ef6 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -44,7 +44,11 @@ class DeleteAccessRule(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) errors = 0 for ac in parsed_args.access_rule: @@ -87,7 +91,11 @@ class ListAccessRule(command.Lister): ).id else: conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) columns = ('ID', 'Service', 'Method', 'Path') data = identity_client.access_rules(user=user_id) @@ -119,7 +127,11 @@ class ShowAccessRule(command.ShowOne): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) access_rule = identity_client.get_access_rule( user_id, parsed_args.access_rule diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 4c4cd3f..3b38f17 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -18,24 +18,25 @@ import datetime import json import logging +import typing as ty import uuid from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common LOG = logging.getLogger(__name__) -class RolesColumn(cliff_columns.FormattableColumn): +class RolesColumn(cliff_columns.FormattableColumn[ty.Any]): """Generate a formatted string of role names.""" def human_readable(self): - return utils.format_list(r['name'] for r in self._value) + return utils.format_list(list(r['name'] for r in self._value)) def _format_application_credential( @@ -148,6 +149,7 @@ class CreateApplicationCredential(command.ShowOne): parser.add_argument( '--role', metavar='<role>', + dest='roles', action='append', default=[], help=_( @@ -204,10 +206,15 @@ class CreateApplicationCredential(command.ShowOne): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) role_ids = [] - for role in parsed_args.role: + for role in parsed_args.roles: if is_uuid_like(role): role_ids.append({'id': role}) else: @@ -272,13 +279,18 @@ class DeleteApplicationCredential(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) errors = 0 for ac in parsed_args.application_credential: try: app_cred = identity_client.find_application_credential( - user_id, ac + user_id, ac, ignore_missing=False ) identity_client.delete_application_credential( user_id, app_cred.id @@ -325,7 +337,11 @@ class ListApplicationCredential(command.Lister): ) else: conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) application_credentials = identity_client.application_credentials( user=user_id @@ -349,10 +365,14 @@ class ShowApplicationCredential(command.ShowOne): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) application_credential = identity_client.find_application_credential( - user_id, parsed_args.application_credential + user_id, parsed_args.application_credential, ignore_missing=False ) return _format_application_credential(application_credential) diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index a161461..7d37e6c 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -9,24 +9,24 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Identity v3 Service Catalog action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class EndpointsColumn(cliff_columns.FormattableColumn): +class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): if not self._value: return "" diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index 933f48a..c58441c 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 7d9c7c4..02eef64 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 536243a..28481c0 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -107,7 +107,9 @@ class CreateDomain(command.ShowOne): ) except sdk_exceptions.ConflictException: if parsed_args.or_show: - domain = identity_client.find_domain(parsed_args.name) + domain = identity_client.find_domain( + parsed_args.name, ignore_missing=False + ) LOG.info(_('Returning existing domain %s'), domain.name) else: raise @@ -238,7 +240,9 @@ class SetDomain(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - domain = identity_client.find_domain(parsed_args.domain) + domain = identity_client.find_domain( + parsed_args.domain, ignore_missing=False + ) kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name @@ -266,6 +270,8 @@ class ShowDomain(command.ShowOne): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - domain = identity_client.find_domain(parsed_args.domain) + domain = identity_client.find_domain( + parsed_args.domain, ignore_missing=False + ) return _format_domain(domain) diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index 84700f4..dbbf7a2 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -14,10 +14,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 1dfa881..9083fdc 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -169,7 +169,9 @@ class DeleteEndpoint(command.Command): result = 0 for i in parsed_args.endpoint: try: - endpoint_id = identity_client.find_endpoint(i).id + endpoint_id = identity_client.find_endpoint( + i, ignore_missing=False + ).id identity_client.delete_endpoint(endpoint_id) except Exception as e: result += 1 @@ -230,12 +232,25 @@ class ListEndpoint(command.Lister): endpoint = None if parsed_args.endpoint: - endpoint = identity_client.find_endpoint(parsed_args.endpoint) - project = None + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) + + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, + ) + + project_id = None if parsed_args.project: - project = identity_client.find_project( - parsed_args.project, - parsed_args.project_domain, + project_id = common._find_sdk_id( + identity_client.find_project, + name_or_id=common._get_token_resource( + identity_client, 'project', parsed_args.project + ), + domain_id=project_domain_id, ) if endpoint: @@ -273,15 +288,17 @@ class ListEndpoint(command.Lister): region = identity_client.get_region(parsed_args.region) kwargs['region_id'] = region.id - if project: + if project_id: data = list( - identity_client.project_endpoints(project=project.id) + identity_client.project_endpoints(project=project_id) ) else: data = list(identity_client.endpoints(**kwargs)) for ep in data: - service = identity_client.find_service(ep.service_id) + service = identity_client.find_service( + ep.service_id, ignore_missing=False + ) ep.service_name = getattr(service, 'name', '') ep.service_type = service.type @@ -382,7 +399,9 @@ class SetEndpoint(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - endpoint = identity_client.find_endpoint(parsed_args.endpoint) + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) kwargs = {} @@ -429,7 +448,9 @@ class ShowEndpoint(command.ShowOne): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - endpoint = identity_client.find_endpoint(parsed_args.endpoint) + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) service = common.find_service_sdk(identity_client, endpoint.service_id) diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py index e4611a1..3965f31 100644 --- a/openstackclient/identity/v3/endpoint_group.py +++ b/openstackclient/identity/v3/endpoint_group.py @@ -16,10 +16,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 4e98086..3e1dea1 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -16,16 +16,25 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) +def _format_protocol(protocol): + columns = ('name', 'idp_id', 'mapping_id') + column_headers = ('id', 'identity_provider', 'mapping') + return ( + column_headers, + utils.get_item_properties(protocol, columns), + ) + + class CreateProtocol(command.ShowOne): _description = _("Create new federation protocol") @@ -58,21 +67,15 @@ class CreateProtocol(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - protocol = identity_client.federation.protocols.create( - protocol_id=parsed_args.federation_protocol, - identity_provider=parsed_args.identity_provider, - mapping=parsed_args.mapping, + identity_client = self.app.client_manager.sdk_connection.identity + + protocol = identity_client.create_federation_protocol( + name=parsed_args.federation_protocol, + idp_id=parsed_args.identity_provider, + mapping_id=parsed_args.mapping, ) - info = dict(protocol._info) - # NOTE(marek-denis): Identity provider is not included in a response - # from Keystone, however it should be listed to the user. Add it - # manually to the output list, simply reusing value provided by the - # user. - info['identity_provider'] = parsed_args.identity_provider - info['mapping'] = info.pop('mapping_id') - info.pop('links', None) - return zip(*sorted(info.items())) + + return _format_protocol(protocol) class DeleteProtocol(command.Command): @@ -99,12 +102,15 @@ class DeleteProtocol(command.Command): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + result = 0 for i in parsed_args.federation_protocol: try: - identity_client.federation.protocols.delete( - parsed_args.identity_provider, i + identity_client.delete_federation_protocol( + idp_id=parsed_args.identity_provider, + protocol=i, + ignore_missing=False, ) except Exception as e: result += 1 @@ -140,9 +146,9 @@ class ListProtocols(command.Lister): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - protocols = identity_client.federation.protocols.list( + protocols = identity_client.federation_protocols( parsed_args.identity_provider ) columns = ('id', 'mapping') @@ -181,21 +187,16 @@ class SetProtocol(command.Command): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - protocol = identity_client.federation.protocols.update( - parsed_args.identity_provider, - parsed_args.federation_protocol, - parsed_args.mapping, - ) - info = dict(protocol._info) - # NOTE(marek-denis): Identity provider is not included in a response - # from Keystone, however it should be listed to the user. Add it - # manually to the output list, simply reusing value provided by the - # user. - info['identity_provider'] = parsed_args.identity_provider - info['mapping'] = info.pop('mapping_id') - return zip(*sorted(info.items())) + kwargs = {'idp_id': parsed_args.identity_provider} + if parsed_args.federation_protocol: + kwargs['name'] = parsed_args.federation_protocol + if parsed_args.mapping: + kwargs['mapping_id'] = parsed_args.mapping + + protocol = identity_client.update_federation_protocol(**kwargs) + return _format_protocol(protocol) class ShowProtocol(command.ShowOne): @@ -220,12 +221,10 @@ class ShowProtocol(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - protocol = identity_client.federation.protocols.get( - parsed_args.identity_provider, parsed_args.federation_protocol + protocol = identity_client.get_federation_protocol( + idp_id=parsed_args.identity_provider, + protocol=parsed_args.federation_protocol, ) - info = dict(protocol._info) - info['mapping'] = info.pop('mapping_id') - info.pop('links', None) - return zip(*sorted(info.items())) + return _format_protocol(protocol) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 92980ac..a2c2fd3 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -211,10 +211,15 @@ class CreateGroup(command.ShowOne): if parsed_args.or_show: if parsed_args.domain: group = identity_client.find_group( - parsed_args.name, domain_id=parsed_args.domain + parsed_args.name, + domain_id=parsed_args.domain, + ignore_missing=False, ) else: - group = identity_client.find_group(parsed_args.name) + group = identity_client.find_group( + parsed_args.name, + ignore_missing=False, + ) LOG.info(_('Returning existing group %s'), group.name) else: raise @@ -310,8 +315,9 @@ class ListGroup(command.Lister): parsed_args.user_domain, ) if domain: - # NOTE(0weng): The API doesn't actually support filtering additionally by domain_id, - # so this doesn't really do anything. + # 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) diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index d0c3243..f1af03f 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -16,10 +16,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -41,6 +41,7 @@ class CreateIdentityProvider(command.ShowOne): identity_remote_id_provider.add_argument( '--remote-id', metavar='<remote-id>', + dest='remote_ids', action='append', help=_( 'Remote IDs to associate with the Identity Provider ' @@ -99,16 +100,15 @@ class CreateIdentityProvider(command.ShowOne): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + remote_ids: list[str] | None = None if parsed_args.remote_id_file: file_content = utils.read_blob_file_contents( parsed_args.remote_id_file ) remote_ids = file_content.splitlines() remote_ids = list(map(str.strip, remote_ids)) - else: - remote_ids = ( - parsed_args.remote_id if parsed_args.remote_id else None - ) + elif parsed_args.remote_ids: + remote_ids = parsed_args.remote_ids domain_id = None if parsed_args.domain: @@ -137,8 +137,9 @@ class CreateIdentityProvider(command.ShowOne): ) idp._info.pop('links', None) - remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', [])) - idp._info['remote_ids'] = remote_ids + idp._info['remote_ids'] = format_columns.ListColumn( + idp._info.pop('remote_ids', []) + ) return zip(*sorted(idp._info.items())) @@ -240,6 +241,7 @@ class SetIdentityProvider(command.Command): identity_remote_id_provider.add_argument( '--remote-id', metavar='<remote-id>', + dest='remote_ids', action='append', help=_( 'Remote IDs to associate with the Identity Provider ' @@ -287,8 +289,8 @@ class SetIdentityProvider(command.Command): ) remote_ids = file_content.splitlines() remote_ids = list(map(str.strip, remote_ids)) - elif parsed_args.remote_id: - remote_ids = parsed_args.remote_id + elif parsed_args.remote_ids: + remote_ids = parsed_args.remote_ids # Setup keyword args for the client kwargs = {} @@ -298,7 +300,7 @@ class SetIdentityProvider(command.Command): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False - if parsed_args.remote_id_file or parsed_args.remote_id: + if parsed_args.remote_id_file or parsed_args.remote_ids: kwargs['remote_ids'] = remote_ids # TODO(pas-ha) actually check for 3.14 microversion diff --git a/openstackclient/identity/v3/implied_role.py b/openstackclient/identity/v3/implied_role.py index 3958896..c1236ad 100644 --- a/openstackclient/identity/v3/implied_role.py +++ b/openstackclient/identity/v3/implied_role.py @@ -17,8 +17,8 @@ import logging -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 7f2fe88..94f6bca 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -15,16 +15,38 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as common_utils LOG = logging.getLogger(__name__) +def _format_limit(limit): + columns = ( + "description", + "id", + "project_id", + "region_id", + "resource_limit", + "resource_name", + "service_id", + ) + column_headers = ( + "description", + "id", + "project_id", + "region_id", + "resource_limit", + "resource_name", + "service_id", + ) + return (column_headers, utils.get_item_properties(limit, columns)) + + class CreateLimit(command.ShowOne): _description = _("Create a limit") @@ -46,6 +68,7 @@ class CreateLimit(command.ShowOne): required=True, help=_('Project to associate the resource limit to'), ) + common_utils.add_project_domain_option_to_parser(parser) parser.add_argument( '--service', metavar='<service>', @@ -67,47 +90,33 @@ class CreateLimit(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - project = common_utils.find_project( - identity_client, parsed_args.project + identity_client = self.app.client_manager.sdk_connection.identity + + kwargs = { + "resource_name": parsed_args.resource_name, + "resource_limit": parsed_args.resource_limit, + } + if parsed_args.description: + kwargs["description"] = parsed_args.description + + kwargs["project_id"] = common_utils.find_project_id_sdk( + identity_client, + parsed_args.project, + domain_name_or_id=parsed_args.project_domain, ) - service = common_utils.find_service( + + kwargs["service_id"] = common_utils.find_service_sdk( identity_client, parsed_args.service - ) - region = None + ).id + if parsed_args.region: - if 'None' not in parsed_args.region: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) - else: - self.log.warning( - _( - "Passing 'None' to indicate no region is deprecated. " - "Instead, don't pass --region." - ) - ) + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id - limit = identity_client.limits.create( - project, - service, - parsed_args.resource_name, - parsed_args.resource_limit, - description=parsed_args.description, - region=region, - ) + limit = identity_client.create_limit(**kwargs) - limit._info.pop('links', None) - return zip(*sorted(limit._info.items())) + return _format_limit(limit) class ListLimit(command.Lister): @@ -136,50 +145,41 @@ class ListLimit(command.Lister): metavar='<project>', help=_('List resource limits associated with project'), ) + common_utils.add_project_domain_option_to_parser(parser) + return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - service = None + kwargs = {} if parsed_args.service: - service = common_utils.find_service( + kwargs["service_id"] = common_utils.find_service_sdk( identity_client, parsed_args.service ) - region = None + if parsed_args.region: - if 'None' not in parsed_args.region: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) - else: - self.log.warning( - _( - "Passing 'None' to indicate no region is deprecated. " - "Instead, don't pass --region." - ) - ) + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id - project = None if parsed_args.project: - project = utils.find_resource( - identity_client.projects, parsed_args.project + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = common_utils.find_domain_id_sdk( + identity_client, parsed_args.project_domain + ) + + kwargs["project_id"] = common_utils._find_sdk_id( + identity_client.find_project, + name_or_id=parsed_args.project, + domain_id=project_domain_id, ) - limits = identity_client.limits.list( - service=service, - resource_name=parsed_args.resource_name, - region=region, - project=project, - ) + if parsed_args.resource_name: + kwargs["resource_name"] = parsed_args.resource_name + + limits = identity_client.limits(**kwargs) columns = ( 'ID', @@ -209,10 +209,9 @@ class ShowLimit(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - limit = identity_client.limits.get(parsed_args.limit_id) - limit._info.pop('links', None) - return zip(*sorted(limit._info.items())) + identity_client = self.app.client_manager.sdk_connection.identity + limit = identity_client.get_limit(parsed_args.limit_id) + return _format_limit(limit) class SetLimit(command.ShowOne): @@ -240,17 +239,16 @@ class SetLimit(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - limit = identity_client.limits.update( - parsed_args.limit_id, - description=parsed_args.description, - resource_limit=parsed_args.resource_limit, - ) + identity_client = self.app.client_manager.sdk_connection.identity - limit._info.pop('links', None) + kwargs = {} + if parsed_args.description: + kwargs["description"] = parsed_args.description + if parsed_args.resource_limit: + kwargs["resource_limit"] = parsed_args.resource_limit + limit = identity_client.update_limit(parsed_args.limit_id, **kwargs) - return zip(*sorted(limit._info.items())) + return _format_limit(limit) class DeleteLimit(command.Command): @@ -262,17 +260,20 @@ class DeleteLimit(command.Command): 'limit_id', metavar='<limit-id>', nargs="+", - help=_('Limit to delete (ID)'), + help=_( + 'Limit to delete (ID) ' + '(repeat option to remove multiple limits)' + ), ) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 for limit_id in parsed_args.limit_id: try: - identity_client.limits.delete(limit_id) + identity_client.delete_limit(limit_id) except Exception as e: errors += 1 LOG.error( diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 8c2d0bf..a041f19 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -18,10 +18,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index d5b8fec..3554903 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 3832154..9924239 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -17,12 +17,12 @@ import logging -from keystoneauth1 import exceptions as ks_exc +from openstack import exceptions as sdk_exc from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common from openstackclient.identity.v3 import tag @@ -30,6 +30,21 @@ from openstackclient.identity.v3 import tag LOG = logging.getLogger(__name__) +def _format_project(project): + # NOTE(0weng): Projects allow unknown attributes in the body, so extract + # the column names separately. + (column_headers, columns) = utils.get_osc_show_columns_for_sdk_resource( + project, + {'is_enabled': 'enabled'}, + ['links', 'location', 'parents_as_ids', 'subtree_as_ids'], + ) + + return ( + column_headers, + utils.get_item_properties(project, columns), + ) + + class CreateProject(command.ShowOne): _description = _("Create new project") @@ -73,8 +88,8 @@ class CreateProject(command.ShowOne): parser.add_argument( '--property', metavar='<key=value>', - action=parseractions.KeyValueAction, dest='properties', + action=parseractions.KeyValueAction, help=_( 'Add a property to <name> ' '(repeat option to set multiple properties)' @@ -90,22 +105,13 @@ class CreateProject(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - domain = None - if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain).id - - parent = None - if parsed_args.parent: - parent = utils.find_resource( - identity_client.projects, - parsed_args.parent, - ).id + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} + if parsed_args.properties: kwargs = parsed_args.properties.copy() + if 'is_domain' in kwargs.keys(): if kwargs['is_domain'].lower() == "true": kwargs['is_domain'] = True @@ -114,39 +120,66 @@ class CreateProject(command.ShowOne): elif kwargs['is_domain'].lower() == "none": kwargs['is_domain'] = None - kwargs['tags'] = list(set(parsed_args.tags)) + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.name: + kwargs['name'] = parsed_args.name + + domain = None + if parsed_args.domain: + domain = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) + kwargs['domain_id'] = domain + + if parsed_args.parent: + kwargs['parent_id'] = common.find_project_id_sdk( + identity_client, + parsed_args.parent, + domain_name_or_id=domain, + ) + + kwargs['is_enabled'] = parsed_args.enabled + + if parsed_args.tags: + kwargs['tags'] = list(set(parsed_args.tags)) - options = {} if parsed_args.immutable is not None: - options['immutable'] = parsed_args.immutable + kwargs['options'] = {'immutable': parsed_args.immutable} try: - project = identity_client.projects.create( - name=parsed_args.name, - domain=domain, - parent=parent, - description=parsed_args.description, - enabled=parsed_args.enabled, - options=options, + project = identity_client.create_project( **kwargs, ) - except ks_exc.Conflict: + except sdk_exc.ConflictException: if parsed_args.or_show: - project = utils.find_resource( - identity_client.projects, - parsed_args.name, - domain_id=domain, - ) + if parsed_args.domain: + project = identity_client.find_project( + parsed_args.name, + domain_id=domain, + ignore_missing=False, + ) + else: + project = identity_client.find_project( + parsed_args.name, ignore_missing=False + ) LOG.info(_('Returning existing project %s'), project.name) else: raise - project._info.pop('links') - return zip(*sorted(project._info.items())) + return _format_project(project) class DeleteProject(command.Command): - _description = _("Delete project(s)") + _description = _( + "Delete project(s). This command will remove specified " + "existing project(s) if an active user is authorized to do " + "this. If there are resources managed by other services " + "(for example, Nova, Neutron, Cinder) associated with " + "specified project(s), delete operation will proceed " + "regardless." + ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -164,23 +197,19 @@ class DeleteProject(command.Command): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - domain = None - if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain) errors = 0 for project in parsed_args.projects: try: - if domain is not None: - project_obj = utils.find_resource( - identity_client.projects, project, domain_id=domain.id - ) - else: - project_obj = utils.find_resource( - identity_client.projects, project - ) - identity_client.projects.delete(project_obj.id) + project = common.find_project_id_sdk( + identity_client, + project, + domain_name_or_id=parsed_args.domain, + validate_actor_existence=True, + validate_domain_actor_existence=False, + ) + identity_client.delete_project(project) except Exception as e: errors += 1 LOG.error( @@ -261,38 +290,46 @@ class ListProject(command.Lister): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - columns: tuple[str, ...] = ('ID', 'Name') + identity_client = self.app.client_manager.sdk_connection.identity + + column_headers: tuple[str, ...] = ('ID', 'Name') + if parsed_args.long: + column_headers += ('Domain ID', 'Description', 'Enabled') + + columns: tuple[str, ...] = ('id', 'name') if parsed_args.long: - columns += ('Domain ID', 'Description', 'Enabled') + columns += ('domain_id', 'description', 'is_enabled') + kwargs = {} domain_id = None if parsed_args.domain: - domain_id = common.find_domain( + domain_id = common.find_domain_id_sdk( identity_client, parsed_args.domain - ).id - kwargs['domain'] = domain_id + ) + kwargs['domain_id'] = domain_id if parsed_args.parent: - parent_id = common.find_project( - identity_client, parsed_args.parent - ).id - kwargs['parent'] = parent_id + parent_id = common.find_project_id_sdk( + identity_client, + parsed_args.parent, + domain_name_or_id=domain_id, + ) + kwargs['parent_id'] = parent_id + user = None if parsed_args.user: if parsed_args.domain: - user_id = utils.find_resource( - identity_client.users, + user = common.find_user_id_sdk( + identity_client, parsed_args.user, - domain_id=domain_id, - ).id + domain_name_or_id=domain_id, + ) else: - user_id = utils.find_resource( - identity_client.users, parsed_args.user - ).id - - kwargs['user'] = user_id + user = common.find_user_id_sdk( + identity_client, + parsed_args.user, + ) if parsed_args.is_enabled is not None: kwargs['is_enabled'] = parsed_args.is_enabled @@ -301,32 +338,29 @@ class ListProject(command.Lister): if parsed_args.my_projects: # NOTE(adriant): my-projects supersedes all the other filters. - kwargs = {'user': self.app.client_manager.auth_ref.user_id} + kwargs = {} + user = self.app.client_manager.auth_ref.user_id - try: - data = identity_client.projects.list(**kwargs) - except ks_exc.Forbidden: - # NOTE(adriant): if no filters, assume a forbidden is non-admin - # wanting their own project list. - if not kwargs: - user = self.app.client_manager.auth_ref.user_id - data = identity_client.projects.list(user=user) - else: - raise + if user: + data = identity_client.user_projects(user, **kwargs) + else: + try: + data = identity_client.projects(**kwargs) + except sdk_exc.ForbiddenException: + # NOTE(adriant): if no filters, assume a forbidden is non-admin + # wanting their own project list. + if not kwargs: + user = self.app.client_manager.auth_ref.user_id + data = identity_client.user_projects(user) + else: + raise if parsed_args.sort: data = utils.sort_items(data, parsed_args.sort) return ( - columns, - ( - utils.get_item_properties( - s, - columns, - formatters={}, - ) - for s in data - ), + column_headers, + (utils.get_item_properties(s, columns) for s in data), ) @@ -385,11 +419,7 @@ class SetProject(command.Command): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - project = common.find_project( - identity_client, parsed_args.project, parsed_args.domain - ) + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.name: @@ -402,9 +432,50 @@ class SetProject(command.Command): kwargs['options'] = {'immutable': parsed_args.immutable} if parsed_args.properties: kwargs.update(parsed_args.properties) - tag.update_tags_in_args(parsed_args, project, kwargs) - identity_client.projects.update(project.id, **kwargs) + if parsed_args.domain: + domain = common.find_domain_id_sdk( + identity_client, + parsed_args.domain, + validate_actor_existence=False, + ) + project = identity_client.find_project( + parsed_args.project, + domain_id=domain, + ignore_missing=True, + ) + else: + project = identity_client.find_project( + parsed_args.project, + ignore_missing=True, + ) + + if ( + parsed_args.tags + or parsed_args.remove_tags + or parsed_args.clear_tags + ): + existing_tags = [] + if project: + existing_tags = project.tags + + if parsed_args.clear_tags: + kwargs['tags'] = [] + else: + existing_tags_set = set(existing_tags) + if parsed_args.remove_tags: + tags = sorted( + existing_tags_set - set(parsed_args.remove_tags) + ) + if parsed_args.tags: + tags = sorted( + existing_tags_set.union(set(parsed_args.tags)) + ) + kwargs['tags'] = tags + + project_id = project.id if project else parsed_args.project + + identity_client.update_project(project_id, **kwargs) class ShowProject(command.ShowOne): @@ -437,31 +508,36 @@ class ShowProject(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - project_str = common._get_token_resource( - identity_client, 'project', parsed_args.project, parsed_args.domain - ) + kwargs = {} + domain = None if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain) - project = utils.find_resource( - identity_client.projects, project_str, domain_id=domain.id - ) - else: - project = utils.find_resource( - identity_client.projects, project_str + domain = common.find_domain_id_sdk( + identity_client, parsed_args.domain ) - if parsed_args.parents or parsed_args.children: - # NOTE(RuiChen): utils.find_resource() can't pass kwargs, - # if id query hit the result at first, so call - # identity manager.get() with kwargs directly. - project = identity_client.projects.get( - project.id, - parents_as_ids=parsed_args.parents, - subtree_as_ids=parsed_args.children, - ) + kwargs['domain_id'] = domain + + # Get project id first; otherwise, find_project() can't find + # parents/children if only project name was given + project = common.find_project_id_sdk( + identity_client, + parsed_args.project, + domain_name_or_id=domain, + validate_actor_existence=False, + validate_domain_actor_existence=False, + ) + + # Include these options as query parameters if they are provided + if parsed_args.parents: + kwargs['parents_as_ids'] = True + if parsed_args.children: + kwargs['subtree_as_ids'] = True + + project = identity_client.find_project( + project, **kwargs, ignore_missing=False + ) - project._info.pop('links') - return zip(*sorted(project._info.items())) + return _format_project(project) diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index 40d3819..4882c9e 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index 34ce210..4be829b 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -15,16 +15,39 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as common_utils LOG = logging.getLogger(__name__) +def _format_registered_limit(registered_limit): + columns = ( + 'default_limit', + 'description', + 'id', + 'region_id', + 'resource_name', + 'service_id', + ) + column_headers = ( + 'default_limit', + 'description', + 'id', + 'region_id', + 'resource_name', + 'service_id', + ) + return ( + column_headers, + utils.get_item_properties(registered_limit, columns), + ) + + class CreateRegisteredLimit(command.ShowOne): _description = _("Create a registered limit") @@ -44,7 +67,10 @@ class CreateRegisteredLimit(command.ShowOne): '--service', metavar='<service>', required=True, - help=_('Service responsible for the resource to limit (required)'), + help=_( + 'Service responsible for the resource to limit (required) ' + '(name or ID)' + ), ) parser.add_argument( '--default-limit', @@ -61,43 +87,28 @@ class CreateRegisteredLimit(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + + kwargs = {} + + if parsed_args.description: + kwargs["description"] = parsed_args.description + + kwargs["service_id"] = common_utils.find_service_sdk( + identity_client, parsed_args.service + ).id - service = utils.find_resource( - identity_client.services, parsed_args.service - ) - region = None if parsed_args.region: - if 'None' not in parsed_args.region: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) - else: - self.log.warning( - _( - "Passing 'None' to indicate no region is deprecated. " - "Instead, don't pass --region." - ) - ) + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id - registered_limit = identity_client.registered_limits.create( - service, - parsed_args.resource_name, - parsed_args.default_limit, - description=parsed_args.description, - region=region, - ) + kwargs["resource_name"] = parsed_args.resource_name + kwargs["default_limit"] = parsed_args.default_limit - registered_limit._info.pop('links', None) - return zip(*sorted(registered_limit._info.items())) + registered_limit = identity_client.create_registered_limit(**kwargs) + + return _format_registered_limit(registered_limit) class DeleteRegisteredLimit(command.Command): @@ -106,20 +117,25 @@ class DeleteRegisteredLimit(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - 'registered_limit_id', - metavar='<registered-limit-id>', + 'registered_limits', + metavar='<registered-limits>', nargs="+", - help=_('Registered limit to delete (ID)'), + help=_( + 'Registered limit(s) to delete (ID) ' + '(repeat option to remove multiple registered limits)' + ), ) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 - for registered_limit_id in parsed_args.registered_limit_id: + for registered_limit_id in parsed_args.registered_limits: try: - identity_client.registered_limits.delete(registered_limit_id) + identity_client.delete_registered_limit( + registered_limit_id, ignore_missing=False + ) except Exception as e: errors += 1 from pprint import pprint @@ -134,7 +150,7 @@ class DeleteRegisteredLimit(command.Command): ) if errors > 0: - total = len(parsed_args.registered_limit_id) + total = len(parsed_args.registered_limits) msg = _( "%(errors)s of %(total)s registered limits failed to delete." ) % {'errors': errors, 'total': total} @@ -149,7 +165,9 @@ class ListRegisteredLimit(command.Lister): parser.add_argument( '--service', metavar='<service>', - help=_('Service responsible for the resource to limit'), + help=_( + 'Service responsible for the resource to limit (name or ID)' + ), ) parser.add_argument( '--resource-name', @@ -165,40 +183,22 @@ class ListRegisteredLimit(command.Lister): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - service = None + kwargs = {} if parsed_args.service: - service = common_utils.find_service( + kwargs["service_id"] = common_utils.find_service_sdk( identity_client, parsed_args.service - ) - region = None + ).id if parsed_args.region: - if 'None' not in parsed_args.region: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) - else: - self.log.warning( - _( - "Passing 'None' to indicate no region is deprecated. " - "Instead, don't pass --region." - ) - ) + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id - registered_limits = identity_client.registered_limits.list( - service=service, - resource_name=parsed_args.resource_name, - region=region, - ) + if parsed_args.resource_name: + kwargs["resource_name"] = parsed_args.resource_name + + registered_limits = identity_client.registered_limits(**kwargs) columns = ( 'ID', @@ -228,9 +228,9 @@ class SetRegisteredLimit(command.ShowOne): '--service', metavar='<service>', help=_( - 'Service to be updated responsible for the resource to ' - 'limit. Either --service, --resource-name or --region must ' - 'be different than existing value otherwise it will be ' + 'Service to be updated responsible for the resource to limit ' + '(name or ID). Either --service, --resource-name or --region ' + 'must be different than existing value otherwise it will be ' 'duplicate entry' ), ) @@ -268,44 +268,33 @@ class SetRegisteredLimit(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - service = None + kwargs = {} if parsed_args.service: - service = common_utils.find_service( + kwargs["service_id"] = common_utils.find_service_sdk( identity_client, parsed_args.service - ) + ).id + + if parsed_args.resource_name: + kwargs["resource_name"] = parsed_args.resource_name + + if parsed_args.default_limit: + kwargs["default_limit"] = parsed_args.default_limit + + if parsed_args.description: + kwargs["description"] = parsed_args.description - region = None if parsed_args.region: - if 'None' not in parsed_args.region: - # NOTE (vishakha): Due to bug #1799153 and for any another - # related case where GET resource API does not support the - # filter by name, osc_lib.utils.find_resource() method cannot - # be used because that method try to fall back to list all the - # resource if requested resource cannot be get via name. Which - # ends up with NoUniqueMatch error. - # So osc_lib.utils.find_resource() function cannot be used for - # 'regions', using common_utils.get_resource() instead. - region = common_utils.get_resource( - identity_client.regions, parsed_args.region - ) - else: - self.log.warning( - _("Passing 'None' to indicate no region is deprecated.") - ) + kwargs["region_id"] = identity_client.get_region( + parsed_args.region + ).id - registered_limit = identity_client.registered_limits.update( - parsed_args.registered_limit_id, - service=service, - resource_name=parsed_args.resource_name, - default_limit=parsed_args.default_limit, - description=parsed_args.description, - region=region, + registered_limit = identity_client.update_registered_limit( + parsed_args.registered_limit_id, **kwargs ) - registered_limit._info.pop('links', None) - return zip(*sorted(registered_limit._info.items())) + return _format_registered_limit(registered_limit) class ShowRegisteredLimit(command.ShowOne): @@ -321,9 +310,8 @@ class ShowRegisteredLimit(command.ShowOne): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - registered_limit = identity_client.registered_limits.get( + identity_client = self.app.client_manager.sdk_connection.identity + registered_limit = identity_client.get_registered_limit( parsed_args.registered_limit_id ) - registered_limit._info.pop('links', None) - return zip(*sorted(registered_limit._info.items())) + return _format_registered_limit(registered_limit) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 3301c3f..3c580d6 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -82,32 +82,12 @@ def _add_identity_and_resource_options_to_parser(parser): common.add_inherited_option_to_parser(parser) -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 - ) - - # Mimic the behavior of - # openstackclient.identity.common._find_identity_resource() - # and ignore if we don't have permission to find a resource. - except sdk_exc.ForbiddenException: - return name_or_id - except sdk_exc.ResourceNotFound as exc: - if not validate_actor_existence: - return name_or_id - raise exceptions.CommandError from exc - return resource.id - - def _process_identity_and_resource_options( parsed_args, identity_client, validate_actor_existence=True ): def _find_user(): domain_id = ( - _find_sdk_id( + common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.user_domain, validate_actor_existence=validate_actor_existence, @@ -115,7 +95,7 @@ def _process_identity_and_resource_options( if parsed_args.user_domain else None ) - return _find_sdk_id( + return common._find_sdk_id( identity_client.find_user, name_or_id=parsed_args.user, validate_actor_existence=validate_actor_existence, @@ -124,7 +104,7 @@ def _process_identity_and_resource_options( def _find_group(): domain_id = ( - _find_sdk_id( + common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.group_domain, validate_actor_existence=validate_actor_existence, @@ -132,7 +112,7 @@ def _process_identity_and_resource_options( if parsed_args.group_domain else None ) - return _find_sdk_id( + return common._find_sdk_id( identity_client.find_group, name_or_id=parsed_args.group, validate_actor_existence=validate_actor_existence, @@ -141,7 +121,7 @@ def _process_identity_and_resource_options( def _find_project(): domain_id = ( - _find_sdk_id( + common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.project_domain, validate_actor_existence=validate_actor_existence, @@ -149,7 +129,7 @@ def _process_identity_and_resource_options( if parsed_args.project_domain else None ) - return _find_sdk_id( + return common._find_sdk_id( identity_client.find_project, name_or_id=parsed_args.project, validate_actor_existence=validate_actor_existence, @@ -162,7 +142,7 @@ def _process_identity_and_resource_options( kwargs['system'] = parsed_args.system elif parsed_args.user and parsed_args.domain: kwargs['user'] = _find_user() - kwargs['domain'] = _find_sdk_id( + kwargs['domain'] = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, validate_actor_existence=validate_actor_existence, @@ -175,7 +155,7 @@ def _process_identity_and_resource_options( kwargs['system'] = parsed_args.system elif parsed_args.group and parsed_args.domain: kwargs['group'] = _find_group() - kwargs['domain'] = _find_sdk_id( + kwargs['domain'] = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, validate_actor_existence=validate_actor_existence, @@ -228,10 +208,10 @@ class AddRole(command.Command): domain_id = None if parsed_args.role_domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain ) - role = _find_sdk_id( + role = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=domain_id, @@ -328,7 +308,7 @@ class CreateRole(command.ShowOne): create_kwargs = {} if parsed_args.domain: - create_kwargs['domain_id'] = _find_sdk_id( + create_kwargs['domain_id'] = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain ) @@ -381,13 +361,13 @@ class DeleteRole(command.Command): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, parsed_args.domain ) errors = 0 for role in parsed_args.roles: try: - role_id = _find_sdk_id( + role_id = common._find_sdk_id( identity_client.find_role, name_or_id=role, domain_id=domain_id, @@ -430,6 +410,7 @@ class ListRole(command.Lister): if parsed_args.domain: domain = identity_client.find_domain( name_or_id=parsed_args.domain, + ignore_missing=False, ) data = identity_client.roles(domain_id=domain.id) return ( @@ -482,11 +463,11 @@ class RemoveRole(command.Command): domain_id = None if parsed_args.role_domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain, ) - role = _find_sdk_id( + role = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=domain_id, @@ -582,7 +563,7 @@ class SetRole(command.Command): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) @@ -591,7 +572,7 @@ class SetRole(command.Command): if parsed_args.immutable is not None: update_kwargs["options"] = {"immutable": parsed_args.immutable} - role = _find_sdk_id( + role = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=domain_id, @@ -623,7 +604,7 @@ class ShowRole(command.ShowOne): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 12e1527..78c010f 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -13,9 +13,7 @@ """Identity v3 Assignment action implementations""" -from openstack import exceptions as sdk_exceptions -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -51,15 +49,6 @@ def _format_role_assignment_(assignment, include_names): ) -def _find_sdk_id(find_command, name_or_id, **kwargs): - try: - return find_command( - name_or_id=name_or_id, ignore_missing=False, **kwargs - ).id - except sdk_exceptions.ForbiddenException: - return name_or_id - - class ListRoleAssignment(command.Lister): _description = _("List role assignments") @@ -135,12 +124,12 @@ class ListRoleAssignment(command.Lister): role_id = None role_domain_id = None if parsed_args.role_domain: - role_domain_id = _find_sdk_id( + role_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain, ) if parsed_args.role: - role_id = _find_sdk_id( + role_id = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=role_domain_id, @@ -148,21 +137,21 @@ class ListRoleAssignment(command.Lister): user_domain_id = None if parsed_args.user_domain: - user_domain_id = _find_sdk_id( + user_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.user_domain, ) user_id = None if parsed_args.user: - user_id = _find_sdk_id( + user_id = common._find_sdk_id( identity_client.find_user, name_or_id=parsed_args.user, domain_id=user_domain_id, ) elif parsed_args.authuser: if auth_ref: - user_id = _find_sdk_id( + user_id = common._find_sdk_id( identity_client.find_user, name_or_id=auth_ref.user_id, ) @@ -173,21 +162,21 @@ class ListRoleAssignment(command.Lister): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) project_domain_id = None if parsed_args.project_domain: - project_domain_id = _find_sdk_id( + project_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.project_domain, ) project_id = None if parsed_args.project: - project_id = _find_sdk_id( + project_id = common._find_sdk_id( identity_client.find_project, name_or_id=common._get_token_resource( identity_client, 'project', parsed_args.project @@ -196,21 +185,21 @@ class ListRoleAssignment(command.Lister): ) elif parsed_args.authproject: if auth_ref: - project_id = _find_sdk_id( + project_id = common._find_sdk_id( identity_client.find_project, name_or_id=auth_ref.project_id, ) group_domain_id = None if parsed_args.group_domain: - group_domain_id = _find_sdk_id( + group_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.group_domain, ) group_id = None if parsed_args.group: - group_id = _find_sdk_id( + group_id = common._find_sdk_id( identity_client.find_group, name_or_id=parsed_args.group, domain_id=group_domain_id, diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index e8f4832..53a7062 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 5b49cff..02aae66 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/tag.py b/openstackclient/identity/v3/tag.py index 0909fd1..f49a1ca 100644 --- a/openstackclient/identity/v3/tag.py +++ b/openstackclient/identity/v3/tag.py @@ -114,6 +114,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name): parser.add_argument( '--remove-tag', action='append', + dest='remove_tags', metavar='<tag>', default=[], help=_( @@ -122,14 +123,3 @@ def add_tag_option_to_parser_for_set(parser, resource_name): ) % resource_name, ) - - -def update_tags_in_args(parsed_args, obj, args): - if parsed_args.clear_tags: - args['tags'] = [] - obj.tags = [] - if parsed_args.remove_tag: - args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tag)) - return - if parsed_args.tags: - args['tags'] = sorted(set(obj.tags).union(set(parsed_args.tags))) diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index 5500ce1..05e374c 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -15,10 +15,10 @@ """Identity v3 Token action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -37,6 +37,7 @@ class AuthorizeRequestToken(command.ShowOne): parser.add_argument( '--role', metavar='<role>', + dest='roles', action='append', default=[], required=True, @@ -52,7 +53,7 @@ class AuthorizeRequestToken(command.ShowOne): # NOTE(stevemar): We want a list of role ids roles = [] - for role in parsed_args.role: + for role in parsed_args.roles: role_id = utils.find_resource( identity_client.roles, role, diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index df8e2f4..80808aa 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -18,10 +18,10 @@ import itertools import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -179,7 +179,9 @@ class CreateTrust(command.ShowOne): roles = [] for role in parsed_args.roles: try: - role_id = identity_client.find_role(role).id + role_id = identity_client.find_role( + role, ignore_missing=False + ).id except sdk_exceptions.ForbiddenException: role_id = role roles.append({"id": role_id}) diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py index d26035d..e1efc15 100644 --- a/openstackclient/identity/v3/unscoped_saml.py +++ b/openstackclient/identity/v3/unscoped_saml.py @@ -17,9 +17,9 @@ The first step of federated auth is to fetch an unscoped token. From there, the user can list domains and projects they are allowed to access, and request a scoped token.""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index fee7dbe..bec1c62 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -20,10 +20,10 @@ import logging import typing as ty from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common @@ -82,9 +82,9 @@ def _get_options_for_user(identity_client, parsed_args): options['multi_factor_auth_enabled'] = True if parsed_args.disable_multi_factor_auth: options['multi_factor_auth_enabled'] = False - if parsed_args.multi_factor_auth_rule: + if parsed_args.multi_factor_auth_rules: auth_rules = [ - rule.split(",") for rule in parsed_args.multi_factor_auth_rule + rule.split(",") for rule in parsed_args.multi_factor_auth_rules ] if auth_rules: options['multi_factor_auth_rules'] = auth_rules @@ -175,7 +175,8 @@ def _add_user_options(parser): parser.add_argument( '--multi-factor-auth-rule', metavar='<rule>', - action="append", + dest='multi_factor_auth_rules', + action='append', default=[], help=_( 'Set multi-factor auth rules. For example, to set a rule ' @@ -298,6 +299,9 @@ class CreateUser(command.ShowOne): "when a user does not have a password." ) ) + else: + kwargs['password'] = password + options = _get_options_for_user(identity_client, parsed_args) if options: kwargs['options'] = options @@ -306,7 +310,6 @@ class CreateUser(command.ShowOne): user = identity_client.create_user( is_enabled=is_enabled, name=parsed_args.name, - password=password, **kwargs, ) except sdk_exc.ConflictException: @@ -420,7 +423,8 @@ class ListUser(command.Lister): dest='is_enabled', default=None, help=_( - 'List only enabled users, does nothing with --project and --group' + 'List only enabled users, does nothing with ' + '--project and --group' ), ) parser.add_argument( @@ -429,7 +433,8 @@ class ListUser(command.Lister): dest='is_enabled', default=None, help=_( - 'List only disabled users, does nothing with --project and --group' + 'List only disabled users, does nothing with ' + '--project and --group' ), ) return parser @@ -441,6 +446,7 @@ class ListUser(command.Lister): if parsed_args.domain: domain = identity_client.find_domain( name_or_id=parsed_args.domain, + ignore_missing=False, ).id group = None @@ -467,15 +473,13 @@ class ListUser(command.Lister): ignore_missing=False, ).id - assignments = identity_client.role_assignments_filter( - project=project - ) - # NOTE(stevemar): If a user has more than one role on a project # then they will have two entries in the returned data. Since we # are looking for any role, let's just track unique user IDs. user_ids = set() - for assignment in assignments: + for assignment in identity_client.role_assignments( + scope_project_id=project + ): if assignment.user: user_ids.add(assignment.user['id']) @@ -689,7 +693,12 @@ class SetPasswordUser(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) # FIXME(gyee): there are two scenarios: # diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 91b0db4..0ea7eca 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -19,15 +19,16 @@ import argparse import logging import os import sys +import typing as ty from cliff import columns as cliff_columns from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] @@ -67,10 +68,7 @@ def _get_columns(item): ) -_formatters = {} - - -class HumanReadableSizeColumn(cliff_columns.FormattableColumn): +class HumanReadableSizeColumn(cliff_columns.FormattableColumn[int]): def human_readable(self): """Return a formatted visibility string @@ -84,7 +82,7 @@ class HumanReadableSizeColumn(cliff_columns.FormattableColumn): return '' -class VisibilityColumn(cliff_columns.FormattableColumn): +class VisibilityColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): """Return a formatted visibility string @@ -340,9 +338,12 @@ class CreateImage(command.ShowOne): if image: display_columns, columns = _get_columns(image) - _formatters['properties'] = format_columns.DictColumn data = utils.get_item_properties( - image, columns, formatters=_formatters + image, + columns, + formatters={ + 'properties': format_columns.DictColumn, + }, ) return (display_columns, data) elif info: @@ -493,19 +494,19 @@ class ListImage(command.Lister): column_headers = columns # List of image data received - data = list(image_client.images(**kwargs)) + images = list(image_client.images(**kwargs)) if parsed_args.property: # NOTE(dtroyer): coerce to a list to subscript it in py3 attr, value = list(parsed_args.property.items())[0] api_utils.simple_filter( - data, + images, attr=attr, value=value, property_field='properties', ) - data = utils.sort_items(data, parsed_args.sort) + data = utils.sort_items(images, parsed_args.sort) return ( column_headers, @@ -839,11 +840,13 @@ class ShowImage(command.ShowOne): parsed_args.image, ignore_missing=False ) + formatters: dict[ + str, type[cliff_columns.FormattableColumn[ty.Any]] + ] = { + 'properties': format_columns.DictColumn, + } if parsed_args.human_readable: - _formatters['size'] = HumanReadableSizeColumn + formatters['size'] = HumanReadableSizeColumn display_columns, columns = _get_columns(image) - _formatters['properties'] = format_columns.DictColumn - data = utils.get_item_properties( - image, columns, formatters=_formatters - ) + data = utils.get_item_properties(image, columns, formatters=formatters) return (display_columns, data) diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index 9fbce84..952d9ed 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -17,10 +17,10 @@ import copy import datetime import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index f35c572..a7db86c 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -30,10 +30,10 @@ from openstack import utils as sdk_utils from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.common import progressbar from openstackclient.i18n import _ @@ -551,7 +551,7 @@ class CreateImage(command.ShowOne): sign_cert_id = parsed_args.sign_cert_id signer = image_signer.ImageSigner() try: - pw = utils.get_password( + pw: str | None = utils.get_password( self.app.stdin, prompt=( "Please enter private key password, leave " @@ -562,12 +562,11 @@ class CreateImage(command.ShowOne): if not pw or len(pw) < 1: pw = None - else: - # load_private_key() requires the password to be - # passed as bytes - pw = pw.encode() - signer.load_private_key(sign_key_path, password=pw) + signer.load_private_key( + sign_key_path, + password=pw.encode() if pw else None, + ) except Exception: msg = _( "Error during sign operation: private key " @@ -623,7 +622,7 @@ class CreateImage(command.ShowOne): ) # TODO(stephenfin): These should be an error in a future # version - LOG.warning(msg % opt_name) + LOG.warning(msg, opt_name) source_volume = volume_client.find_volume( parsed_args.volume, ignore_missing=False @@ -933,18 +932,19 @@ class ListImage(command.Lister): if 'limit' in kwargs: # Disable automatic pagination in SDK kwargs['paginated'] = False - data = list(image_client.images(**kwargs)) + + images = list(image_client.images(**kwargs)) if parsed_args.property: for attr, value in parsed_args.property.items(): api_utils.simple_filter( - data, + images, attr=attr, value=value, property_field='properties', ) - data = utils.sort_items(data, parsed_args.sort, str) + data = utils.sort_items(images, parsed_args.sort, str) return ( column_headers, @@ -1393,7 +1393,10 @@ class SetImage(command.Command): if parsed_args.visibility is not None: kwargs['visibility'] = parsed_args.visibility - if parsed_args.project: + # Only set owner_id if --project is used WITHOUT membership flags + # When --project is used with --accept/--reject/--pending, it should + # only identify which member's status to update, not change ownership + if parsed_args.project and not parsed_args.membership: # We already did the project lookup above kwargs['owner_id'] = project_id @@ -1716,6 +1719,7 @@ class ImportImage(command.ShowOne): ) stores_group.add_argument( '--all-stores', + action='store_true', help=_( "Make image available to all stores " "(either '--store' or '--all-stores' required with the " diff --git a/openstackclient/image/v2/info.py b/openstackclient/image/v2/info.py index 469b15a..6884813 100644 --- a/openstackclient/image/v2/info.py +++ b/openstackclient/image/v2/info.py @@ -12,8 +12,8 @@ from osc_lib.cli import format_columns -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py index f753286..af30f71 100644 --- a/openstackclient/image/v2/metadef_namespaces.py +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -18,10 +18,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ _formatters = { diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 09fc249..d5cbec1 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 6027773..3a923c5 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -15,10 +15,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -127,7 +127,8 @@ class DeleteMetadefProperty(command.Command): nargs="*", help=_( "Metadef properties to delete (name) " - "(omit this argument to delete all properties in the namespace)" + "(omit this argument to delete all properties " + "in the namespace)" ), ) return parser diff --git a/openstackclient/image/v2/metadef_resource_type_association.py b/openstackclient/image/v2/metadef_resource_type_association.py index 306783c..4d3ee46 100644 --- a/openstackclient/image/v2/metadef_resource_type_association.py +++ b/openstackclient/image/v2/metadef_resource_type_association.py @@ -12,10 +12,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/image/v2/metadef_resource_types.py b/openstackclient/image/v2/metadef_resource_types.py index 3477df3..88001b7 100644 --- a/openstackclient/image/v2/metadef_resource_types.py +++ b/openstackclient/image/v2/metadef_resource_types.py @@ -12,9 +12,9 @@ """Image V2 Action Implementations""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/task.py b/openstackclient/image/v2/task.py index 1b7170f..a0f1d35 100644 --- a/openstackclient/image/v2/task.py +++ b/openstackclient/image/v2/task.py @@ -11,9 +11,9 @@ # under the License. from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ _formatters = { diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 9bcd165..bc110e0 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -12,17 +12,16 @@ # import abc -import argparse import contextlib import logging import typing as ty -import cliff.app +from cliff import _argparse import openstack.exceptions from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import utils @@ -68,8 +67,6 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): present the options for both network types, often qualified accordingly. """ - app: cliff.app.App - @property def _network_type(self): """Discover whether the running cloud is using neutron or nova-network. @@ -84,7 +81,7 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): # Have we set it up yet for this command? if not hasattr(self, '_net_type'): try: - if self.app.client_manager.is_network_endpoint_enabled(): + if self.app.client_manager.is_network_endpoint_enabled(): # type: ignore net_type = _NET_TYPE_NEUTRON else: net_type = _NET_TYPE_COMPUTE @@ -136,7 +133,7 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): ) ) - def get_parser(self, prog_name: str) -> argparse.ArgumentParser: + def get_parser(self, prog_name: str) -> _argparse.ArgumentParser: LOG.debug('get_parser(%s)', prog_name) parser = super().get_parser(prog_name) # type: ignore parser = self.update_parser_common(parser) @@ -163,11 +160,13 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): def take_action(self, parsed_args): if self.is_neutron: return self.take_action_network( - self.app.client_manager.network, parsed_args + self.app.client_manager.network, # type: ignore + parsed_args, ) elif self.is_nova_network: return self.take_action_compute( - self.app.client_manager.compute, parsed_args + self.app.client_manager.compute, # type: ignore + parsed_args, ) def take_action_network(self, client, parsed_args): @@ -203,6 +202,8 @@ class NetworkAndComputeDelete(NetworkAndComputeCommand, metaclass=abc.ABCMeta): following the rules in doc/source/command-errors.rst. """ + resource: str + def take_action(self, parsed_args): ret = 0 resources = getattr(parsed_args, self.resource, []) diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index d47c34c..178c3af 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -16,10 +16,10 @@ import logging import netaddr -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -151,13 +151,14 @@ class ListAddressGroup(command.Lister): parser.add_argument( '--name', metavar='<name>', - help=_("List only address groups of given name in output"), + help=_("List only address groups with the specified name"), ) parser.add_argument( '--project', metavar="<project>", help=_( - "List address groups according to their project (name or ID)" + "List only address groups with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index a7f396b..8a38dab 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -160,7 +160,7 @@ class ListAddressScope(command.Lister): parser.add_argument( '--name', metavar='<name>', - help=_("List only address scopes of given name in output"), + help=_("List only address scopes with the specified name"), ) parser.add_argument( '--ip-version', @@ -169,14 +169,16 @@ class ListAddressScope(command.Lister): metavar='<ip-version>', dest='ip_version', help=_( - "List address scopes of given IP version networks (4 or 6)" + "List only address scopes with the specified IP version " + "networks (4 or 6)" ), ) parser.add_argument( '--project', metavar="<project>", help=_( - "List address scopes according to their project (name or ID)" + "List only address scopes with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -185,12 +187,12 @@ class ListAddressScope(command.Lister): shared_group.add_argument( '--share', action='store_true', - help=_("List address scopes shared between projects"), + help=_("List only address scopes shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List address scopes not shared between projects"), + help=_("List only address scopes not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index fddec68..2447585 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -16,10 +16,10 @@ import logging from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common from openstackclient.network import utils as network_utils @@ -150,7 +150,7 @@ class CreateDefaultSecurityGroupRule( return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network # Build the create attributes. attrs = {} attrs['protocol'] = network_utils.get_protocol(parsed_args) @@ -248,7 +248,7 @@ class DeleteDefaultSecurityGroupRule(command.Command): def take_action(self, parsed_args): result = 0 - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network for r in parsed_args.rule: try: obj = client.find_default_security_group_rule( @@ -300,7 +300,8 @@ class ListDefaultSecurityGroupRule(command.Lister): metavar='<protocol>', type=network_utils.convert_to_lowercase, help=_( - "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " + "List only default rules with the specified IP protocol " + "(ah, dhcp, egp, esp, gre, " "icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, " "ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, " "sctp, tcp, udp, udplite, vrrp and integer " @@ -319,7 +320,7 @@ class ListDefaultSecurityGroupRule(command.Lister): '--ingress', action='store_true', help=_( - "List default rules which will be applied to incoming " + "List only default rules which will be applied to incoming " "network traffic" ), ) @@ -327,14 +328,14 @@ class ListDefaultSecurityGroupRule(command.Lister): '--egress', action='store_true', help=_( - "List default rules which will be applied to outgoing " + "List only default rules which will be applied to outgoing " "network traffic" ), ) return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network column_headers = ( 'ID', 'IP Protocol', @@ -403,7 +404,7 @@ class ShowDefaultSecurityGroupRule(command.ShowOne): return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network obj = client.find_default_security_group_rule( parsed_args.rule, ignore_missing=False ) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 0b2f7fa..76b91a0 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -247,7 +247,7 @@ class ListFloatingIP(common.NetworkAndComputeLister): action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given network " + "List only floating IP(s) with the specified network " "(name or ID) " "(repeat option to fiter on multiple networks)" ) @@ -260,7 +260,8 @@ class ListFloatingIP(common.NetworkAndComputeLister): action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given port (name or ID) " + "List only floating IP(s) with the specified port " + "(name or ID) " "(repeat option to fiter on multiple ports)" ) ), @@ -269,14 +270,20 @@ class ListFloatingIP(common.NetworkAndComputeLister): '--fixed-ip-address', metavar='<ip-address>', help=self.enhance_help_neutron( - _("List floating IP(s) according to given fixed IP address") + _( + "List only floating IP(s) with the specified fixed IP " + "address" + ) ), ) parser.add_argument( '--floating-ip-address', metavar='<ip-address>', help=self.enhance_help_neutron( - _("List floating IP(s) according to given floating IP address") + _( + "List only floating IP(s) with the specified floating IP " + "address" + ) ), ) parser.add_argument( @@ -285,8 +292,8 @@ class ListFloatingIP(common.NetworkAndComputeLister): choices=['ACTIVE', 'DOWN'], help=self.enhance_help_neutron( _( - "List floating IP(s) according to given status ('ACTIVE', " - "'DOWN')" + "List only floating IP(s) with the specified status " + "('ACTIVE', 'DOWN')" ) ), ) @@ -295,8 +302,8 @@ class ListFloatingIP(common.NetworkAndComputeLister): metavar='<project>', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given project " - "(name or ID) " + "List only floating IP(s) with the specified project " + "(name or ID)" ) ), ) @@ -308,7 +315,7 @@ class ListFloatingIP(common.NetworkAndComputeLister): action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given router " + "List only floating IP(s) with the specified router " "(name or ID) " "(repeat option to fiter on multiple routers)" ) diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 3810ded..cf770c2 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -16,10 +16,10 @@ import logging import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -265,8 +265,8 @@ class ListFloatingIPPortForwarding(command.Lister): '--port', metavar='<port>', help=_( - "Filter the list result by the ID or name of " - "the internal network port" + "List only floating IP port forwardings with the " + "specified internal network port (name or ID)" ), ) parser.add_argument( @@ -274,14 +274,17 @@ class ListFloatingIPPortForwarding(command.Lister): metavar='<port-number>', dest='external_protocol_port', help=_( - "Filter the list result by the " - "protocol port number of the floating IP" + "List only floating IP port forwardings with the " + "specified external protocol port number" ), ) parser.add_argument( '--protocol', metavar='<protocol>', - help=_("Filter the list result by the port protocol"), + help=_( + "List only floating IP port forwardings with the " + "specified protocol number" + ), ) return parser diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index 51ea011..f78c7ec 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -14,9 +14,9 @@ """IP Availability Info implementations""" from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -47,14 +47,17 @@ class ListIPAvailability(command.Lister): metavar='<ip-version>', dest='ip_version', help=_( - "List IP availability of given IP version " - "networks (default is 4)" + "List only IP availability with the specified IP version " + "networks (4 or 6, default is 4)" ), ) parser.add_argument( '--project', metavar='<project>', - help=_("List IP availability of given project (name or ID)"), + help=_( + "List only IP availability with the specified project " + "(name or ID)" + ), ) identity_common.add_project_domain_option_to_parser(parser) return parser diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index 011db39..742c263 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -150,19 +150,26 @@ class ListConntrackHelper(command.Lister): parser.add_argument( '--helper', metavar='<helper>', - help=_('The netfilter conntrack helper module'), + help=_( + 'List only helpers using the specified netfilter conntrack ' + 'helper module' + ), ) parser.add_argument( '--protocol', metavar='<protocol>', help=_( - 'The network protocol for the netfilter conntrack target rule' + 'List only helpers with the specified network protocol for ' + 'the netfilter conntrack target rule' ), ) parser.add_argument( '--port', metavar='<port>', - help=_('The network port for the netfilter conntrack target rule'), + help=_( + 'List only helpers with the specified network port for ' + 'the netfilter conntrack target rule (name or ID)' + ), ) return parser diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index 7fd5881..dde6ccd 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -198,32 +198,38 @@ class ListLocalIP(command.Lister): parser.add_argument( '--name', metavar='<name>', - help=_("List only Local IPs of given name in output"), + help=_("List only local IP(s) with the specified name"), ) parser.add_argument( '--project', metavar="<project>", - help=_("List Local IPs according to their project (name or ID)"), + help=_( + "List only local IP(s) with the specified project (name or ID)" + ), ) parser.add_argument( '--network', metavar='<network>', - help=_("List Local IP(s) according to given network (name or ID)"), + help=_( + "List only local IP(s) with the specified network (name or ID)" + ), ) parser.add_argument( '--local-port', metavar='<local-port>', - help=_("List Local IP(s) according to given port (name or ID)"), + help=_( + "List only local IP(s) with the specified port (name or ID)" + ), ) parser.add_argument( '--local-ip-address', metavar='<local-ip-address>', - help=_("List Local IP(s) according to given Local IP Address"), + help=_("List only local IP(s) with the specified IP address"), ) parser.add_argument( '--ip-mode', metavar='<ip_mode>', - help=_("List Local IP(s) according to given IP mode"), + help=_("List only local IP(s) with the specified IP mode"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index c6b5918..123faa6 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -150,18 +150,21 @@ class ListLocalIPAssociation(command.Lister): '--fixed-port', metavar='<fixed-port>', help=_( - "Filter the list result by the ID or name of the fixed port" + "List only local IP assocations with the specified fixed IP " + "port (name or ID)" ), ) parser.add_argument( '--fixed-ip', metavar='<fixed-ip>', - help=_("Filter the list result by fixed ip"), + help=_( + "List only local IP associations with the specified fixed IP" + ), ) parser.add_argument( '--host', metavar='<host>', - help=_("Filter the list result by given host"), + help=_("List only local IP associations with the specified host"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index fe97cd0..78a7ae9 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -143,30 +143,37 @@ class ListNDPProxy(command.Lister): '--router', metavar='<router>', help=_( - "List only NDP proxies belonging to this router (name or ID)" + "List only NDP proxies associated with the specifed router " + "(name or ID)" ), ) parser.add_argument( '--port', metavar='<port>', help=_( - "List only NDP proxies associated to this port (name or ID)" + "List only NDP proxies associated with the specified port " + "(name or ID)" ), ) parser.add_argument( '--ip-address', metavar='<ip-address>', - help=_("List only NDP proxies associated to this IPv6 address"), + help=_( + "List only NDP proxies associated with the specified " + "IPv6 address" + ), ) parser.add_argument( '--project', metavar='<project>', - help=_("List only NDP proxies of given project (name or ID)"), + help=_( + "List only NDP proxies with the specified project (name or ID)" + ), ) parser.add_argument( '--name', metavar='<name>', - help=_("List only NDP proxies of given name"), + help=_("List only NDP proxies with the specified name"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index e04efed..c8e1f36 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -24,12 +24,12 @@ from openstackclient.identity import common as identity_common from openstackclient.network import common -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' -class RouterExternalColumn(cliff_columns.FormattableColumn): +class RouterExternalColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'External' if self._value else 'Internal' @@ -401,6 +401,16 @@ class CreateNetwork( ) raise exceptions.CommandError(msg) + if ( + parsed_args.segmentation_id + and not parsed_args.provider_network_type + ): + msg = _( + "--provider-segment requires --provider-network-type " + "to be specified." + ) + raise exceptions.CommandError(msg) + attrs.update( self._parse_extra_properties(parsed_args.extra_properties) ) @@ -459,12 +469,12 @@ class ListNetwork(common.NetworkAndComputeLister): router_ext_group.add_argument( '--external', action='store_true', - help=self.enhance_help_neutron(_("List external networks")), + help=self.enhance_help_neutron(_("List only external networks")), ) router_ext_group.add_argument( '--internal', action='store_true', - help=self.enhance_help_neutron(_("List internal networks")), + help=self.enhance_help_neutron(_("List only internal networks")), ) parser.add_argument( '--long', @@ -477,24 +487,26 @@ class ListNetwork(common.NetworkAndComputeLister): '--name', metavar='<name>', help=self.enhance_help_neutron( - _("List networks according to their name") + _("List only networks with the specified name") ), ) admin_state_group = parser.add_mutually_exclusive_group() admin_state_group.add_argument( '--enable', action='store_true', - help=self.enhance_help_neutron(_("List enabled networks")), + help=self.enhance_help_neutron(_("List only enabled networks")), ) admin_state_group.add_argument( '--disable', action='store_true', - help=self.enhance_help_neutron(_("List disabled networks")), + help=self.enhance_help_neutron(_("List only disabled networks")), ) parser.add_argument( '--project', metavar='<project>', - help=_("List networks according to their project (name or ID)"), + help=_( + "List only networks with the specified project (name or ID)" + ), ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron @@ -504,14 +516,14 @@ class ListNetwork(common.NetworkAndComputeLister): '--share', action='store_true', help=self.enhance_help_neutron( - _("List networks shared between projects") + _("List only networks shared between projects") ), ) shared_group.add_argument( '--no-share', action='store_true', help=self.enhance_help_neutron( - _("List networks not shared between projects") + _("List only networks not shared between projects") ), ) parser.add_argument( @@ -520,7 +532,7 @@ class ListNetwork(common.NetworkAndComputeLister): choices=['ACTIVE', 'BUILD', 'DOWN', 'ERROR'], help=self.enhance_help_neutron( _( - "List networks according to their status " + "List only networks with the specified status " "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" ) ), @@ -531,7 +543,8 @@ class ListNetwork(common.NetworkAndComputeLister): choices=['flat', 'geneve', 'gre', 'local', 'vlan', 'vxlan'], help=self.enhance_help_neutron( _( - "List networks according to their physical mechanisms. " + "List only networks with the specified physical " + "mechanisms. " "The supported options are: flat, geneve, gre, local, " "vlan and vxlan." ) @@ -542,7 +555,10 @@ class ListNetwork(common.NetworkAndComputeLister): metavar='<provider-physical-network>', dest='physical_network', help=self.enhance_help_neutron( - _("List networks according to name of the physical network") + _( + "List only networks with the specified physical network " + "name" + ) ), ) parser.add_argument( @@ -551,8 +567,9 @@ class ListNetwork(common.NetworkAndComputeLister): dest='segmentation_id', help=self.enhance_help_neutron( _( - "List networks according to VLAN ID for VLAN networks or " - "Tunnel ID for GENEVE/GRE/VXLAN networks" + "List only networks with the specified provider segment " + "ID (VLAN ID for VLAN networks or " + "Tunnel ID for GENEVE/GRE/VXLAN networks)" ) ), ) @@ -561,7 +578,7 @@ class ListNetwork(common.NetworkAndComputeLister): metavar='<agent-id>', dest='agent_id', help=self.enhance_help_neutron( - _('List networks hosted by agent (ID only)') + _('List only networks hosted the specified agent (ID only)') ), ) _tag.add_tag_filtering_option_to_parser( diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index 35b0fab..806422b 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -17,21 +17,21 @@ import logging from cliff import columns as cliff_columns from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class AliveColumn(cliff_columns.FormattableColumn): +class AliveColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return ":-)" if self._value else "XXX" -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' @@ -205,12 +205,12 @@ class ListNetworkAgent(command.Lister): agent_type_group.add_argument( '--network', metavar='<network>', - help=_('List agents hosting a network (name or ID)'), + help=_('List agents hosting the specified network (name or ID)'), ) agent_type_group.add_argument( '--router', metavar='<router>', - help=_('List agents hosting this router (name or ID)'), + help=_('List agents hosting the specified router (name or ID)'), ) parser.add_argument( '--long', diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index 3f5795e..1107cb7 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -15,9 +15,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index c4d262d..99993f6 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index eeb4ce8..9792b01 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -13,10 +13,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index 625b742..bb3bf94 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index c755154..7a7c231 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -16,10 +16,10 @@ import logging import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 1a96f93..ab620b8 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -14,12 +14,13 @@ # under the License. import logging +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -27,7 +28,7 @@ from openstackclient.network import common LOG = logging.getLogger(__name__) -class RulesColumn(cliff_columns.FormattableColumn): +class RulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return '\n'.join(str(v) for v in self._value) @@ -190,7 +191,8 @@ class ListNetworkQosPolicy(command.Lister): '--project', metavar='<project>', help=_( - "List QoS policies according to their project (name or ID)" + "List only QoS policies with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -198,12 +200,12 @@ class ListNetworkQosPolicy(command.Lister): shared_group.add_argument( '--share', action='store_true', - help=_("List QoS policies shared between projects"), + help=_("List only QoS policies shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List QoS policies not shared between projects"), + help=_("List only QoS policies not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 86f342f..b2ddd82 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -15,10 +15,10 @@ import itertools -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py index fd4da84..c79c5fe 100644 --- a/openstackclient/network/v2/network_qos_rule_type.py +++ b/openstackclient/network/v2/network_qos_rule_type.py @@ -13,9 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index 0df7437..200175a 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -234,8 +234,8 @@ class ListNetworkRBAC(command.Lister): 'network', ], help=_( - 'List network RBAC policies according to ' - 'given object type ("address_group", "address_scope", ' + 'List only network RBAC policies with the specified ' + 'object type ("address_group", "address_scope", ' '"security_group", "subnetpool", "qos_policy" or ' '"network")' ), @@ -245,14 +245,17 @@ class ListNetworkRBAC(command.Lister): metavar='<action>', choices=['access_as_external', 'access_as_shared'], help=_( - 'List network RBAC policies according to given ' + 'List only network RBAC policies with the specified ' 'action ("access_as_external" or "access_as_shared")' ), ) parser.add_argument( '--target-project', metavar='<target-project>', - help=_('List network RBAC policies for a specific target project'), + help=_( + 'List only network RBAC policies with the specified ' + 'target project (name or ID)' + ), ) parser.add_argument( '--long', diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 5e40108..c2e7bec 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common @@ -160,7 +160,7 @@ class ListNetworkSegment(command.Lister): '--network', metavar='<network>', help=_( - 'List network segments that belong to this ' + 'List only network segments associated with the specified ' 'network (name or ID)' ), ) diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index e2716f1..c8b9a0e 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -20,10 +20,10 @@ import itertools import logging import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -315,25 +315,32 @@ class ListNetworkSegmentRange(command.Lister): used_group.add_argument( '--used', action='store_true', - help=_('List network segment ranges that have segments in use'), + help=_( + 'List only network segment ranges that have segments in use' + ), ) used_group.add_argument( '--unused', action='store_true', help=_( - 'List network segment ranges that have segments not in use' + 'List only network segment ranges that have segments ' + 'not in use' ), ) available_group = parser.add_mutually_exclusive_group() available_group.add_argument( '--available', action='store_true', - help=_('List network segment ranges that have available segments'), + help=_( + 'List only network segment ranges that have available segments' + ), ) available_group.add_argument( '--unavailable', action='store_true', - help=_('List network segment ranges without available segments'), + help=_( + 'List only network segment ranges without available segments' + ), ) return parser @@ -395,7 +402,7 @@ class ListNetworkSegmentRange(command.Lister): 'available', ) - display_props: tuple[str, ...] = tuple() + display_props: tuple[ty.Any, ...] = tuple() for s in data: props = utils.get_item_properties(s, columns) if ( diff --git a/openstackclient/network/v2/network_service_provider.py b/openstackclient/network/v2/network_service_provider.py index f743bfa..1433c09 100644 --- a/openstackclient/network/v2/network_service_provider.py +++ b/openstackclient/network/v2/network_service_provider.py @@ -13,9 +13,9 @@ """Network Service Providers Implementation""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index f8f1c5b..974a997 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -23,10 +23,10 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import identity as identity_utils from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -36,7 +36,7 @@ TRUNKS = 'trunks' SUB_PORTS = 'sub_ports' -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' @@ -88,9 +88,12 @@ class CreateNetworkTrunk(command.ShowOne): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) - obj = client.create_trunk(**attrs) + network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity + attrs = _get_attrs_for_trunk( + network_client, identity_client, parsed_args + ) + obj = network_client.create_trunk(**attrs) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( obj, columns, formatters=_formatters @@ -112,12 +115,15 @@ class DeleteNetworkTrunk(command.Command): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network + network_client = self.app.client_manager.network result = 0 for trunk in parsed_args.trunk: try: - trunk_id = client.find_trunk(trunk).id - client.delete_trunk(trunk_id) + trunk_id = network_client.find_trunk( + trunk, + ignore_missing=False, + ).id + network_client.delete_trunk(trunk_id) except Exception as e: result += 1 LOG.error( @@ -150,8 +156,8 @@ class ListNetworkTrunk(command.Lister): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - data = client.trunks() + network_client = self.app.client_manager.network + data = network_client.trunks() headers: tuple[str, ...] = ('ID', 'Name', 'Parent Port', 'Description') columns: tuple[str, ...] = ('id', 'name', 'port_id', 'description') if parsed_args.long: @@ -215,11 +221,17 @@ class SetNetworkTrunk(command.Command): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk) - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) + network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) + attrs = _get_attrs_for_trunk( + network_client, identity_client, parsed_args + ) try: - client.update_trunk(trunk_id, **attrs) + network_client.update_trunk(trunk_id, **attrs) except Exception as e: msg = _("Failed to set trunk '%(t)s': %(e)s") % { 't': parsed_args.trunk, @@ -228,10 +240,10 @@ class SetNetworkTrunk(command.Command): raise exceptions.CommandError(msg) if parsed_args.set_subports: subport_attrs = _get_attrs_for_subports( - self.app.client_manager, parsed_args + network_client, parsed_args ) try: - client.add_trunk_subports(trunk_id, subport_attrs) + network_client.add_trunk_subports(trunk_id, subport_attrs) except Exception as e: msg = _("Failed to add subports to trunk '%(t)s': %(e)s") % { 't': parsed_args.trunk, @@ -251,9 +263,12 @@ class ShowNetworkTrunk(command.ShowOne): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk).id - obj = client.get_trunk(trunk_id) + network_client = self.app.client_manager.network + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ).id + obj = network_client.get_trunk(trunk_id) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( obj, columns, formatters=_formatters @@ -270,14 +285,17 @@ class ListNetworkSubport(command.Lister): '--trunk', required=True, metavar="<trunk>", - help=_("List subports belonging to this trunk (name or ID)"), + help=_("List only subports belonging to this trunk (name or ID)"), ) return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk) - data = client.get_trunk_subports(trunk_id) + network_client = self.app.client_manager.network + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) + data = network_client.get_trunk_subports(trunk_id) headers: tuple[str, ...] = ( 'Port', 'Segmentation Type', @@ -324,10 +342,13 @@ class UnsetNetworkTrunk(command.Command): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) - trunk_id = client.find_trunk(parsed_args.trunk) - client.delete_trunk_subports(trunk_id, attrs) + network_client = self.app.client_manager.network + attrs = _get_attrs_for_subports(network_client, parsed_args) + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) + network_client.delete_trunk_subports(trunk_id, attrs) _formatters = { @@ -343,7 +364,7 @@ def _get_columns(item): ) -def _get_attrs_for_trunk(client_manager, parsed_args): +def _get_attrs_for_trunk(network_client, identity_client, parsed_args): attrs: dict[str, ty.Any] = {} if parsed_args.name is not None: attrs['name'] = str(parsed_args.name) @@ -354,18 +375,18 @@ def _get_attrs_for_trunk(client_manager, parsed_args): if parsed_args.disable: attrs['admin_state_up'] = False if 'parent_port' in parsed_args and parsed_args.parent_port is not None: - port_id = client_manager.network.find_port(parsed_args.parent_port)[ - 'id' - ] + port_id = network_client.find_port( + parsed_args.parent_port, + ignore_missing=False, + ).id attrs['port_id'] = port_id if 'add_subports' in parsed_args and parsed_args.add_subports is not None: attrs[SUB_PORTS] = _format_subports( - client_manager, parsed_args.add_subports + network_client, parsed_args.add_subports ) # "trunk set" command doesn't support setting project. if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity project_id = identity_utils.find_project( identity_client, parsed_args.project, @@ -376,12 +397,15 @@ def _get_attrs_for_trunk(client_manager, parsed_args): return attrs -def _format_subports(client_manager, subports): +def _format_subports(network_client, subports): attrs = [] for subport in subports: subport_attrs = {} if subport.get('port'): - port_id = client_manager.network.find_port(subport['port'])['id'] + port_id = network_client.find_port( + subport['port'], + ignore_missing=False, + ).id subport_attrs['port_id'] = port_id if subport.get('segmentation-id'): try: @@ -400,21 +424,20 @@ def _format_subports(client_manager, subports): return attrs -def _get_attrs_for_subports(client_manager, parsed_args): +def _get_attrs_for_subports(network_client, parsed_args): attrs = [] if 'set_subports' in parsed_args and parsed_args.set_subports is not None: - attrs = _format_subports(client_manager, parsed_args.set_subports) + attrs = _format_subports(network_client, parsed_args.set_subports) if ( 'unset_subports' in parsed_args and parsed_args.unset_subports is not None ): subports_list = [] for subport in parsed_args.unset_subports: - port_id = client_manager.network.find_port(subport)['id'] + port_id = network_client.find_port( + subport, + ignore_missing=False, + )['id'] subports_list.append({'port_id': port_id}) attrs = subports_list return attrs - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, str(id_or_name))['id'] diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 5cfc41d..e120515 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -22,11 +22,11 @@ import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -34,7 +34,7 @@ from openstackclient.network import common LOG = logging.getLogger(__name__) -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' @@ -636,11 +636,11 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - _network = client.find_network( + network_client = self.app.client_manager.network + network = network_client.find_network( parsed_args.network, ignore_missing=False ) - parsed_args.network = _network.id + parsed_args.network = network.id _prepare_fixed_ips(self.app.client_manager, parsed_args) attrs = _get_attrs(self.app.client_manager, parsed_args) @@ -654,7 +654,7 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): if parsed_args.security_groups is not None: attrs['security_group_ids'] = [ - client.find_security_group(sg, ignore_missing=False).id + network_client.find_security_group(sg, ignore_missing=False).id for sg in parsed_args.security_groups ] @@ -667,7 +667,7 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): attrs["extra_dhcp_opts"] = _convert_extra_dhcp_options(parsed_args) if parsed_args.qos_policy: - attrs['qos_policy_id'] = client.find_qos_policy( + attrs['qos_policy_id'] = network_client.find_qos_policy( parsed_args.qos_policy, ignore_missing=False ).id @@ -675,7 +675,9 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): _validate_port_hints(parsed_args.hint) expanded_hints = _expand_port_hint_aliases(parsed_args.hint) try: - client.find_extension('port-hints', ignore_missing=False) + network_client.find_extension( + 'port-hints', ignore_missing=False + ) except Exception as e: msg = _('Not supported by Network API: %(e)s') % {'e': e} raise exceptions.CommandError(msg) @@ -686,7 +688,7 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): in expanded_hints['openvswitch']['other_config'] ): try: - client.find_extension( + network_client.find_extension( 'port-hint-ovs-tx-steering', ignore_missing=False ) except Exception as e: @@ -695,7 +697,9 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): attrs['hints'] = expanded_hints set_tags_in_post = bool( - client.find_extension('tag-ports-during-bulk-creation') + network_client.find_extension( + 'tag-ports-during-bulk-creation', ignore_missing=True + ) ) if set_tags_in_post: if parsed_args.no_tag: @@ -707,14 +711,12 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): self._parse_extra_properties(parsed_args.extra_properties) ) - with common.check_missing_extension_if_error( - self.app.client_manager.network, attrs - ): - obj = client.create_port(**attrs) + with common.check_missing_extension_if_error(network_client, attrs): + obj = network_client.create_port(**attrs) if not set_tags_in_post: # tags cannot be set when created, so tags need to be set later. - _tag.update_tags_for_set(client, obj, parsed_args) + _tag.update_tags_for_set(network_client, obj, parsed_args) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) @@ -808,7 +810,7 @@ class ListPort(command.Lister): parser.add_argument( '--mac-address', metavar='<mac-address>', - help=_("List only ports with this MAC address"), + help=_("List only ports with the specified MAC address"), ) parser.add_argument( '--long', @@ -819,12 +821,12 @@ class ListPort(command.Lister): parser.add_argument( '--project', metavar='<project>', - help=_("List ports according to their project (name or ID)"), + help=_("List only ports with the specified project (name or ID)"), ) parser.add_argument( '--name', metavar='<name>', - help=_("List ports according to their name"), + help=_("List only ports with the specified name"), ) parser.add_argument( '--security-group', @@ -842,7 +844,7 @@ class ListPort(command.Lister): metavar='<status>', choices=('ACTIVE', 'BUILD', 'DOWN', 'ERROR'), help=_( - "List ports according to their status " + "List only ports with the specified status " "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" ), ) @@ -859,7 +861,7 @@ class ListPort(command.Lister): "Desired IP and/or subnet for filtering ports " "(name or ID): subnet=<subnet>,ip-address=<ip-address>," "ip-substring=<ip-substring> " - "(repeat option to set multiple fixed IP addresses)" + "(repeat option to filter multiple fixed IP addresses)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('ports')) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 67e3de0..939167d 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -23,11 +23,11 @@ import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -35,12 +35,12 @@ from openstackclient.network import common LOG = logging.getLogger(__name__) -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' -class RouterInfoColumn(cliff_columns.FormattableColumn): +class RouterInfoColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): try: return json.dumps(self._value) @@ -48,7 +48,7 @@ class RouterInfoColumn(cliff_columns.FormattableColumn): return '' -class RoutesColumn(cliff_columns.FormattableColumn): +class RoutesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): # Map the route keys to match --route option. for route in self._value or []: @@ -89,7 +89,12 @@ def _get_columns(item): def is_multiple_gateways_supported(n_client): - return n_client.find_extension("external-gateway-multihoming") is not None + return ( + n_client.find_extension( + "external-gateway-multihoming", ignore_missing=True + ) + is not None + ) def _passed_multiple_gateways(extension_supported, external_gateways): @@ -236,7 +241,9 @@ def _get_attrs(client_manager, parsed_args): # "router set" command doesn't support setting flavor_id. if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None: - flavor = n_client.find_flavor(parsed_args.flavor_id) + flavor = n_client.find_flavor( + parsed_args.flavor_id, ignore_missing=False + ) attrs['flavor_id'] = flavor.id elif 'flavor' in parsed_args and parsed_args.flavor is not None: flavor = n_client.find_flavor(parsed_args.flavor, ignore_missing=False) @@ -719,13 +726,17 @@ class ListRouter(command.Lister): parser.add_argument( '--project', metavar='<project>', - help=_("List routers according to their project (name or ID)"), + help=_( + "List only routers with the specified project (name or ID)" + ), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--agent', metavar='<agent-id>', - help=_("List routers hosted by an agent (ID only)"), + help=_( + "List only routers hosted by the specified agent (ID only)" + ), ) _tag.add_tag_filtering_option_to_parser(parser, _('routers')) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 6fb98c6..c6930de 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -14,13 +14,14 @@ """Security Group action implementations""" import argparse +import typing as ty from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import utils from osc_lib.utils import tags as _tag from openstackclient.api import compute_v2 +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -65,12 +66,12 @@ def _format_compute_security_group_rules(sg_rules): return utils.format_list(rules, separator='\n') -class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn): +class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return _format_network_security_group_rules(self._value) -class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn): +class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return _format_compute_security_group_rules(self._value) @@ -246,7 +247,10 @@ class ListSecurityGroup(common.NetworkAndComputeLister): '--project', metavar='<project>', help=self.enhance_help_neutron( - _("List security groups according to the project (name or ID)") + _( + "List only security groups with the specified project " + "(name or ID)" + ) ), ) identity_common.add_project_domain_option_to_parser( @@ -259,14 +263,14 @@ class ListSecurityGroup(common.NetworkAndComputeLister): action='store_true', dest='shared', default=None, - help=_("List security groups shared between projects"), + help=_("List only security groups shared between projects"), ) shared_group.add_argument( '--no-share', action='store_false', dest='shared', default=None, - help=_("List security groups not shared between projects"), + help=_("List only security groups not shared between projects"), ) _tag.add_tag_filtering_option_to_parser( diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 6cb0f7f..f6baac3 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -387,7 +387,8 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( _( - "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " + "List only rules with the specified IP protocol " + "(ah, dhcp, egp, esp, gre, " "icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, " "ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, " "sctp, tcp, udp, udplite, vrrp and integer " @@ -401,7 +402,10 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): metavar='<ethertype>', type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( - _("List rules by the Ethertype (IPv4 or IPv6)") + _( + "List only rules with the specified Ethertype " + "(IPv4 or IPv6)" + ) ), ) direction_group = parser.add_mutually_exclusive_group() @@ -409,14 +413,14 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): '--ingress', action='store_true', help=self.enhance_help_neutron( - _("List rules applied to incoming network traffic") + _("List only rules applied to incoming network traffic") ), ) direction_group.add_argument( '--egress', action='store_true', help=self.enhance_help_neutron( - _("List rules applied to outgoing network traffic") + _("List only rules applied to outgoing network traffic") ), ) parser.add_argument( @@ -430,7 +434,9 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): parser.add_argument( '--project', metavar='<project>', - help=self.enhance_help_neutron(_("Owner's project (name or ID)")), + help=self.enhance_help_neutron( + _("List only rules with the specified project (name or ID)") + ), ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 30196bf..3357f89 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -20,11 +20,11 @@ import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -44,7 +44,7 @@ def _update_arguments(obj_list, parsed_args_list, option): raise exceptions.CommandError(msg) -class AllocationPoolsColumn(cliff_columns.FormattableColumn): +class AllocationPoolsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): pool_formatted = [ '{}-{}'.format(pool.get('start', ''), pool.get('end', '')) @@ -53,7 +53,7 @@ class AllocationPoolsColumn(cliff_columns.FormattableColumn): return ','.join(pool_formatted) -class HostRoutesColumn(cliff_columns.FormattableColumn): +class HostRoutesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): # Map the host route keys to match --host-route option. return utils.format_list_of_dicts( @@ -61,7 +61,7 @@ class HostRoutesColumn(cliff_columns.FormattableColumn): ) -class UnsortedListColumn(cliff_columns.FormattableColumn): +class UnsortedListColumn(cliff_columns.FormattableColumn[list[ty.Any]]): # format_columns.ListColumn sorts the output, but for things like # DNS server addresses the order matters def human_readable(self): @@ -493,7 +493,7 @@ class ListSubnet(command.Lister): metavar='<ip-version>', dest='ip_version', help=_( - "List only subnets of given IP version in output. " + "List only subnets with the specified IP version. " "Allowed values for IP version are 4 and 6." ), ) @@ -501,12 +501,12 @@ class ListSubnet(command.Lister): dhcp_enable_group.add_argument( '--dhcp', action='store_true', - help=_("List subnets which have DHCP enabled"), + help=_("List only subnets which have DHCP enabled"), ) dhcp_enable_group.add_argument( '--no-dhcp', action='store_true', - help=_("List subnets which have DHCP disabled"), + help=_("List only subnets which have DHCP disabled"), ) parser.add_argument( '--service-type', @@ -514,7 +514,7 @@ class ListSubnet(command.Lister): action='append', dest='service_types', help=_( - "List only subnets of a given service type in output, " + "List only subnets with the specified service type, " "for example, network:floatingip_agent_gateway. " "Must be a valid device owner value for a network port " "(repeat option to list multiple service types)." @@ -524,8 +524,7 @@ class ListSubnet(command.Lister): '--project', metavar='<project>', help=_( - "List only subnets which belong to a given project " - "in output (name or ID)" + "List only subnets with the specified project (name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -533,26 +532,26 @@ class ListSubnet(command.Lister): '--network', metavar='<network>', help=_( - "List only subnets which belong to a given network " - "in output (name or ID)" + "List only subnets which belong to the specified network " + "(name or ID)" ), ) parser.add_argument( '--gateway', metavar='<gateway>', - help=_("List only subnets of given gateway IP in output"), + help=_("List only subnets with the specified gateway IP"), ) parser.add_argument( '--name', metavar='<name>', - help=_("List only subnets of given name in output"), + help=_("List only subnets with the specified name"), ) parser.add_argument( '--subnet-range', metavar='<subnet-range>', help=_( - "List only subnets of given subnet range " - "(in CIDR notation) in output. " + "List only subnets with the specified subnet range " + "(in CIDR notation). " "For example, --subnet-range 10.10.0.0/16" ), ) @@ -560,8 +559,8 @@ class ListSubnet(command.Lister): '--subnet-pool', metavar='<subnet-pool>', help=_( - "List only subnets which belong to a given subnet pool " - "in output (name or ID)" + "List only subnets which belong to the specified subnet pool " + "(name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnets')) diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 2917c40..399ce48 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -17,11 +17,11 @@ import logging from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -278,26 +278,27 @@ class ListSubnetPool(command.Lister): shared_group.add_argument( '--share', action='store_true', - help=_("List subnet pools shared between projects"), + help=_("List only subnet pools shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List subnet pools not shared between projects"), + help=_("List only subnet pools not shared between projects"), ) default_group = parser.add_mutually_exclusive_group() default_group.add_argument( '--default', action='store_true', help=_( - "List subnet pools used as the default external subnet pool" + "List only subnet pools used as the default external " + "subnet pool" ), ) default_group.add_argument( '--no-default', action='store_true', help=_( - "List subnet pools not used as the default external " + "List only subnet pools not used as the default external " "subnet pool" ), ) @@ -305,21 +306,22 @@ class ListSubnetPool(command.Lister): '--project', metavar='<project>', help=_( - "List subnet pools according to their project (name or ID)" + "List only subnet pools with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--name', metavar='<name>', - help=_("List only subnet pools of given name in output"), + help=_("List only subnet pools with the specified name"), ) parser.add_argument( '--address-scope', metavar='<address-scope>', help=_( - "List only subnet pools of given address scope " - "in output (name or ID)" + "List only subnet pools with the specified address scope " + "(name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnet pools')) diff --git a/openstackclient/network/v2/taas/__init__.py b/openstackclient/network/v2/taas/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/openstackclient/network/v2/taas/__init__.py diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py new file mode 100644 index 0000000..206fbde --- /dev/null +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -0,0 +1,245 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import format_columns +from osc_lib.cli import identity as identity_utils +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient import command +from openstackclient.i18n import _ +from openstackclient.identity import common +from openstackclient.network.v2.taas import tap_service + +LOG = logging.getLogger(__name__) + +TAP_FLOW = 'tap_flow' +TAP_FLOWS = f'{TAP_FLOW}s' + +_attr_map = [ + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('source_port', 'source_port', column_util.LIST_BOTH), + ('tap_service_id', 'tap_service_id', column_util.LIST_BOTH), + ('direction', 'Direction', column_util.LIST_BOTH), +] + +_formatters = { + 'vlan_filter': format_columns.ListColumn, +} + + +def _add_updatable_args(parser): + parser.add_argument('--name', help=_('Name of the tap flow.')) + parser.add_argument( + '--description', help=_('Description of the tap flow.') + ) + + +class CreateTapFlow(command.ShowOne): + _description = _("Create a new tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + required=True, + metavar="SOURCE_PORT", + help=_('Source port (name or ID) to monitor.'), + ) + parser.add_argument( + '--tap-service', + required=True, + metavar="TAP_SERVICE", + help=_( + 'Tap service (name or ID) to associate with this tap flow.' + ), + ) + parser.add_argument( + '--direction', + required=True, + metavar="DIRECTION", + choices=['IN', 'OUT', 'BOTH'], + type=lambda s: s.upper(), + help=_( + 'Direction of the Tap flow. Valid options are: ' + 'IN, OUT and BOTH' + ), + ) + parser.add_argument( + '--vlan-filter', + required=False, + metavar="VLAN_FILTER", + help=_('VLAN IDs to mirror in the form of range string.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port is not None: + source_port = client.find_port( + parsed_args.port, ignore_missing=False + ).id + attrs['source_port'] = source_port + if parsed_args.tap_service is not None: + tap_service_id = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + attrs['tap_service_id'] = tap_service_id + if parsed_args.direction is not None: + attrs['direction'] = parsed_args.direction + if parsed_args.vlan_filter is not None: + attrs['vlan_filter'] = parsed_args.vlan_filter + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_flow(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapFlow(command.Lister): + _description = _("List tap flows.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_flows(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + ( + osc_utils.get_dict_properties( + s, columns, formatters=_formatters + ) + for s in objs + ), + ) + + +class ShowTapFlow(command.ShowOne): + _description = _("Show tap flow details.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + help=_("Tap flow to display (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_flow( + parsed_args.tap_flow, ignore_missing=False + ).id + obj = client.get_tap_flow(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapFlow(command.Command): + _description = _("Delete a tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + nargs="+", + help=_("Tap flow to delete (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_flow: + try: + id = client.find_tap_flow(id_or_name, ignore_missing=False).id + client.delete_tap_flow(id) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete tap flow with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s tap flow.") % { + 'fails': fails, + 'total': len(parsed_args.tap_flow), + } + raise exceptions.CommandError(msg) + + +class UpdateTapFlow(command.ShowOne): + _description = _("Update a tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + help=_("Tap flow to modify (name or ID)."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_f = client.find_tap_flow( + parsed_args.tap_flow, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_flow(original_t_f, **attrs) + columns, display_columns = column_util.get_columns(obj, _attr_map) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py new file mode 100644 index 0000000..876109d --- /dev/null +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -0,0 +1,237 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import identity as identity_utils +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient import command +from openstackclient.i18n import _ +from openstackclient.identity import common +from openstackclient.network.v2 import port as osc_port +from openstackclient.network.v2.taas import tap_service + +LOG = logging.getLogger(__name__) + +TAP_MIRROR = 'tap_mirror' +TAP_MIRRORS = f'{TAP_MIRROR}s' + +_attr_map = [ + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('directions', 'Directions', column_util.LIST_LONG_ONLY), + ('remote_ip', 'Remote IP', column_util.LIST_BOTH), + ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY), +] + + +def _get_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateTapMirror(command.ShowOne): + _description = _("Create a new tap mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + tap_service._add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port (name or ID) to which the Tap Mirror is connected.'), + ) + parser.add_argument( + '--directions', + dest='directions', + action=osc_port.JSONKeyValueAction, + required=True, + help=_( + 'Dictionary of direction and tunnel_id. Valid directions are: ' + 'IN and OUT.' + ), + ) + parser.add_argument( + '--remote-ip', + dest='remote_ip', + required=True, + help=_( + 'Remote IP address for the tap mirror (remote end of the ' + 'GRE or ERSPAN v1 tunnel).' + ), + ) + parser.add_argument( + '--mirror-type', + dest='mirror_type', + required=True, + help=_('Mirror type. Valid values are: gre and erspanv1.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_id is not None: + port_id = client.find_port( + parsed_args.port_id, ignore_missing=False + ).id + attrs['port_id'] = port_id + if parsed_args.directions is not None: + attrs['directions'] = parsed_args.directions + if parsed_args.remote_ip is not None: + attrs['remote_ip'] = parsed_args.remote_ip + if parsed_args.mirror_type is not None: + attrs['mirror_type'] = parsed_args.mirror_type + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_mirror(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapMirror(command.Lister): + _description = _("List tap mirrors.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_mirrors(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + (osc_utils.get_dict_properties(s, columns) for s in objs), + ) + + +class ShowTapMirror(command.ShowOne): + _description = _("Show tap mirror details.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + help=_("Tap mirror to display (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_mirror( + parsed_args.tap_mirror, ignore_missing=False + ).id + obj = client.get_tap_mirror(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapMirror(command.Command): + _description = _("Delete a tap mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + nargs="+", + help=_("Tap mirror to delete (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_mirror: + try: + id = client.find_tap_mirror( + id_or_name, ignore_missing=False + ).id + + client.delete_tap_mirror(id) + LOG.warning("Tap Mirror %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete Tap Mirror with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s Tap Mirror.") % { + 'fails': fails, + 'total': len(parsed_args.tap_mirror), + } + raise exceptions.CommandError(msg) + + +class UpdateTapMirror(command.ShowOne): + _description = _("Update a tap mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + help=_("Tap mirror to modify (name or ID)."), + ) + tap_service._add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_mirror( + parsed_args.tap_mirror, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_mirror(original_t_s, **attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py new file mode 100644 index 0000000..df27658 --- /dev/null +++ b/openstackclient/network/v2/taas/tap_service.py @@ -0,0 +1,211 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import identity as identity_utils +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient import command +from openstackclient.i18n import _ +from openstackclient.identity import common + +LOG = logging.getLogger(__name__) + +TAP_SERVICE = 'tap_service' +TAP_SERVICES = f'{TAP_SERVICE}s' + +_attr_map = [ + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), +] + + +def _add_updatable_args(parser): + parser.add_argument('--name', help=_('Name of the tap service.')) + parser.add_argument( + '--description', help=_('Description of the tap service.') + ) + + +def _get_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateTapService(command.ShowOne): + _description = _("Create a new tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port (name or ID) to connect to the tap service.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_id is not None: + port_id = client.find_port( + parsed_args.port_id, ignore_missing=False + ).id + attrs['port_id'] = port_id + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_service(**attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapService(command.Lister): + _description = _("List tap services.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_services(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + (osc_utils.get_dict_properties(s, columns) for s in objs), + ) + + +class ShowTapService(command.ShowOne): + _description = _("Show tap service details.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + help=_("Tap service to display (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + obj = client.get_tap_service(id) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapService(command.Command): + _description = _("Delete a tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + nargs="+", + help=_("Tap service to delete (name or ID)."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_service: + try: + id = client.find_tap_service( + id_or_name, ignore_missing=False + ).id + + client.delete_tap_service(id) + LOG.warning("Tap service %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete tap service with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s tap service.") % { + 'fails': fails, + 'total': len(parsed_args.tap_service), + } + raise exceptions.CommandError(msg) + + +class UpdateTapService(command.ShowOne): + _description = _("Update a tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + help=_("Tap service to modify (name or ID)."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_service(original_t_s, **attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py index 686fdcc..199e522 100644 --- a/openstackclient/object/v1/account.py +++ b/openstackclient/object/v1/account.py @@ -15,8 +15,8 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index f4250db..b0f92c7 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -19,9 +19,9 @@ import logging from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index cb8e4ff..e8ee0fc 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -19,10 +19,10 @@ import logging from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 6bbbb5f..743ed2b 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -26,16 +26,27 @@ from osc_lib import shell import openstackclient from openstackclient.common import clientmanager - DEFAULT_DOMAIN = 'default' +# list of modules that were originally out-of-tree and are now in +# core OSC +IGNORED_MODULES = ( + 'neutron_taas.taas_client.osc', + 'neutronclient.osc.v2.taas', +) class OpenStackShell(shell.OpenStackShell): + client_manager: clientmanager.ClientManager + def __init__(self): + command_manager = commandmanager.CommandManager( + 'openstack.cli', ignored_modules=IGNORED_MODULES + ) + super().__init__( description=__doc__.strip(), version=openstackclient.__version__, - command_manager=commandmanager.CommandManager('openstack.cli'), + command_manager=command_manager, deferred_help=True, ) @@ -48,8 +59,10 @@ class OpenStackShell(shell.OpenStackShell): # about them warnings.filterwarnings('ignore', module='openstack') - def build_option_parser(self, description, version): - parser = super().build_option_parser(description, version) + def build_option_parser(self, description, version, argparse_kwargs=None): + parser = super().build_option_parser( + description, version, argparse_kwargs + ) parser = clientmanager.build_plugin_option_parser(parser) parser = auth.build_auth_plugins_option_parser(parser) return parser @@ -65,10 +78,7 @@ class OpenStackShell(shell.OpenStackShell): self._auth_type = 'password' def _load_plugins(self): - """Load plugins via stevedore - - osc-lib has no opinion on what plugins should be loaded - """ + """Load plugins via stevedore.""" # Loop through extensions to get API versions for mod in clientmanager.PLUGIN_MODULES: default_version = getattr(mod, 'DEFAULT_API_VERSION', None) diff --git a/openstackclient/tests/functional/identity/v3/test_catalog.py b/openstackclient/tests/functional/identity/v3/test_catalog.py index f4089b7..429f94a 100644 --- a/openstackclient/tests/functional/identity/v3/test_catalog.py +++ b/openstackclient/tests/functional/identity/v3/test_catalog.py @@ -10,35 +10,54 @@ # License for the specific language governing permissions and limitations # under the License. + from openstackclient.tests.functional.identity.v3 import common class CatalogTests(common.IdentityTests): - def test_catalog_list(self): + """Functional tests for catalog commands""" + + def test_catalog(self): + """Test catalog list and show functionality""" + # Create a test service for isolated testing + _dummy_service_name = self._create_dummy_service(add_clean_up=True) + + # list catalogs raw_output = self.openstack('catalog list') items = self.parse_listing(raw_output) self.assert_table_structure(items, ['Name', 'Type', 'Endpoints']) - def test_catalog_show(self): - """test catalog show command - - The output example: - +-----------+----------------------------------------+ - | Field | Value | - +-----------+----------------------------------------+ - | endpoints | test1 | - | | public: http://localhost:5000/v2.0 | - | | test1 | - | | internal: http://localhost:5000/v2.0 | - | | test1 | - | | admin: http://localhost:35357/v2.0 | - | | | - | id | e1e68b5ba21a43a39ff1cf58e736c3aa | - | name | keystone | - | type | identity | - +-----------+----------------------------------------+ - """ - raw_output = self.openstack('catalog show {}'.format('identity')) + # Verify created service appears in catalog + service_names = [ + item.get('Name') for item in items if item.get('Name') + ] + self.assertIn( + _dummy_service_name, + service_names, + "Created dummy service should be present in catalog", + ) + + # show service (by name) + raw_output = self.openstack(f'catalog show {_dummy_service_name}') items = self.parse_show(raw_output) - # items may have multiple endpoint urls with empty key - self.assert_show_fields(items, ['endpoints', 'name', 'type', '', 'id']) + self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id']) + + # Extract the type from the dummy service + _dummy_service_type = next( + (item['type'] for item in items if 'type' in item), None + ) + + # show service (by type) + raw_output = self.openstack(f'catalog show {_dummy_service_type}') + items = self.parse_show(raw_output) + self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id']) + + # show service (non-existent) + result = self.openstack( + 'catalog show nonexistent-service-xyz', fail_ok=True + ) + self.assertEqual( + '', + result.strip(), + "Non-existent service should return empty result", + ) diff --git a/openstackclient/tests/functional/identity/v3/test_limit.py b/openstackclient/tests/functional/identity/v3/test_limit.py index 8c0bbcd..497d33a 100644 --- a/openstackclient/tests/functional/identity/v3/test_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_limit.py @@ -100,6 +100,53 @@ class LimitTestCase(common.IdentityTests): self.assert_show_fields(items, self.LIMIT_FIELDS) registered_limit_id = self._create_dummy_registered_limit() + def test_limit_create_with_project_domain(self): + registered_limit_id = self._create_dummy_registered_limit() + raw_output = self.openstack( + f'registered limit show {registered_limit_id}', + cloud=SYSTEM_CLOUD, + ) + items = self.parse_show(raw_output) + service_id = self._extract_value_from_items('service_id', items) + resource_name = self._extract_value_from_items('resource_name', items) + + raw_output = self.openstack(f'service show {service_id}') + items = self.parse_show(raw_output) + service_name = self._extract_value_from_items('name', items) + + project_name = self._create_dummy_project() + raw_output = self.openstack( + f'project show {project_name}', + cloud=SYSTEM_CLOUD, + ) + items = self.parse_show(raw_output) + domain_id = self._extract_value_from_items('domain_id', items) + + params = { + 'project_name': project_name, + 'project_domain': domain_id, + 'service_name': service_name, + 'resource_name': resource_name, + 'resource_limit': 15, + } + raw_output = self.openstack( + 'limit create' + ' --project {project_name}' + ' --project-domain {project_domain}' + ' --service {service_name}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), + cloud=SYSTEM_CLOUD, + ) + items = self.parse_show(raw_output) + limit_id = self._extract_value_from_items('id', items) + self.addCleanup( + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD + ) + + self.assert_show_fields(items, self.LIMIT_FIELDS) + registered_limit_id = self._create_dummy_registered_limit() + def test_limit_create_with_service_id(self): self._create_dummy_limit() diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py index 76e33c2..1255841 100644 --- a/openstackclient/tests/functional/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -62,6 +62,47 @@ class RoleAssignmentTests(common.IdentityTests): items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_group_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + group_name = 'group_name' + self.openstack(f'group create --domain {domain_name_A} {group_name}') + self.addCleanup( + self.openstack, + f'group delete --domain {domain_name_A} {group_name}', + ) + self.openstack(f'group create --domain {domain_name_B} {group_name}') + self.addCleanup( + self.openstack, + f'group delete --domain {domain_name_B} {group_name}', + ) + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--group {group_name} --group-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--group {group_name} --group-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--group {group_name} --group-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--group {group_name} --group-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_domain(self): role_name = self._create_dummy_role() username = self._create_dummy_user() @@ -85,6 +126,89 @@ class RoleAssignmentTests(common.IdentityTests): items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_user_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + username = 'username' + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + self.openstack(f'user create --domain {domain_name_B} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_B} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--user {username} --user-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--user {username} --user-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + + def test_role_assignment_list_role_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = 'role_name' + username = 'username' + self.openstack(f'role create --domain {domain_name_A} {role_name}') + self.addCleanup( + self.openstack, f'role delete --domain {domain_name_A} {role_name}' + ) + self.openstack(f'role create --domain {domain_name_B} {role_name}') + self.addCleanup( + self.openstack, f'role delete --domain {domain_name_B} {role_name}' + ) + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--user {username} --domain {domain_name_A} ' + f'--role-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--user {username} --domain {domain_name_A} ' + f'--role-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--role {role_name} --role-domain {domain_name_A}' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--role {role_name} --role-domain {domain_name_B}' + ) + items = self.parse_listing(raw_output) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_project(self): role_name = self._create_dummy_role() username = self._create_dummy_user() @@ -108,6 +232,56 @@ class RoleAssignmentTests(common.IdentityTests): items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_project_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + project_name = 'project_name' + username = 'username' + self.openstack( + f'project create --domain {domain_name_A} {project_name}' + ) + self.addCleanup( + self.openstack, + f'project delete --domain {domain_name_A} {project_name}', + ) + self.openstack( + f'project create --domain {domain_name_B} {project_name}' + ) + self.addCleanup( + self.openstack, + f'project delete --domain {domain_name_B} {project_name}', + ) + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--project {project_name} --project-domain {domain_name_A} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {project_name} --project-domain {domain_name_A} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--project {project_name} --project-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--project {project_name} --project-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_effective(self): raw_output = self.openstack('role assignment list --effective') items = self.parse_listing(raw_output) diff --git a/openstackclient/tests/functional/image/v2/test_cache.py b/openstackclient/tests/functional/image/v2/test_cache.py new file mode 100644 index 0000000..58245e5 --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_cache.py @@ -0,0 +1,54 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from openstackclient.tests.functional.image import base + + +class CacheTests(base.BaseImageTests): + """Functional tests for Cache commands""" + + def test_cached_image(self): + """Test cached image operations including queue and clear""" + # Create test image + name = uuid.uuid4().hex + output = self.openstack( + f'image create {name}', + parse_output=True, + ) + image_id = output["id"] + self.assertOutput(name, output['name']) + + # Register cleanup for created image + self.addCleanup( + self.openstack, 'cached image delete ' + image_id, fail_ok=True + ) + self.addCleanup(self.openstack, 'image delete ' + image_id) + + # Queue image for caching + self.openstack('cached image queue ' + image_id) + + # Verify queuing worked + cache_output = self.openstack('cached image list', parse_output=True) + self.assertIsInstance(cache_output, list) + image_ids = [img['ID'] for img in cache_output] + self.assertIn(image_id, image_ids) + + # Clear cached images + self.openstack('cached image clear') + + # Verify clearing worked + output = self.openstack('cached image list', parse_output=True) + if output: + image_ids = [img['ID'] for img in output] + self.assertNotIn(image_id, image_ids) diff --git a/openstackclient/tests/functional/image/v2/test_metadef_objects.py b/openstackclient/tests/functional/image/v2/test_metadef_objects.py new file mode 100644 index 0000000..5216c93 --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_metadef_objects.py @@ -0,0 +1,69 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from openstackclient.tests.functional import base + + +class MetadefObjectTests(base.TestCase): + def setUp(self): + super().setUp() + self.obj_name = self.getUniqueString('metadef-obj') + self.ns_name = self.getUniqueString('metadef-ns') + self.openstack(f"image metadef namespace create {self.ns_name}") + self.addCleanup( + lambda: self.openstack( + f"image metadef namespace delete {self.ns_name}" + ) + ) + + def test_metadef_objects(self): + # CREATE + created = self.openstack( + ( + "image metadef object create " + f"--namespace {self.ns_name} " + f"{self.obj_name}" + ), + parse_output=True, + ) + self.addCleanup( + lambda: self.openstack( + f"image metadef object delete {self.ns_name} {self.obj_name}" + ) + ) + self.assertEqual(self.obj_name, created["name"]) + self.assertEqual(self.ns_name, created["namespace_name"]) + + # UPDATE + new_name = f"{self.obj_name}-updated" + self.openstack( + "image metadef object update " + f"{self.ns_name} {self.obj_name} " + f"--name {new_name}" + ) + self.obj_name = new_name + + # READ (get) + shown = self.openstack( + f"image metadef object show {self.ns_name} {self.obj_name}", + parse_output=True, + ) + self.assertEqual(self.obj_name, shown["name"]) + self.assertEqual(self.ns_name, shown["namespace_name"]) + + # READ (list) + rows = self.openstack( + f"image metadef object list {self.ns_name}", + parse_output=True, + ) + names = {row["name"] for row in rows} + self.assertIn(self.obj_name, names) diff --git a/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py b/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py new file mode 100644 index 0000000..ab8dc13 --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py @@ -0,0 +1,55 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +from openstackclient.tests.functional.image import base + + +class ImageMetadefResourceTypeTests(base.BaseImageTests): + """Functional tests for image metadef resource type commands.""" + + def setUp(self): + super().setUp() + + # Create unique namespace name using UUID + self.namespace_name = 'test-mdef-ns-' + uuid.uuid4().hex + self.resource_type_name = 'test-mdef-rt-' + uuid.uuid4().hex + + # Create namespace + self.openstack('image metadef namespace create ' + self.namespace_name) + self.addCleanup( + self.openstack, + 'image metadef namespace delete ' + self.namespace_name, + ) + + def test_metadef_resource_type(self): + """Test image metadef resource type commands""" + + self.openstack( + 'image metadef resource type association create ' + f'{self.namespace_name} {self.resource_type_name}', + ) + self.addCleanup( + self.openstack, + 'image metadef resource type association delete ' + f'{self.namespace_name} {self.resource_type_name}', + ) + + output = self.openstack( + 'image metadef resource type list', + parse_output=True, + ) + + self.assertIn( + self.resource_type_name, [item['Name'] for item in output] + ) diff --git a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py index 2c9b72b..b84bb03 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py @@ -23,7 +23,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): super().setUpClass() - # create a volume for all tests to create snapshot + # create a test volume used by all snapshot tests cmd_output = cls.openstack( 'volume create ' + '--size 1 ' + cls.VOLLY, parse_output=True, @@ -40,147 +40,57 @@ class VolumeSnapshotTests(common.BaseVolumeTests): finally: super().tearDownClass() - def test_volume_snapshot_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name1, - cmd_output["name"], - ) + def test_volume_snapshot(self): + # create volume snapshot + name = uuid.uuid4().hex - name2 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + '--volume ' + + self.VOLLY + + ' --description aaaa ' + + '--property Alpha=a ' + + name, parse_output=True, ) - self.assertEqual( - name2, - cmd_output["name"], - ) - - self.wait_for_status('volume snapshot', name1, 'available') - self.wait_for_status('volume snapshot', name2, 'available') + snap_id = cmd_output['id'] - del_output = self.openstack( - 'volume snapshot delete ' + name1 + ' ' + name2 + self.addCleanup(self.wait_for_delete, 'volume snapshot', snap_id) + # delete volume snapshot + self.addCleanup( + self.openstack, + 'volume snapshot delete ' + snap_id, ) - self.assertOutput('', del_output) - self.wait_for_delete('volume snapshot', name1) - self.wait_for_delete('volume snapshot', name2) + self.wait_for_status('volume snapshot', snap_id, 'available') - def test_volume_snapshot_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, + # show volume snapshot + snapshot_info = self.openstack( + 'volume snapshot show ' + name, parse_output=True, ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name1) - self.assertEqual( - name1, - cmd_output["name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name1, 'available') - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name2) - self.assertEqual( - name2, - cmd_output["name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name2, 'available') - raw_output = self.openstack( - 'volume snapshot set ' + '--state error ' + name2 - ) - self.assertOutput('', raw_output) + self.assertEqual(name, snapshot_info['name']) + self.assertEqual('aaaa', snapshot_info["description"]) + self.assertEqual({'Alpha': 'a'}, snapshot_info["properties"]) - # Test list --long, --status + # list volume snapshot --name cmd_output = self.openstack( - 'volume snapshot list ' + '--long ' + '--status error', + 'volume snapshot list --name ' + name, parse_output=True, ) - names = [x["Name"] for x in cmd_output] - self.assertNotIn(name1, names) - self.assertIn(name2, names) + names = [x['Name'] for x in cmd_output] + self.assertIn(name, names) - # Test list --volume + # list volume snapshot --volume cmd_output = self.openstack( 'volume snapshot list ' + '--volume ' + self.VOLLY, parse_output=True, ) names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) + self.assertIn(name, names) - # Test list --name - cmd_output = self.openstack( - 'volume snapshot list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_volume_snapshot_set(self): - """Test create, set, unset, show, delete volume snapshot""" - name = uuid.uuid4().hex + # set volume snapshot new_name = name + "_" - cmd_output = self.openstack( - 'volume snapshot create ' - + '--volume ' - + self.VOLLY - + ' --description aaaa ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name) - self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name) - self.assertEqual( - name, - cmd_output["name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["description"], - ) - self.assertEqual( - {'Alpha': 'a'}, - cmd_output["properties"], - ) - self.wait_for_status('volume snapshot', name, 'available') - - # Test volume snapshot set raw_output = self.openstack( 'volume snapshot set ' + '--name ' @@ -188,11 +98,10 @@ class VolumeSnapshotTests(common.BaseVolumeTests): + ' --description bbbb ' + '--property Alpha=c ' + '--property Beta=b ' - + name, + + snap_id, ) self.assertOutput('', raw_output) - # Show snapshot set result cmd_output = self.openstack( 'volume snapshot show ' + new_name, parse_output=True, @@ -202,10 +111,6 @@ class VolumeSnapshotTests(common.BaseVolumeTests): cmd_output["name"], ) self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( 'bbbb', cmd_output["description"], ) @@ -214,7 +119,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): cmd_output["properties"], ) - # Test volume snapshot unset + # unset volume snapshot raw_output = self.openstack( 'volume snapshot unset ' + '--property Alpha ' + new_name, ) @@ -229,16 +134,25 @@ class VolumeSnapshotTests(common.BaseVolumeTests): cmd_output["properties"], ) - # Test volume snapshot set --no-property + # set volume snapshot --no-property, --state error raw_output = self.openstack( - 'volume snapshot set ' + '--no-property ' + new_name, + 'volume snapshot set ' + + '--no-property ' + + '--state error ' + + new_name, ) self.assertOutput('', raw_output) + cmd_output = self.openstack( 'volume snapshot show ' + new_name, parse_output=True, ) - self.assertNotIn( - {'Beta': 'b'}, - cmd_output["properties"], + self.assertEqual({}, cmd_output["properties"]) + + # list volume snapshot --long --status + cmd_output = self.openstack( + 'volume snapshot list ' + '--long ' + '--status error', + parse_output=True, ) + names = [x["Name"] for x in cmd_output] + self.assertIn(new_name, names) diff --git a/openstackclient/tests/unit/common/test_command.py b/openstackclient/tests/unit/common/test_command.py index 8233f89..1f1efce 100644 --- a/openstackclient/tests/unit/common/test_command.py +++ b/openstackclient/tests/unit/common/test_command.py @@ -14,9 +14,9 @@ from unittest import mock -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.tests.unit import fakes as test_fakes from openstackclient.tests.unit import utils as test_utils diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py index 53d9d65..dd68431 100644 --- a/openstackclient/tests/unit/common/test_extension.py +++ b/openstackclient/tests/unit/common/test_extension.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.common import extension from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -295,8 +294,8 @@ class TestExtensionShow(TestExtension): self.cmd = extension.ShowExtension(self.app, None) - self.app.client_manager.network.find_extension = mock.Mock( - return_value=self.extension_details + self.app.client_manager.network.find_extension.return_value = ( + self.extension_details ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/common/test_logs.py b/openstackclient/tests/unit/common/test_logs.py deleted file mode 100644 index 234ba42..0000000 --- a/openstackclient/tests/unit/common/test_logs.py +++ /dev/null @@ -1,221 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release -# or Jun 2017. - -import logging -from unittest import mock - -from osc_lib import logs - -from openstackclient.tests.unit import utils - - -class TestContext(utils.TestCase): - def test_log_level_from_options(self): - opts = mock.Mock() - opts.verbose_level = 0 - self.assertEqual(logging.ERROR, logs.log_level_from_options(opts)) - opts.verbose_level = 1 - self.assertEqual(logging.WARNING, logs.log_level_from_options(opts)) - opts.verbose_level = 2 - self.assertEqual(logging.INFO, logs.log_level_from_options(opts)) - opts.verbose_level = 3 - self.assertEqual(logging.DEBUG, logs.log_level_from_options(opts)) - - def test_log_level_from_config(self): - cfg = {'verbose_level': 0} - self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 2} - self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 3} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'critical'} - self.assertEqual(logging.CRITICAL, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'error'} - self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'warning'} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'info'} - self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'debug'} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'bogus'} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'info', 'debug': True} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - - @mock.patch('warnings.simplefilter') - def test_set_warning_filter(self, simplefilter): - logs.set_warning_filter(logging.ERROR) - simplefilter.assert_called_with("ignore") - logs.set_warning_filter(logging.WARNING) - simplefilter.assert_called_with("ignore") - logs.set_warning_filter(logging.INFO) - simplefilter.assert_called_with("once") - - -class TestFileFormatter(utils.TestCase): - def test_nothing(self): - formatter = logs._FileFormatter() - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s %(message)s' - ), - formatter.fmt, - ) - - def test_options(self): - class Opts: - cloud = 'cloudy' - os_project_name = 'projecty' - username = 'usernamey' - - options = Opts() - formatter = logs._FileFormatter(options=options) - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [cloudy usernamey projecty] %(message)s' - ), - formatter.fmt, - ) - - def test_config(self): - config = mock.Mock() - config.config = {'cloud': 'cloudy'} - config.auth = {'project_name': 'projecty', 'username': 'usernamey'} - formatter = logs._FileFormatter(config=config) - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [cloudy usernamey projecty] %(message)s' - ), - formatter.fmt, - ) - - -class TestLogConfigurator(utils.TestCase): - def setUp(self): - super().setUp() - self.options = mock.Mock() - self.options.verbose_level = 1 - self.options.log_file = None - self.options.debug = False - self.root_logger = mock.Mock() - self.root_logger.setLevel = mock.Mock() - self.root_logger.addHandler = mock.Mock() - self.requests_log = mock.Mock() - self.requests_log.setLevel = mock.Mock() - self.cliff_log = mock.Mock() - self.cliff_log.setLevel = mock.Mock() - self.stevedore_log = mock.Mock() - self.stevedore_log.setLevel = mock.Mock() - self.iso8601_log = mock.Mock() - self.iso8601_log.setLevel = mock.Mock() - self.loggers = [ - self.root_logger, - self.requests_log, - self.cliff_log, - self.stevedore_log, - self.iso8601_log, - ] - - @mock.patch('logging.StreamHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - def test_init(self, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - console_logger = mock.Mock() - console_logger.setFormatter = mock.Mock() - console_logger.setLevel = mock.Mock() - handle.return_value = console_logger - - configurator = logs.LogConfigurator(self.options) - - getLogger.assert_called_with('iso8601') # last call - warning_filter.assert_called_with(logging.WARNING) - self.root_logger.setLevel.assert_called_with(logging.DEBUG) - self.root_logger.addHandler.assert_called_with(console_logger) - self.requests_log.setLevel.assert_called_with(logging.ERROR) - self.cliff_log.setLevel.assert_called_with(logging.ERROR) - self.stevedore_log.setLevel.assert_called_with(logging.ERROR) - self.iso8601_log.setLevel.assert_called_with(logging.ERROR) - self.assertFalse(configurator.dump_trace) - - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - def test_init_no_debug(self, warning_filter, getLogger): - getLogger.side_effect = self.loggers - self.options.debug = True - - configurator = logs.LogConfigurator(self.options) - - warning_filter.assert_called_with(logging.DEBUG) - self.requests_log.setLevel.assert_called_with(logging.DEBUG) - self.assertTrue(configurator.dump_trace) - - @mock.patch('logging.FileHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - @mock.patch('osc_lib.logs._FileFormatter') - def test_init_log_file(self, formatter, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - self.options.log_file = '/tmp/log_file' - file_logger = mock.Mock() - file_logger.setFormatter = mock.Mock() - file_logger.setLevel = mock.Mock() - handle.return_value = file_logger - mock_formatter = mock.Mock() - formatter.return_value = mock_formatter - - logs.LogConfigurator(self.options) - - handle.assert_called_with(filename=self.options.log_file) - self.root_logger.addHandler.assert_called_with(file_logger) - file_logger.setFormatter.assert_called_with(mock_formatter) - file_logger.setLevel.assert_called_with(logging.WARNING) - - @mock.patch('logging.FileHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - @mock.patch('osc_lib.logs._FileFormatter') - def test_configure(self, formatter, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - configurator = logs.LogConfigurator(self.options) - cloud_config = mock.Mock() - config_log = '/tmp/config_log' - cloud_config.config = { - 'log_file': config_log, - 'verbose_level': 1, - 'log_level': 'info', - } - file_logger = mock.Mock() - file_logger.setFormatter = mock.Mock() - file_logger.setLevel = mock.Mock() - handle.return_value = file_logger - mock_formatter = mock.Mock() - formatter.return_value = mock_formatter - - configurator.configure(cloud_config) - - warning_filter.assert_called_with(logging.INFO) - handle.assert_called_with(filename=config_log) - self.root_logger.addHandler.assert_called_with(file_logger) - file_logger.setFormatter.assert_called_with(mock_formatter) - file_logger.setLevel.assert_called_with(logging.INFO) - self.assertFalse(configurator.dump_trace) diff --git a/openstackclient/tests/unit/common/test_module.py b/openstackclient/tests/unit/common/test_module.py index 476538a..8396203 100644 --- a/openstackclient/tests/unit/common/test_module.py +++ b/openstackclient/tests/unit/common/test_module.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Test module module""" @@ -19,10 +18,18 @@ import sys from unittest import mock from openstackclient.common import module as osc_module -from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils +class FakeModule: + def __init__(self, name, version): + self.name = name + self.__version__ = version + # Workaround for openstacksdk case + self.version = mock.Mock() + self.version.__version__ = version + + # NOTE(dtroyer): module_1 must match the version list filter (not --all) # currently == '*client*' module_name_1 = 'fakeclient' @@ -45,11 +52,11 @@ module_version_5 = '0.0.1' MODULES = { 'sys': sys, - module_name_1: fakes.FakeModule(module_name_1, module_version_1), - module_name_2: fakes.FakeModule(module_name_2, module_version_2), - module_name_3: fakes.FakeModule(module_name_3, module_version_3), - module_name_4: fakes.FakeModule(module_name_4, module_version_4), - module_name_5: fakes.FakeModule(module_name_5, module_version_5), + module_name_1: FakeModule(module_name_1, module_version_1), + module_name_2: FakeModule(module_name_2, module_version_2), + module_name_3: FakeModule(module_name_3, module_version_3), + module_name_4: FakeModule(module_name_4, module_version_4), + module_name_5: FakeModule(module_name_5, module_version_5), } diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 53df4da..7bfb2e7 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy from unittest import mock from openstack.block_storage.v3 import quota_set as _volume_quota_set @@ -1041,6 +1042,26 @@ class TestQuotaShow(TestQuota): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__missing_services(self): + self.app.client_manager.compute_endpoint_enabled = False + self.app.client_manager.volume_endpoint_enabled = False + self.app.client_manager.network_endpoint_enabled = False + + arglist = [ + self.projects[0].name, + ] + verifylist = [ + ('service', 'all'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.compute_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_not_called() + def test_quota_show__with_compute(self): arglist = [ '--compute', @@ -1102,6 +1123,64 @@ class TestQuotaShow(TestQuota): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__with_network_and_usage(self): + # ensure we do not interfere with other tests + self._network_quota_details = copy.deepcopy( + self._network_quota_details + ) + # set a couple of resources + self._network_quota_details["floating_ips"].update( + limit=30, reserved=20, used=7 + ) + self._network_quota_details["security_group_rules"].update( + limit=9, reserved=7, used=5 + ) + + arglist = [ + '--network', + '--usage', + self.projects[0].name, + ] + verifylist = [ + ('service', 'network'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, result_gen = self.cmd.take_action(parsed_args) + + self.assertEqual(('Resource', 'Limit', 'In Use', 'Reserved'), headers) + + result = sorted(result_gen) + + self.assertEqual( + [ + ('floating-ips', 30, 7, 20), + ('health_monitors', 0, 0, 0), + ('l7_policies', 0, 0, 0), + ('listeners', 0, 0, 0), + ('load_balancers', 0, 0, 0), + ('networks', 0, 0, 0), + ('pools', 0, 0, 0), + ('ports', 0, 0, 0), + ('rbac_policies', 0, 0, 0), + ('routers', 0, 0, 0), + ('secgroup-rules', 9, 5, 7), + ('secgroups', 0, 0, 0), + ('subnet_pools', 0, 0, 0), + ('subnets', 0, 0, 0), + ], + result, + ) + + self.compute_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_called_once_with( + self.projects[0].id, + details=True, + ) + self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__with_default(self): arglist = [ '--default', diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 813cbb9..b68e76e 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -165,9 +165,11 @@ class TestAggregateDelete(TestAggregate): sdk_fakes.generate_fake_resources(_aggregate.Aggregate, 2) ) - self.compute_client.find_aggregate = mock.Mock( - side_effect=[self.fake_ags[0], self.fake_ags[1]] - ) + self.compute_client.find_aggregate.side_effect = [ + self.fake_ags[0], + self.fake_ags[1], + ] + self.cmd = aggregate.DeleteAggregate(self.app, None) def test_aggregate_delete(self): diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index 423290c..8d9d36c 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -13,7 +13,6 @@ # under the License. # -from unittest import mock from openstack.compute.v2 import server as _server from openstack.test import fakes as sdk_fakes @@ -90,9 +89,7 @@ class TestConsoleUrlShow(compute_fakes.TestComputev2): 'protocol': 'fake_protocol', 'type': 'fake_type', } - self.compute_client.create_console = mock.Mock( - return_value=fake_console_data - ) + self.compute_client.create_console.return_value = fake_console_data self.columns = ( 'protocol', diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 0b3a4ae..25bc8ea 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -680,9 +680,7 @@ class TestFlavorList(TestFlavor): ) self.compute_client.flavors.side_effect = [[flavor], []] - self.compute_client.fetch_flavor_extra_specs = mock.Mock( - return_value=None - ) + self.compute_client.fetch_flavor_extra_specs.return_value = None arglist = [ '--long', diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index 045efda..89d4d45 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -12,20 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. # -from unittest import mock from openstackclient.compute.v2 import hypervisor_stats from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import fakes -class TestHypervisorStats(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - self.compute_client.get = mock.Mock() - - # Not in fakes.py because hypervisor stats has been deprecated @@ -61,7 +53,7 @@ def create_one_hypervisor_stats(attrs=None): return stats_info -class TestHypervisorStatsShow(TestHypervisorStats): +class TestHypervisorStatsShow(compute_fakes.TestComputev2): _stats = create_one_hypervisor_stats() def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 8695136..8da3516 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -13,7 +13,6 @@ # under the License. import base64 -import collections import getpass import json import tempfile @@ -21,9 +20,11 @@ from unittest import mock import uuid import iso8601 +from openstack.compute.v2 import flavor as _flavor from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_group as _server_group from openstack import exceptions as sdk_exceptions +from openstack.image.v2 import image as _image from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -1212,7 +1213,6 @@ class TestServerCreate(TestServer): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -1261,7 +1261,6 @@ class TestServerCreate(TestServer): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress None, # project_id format_columns.DictColumn({}), # properties @@ -4450,6 +4449,55 @@ class TestServerDelete(compute_fakes.TestComputev2): ) self.assertIsNone(result) + def test_server_delete_multi_servers_with_exceptions(self): + servers = compute_fakes.create_servers(count=2) + self.compute_client.find_server.side_effect = [ + servers[0], + sdk_exceptions.ResourceNotFound(), + servers[1], + ] + + arglist = [servers[0].id, 'unexist_server', servers[1].id] + + verifylist = [ + ('force', False), + ('all_projects', False), + ('wait', False), + ( + 'server', + [servers[0].id, 'unexist_server', servers[1].id], + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 3 servers failed to delete.', str(exc)) + + self.compute_client.find_server.assert_has_calls( + [ + mock.call( + servers[0].id, ignore_missing=False, all_projects=False + ), + mock.call( + 'unexist_server', ignore_missing=False, all_projects=False + ), + mock.call( + servers[1].id, ignore_missing=False, all_projects=False + ), + ] + ) + + self.compute_client.delete_server.assert_has_calls( + [ + mock.call(servers[0], force=False), + mock.call(servers[1], force=False), + ] + ) + def test_server_delete_with_all_projects(self): arglist = [ self.server.id, @@ -4583,10 +4631,8 @@ class _TestServerList(TestServer): 'Flavor Name', 'Flavor ID', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', - 'Scheduler Hints', ) columns_all_projects = ( 'ID', @@ -4653,17 +4699,19 @@ class TestServerList(_TestServerList): def setUp(self): super().setUp() - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image ] - Flavor = collections.namedtuple('Flavor', 'id name') self.compute_client.flavors.return_value = [ - Flavor(id=s.flavor['id'], name=self.flavor.name) + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) for s in self.servers ] @@ -4732,10 +4780,8 @@ class TestServerList(_TestServerList): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(None), ) for s in self.servers ) @@ -4809,13 +4855,9 @@ class TestServerList(_TestServerList): '-c', 'Availability Zone', '-c', - 'Pinned Availability Zone', - '-c', 'Host', '-c', 'Properties', - '-c', - 'Scheduler Hints', '--long', ] verifylist = [ @@ -4835,10 +4877,8 @@ class TestServerList(_TestServerList): self.assertIn('Image ID', columns) self.assertIn('Flavor ID', columns) self.assertIn('Availability Zone', columns) - self.assertIn('Pinned Availability Zone', columns) self.assertIn('Host', columns) self.assertIn('Properties', columns) - self.assertIn('Scheduler Hints', columns) self.assertCountEqual(columns, set(columns)) def test_server_list_no_name_lookup_option(self): @@ -5249,10 +5289,8 @@ class TestServerList(_TestServerList): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(s.scheduler_hints), ) for s in self.servers ) @@ -5281,9 +5319,10 @@ class TestServerList(_TestServerList): self.compute_client.servers.return_value = servers # Make sure the returned image and flavor IDs match the servers. - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in servers if s.image @@ -5305,10 +5344,8 @@ class TestServerList(_TestServerList): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(s.scheduler_hints), s.host_status, ) for s in servers @@ -5343,7 +5380,6 @@ class TestServerListV273(_TestServerList): 'Image ID', 'Flavor', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', 'Scheduler Hints', @@ -5368,9 +5404,10 @@ class TestServerListV273(_TestServerList): self.servers = self.setup_sdk_servers_mock(3) self.compute_client.servers.return_value = self.servers - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image @@ -5541,6 +5578,307 @@ class TestServerListV273(_TestServerList): self.assertEqual(expected_row, partial_server) +class TestServerListV296(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Pinned Availability Zone', + ) + + def setUp(self): + super().setUp() + self.set_compute_api_version('2.96') + + self.image_client.images.return_value = [ + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] + + self.compute_client.flavors.return_value = [ + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) + for s in self.servers + ] + + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + getattr(s, 'pinned_availability_zone', ''), + ) + for s in self.servers + ) + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-c', + 'Project ID', + '-c', + 'User ID', + '-c', + 'Created At', + '-c', + 'Security Groups', + '-c', + 'Task State', + '-c', + 'Power State', + '-c', + 'Image ID', + '-c', + 'Flavor ID', + '-c', + 'Availability Zone', + '-c', + 'Host', + '-c', + 'Properties', + '-c', + 'Pinned Availability Zone', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.servers.assert_called_with(**self.kwargs) + self.assertIn('Project ID', columns) + self.assertIn('User ID', columns) + self.assertIn('Created At', columns) + self.assertIn('Security Groups', columns) + self.assertIn('Task State', columns) + self.assertIn('Power State', columns) + self.assertIn('Image ID', columns) + self.assertIn('Flavor ID', columns) + self.assertIn('Availability Zone', columns) + self.assertIn('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertCountEqual(columns, set(columns)) + + +class TestServerListV2100(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Pinned Availability Zone', + 'Scheduler Hints', + ) + + def setUp(self): + super().setUp() + self.set_compute_api_version('2.100') + + self.image_client.images.return_value = [ + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] + + self.compute_client.flavors.return_value = [ + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) + for s in self.servers + ] + + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + getattr(s, 'pinned_availability_zone', ''), + format_columns.DictListColumn(None), + ) + for s in self.servers + ) + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-c', + 'Project ID', + '-c', + 'User ID', + '-c', + 'Created At', + '-c', + 'Security Groups', + '-c', + 'Task State', + '-c', + 'Power State', + '-c', + 'Image ID', + '-c', + 'Flavor ID', + '-c', + 'Availability Zone', + '-c', + 'Host', + '-c', + 'Properties', + '-c', + 'Pinned Availability Zone', + '-c', + 'Scheduler Hints', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.servers.assert_called_with(**self.kwargs) + self.assertIn('Project ID', columns) + self.assertIn('User ID', columns) + self.assertIn('Created At', columns) + self.assertIn('Security Groups', columns) + self.assertIn('Task State', columns) + self.assertIn('Power State', columns) + self.assertIn('Image ID', columns) + self.assertIn('Flavor ID', columns) + self.assertIn('Availability Zone', columns) + self.assertIn('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertIn('Scheduler Hints', columns) + self.assertCountEqual(columns, set(columns)) + + class TestServerAction(compute_fakes.TestComputev2): def run_method_with_sdk_servers(self, method_name, server_count): servers = compute_fakes.create_servers(count=server_count) @@ -7558,15 +7896,13 @@ class TestServerResize(compute_fakes.TestComputev2): ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.compute_client.find_server.assert_called_once_with( - self.server.id, ignore_missing=False + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, ) - self.compute_client.find_flavor.assert_not_called() - self.compute_client.resize_server.assert_not_called() - self.assertIsNone(result) def test_server_resize(self): arglist = [ @@ -8533,7 +8869,6 @@ class TestServerShow(TestServer): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -8583,7 +8918,6 @@ class TestServerShow(TestServer): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress 'tenant-id-xxx', # project_id format_columns.DictColumn({}), # properties @@ -9522,7 +9856,6 @@ class TestServerGeneral(TestServer): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), @@ -9609,7 +9942,6 @@ class TestServerGeneral(TestServer): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 0d039d2..cc8acfb 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -161,9 +161,7 @@ class TestServerBackupCreate(compute_fakes.TestComputev2): @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_backup_wait_ok(self, mock_wait_for_status): - self.image_client.get_image = mock.Mock( - side_effect=self.image, - ) + self.image_client.get_image.side_effect = (self.image,) arglist = [ '--name', diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index c64a2b9..a47ea72 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -77,9 +77,7 @@ class TestServiceDelete(compute_fakes.TestComputev2): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [None, exceptions.CommandError] - self.compute_client.delete_service = mock.Mock( - side_effect=delete_mock_result - ) + self.compute_client.delete_service.side_effect = delete_mock_result try: self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py index 0d378ff..be9d7f2 100644 --- a/openstackclient/tests/unit/fakes.py +++ b/openstackclient/tests/unit/fakes.py @@ -12,13 +12,43 @@ # License for the specific language governing permissions and limitations # under the License. +# TODO(stephenfin): Remove the contents of this module in favour of the osc_lib +# version once our min version is bumped to 4.3.0 + import json -import sys from unittest import mock from keystoneauth1 import fixture +from osc_lib.tests.fakes import ( + FakeApp, + FakeClientManager as BaseFakeClientManager, + FakeLog, + FakeOptions, + FakeResource as BaseFakeResource, + FakeStdout, +) import requests +__all__ = [ + 'AUTH_TOKEN', + 'AUTH_URL', + 'INTERFACE', + 'PASSWORD', + 'PROJECT_NAME', + 'REGION_NAME', + 'TEST_RESPONSE_DICT', + 'TEST_RESPONSE_DICT_V3', + 'TEST_VERSIONS', + 'USERNAME', + 'VERSION', + 'FakeApp', + 'FakeClientManager', + 'FakeLog', + 'FakeOptions', + 'FakeResource', + 'FakeResponse', + 'FakeStdout', +] AUTH_TOKEN = "foobar" AUTH_URL = "http://0.0.0.0" @@ -47,79 +77,15 @@ TEST_RESPONSE_DICT_V3.set_project_scope() TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) -class FakeStdout: - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result = result + line - return result - - -class FakeLog: - def __init__(self): - self.messages = {} - - def debug(self, msg): - self.messages['debug'] = msg - - def info(self, msg): - self.messages['info'] = msg - - def warning(self, msg): - self.messages['warning'] = msg - - def error(self, msg): - self.messages['error'] = msg - - def critical(self, msg): - self.messages['critical'] = msg - - -class FakeApp: - def __init__(self, _stdout, _log): - self.stdout = _stdout - self.client_manager = None - self.api_version = {} - self.stdin = sys.stdin - self.stdout = _stdout or sys.stdout - self.stderr = sys.stderr - self.log = _log - - -class FakeOptions: - def __init__(self, **kwargs): - self.os_beta_command = False - - -class FakeClient: - def __init__(self, **kwargs): - self.endpoint = kwargs['endpoint'] - self.token = kwargs['token'] - - -class FakeClientManager: +class FakeClientManager(BaseFakeClientManager): _api_version = { 'image': '2', } def __init__(self): - self.compute = None - self.identity = None - self.image = None - self.object_store = None - self.volume = None - self.network = None - self.sdk_connection = mock.Mock() + super().__init__() - self.session = None - self.auth_ref = None - self.auth_plugin_name = None + self.sdk_connection = mock.Mock() self.network_endpoint_enabled = True self.compute_endpoint_enabled = True @@ -158,64 +124,7 @@ class FakeClientManager: return self.volume_endpoint_enabled -class FakeModule: - def __init__(self, name, version): - self.name = name - self.__version__ = version - # Workaround for openstacksdk case - self.version = mock.Mock() - self.version.__version__ = version - - -class FakeResource: - def __init__(self, manager=None, info=None, loaded=False, methods=None): - """Set attributes and methods for a resource. - - :param manager: - The resource manager - :param Dictionary info: - A dictionary with all attributes - :param bool loaded: - True if the resource is loaded in memory - :param Dictionary methods: - A dictionary with all methods - """ - info = info or {} - methods = methods or {} - - self.__name__ = type(self).__name__ - self.manager = manager - self._info = info - self._add_details(info) - self._add_methods(methods) - self._loaded = loaded - - def _add_details(self, info): - for k, v in info.items(): - setattr(self, k, v) - - def _add_methods(self, methods): - """Fake methods with MagicMock objects. - - For each <@key, @value> pairs in methods, add an callable MagicMock - object named @key as an attribute, and set the mock's return_value to - @value. When users access the attribute with (), @value will be - returned, which looks like a function call. - """ - for name, ret in methods.items(): - method = mock.Mock(return_value=ret) - setattr(self, name, method) - - def __repr__(self): - reprkeys = sorted( - k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager' - ) - info = ", ".join(f"{k}={getattr(self, k)}" for k in reprkeys) - return f"<{self.__class__.__name__} {info}>" - - def keys(self): - return self._info.keys() - +class FakeResource(BaseFakeResource): def to_dict(self): return self._info @@ -247,11 +156,3 @@ class FakeResponse(requests.Response): self._content = json.dumps(data) if not isinstance(self._content, bytes): self._content = self._content.encode() - - -class FakeModel(dict): - def __getattr__(self, key): - try: - return self[key] - except KeyError: - raise AttributeError(key) diff --git a/openstackclient/tests/unit/identity/test_common.py b/openstackclient/tests/unit/identity/test_common.py new file mode 100644 index 0000000..ae85262 --- /dev/null +++ b/openstackclient/tests/unit/identity/test_common.py @@ -0,0 +1,100 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions + +from openstackclient.identity import common +from openstackclient.tests.unit import utils as test_utils + + +class TestFindSDKId(test_utils.TestCase): + def setUp(self): + super().setUp() + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client = mock.Mock() + self.identity_sdk_client.find_user = mock.Mock() + + def test_find_sdk_id_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_not_found_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + self.assertRaises( + exceptions.CommandError, + common._find_sdk_id, + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + def test_find_sdk_id_not_found_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + + self.assertEqual(self.user.id, result) diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py index 9b203b2..bb6a643 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_project.py +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -195,7 +195,7 @@ class TestProjectCreate(TestProject): self.fake_project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('name', self.fake_project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -464,7 +464,7 @@ class TestProjectSet(TestProject): ('description', None), ('enable', False), ('disable', False), - ('property', None), + ('properties', None), ] self.projects_mock.get.side_effect = exceptions.NotFound(None) self.projects_mock.find.side_effect = exceptions.NotFound(None) @@ -588,7 +588,7 @@ class TestProjectSet(TestProject): self.fake_project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('project', self.fake_project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -683,7 +683,7 @@ class TestProjectUnset(TestProject): self.fake_proj.name, ] verifylist = [ - ('property', ['fee', 'fo']), + ('properties', ['fee', 'fo']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index ad5ceb2..c42ed47 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -693,6 +693,14 @@ class TestIdentityv3( ): ... +class FakeModel(dict): + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + # We don't use FakeClientMixin since we want a different fake legacy client class TestFederatedIdentity(utils.TestCommand): def setUp(self): @@ -1075,7 +1083,7 @@ class FakeEndpoint: # Overwrite default attributes if there are some attributes set endpoint_filter_info.update(attrs) - endpoint_filter = fakes.FakeModel(copy.deepcopy(endpoint_filter_info)) + endpoint_filter = FakeModel(copy.deepcopy(endpoint_filter_info)) return endpoint_filter @@ -1133,7 +1141,7 @@ class FakeEndpointGroup: # Overwrite default attributes if there are some attributes set endpointgroup_filter_info.update(attrs) - endpointgroup_filter = fakes.FakeModel( + endpointgroup_filter = FakeModel( copy.deepcopy(endpointgroup_filter_info) ) diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index a5fda7c..3a3a80e 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -120,7 +120,7 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3): verifylist = [ ('name', self.application_credential.name), ('secret', 'moresecuresecret'), - ('role', [self.roles.id]), + ('roles', [self.roles.id]), ('expiration', '2024-01-01T00:00:00'), ('description', 'credential for testing'), ] @@ -302,7 +302,7 @@ class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3): calls = [] for a in arglist: - calls.append(call(user_id, a)) + calls.append(call(user_id, a, ignore_missing=False)) self.identity_sdk_client.find_application_credential.assert_has_calls( calls @@ -457,7 +457,7 @@ class TestApplicationCredentialShow(identity_fakes.TestIdentityv3): columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_application_credential.assert_called_with( - user_id, self.application_credential.id + user_id, self.application_credential.id, ignore_missing=False ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index abe8076..cc0593d 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -520,7 +520,7 @@ class TestDomainShow(identity_fakes.TestIdentityv3): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_domain.assert_called_with( - self.domain.id, + self.domain.id, ignore_missing=False ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py index 84e10e5..ba69317 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -678,7 +678,7 @@ class TestEndpointShow(identity_fakes.TestIdentityv3): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_endpoint.assert_called_with( - self.endpoint.id, + self.endpoint.id, ignore_missing=False ) collist = ( diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index d212e45..598402e 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -253,7 +253,7 @@ class TestGroupCreate(identity_fakes.TestIdentityv3): columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_group.assert_called_once_with( - self.group.name + self.group.name, ignore_missing=False ) self.assertEqual(self.columns, columns) datalist = ( @@ -286,7 +286,9 @@ class TestGroupCreate(identity_fakes.TestIdentityv3): 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.group_with_options.name, + domain_id=self.domain.id, + ignore_missing=False, ) self.assertEqual(self.columns, columns) datalist = ( diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py index 20e7f49..c65e947 100644 --- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -127,7 +127,7 @@ class TestIdentityProviderCreate(TestIdentityProvider): ] verifylist = [ ('identity_provider_id', identity_fakes.idp_id), - ('remote_id', identity_fakes.idp_remote_ids[:1]), + ('remote_ids', identity_fakes.idp_remote_ids[:1]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -157,7 +157,7 @@ class TestIdentityProviderCreate(TestIdentityProvider): ] verifylist = [ ('identity_provider_id', identity_fakes.idp_id), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -561,7 +561,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('description', new_description), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) @@ -597,7 +597,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('description', None), ('enable', False), ('disable', True), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -637,7 +637,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('description', None), ('enable', True), ('disable', False), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -675,7 +675,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('description', None), ('enable', True), ('disable', False), - ('remote_id', [self.new_remote_id]), + ('remote_ids', [self.new_remote_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -756,7 +756,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -776,7 +776,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', 60), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -800,7 +800,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', 0), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -816,7 +816,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', -1), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_limit.py b/openstackclient/tests/unit/identity/v3/test_limit.py index a1d180b..4c2184c 100644 --- a/openstackclient/tests/unit/identity/v3/test_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_limit.py @@ -10,87 +10,85 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - -from keystoneauth1.exceptions import http as ksa_exceptions +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import limit as _limit +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import region as _region +from openstack.identity.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import limit -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestLimit(identity_fakes.TestIdentityv3): +class TestLimitCreate(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - identity_manager = self.identity_client - - self.limit_mock = identity_manager.limits - - self.services_mock = identity_manager.services - self.services_mock.reset_mock() - - self.projects_mock = identity_manager.projects - self.projects_mock.reset_mock() - - self.regions_mock = identity_manager.regions - self.regions_mock.reset_mock() + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.project = sdk_fakes.generate_fake_resource( + _project.Project, domain_id=self.domain.id + ) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.resource_limit = 15 -class TestLimitCreate(TestLimit): - def setUp(self): - super().setUp() - - self.service = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.SERVICE), loaded=True - ) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + self.identity_sdk_client.find_project.return_value = self.project + self.identity_sdk_client.find_domain.return_value = self.domain - self.project = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.PROJECT), loaded=True + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=self.resource_limit, + region_id=None, ) - self.projects_mock.get.return_value = self.project - - self.region = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.REGION), loaded=True + self.limit_with_options = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_limit=self.resource_limit, + resource_name='foobars', + description='test description', + region_id=self.region.id, ) - self.regions_mock.get.return_value = self.region self.cmd = limit.CreateLimit(self.app, None) def test_limit_create_without_options(self): - self.limit_mock.create.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT), loaded=True - ) + self.identity_sdk_client.create_limit.return_value = self.limit - resource_limit = 15 arglist = [ '--project', - identity_fakes.project_id, + self.project.id, '--service', - identity_fakes.service_id, + self.service.id, '--resource-limit', - str(resource_limit), - identity_fakes.limit_resource_name, + str(self.resource_limit), + self.limit.resource_name, ] verifylist = [ - ('project', identity_fakes.project_id), - ('service', identity_fakes.service_id), - ('resource_name', identity_fakes.limit_resource_name), - ('resource_limit', resource_limit), + ('project', self.project.id), + ('service', self.service.id), + ('resource_name', self.limit.resource_name), + ('resource_limit', self.resource_limit), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - kwargs = {'description': None, 'region': None} - self.limit_mock.create.assert_called_with( - self.project, - self.service, - identity_fakes.limit_resource_name, - resource_limit, - **kwargs, + self.identity_sdk_client.create_limit.assert_called_with( + project_id=self.project.id, + service_id=self.service.id, + resource_name=self.limit.resource_name, + resource_limit=self.resource_limit, ) collist = ( @@ -105,55 +103,58 @@ class TestLimitCreate(TestLimit): self.assertEqual(collist, columns) datalist = ( None, - identity_fakes.limit_id, - identity_fakes.project_id, + self.limit.id, + self.project.id, None, - resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + self.resource_limit, + self.limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_limit_create_with_options(self): - self.limit_mock.create.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT_OPTIONS), loaded=True + self.identity_sdk_client.create_limit.return_value = ( + self.limit_with_options ) resource_limit = 15 arglist = [ '--project', - identity_fakes.project_id, + self.project.id, + '--project-domain', + self.domain.name, '--service', - identity_fakes.service_id, + self.service.id, '--resource-limit', str(resource_limit), '--region', - identity_fakes.region_id, + self.region.id, '--description', - identity_fakes.limit_description, - identity_fakes.limit_resource_name, + self.limit_with_options.description, + self.limit_with_options.resource_name, ] verifylist = [ - ('project', identity_fakes.project_id), - ('service', identity_fakes.service_id), - ('resource_name', identity_fakes.limit_resource_name), + ('project', self.project.id), + ('project_domain', self.domain.name), + ('service', self.service.id), + ('resource_name', self.limit_with_options.resource_name), ('resource_limit', resource_limit), - ('region', identity_fakes.region_id), - ('description', identity_fakes.limit_description), + ('region', self.region.id), + ('description', self.limit_with_options.description), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'description': identity_fakes.limit_description, - 'region': self.region, + 'project_id': self.project.id, + 'service_id': self.service.id, + 'region_id': self.region.id, + 'resource_name': self.limit_with_options.resource_name, + 'resource_limit': resource_limit, + 'description': self.limit_with_options.description, } - self.limit_mock.create.assert_called_with( - self.project, - self.service, - identity_fakes.limit_resource_name, - resource_limit, + self.identity_sdk_client.create_limit.assert_called_with( **kwargs, ) @@ -168,37 +169,41 @@ class TestLimitCreate(TestLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.limit_description, - identity_fakes.limit_id, - identity_fakes.project_id, - identity_fakes.region_id, + self.limit_with_options.description, + self.limit_with_options.id, + self.project.id, + self.region.id, resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + self.limit_with_options.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestLimitDelete(TestLimit): +class TestLimitDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() self.cmd = limit.DeleteLimit(self.app, None) def test_limit_delete(self): - self.limit_mock.delete.return_value = None + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit + ) + self.identity_sdk_client.delete_limit.return_value = None - arglist = [identity_fakes.limit_id] - verifylist = [('limit_id', [identity_fakes.limit_id])] + arglist = [self.limit.id] + verifylist = [('limit_id', [self.limit.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.limit_mock.delete.assert_called_with(identity_fakes.limit_id) + self.identity_sdk_client.delete_limit.assert_called_with(self.limit.id) self.assertIsNone(result) def test_limit_delete_with_exception(self): - return_value = ksa_exceptions.NotFound() - self.limit_mock.delete.side_effect = return_value + self.identity_sdk_client.delete_limit.side_effect = ( + sdk_exc.ResourceNotFound + ) arglist = ['fake-limit-id'] verifylist = [('limit_id', ['fake-limit-id'])] @@ -211,24 +216,42 @@ class TestLimitDelete(TestLimit): self.assertEqual('1 of 1 limits failed to delete.', str(e)) -class TestLimitShow(TestLimit): +class TestLimitShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.limit_mock.get.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT), loaded=True + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.resource_limit = 15 + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + self.identity_sdk_client.find_project.return_value = self.project + + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=self.resource_limit, + region_id=None, ) + self.identity_sdk_client.get_limit.return_value = self.limit + self.cmd = limit.ShowLimit(self.app, None) def test_limit_show(self): - arglist = [identity_fakes.limit_id] - verifylist = [('limit_id', identity_fakes.limit_id)] + arglist = [self.limit.id] + verifylist = [('limit_id', self.limit.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.get.assert_called_with(identity_fakes.limit_id) + self.identity_sdk_client.get_limit.assert_called_with(self.limit.id) collist = ( 'description', @@ -242,45 +265,61 @@ class TestLimitShow(TestLimit): self.assertEqual(collist, columns) datalist = ( None, - identity_fakes.limit_id, - identity_fakes.project_id, + self.limit.id, + self.project.id, None, - identity_fakes.limit_resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + self.resource_limit, + self.limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestLimitSet(TestLimit): +class TestLimitSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() + + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.resource_limit = 15 + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + self.identity_sdk_client.find_project.return_value = self.project + self.cmd = limit.SetLimit(self.app, None) def test_limit_set_description(self): - limit = copy.deepcopy(identity_fakes.LIMIT) - limit['description'] = identity_fakes.limit_description - self.limit_mock.update.return_value = fakes.FakeResource( - None, limit, loaded=True + description = 'limit of foobars' + limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=description, + resource_limit=self.resource_limit, + region_id=None, ) + self.identity_sdk_client.update_limit.return_value = limit arglist = [ '--description', - identity_fakes.limit_description, - identity_fakes.limit_id, + description, + limit.id, ] verifylist = [ - ('description', identity_fakes.limit_description), - ('limit_id', identity_fakes.limit_id), + ('description', description), + ('limit_id', limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.update.assert_called_with( - identity_fakes.limit_id, - description=identity_fakes.limit_description, - resource_limit=None, + self.identity_sdk_client.update_limit.assert_called_with( + limit.id, + description=description, ) collist = ( @@ -294,40 +333,44 @@ class TestLimitSet(TestLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.limit_description, - identity_fakes.limit_id, - identity_fakes.project_id, + description, + limit.id, + self.project.id, None, - identity_fakes.limit_resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + limit.resource_limit, + limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_limit_set_resource_limit(self): resource_limit = 20 - limit = copy.deepcopy(identity_fakes.LIMIT) - limit['resource_limit'] = resource_limit - self.limit_mock.update.return_value = fakes.FakeResource( - None, limit, loaded=True + limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=resource_limit, + region_id=None, ) + self.identity_sdk_client.update_limit.return_value = limit arglist = [ '--resource-limit', str(resource_limit), - identity_fakes.limit_id, + limit.id, ] verifylist = [ ('resource_limit', resource_limit), - ('limit_id', identity_fakes.limit_id), + ('limit_id', limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.update.assert_called_with( - identity_fakes.limit_id, - description=None, + self.identity_sdk_client.update_limit.assert_called_with( + limit.id, resource_limit=resource_limit, ) @@ -343,25 +386,36 @@ class TestLimitSet(TestLimit): self.assertEqual(collist, columns) datalist = ( None, - identity_fakes.limit_id, - identity_fakes.project_id, + limit.id, + self.project.id, None, resource_limit, - identity_fakes.limit_resource_name, - identity_fakes.service_id, + limit.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestLimitList(TestLimit): +class TestLimitList(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.limit_mock.list.return_value = [ - fakes.FakeResource( - None, copy.deepcopy(identity_fakes.LIMIT), loaded=True - ) - ] + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + + self.resource_limit = 15 + + self.limit = sdk_fakes.generate_fake_resource( + resource_type=_limit.Limit, + project_id=self.project.id, + service_id=self.service.id, + resource_name='foobars', + description=None, + resource_limit=self.resource_limit, + region_id=None, + ) + self.identity_sdk_client.limits.return_value = [self.limit] self.cmd = limit.ListLimit(self.app, None) @@ -372,9 +426,7 @@ class TestLimitList(TestLimit): columns, data = self.cmd.take_action(parsed_args) - self.limit_mock.list.assert_called_with( - service=None, resource_name=None, region=None, project=None - ) + self.identity_sdk_client.limits.assert_called_with() collist = ( 'ID', @@ -388,11 +440,11 @@ class TestLimitList(TestLimit): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.limit_id, - identity_fakes.project_id, - identity_fakes.service_id, - identity_fakes.limit_resource_name, - identity_fakes.limit_resource_limit, + self.limit.id, + self.project.id, + self.service.id, + self.limit.resource_name, + self.limit.resource_limit, None, None, ), diff --git a/openstackclient/tests/unit/identity/v3/test_oauth.py b/openstackclient/tests/unit/identity/v3/test_oauth.py index 576f8b2..9dcf0be 100644 --- a/openstackclient/tests/unit/identity/v3/test_oauth.py +++ b/openstackclient/tests/unit/identity/v3/test_oauth.py @@ -109,7 +109,7 @@ class TestRequestTokenAuthorize(TestOAuth1): ] verifylist = [ ('request_key', identity_fakes.request_token_id), - ('role', [identity_fakes.role_name]), + ('roles', [identity_fakes.role_name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 024b74f..dae1aeb 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -13,31 +13,20 @@ # under the License. from unittest import mock -from unittest.mock import call +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions -from osc_lib import utils -from openstackclient.identity import common from openstackclient.identity.v3 import project from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestProject(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - - -class TestProjectCreate(TestProject): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestProjectCreate(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'description', @@ -46,39 +35,41 @@ class TestProjectCreate(TestProject): 'id', 'is_domain', 'name', + 'options', 'parent_id', 'tags', ) + project_kwargs_no_options = { + 'description': None, + 'domain_id': None, + 'enabled': True, + 'is_domain': False, + 'parent_id': None, + 'tags': [], + } + def setUp(self): super().setUp() - self.project = identity_fakes.FakeProject.create_one_project( - attrs={'domain_id': self.domain.id} - ) - self.domains_mock.get.return_value = self.domain - self.projects_mock.create.return_value = self.project - self.datalist = ( - self.project.description, - self.project.domain_id, - True, - self.project.id, - False, - self.project.name, - self.project.parent_id, - self.project.tags, - ) + self.identity_sdk_client.find_domain.return_value = self.domain + # Get the command object to test self.cmd = project.CreateProject(self.app, None) def test_project_create_no_options(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, **self.project_kwargs_no_options + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ - self.project.name, + project.name, ] verifylist = [ ('parent', None), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('tags', []), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -88,53 +79,43 @@ class TestProjectCreate(TestProject): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, - 'tags': [], - 'options': {}, + 'name': project.name, + 'is_enabled': True, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) - collist = ( - 'description', - 'domain_id', - 'enabled', - 'id', - 'is_domain', - 'name', - 'parent_id', - 'tags', - ) - self.assertEqual(collist, columns) datalist = ( - self.project.description, - self.project.domain_id, + None, + None, True, - self.project.id, + project.id, False, - self.project.name, - self.project.parent_id, - self.project.tags, + project.name, + {}, + None, + [], ) self.assertEqual(datalist, data) def test_project_create_description(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, description='new desc'), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--description', 'new desc', - self.project.name, + project.name, ] verifylist = [ ('description', 'new desc'), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', []), ] @@ -145,33 +126,43 @@ class TestProjectCreate(TestProject): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, + 'name': project.name, 'description': 'new desc', - 'enabled': True, - 'parent': None, - 'tags': [], - 'options': {}, + 'is_enabled': True, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + 'new desc', + None, + True, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_domain(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, domain_id=self.domain.id), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--domain', - self.project.domain_id, - self.project.name, + project.domain_id, + project.name, ] verifylist = [ - ('domain', self.project.domain_id), + ('domain', project.domain_id), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', []), ] @@ -184,63 +175,90 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': self.project.domain_id, - 'description': None, - 'enabled': True, - 'parent': None, - 'tags': [], - 'options': {}, + 'name': project.name, + 'domain_id': project.domain_id, + 'is_enabled': True, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + self.domain.id, + True, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_domain_no_perms(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, domain_id=self.domain.id), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--domain', - self.project.domain_id, - self.project.name, + project.domain_id, + project.name, ] verifylist = [ - ('domain', self.project.domain_id), + ('domain', project.domain_id), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', []), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - mocker = mock.Mock() - mocker.return_value = None - with mock.patch("osc_lib.utils.find_resource", mocker): - columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_domain.side_effect = ( + sdk_exc.ForbiddenException + ) + self.identity_sdk_client.find_domain.return_value = None + + columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'name': self.project.name, - 'domain': self.project.domain_id, - 'description': None, - 'enabled': True, - 'parent': None, - 'tags': [], - 'options': {}, + 'name': project.name, + 'domain_id': project.domain_id, + 'is_enabled': True, } - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + self.domain.id, + True, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_enable(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, enabled=True), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--enable', - self.project.name, + project.name, ] verifylist = [ ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', []), ] @@ -253,29 +271,39 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, - 'tags': [], - 'options': {}, + 'name': project.name, + 'is_enabled': True, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + True, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_disable(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, enabled=False), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--disable', - self.project.name, + project.name, ] verifylist = [ ('enabled', False), - ('name', self.project.name), + ('name', project.name), ('parent', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -287,32 +315,42 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': False, - 'parent': None, - 'tags': [], - 'options': {}, + 'name': project.name, + 'is_enabled': False, } - # ProjectManager.create(name=, domain=, - # description=, enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + False, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_property(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, fee='fi', fo='fum'), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--property', 'fee=fi', '--property', 'fo=fum', - self.project.name, + project.name, ] verifylist = [ + ('name', project.name), ('properties', {'fee': 'fi', 'fo': 'fum'}), - ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -323,36 +361,62 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, + 'name': project.name, + 'is_enabled': True, 'fee': 'fi', 'fo': 'fum', - 'tags': [], - 'options': {}, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) - self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual( + ( + 'description', + 'domain_id', + 'enabled', + 'fee', + 'fo', + 'id', + 'is_domain', + 'name', + 'options', + 'parent_id', + 'tags', + ), + columns, + ) + datalist = ( + None, + None, + True, + 'fi', + 'fum', + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_is_domain_false_property(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, is_domain=False), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--property', 'is_domain=false', - self.project.name, + project.name, ] verifylist = [ ('parent', None), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('tags', []), ('properties', {'is_domain': 'false'}), - ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -364,33 +428,44 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, + 'name': project.name, + 'is_enabled': True, 'is_domain': False, - 'tags': [], - 'options': {}, } - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + True, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_is_domain_true_property(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, is_domain=True), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--property', 'is_domain=true', - self.project.name, + project.name, ] verifylist = [ ('parent', None), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('tags', []), ('properties', {'is_domain': 'true'}), - ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -402,33 +477,44 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, + 'name': project.name, + 'is_enabled': True, 'is_domain': True, - 'tags': [], - 'options': {}, } - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + True, + project.id, + True, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_is_domain_none_property(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, is_domain=None), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--property', 'is_domain=none', - self.project.name, + project.name, ] verifylist = [ ('parent', None), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('tags', []), ('properties', {'is_domain': 'none'}), - ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -440,40 +526,51 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, + 'name': project.name, + 'is_enabled': True, 'is_domain': None, - 'tags': [], - 'options': {}, } - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + True, + project.id, + None, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_parent(self): - self.parent = identity_fakes.FakeProject.create_one_project() - self.project = identity_fakes.FakeProject.create_one_project( - attrs={'domain_id': self.domain.id, 'parent_id': self.parent.id} + parent = sdk_fakes.generate_fake_resource(_project.Project) + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, + domain_id=self.domain.id, + parent_id=parent.id, + ), ) - self.projects_mock.get.return_value = self.parent - self.projects_mock.create.return_value = self.project + self.identity_sdk_client.find_project.return_value = parent + self.identity_sdk_client.create_project.return_value = project arglist = [ '--domain', - self.project.domain_id, + project.domain_id, '--parent', - self.parent.name, - self.project.name, + parent.name, + project.name, ] verifylist = [ - ('domain', self.project.domain_id), - ('parent', self.parent.name), + ('domain', project.domain_id), + ('parent', parent.name), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('tags', []), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -481,61 +578,52 @@ class TestProjectCreate(TestProject): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'name': self.project.name, - 'domain': self.project.domain_id, - 'parent': self.parent.id, - 'description': None, - 'enabled': True, - 'tags': [], - 'options': {}, + 'name': project.name, + 'domain_id': project.domain_id, + 'parent_id': parent.id, + 'is_enabled': True, } + self.identity_sdk_client.create_project.assert_called_with(**kwargs) - self.projects_mock.create.assert_called_with(**kwargs) - - collist = ( - 'description', - 'domain_id', - 'enabled', - 'id', - 'is_domain', - 'name', - 'parent_id', - 'tags', - ) - self.assertEqual(columns, collist) + self.assertEqual(self.columns, columns) datalist = ( - self.project.description, - self.project.domain_id, - self.project.enabled, - self.project.id, - self.project.is_domain, - self.project.name, - self.parent.id, - self.project.tags, + None, + self.domain.id, + True, + project.id, + False, + project.name, + {}, + parent.id, + [], ) self.assertEqual(data, datalist) def test_project_create_invalid_parent(self): - self.projects_mock.resource_class.__name__ = 'Project' - self.projects_mock.get.side_effect = exceptions.NotFound( - 'Invalid parent' + self.identity_sdk_client.find_project.side_effect = ( + sdk_exc.ResourceNotFound ) - self.projects_mock.find.side_effect = exceptions.NotFound( - 'Invalid parent' + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, + domain_id=self.domain.id, + parent_id='invalid', + ), ) arglist = [ '--domain', - self.project.domain_id, + project.domain_id, '--parent', 'invalid', - self.project.name, + project.name, ] verifylist = [ - ('domain', self.project.domain_id), + ('domain', project.domain_id), ('parent', 'invalid'), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -546,17 +634,27 @@ class TestProjectCreate(TestProject): ) def test_project_create_with_tags(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, + domain_id=self.domain.id, + tags=['foo'], + ), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--domain', - self.project.domain_id, + project.domain_id, '--tag', 'foo', - self.project.name, + project.name, ] verifylist = [ - ('domain', self.project.domain_id), + ('domain', project.domain_id), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', ['foo']), ] @@ -569,29 +667,45 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': self.project.domain_id, - 'description': None, - 'enabled': True, - 'parent': None, + 'name': project.name, + 'domain_id': project.domain_id, + 'is_enabled': True, 'tags': ['foo'], - 'options': {}, } - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + self.domain.id, + True, + project.id, + False, + project.name, + {}, + None, + ['foo'], + ) + self.assertEqual(datalist, data) def test_project_create_with_immutable_option(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, options={'immutable': True} + ), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--immutable', - self.project.name, + project.name, ] verifylist = [ ('immutable', True), ('description', None), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', []), ] @@ -604,31 +718,44 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, - 'tags': [], + 'name': project.name, + 'is_enabled': True, 'options': {'immutable': True}, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + True, + project.id, + False, + project.name, + {'immutable': True}, + None, + [], + ) + self.assertEqual(datalist, data) def test_project_create_with_no_immutable_option(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, options={'immutable': False} + ), + ) + self.identity_sdk_client.create_project.return_value = project + arglist = [ '--no-immutable', - self.project.name, + project.name, ] verifylist = [ ('immutable', False), ('description', None), ('enabled', True), - ('name', self.project.name), + ('name', project.name), ('parent', None), ('tags', []), ] @@ -641,36 +768,121 @@ class TestProjectCreate(TestProject): # Set expected values kwargs = { - 'name': self.project.name, - 'domain': None, - 'description': None, - 'enabled': True, - 'parent': None, - 'tags': [], + 'name': project.name, + 'is_enabled': True, 'options': {'immutable': False}, } - # ProjectManager.create(name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_project.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + datalist = ( + None, + None, + True, + project.id, + False, + project.name, + {'immutable': False}, + None, + [], + ) + self.assertEqual(datalist, data) + def test_project_create_conflict_with_or_show(self): + project = sdk_fakes.generate_fake_resource( + _project.Project, **self.project_kwargs_no_options + ) + self.identity_sdk_client.create_project.side_effect = ( + sdk_exc.ConflictException + ) + self.identity_sdk_client.find_project.return_value = project + + arglist = [ + '--or-show', + project.name, + ] + verifylist = [ + ('or_show', True), + ('description', None), + ('enabled', True), + ('name', project.name), + ('parent', None), + ('tags', []), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + kwargs = { + 'name': project.name, + 'is_enabled': True, + } + self.identity_sdk_client.create_project.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + datalist = ( + None, + None, + True, + project.id, + False, + project.name, + {}, + None, + [], + ) + self.assertEqual(datalist, data) + + def test_project_create_conflict_without_or_show(self): + self.identity_sdk_client.create_project.side_effect = ( + sdk_exc.ConflictException + ) + project = sdk_fakes.generate_fake_resource( + _project.Project, **self.project_kwargs_no_options + ) -class TestProjectDelete(TestProject): - project = identity_fakes.FakeProject.create_one_project() + arglist = [ + project.name, + ] + verifylist = [ + ('or_show', False), + ('description', None), + ('enabled', True), + ('name', project.name), + ('parent', None), + ('tags', []), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + sdk_exc.ConflictException, + self.cmd.take_action, + parsed_args, + ) + + +class TestProjectDelete(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.projects_mock.get.return_value = self.project - self.projects_mock.delete.return_value = None + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.project_with_domain = sdk_fakes.generate_fake_resource( + _project.Project, + name=self.project.name, + domain_id=self.domain.id, + ) + self.identity_sdk_client.delete_project.return_value = None # Get the command object to test self.cmd = project.DeleteProject(self.app, None) def test_project_delete_no_options(self): + self.identity_sdk_client.find_project.return_value = self.project + arglist = [ self.project.id, ] @@ -681,16 +893,72 @@ class TestProjectDelete(TestProject): result = self.cmd.take_action(parsed_args) - self.projects_mock.delete.assert_called_with( + self.identity_sdk_client.delete_project.assert_called_with( self.project.id, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_projects_with_exception(self, find_mock): - find_mock.side_effect = [self.project, exceptions.CommandError] + def test_project_multi_delete(self): + self.identity_sdk_client.find_project.side_effect = [ + self.project, + self.project_with_domain, + ] + arglist = [self.project.id, self.project_with_domain.id] + verifylist = [ + ('projects', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.identity_sdk_client.delete_project.assert_has_calls( + [ + mock.call(self.project.id), + mock.call(self.project_with_domain.id), + ] + ) + self.assertIsNone(result) + + def test_project_delete_with_forbidden_domain(self): + self.identity_sdk_client.find_domain.side_effect = [ + sdk_exc.ForbiddenException + ] + self.identity_sdk_client.find_project.return_value = ( + self.project_with_domain + ) + + arglist = [ + '--domain', + self.project_with_domain.domain_id, + self.project_with_domain.name, + ] + verifylist = [ + ('domain', self.domain.id), + ('projects', [self.project_with_domain.name]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_project.assert_called_with( + name_or_id=self.project_with_domain.name, + ignore_missing=False, + domain_id=self.domain.id, + ) + self.identity_sdk_client.delete_project.assert_called_once_with( + self.project_with_domain.id + ) + self.assertIsNone(result) + + def test_delete_multi_projects_with_exception(self): + self.identity_sdk_client.find_project.side_effect = [ + self.project, + self.project_with_domain, + sdk_exc.NotFoundException, + ] + arglist = [ self.project.id, + self.project_with_domain.id, 'unexist_project', ] verifylist = [ @@ -702,21 +970,36 @@ class TestProjectDelete(TestProject): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - self.assertEqual('1 of 2 projects failed to delete.', str(e)) + self.assertEqual('1 of 3 projects failed to delete.', str(e)) - find_mock.assert_any_call(self.projects_mock, self.project.id) - find_mock.assert_any_call(self.projects_mock, 'unexist_project') + self.identity_sdk_client.find_project.assert_has_calls( + [ + mock.call(name_or_id=self.project.id, ignore_missing=False), + mock.call( + name_or_id=self.project_with_domain.id, + ignore_missing=False, + ), + mock.call(name_or_id='unexist_project', ignore_missing=False), + ] + ) - self.assertEqual(2, find_mock.call_count) - self.projects_mock.delete.assert_called_once_with(self.project.id) + self.assertEqual(3, self.identity_sdk_client.find_project.call_count) + self.identity_sdk_client.delete_project.assert_has_calls( + [ + mock.call(self.project.id), + mock.call(self.project_with_domain.id), + ] + ) -class TestProjectList(TestProject): - domain = identity_fakes.FakeDomain.create_one_domain() - project = identity_fakes.FakeProject.create_one_project( - attrs={'domain_id': domain.id} +class TestProjectList(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource( + _project.Project, domain_id=domain.id + ) + projects = list( + sdk_fakes.generate_fake_resources(_project.Project, count=2) ) - projects = identity_fakes.FakeProject.create_projects() columns = ( 'ID', @@ -746,12 +1029,12 @@ class TestProjectList(TestProject): def setUp(self): super().setUp() - self.projects_mock.list.return_value = [self.project] - # Get the command object to test self.cmd = project.ListProject(self.app, None) def test_project_list_no_options(self): + self.identity_sdk_client.projects.return_value = [self.project] + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -760,12 +1043,14 @@ class TestProjectList(TestProject): # 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.projects_mock.list.assert_called_with() + self.identity_sdk_client.projects.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_project_list_long(self): + self.identity_sdk_client.projects.return_value = [self.project] + arglist = [ '--long', ] @@ -778,7 +1063,7 @@ class TestProjectList(TestProject): # 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.projects_mock.list.assert_called_with() + self.identity_sdk_client.projects.assert_called_with() collist = ('ID', 'Name', 'Domain ID', 'Description', 'Enabled') self.assertEqual(collist, columns) @@ -794,6 +1079,8 @@ class TestProjectList(TestProject): self.assertEqual(datalist, tuple(data)) def test_project_list_domain(self): + self.identity_sdk_client.projects.return_value = [self.project] + arglist = [ '--domain', self.project.domain_id, @@ -802,7 +1089,7 @@ class TestProjectList(TestProject): ('domain', self.project.domain_id), ] - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_domain.return_value = self.domain parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -810,14 +1097,16 @@ class TestProjectList(TestProject): # 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.projects_mock.list.assert_called_with( - domain=self.project.domain_id + self.identity_sdk_client.projects.assert_called_with( + domain_id=self.project.domain_id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_project_list_domain_no_perms(self): + self.identity_sdk_client.projects.return_value = [self.project] + arglist = [ '--domain', self.project.domain_id, @@ -826,23 +1115,30 @@ class TestProjectList(TestProject): ('domain', self.project.domain_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - mocker = mock.Mock() - mocker.return_value = None - with mock.patch("osc_lib.utils.find_resource", mocker): - columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_project.side_effect = ( + sdk_exc.ResourceNotFound + ) + self.identity_sdk_client.find_domain.return_value = self.domain + + columns, data = self.cmd.take_action(parsed_args) - self.projects_mock.list.assert_called_with( - domain=self.project.domain_id + self.identity_sdk_client.projects.assert_called_with( + domain_id=self.project.domain_id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_project_list_parent(self): - self.parent = identity_fakes.FakeProject.create_one_project() - self.project = identity_fakes.FakeProject.create_one_project( - attrs={'domain_id': self.domain.id, 'parent_id': self.parent.id} + self.parent = sdk_fakes.generate_fake_resource(_project.Project) + self.project = sdk_fakes.generate_fake_resource( + _project.Project, + id=self.project.id, + name=self.project.name, + domain_id=self.domain.id, + parent_id=self.parent.id, ) + self.identity_sdk_client.projects.return_value = [self.project] arglist = [ '--parent', @@ -852,18 +1148,48 @@ class TestProjectList(TestProject): ('parent', self.parent.id), ] - self.projects_mock.get.return_value = self.parent + self.identity_sdk_client.find_project.return_value = self.parent + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.projects.assert_called_with( + parent_id=self.parent.id + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + + def test_project_list_user(self): + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.project = sdk_fakes.generate_fake_resource( + _project.UserProject, + id=self.project.id, + name=self.project.name, + user_id=self.user.id, + ) + self.identity_sdk_client.user_projects.return_value = [self.project] + + arglist = [ + '--user', + self.user.id, + ] + verifylist = [ + ('user', self.user.id), + ] + + self.identity_sdk_client.find_user.return_value = self.user parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.projects_mock.list.assert_called_with(parent=self.parent.id) + self.identity_sdk_client.user_projects.assert_called_with(self.user.id) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_project_list_sort(self): - self.projects_mock.list.return_value = self.projects + self.identity_sdk_client.projects.return_value = self.projects arglist = [ '--sort', @@ -877,7 +1203,7 @@ class TestProjectList(TestProject): # 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.projects_mock.list.assert_called_with() + self.identity_sdk_client.projects.assert_called_with() collist = ('ID', 'Name') self.assertEqual(collist, columns) @@ -896,6 +1222,8 @@ class TestProjectList(TestProject): self.assertEqual(datalists, tuple(data)) def test_project_list_my_projects(self): + self.identity_sdk_client.user_projects.return_value = [self.project] + auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_PROJECT_ID, ) @@ -913,8 +1241,8 @@ class TestProjectList(TestProject): # 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.projects_mock.list.assert_called_with( - user=self.app.client_manager.auth_ref.user_id + self.identity_sdk_client.user_projects.assert_called_with( + self.app.client_manager.auth_ref.user_id ) collist = ('ID', 'Name') @@ -928,6 +1256,8 @@ class TestProjectList(TestProject): self.assertEqual(datalist, tuple(data)) def test_project_list_with_option_enabled(self): + self.identity_sdk_client.projects.return_value = [self.project] + arglist = ['--enabled'] verifylist = [('is_enabled', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -938,25 +1268,28 @@ class TestProjectList(TestProject): columns, data = self.cmd.take_action(parsed_args) kwargs = {'is_enabled': True} - self.projects_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.projects.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) -class TestProjectSet(TestProject): - domain = identity_fakes.FakeDomain.create_one_domain() - project = identity_fakes.FakeProject.create_one_project( - attrs={'domain_id': domain.id, 'tags': ['tag1', 'tag2', 'tag3']} +class TestProjectSet(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + + project_kwargs_no_options = { + 'domain_id': domain.id, + 'tags': ['tag1', 'tag2', 'tag3'], + } + project = sdk_fakes.generate_fake_resource( + _project.Project, **project_kwargs_no_options ) def setUp(self): super().setUp() - self.domains_mock.get.return_value = self.domain - - self.projects_mock.get.return_value = self.project - self.projects_mock.update.return_value = self.project + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = self.project # Get the command object to test self.cmd = project.SetProject(self.app, None) @@ -997,9 +1330,10 @@ class TestProjectSet(TestProject): kwargs = { 'name': 'qwerty', } - # ProjectManager.update(project, name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_description(self): @@ -1024,7 +1358,9 @@ class TestProjectSet(TestProject): kwargs = { 'description': 'new desc', } - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_enable(self): @@ -1047,7 +1383,9 @@ class TestProjectSet(TestProject): kwargs = { 'enabled': True, } - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_disable(self): @@ -1070,7 +1408,9 @@ class TestProjectSet(TestProject): kwargs = { 'enabled': False, } - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_property(self): @@ -1097,7 +1437,9 @@ class TestProjectSet(TestProject): 'fee': 'fi', 'fo': 'fum', } - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_tags(self): @@ -1112,7 +1454,7 @@ class TestProjectSet(TestProject): ] verifylist = [ ('name', 'qwerty'), - ('domain', self.project.domain_id), + ('domain', self.domain.id), ('enabled', None), ('project', self.project.name), ('tags', ['foo']), @@ -1126,9 +1468,9 @@ class TestProjectSet(TestProject): 'name': 'qwerty', 'tags': sorted({'tag1', 'tag2', 'tag3', 'foo'}), } - # ProjectManager.update(project, name=, domain=, description=, - # enabled=, **kwargs) - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_remove_tags(self): @@ -1142,14 +1484,16 @@ class TestProjectSet(TestProject): verifylist = [ ('enabled', None), ('project', self.project.name), - ('remove_tag', ['tag1', 'tag2']), + ('remove_tags', ['tag1', 'tag2']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = {'tags': list({'tag3'})} - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_with_immutable_option(self): @@ -1173,7 +1517,9 @@ class TestProjectSet(TestProject): kwargs = { 'options': {'immutable': True}, } - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) def test_project_set_with_no_immutable_option(self): @@ -1197,114 +1543,108 @@ class TestProjectSet(TestProject): kwargs = { 'options': {'immutable': False}, } - self.projects_mock.update.assert_called_with(self.project.id, **kwargs) + self.identity_sdk_client.update_project.assert_called_with( + self.project.id, **kwargs + ) self.assertIsNone(result) -class TestProjectShow(TestProject): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestProjectShow(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + + columns = ( + 'description', + 'domain_id', + 'enabled', + 'id', + 'is_domain', + 'name', + 'options', + 'parent_id', + 'tags', + ) + + project_kwargs_no_options = { + 'description': None, + 'domain_id': None, + 'enabled': True, + 'is_domain': False, + 'parent_id': None, + 'tags': [], + } def setUp(self): super().setUp() - self.project = identity_fakes.FakeProject.create_one_project( - attrs={'domain_id': self.domain.id} - ) - # Get the command object to test self.cmd = project.ShowProject(self.app, None) def test_project_show(self): - self.projects_mock.get.return_value = self.project + project = sdk_fakes.generate_fake_resource( + _project.Project, **self.project_kwargs_no_options + ) + self.identity_sdk_client.find_project.return_value = project arglist = [ - self.project.id, + project.id, ] verifylist = [ - ('project', self.project.id), + ('project', project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.identity_client.tokens.get_token_data.return_value = { - 'token': { - 'project': { - 'domain': {}, - 'name': parsed_args.project, - 'id': parsed_args.project, - } - } - } - # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.projects_mock.get.assert_called_once_with(self.project.id) - - collist = ( - 'description', - 'domain_id', - 'enabled', - 'id', - 'is_domain', - 'name', - 'parent_id', - 'tags', + self.identity_sdk_client.find_project.assert_called_with( + project.id, ignore_missing=False ) - self.assertEqual(collist, columns) + + self.assertEqual(self.columns, columns) datalist = ( - self.project.description, - self.project.domain_id, + None, + None, True, - self.project.id, + project.id, False, - self.project.name, - self.project.parent_id, - self.project.tags, + project.name, + {}, + None, + [], ) self.assertEqual(datalist, data) def test_project_show_parents(self): - self.project = identity_fakes.FakeProject.create_one_project( - attrs={ - 'parent_id': self.project.parent_id, - 'parents': [{'project': {'id': self.project.parent_id}}], - } + parent = sdk_fakes.generate_fake_resource( + _project.Project, parent_id='default' + ) + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, + parent_id=parent.id, + parents={parent.id: {parent.parent_id: None}}, + ), ) - self.projects_mock.get.return_value = self.project + self.identity_sdk_client.find_project.return_value = project arglist = [ - self.project.id, + project.id, '--parents', ] verifylist = [ - ('project', self.project.id), + ('project', project.id), ('parents', True), ('children', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.identity_client.tokens.get_token_data.return_value = { - 'token': { - 'project': { - 'domain': {}, - 'name': parsed_args.project, - 'id': parsed_args.project, - } - } - } columns, data = self.cmd.take_action(parsed_args) - self.projects_mock.get.assert_has_calls( - [ - call(self.project.id), - call( - self.project.id, - parents_as_ids=True, - subtree_as_ids=False, - ), - ] + self.identity_sdk_client.find_project.assert_called_with( + project.id, parents_as_ids=True, ignore_missing=False ) collist = ( @@ -1314,63 +1654,51 @@ class TestProjectShow(TestProject): 'id', 'is_domain', 'name', + 'options', 'parent_id', 'parents', 'tags', ) - self.assertEqual(columns, collist) + self.assertEqual(collist, columns) datalist = ( - self.project.description, - self.project.domain_id, - self.project.enabled, - self.project.id, - self.project.is_domain, - self.project.name, - self.project.parent_id, - [{'project': {'id': self.project.parent_id}}], - self.project.tags, + None, + None, + True, + project.id, + False, + project.name, + {}, + parent.id, + {parent.id: {'default': None}}, + [], ) - self.assertEqual(data, datalist) + self.assertEqual(datalist, data) def test_project_show_subtree(self): - self.project = identity_fakes.FakeProject.create_one_project( - attrs={ - 'parent_id': self.project.parent_id, - 'subtree': [{'project': {'id': 'children-id'}}], - } + child = sdk_fakes.generate_fake_resource( + _project.Project, subtree=None + ) + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, subtree={child.id: None}), ) - self.projects_mock.get.return_value = self.project + self.identity_sdk_client.find_project.return_value = project arglist = [ - self.project.id, + project.id, '--children', ] verifylist = [ - ('project', self.project.id), + ('project', project.id), ('parents', False), ('children', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.identity_client.tokens.get_token_data.return_value = { - 'token': { - 'project': { - 'domain': {}, - 'name': parsed_args.project, - 'id': parsed_args.project, - } - } - } columns, data = self.cmd.take_action(parsed_args) - self.projects_mock.get.assert_has_calls( - [ - call(self.project.id), - call( - self.project.id, - parents_as_ids=False, - subtree_as_ids=True, - ), - ] + + self.identity_sdk_client.find_project.assert_called_with( + project.id, subtree_as_ids=True, ignore_missing=False ) collist = ( @@ -1380,65 +1708,63 @@ class TestProjectShow(TestProject): 'id', 'is_domain', 'name', + 'options', 'parent_id', 'subtree', 'tags', ) - self.assertEqual(columns, collist) + self.assertEqual(collist, columns) datalist = ( - self.project.description, - self.project.domain_id, - self.project.enabled, - self.project.id, - self.project.is_domain, - self.project.name, - self.project.parent_id, - [{'project': {'id': 'children-id'}}], - self.project.tags, + None, + None, + True, + project.id, + False, + project.name, + {}, + None, + {child.id: None}, + [], ) - self.assertEqual(data, datalist) + self.assertEqual(datalist, data) def test_project_show_parents_and_children(self): - self.project = identity_fakes.FakeProject.create_one_project( - attrs={ - 'parent_id': self.project.parent_id, - 'parents': [{'project': {'id': self.project.parent_id}}], - 'subtree': [{'project': {'id': 'children-id'}}], - } + parent = sdk_fakes.generate_fake_resource( + _project.Project, parent_id='default' + ) + child = sdk_fakes.generate_fake_resource( + _project.Project, subtree=None ) - self.projects_mock.get.return_value = self.project + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict( + self.project_kwargs_no_options, + parent_id=parent.id, + parents={parent.id: {parent.parent_id: None}}, + subtree={child.id: None}, + ), + ) + self.identity_sdk_client.find_project.return_value = project arglist = [ - self.project.id, + project.id, '--parents', '--children', ] verifylist = [ - ('project', self.project.id), + ('project', project.id), ('parents', True), ('children', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.identity_client.tokens.get_token_data.return_value = { - 'token': { - 'project': { - 'domain': {}, - 'name': parsed_args.project, - 'id': parsed_args.project, - } - } - } columns, data = self.cmd.take_action(parsed_args) - self.projects_mock.get.assert_has_calls( - [ - call(self.project.id), - call( - self.project.id, - parents_as_ids=True, - subtree_as_ids=True, - ), - ] + + self.identity_sdk_client.find_project.assert_called_with( + project.id, + parents_as_ids=True, + subtree_as_ids=True, + ignore_missing=False, ) collist = ( @@ -1448,42 +1774,36 @@ class TestProjectShow(TestProject): 'id', 'is_domain', 'name', + 'options', 'parent_id', 'parents', 'subtree', 'tags', ) - self.assertEqual(columns, collist) + self.assertEqual(collist, columns) datalist = ( - self.project.description, - self.project.domain_id, - self.project.enabled, - self.project.id, - self.project.is_domain, - self.project.name, - self.project.parent_id, - [{'project': {'id': self.project.parent_id}}], - [{'project': {'id': 'children-id'}}], - self.project.tags, + None, + None, + True, + project.id, + False, + project.name, + {}, + parent.id, + {parent.id: {'default': None}}, + {child.id: None}, + [], ) - self.assertEqual(data, datalist) + self.assertEqual(datalist, data) def test_project_show_with_domain(self): - project = identity_fakes.FakeProject.create_one_project( - {"name": self.project.name} - ) - - self.identity_client.tokens.get_token_data.return_value = { - 'token': { - 'project': { - 'domain': {"id": self.project.domain_id}, - 'name': self.project.name, - 'id': self.project.id, - } - } - } + project = sdk_fakes.generate_fake_resource( + _project.Project, + **dict(self.project_kwargs_no_options, domain_id=self.domain.id), + ) + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = project - identity_client = self.identity_client arglist = [ "--domain", self.domain.id, @@ -1495,23 +1815,22 @@ class TestProjectShow(TestProject): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - project_str = common._get_token_resource( - identity_client, 'project', parsed_args.project, parsed_args.domain + columns, data = self.cmd.take_action(parsed_args) + + self.identity_sdk_client.find_project.assert_called_with( + project.id, domain_id=self.domain.id, ignore_missing=False ) - self.assertEqual(self.project.id, project_str) - arglist = [ - "--domain", - project.domain_id, + self.assertEqual(self.columns, columns) + datalist = ( + None, + self.domain.id, + True, + project.id, + False, project.name, - ] - verifylist = [ - ('domain', project.domain_id), - ('project', project.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - project_str = common._get_token_resource( - identity_client, 'project', parsed_args.project, parsed_args.domain + {}, + None, + [], ) - self.assertEqual(project.name, project_str) + self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_protocol.py b/openstackclient/tests/unit/identity/v3/test_protocol.py index c856996..4ac77d6 100644 --- a/openstackclient/tests/unit/identity/v3/test_protocol.py +++ b/openstackclient/tests/unit/identity/v3/test_protocol.py @@ -12,202 +12,211 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - +from openstack.identity.v3 import federation_protocol as _federation_protocol +from openstack.identity.v3 import mapping as _mapping +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import federation_protocol -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestProtocol(identity_fakes.TestFederatedIdentity): - def setUp(self): - super().setUp() - - federation_lib = self.identity_client.federation - self.protocols_mock = federation_lib.protocols - self.protocols_mock.reset_mock() - - -class TestProtocolCreate(TestProtocol): +class TestProtocolCreate(identity_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - proto = copy.deepcopy(identity_fakes.PROTOCOL_OUTPUT) - resource = fakes.FakeResource(None, proto, loaded=True) - self.protocols_mock.create.return_value = resource + self.proto = sdk_fakes.generate_fake_resource( + _federation_protocol.FederationProtocol + ) + self.identity_sdk_client.create_federation_protocol.return_value = ( + self.proto + ) self.cmd = federation_protocol.CreateProtocol(self.app, None) def test_create_protocol(self): argslist = [ - identity_fakes.protocol_id, + self.proto.name, '--identity-provider', - identity_fakes.idp_id, + self.proto.idp_id, '--mapping', - identity_fakes.mapping_id, + self.proto.mapping_id, ] verifylist = [ - ('federation_protocol', identity_fakes.protocol_id), - ('identity_provider', identity_fakes.idp_id), - ('mapping', identity_fakes.mapping_id), + ('federation_protocol', self.proto.name), + ('identity_provider', self.proto.idp_id), + ('mapping', self.proto.mapping_id), ] parsed_args = self.check_parser(self.cmd, argslist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.protocols_mock.create.assert_called_with( - protocol_id=identity_fakes.protocol_id, - identity_provider=identity_fakes.idp_id, - mapping=identity_fakes.mapping_id, + self.identity_sdk_client.create_federation_protocol.assert_called_with( + name=self.proto.id, + idp_id=self.proto.idp_id, + mapping_id=self.proto.mapping_id, ) collist = ('id', 'identity_provider', 'mapping') self.assertEqual(collist, columns) datalist = ( - identity_fakes.protocol_id, - identity_fakes.idp_id, - identity_fakes.mapping_id, + self.proto.id, + self.proto.idp_id, + self.proto.mapping_id, ) self.assertEqual(datalist, data) -class TestProtocolDelete(TestProtocol): +class TestProtocolDelete(identity_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.protocols_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROTOCOL_OUTPUT), - loaded=True, + self.proto = sdk_fakes.generate_fake_resource( + _federation_protocol.FederationProtocol ) - - self.protocols_mock.delete.return_value = None + self.identity_sdk_client.delete_federation_protocol.return_value = None self.cmd = federation_protocol.DeleteProtocol(self.app, None) - def test_delete_identity_provider(self): + def test_delete_protocol(self): arglist = [ '--identity-provider', - identity_fakes.idp_id, - identity_fakes.protocol_id, + self.proto.idp_id, + self.proto.name, ] verifylist = [ - ('federation_protocol', [identity_fakes.protocol_id]), - ('identity_provider', identity_fakes.idp_id), + ('federation_protocol', [self.proto.id]), + ('identity_provider', self.proto.idp_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.protocols_mock.delete.assert_called_with( - identity_fakes.idp_id, identity_fakes.protocol_id + self.identity_sdk_client.delete_federation_protocol.assert_called_with( + idp_id=self.proto.idp_id, + protocol=self.proto.id, + ignore_missing=False, ) self.assertIsNone(result) -class TestProtocolList(TestProtocol): +class TestProtocolList(identity_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - self.protocols_mock.get.return_value = fakes.FakeResource( - None, identity_fakes.PROTOCOL_ID_MAPPING, loaded=True + self.proto1 = sdk_fakes.generate_fake_resource( + _federation_protocol.FederationProtocol ) - - self.protocols_mock.list.return_value = [ - fakes.FakeResource( - None, identity_fakes.PROTOCOL_ID_MAPPING, loaded=True - ) + self.proto2 = sdk_fakes.generate_fake_resource( + _federation_protocol.FederationProtocol, idp_id=self.proto1 + ) + self.identity_sdk_client.federation_protocols.return_value = [ + self.proto1, + self.proto2, ] - self.cmd = federation_protocol.ListProtocols(self.app, None) def test_list_protocols(self): - arglist = ['--identity-provider', identity_fakes.idp_id] - verifylist = [('identity_provider', identity_fakes.idp_id)] + arglist = ['--identity-provider', self.proto1.idp_id] + verifylist = [('identity_provider', self.proto1.idp_id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.protocols_mock.list.assert_called_with(identity_fakes.idp_id) + self.identity_sdk_client.federation_protocols.assert_called_with( + self.proto1.idp_id + ) + self.assertEqual(columns, ('id', 'mapping')) + datalist = ( + ( + self.proto1.name, + self.proto1.mapping_id, + ), + ( + self.proto2.name, + self.proto2.mapping_id, + ), + ) + self.assertEqual(datalist, tuple(data)) -class TestProtocolSet(TestProtocol): + +class TestProtocolSet(identity_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - self.protocols_mock.get.return_value = fakes.FakeResource( - None, identity_fakes.PROTOCOL_OUTPUT, loaded=True + self.proto = sdk_fakes.generate_fake_resource( + _federation_protocol.FederationProtocol ) - self.protocols_mock.update.return_value = fakes.FakeResource( - None, identity_fakes.PROTOCOL_OUTPUT_UPDATED, loaded=True + self.mapping = sdk_fakes.generate_fake_resource(_mapping.Mapping) + self.identity_sdk_client.update_federation_protocol.return_value = ( + self.proto ) - self.cmd = federation_protocol.SetProtocol(self.app, None) def test_set_new_mapping(self): arglist = [ - identity_fakes.protocol_id, + self.proto.name, '--identity-provider', - identity_fakes.idp_id, + self.proto.idp_id, '--mapping', - identity_fakes.mapping_id, + self.mapping.name, ] verifylist = [ - ('identity_provider', identity_fakes.idp_id), - ('federation_protocol', identity_fakes.protocol_id), - ('mapping', identity_fakes.mapping_id), + ('identity_provider', self.proto.idp_id), + ('federation_protocol', self.proto.name), + ('mapping', self.mapping.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.protocols_mock.update.assert_called_with( - identity_fakes.idp_id, - identity_fakes.protocol_id, - identity_fakes.mapping_id, + self.identity_sdk_client.update_federation_protocol.assert_called_with( + idp_id=self.proto.idp_id, + name=self.proto.name, + mapping_id=self.mapping.id, ) collist = ('id', 'identity_provider', 'mapping') self.assertEqual(collist, columns) datalist = ( - identity_fakes.protocol_id, - identity_fakes.idp_id, - identity_fakes.mapping_id_updated, + self.proto.name, + self.proto.idp_id, + self.proto.mapping_id, ) self.assertEqual(datalist, data) -class TestProtocolShow(TestProtocol): +class TestProtocolShow(identity_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - self.protocols_mock.get.return_value = fakes.FakeResource( - None, identity_fakes.PROTOCOL_OUTPUT, loaded=False + self.proto = sdk_fakes.generate_fake_resource( + _federation_protocol.FederationProtocol + ) + self.identity_sdk_client.get_federation_protocol.return_value = ( + self.proto ) - self.cmd = federation_protocol.ShowProtocol(self.app, None) def test_show_protocol(self): arglist = [ - identity_fakes.protocol_id, + self.proto.name, '--identity-provider', - identity_fakes.idp_id, + self.proto.idp_id, ] verifylist = [ - ('federation_protocol', identity_fakes.protocol_id), - ('identity_provider', identity_fakes.idp_id), + ('federation_protocol', self.proto.name), + ('identity_provider', self.proto.idp_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.protocols_mock.get.assert_called_with( - identity_fakes.idp_id, identity_fakes.protocol_id + self.identity_sdk_client.get_federation_protocol.assert_called_with( + idp_id=self.proto.idp_id, protocol=self.proto.name ) collist = ('id', 'identity_provider', 'mapping') self.assertEqual(collist, columns) datalist = ( - identity_fakes.protocol_id, - identity_fakes.idp_id, - identity_fakes.mapping_id, + self.proto.name, + self.proto.idp_id, + self.proto.mapping_id, ) self.assertEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py index 792096c..1864914 100644 --- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py @@ -10,72 +10,79 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - -from keystoneauth1.exceptions import http as ksa_exceptions +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import region as _region +from openstack.identity.v3 import registered_limit as _registered_limit +from openstack.identity.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import registered_limit -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestRegisteredLimit(identity_fakes.TestIdentityv3): +class TestRegisteredLimitCreate(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.registered_limit_mock = self.identity_client.registered_limits - - self.services_mock = self.identity_client.services - self.services_mock.reset_mock() - - self.regions_mock = self.identity_client.regions - self.regions_mock.reset_mock() + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.description = 'default limit of foobars' + self.default_limit = 10 + self.resource_name = 'foobars' -class TestRegisteredLimitCreate(TestRegisteredLimit): - def setUp(self): - super().setUp() + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region - self.service = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.SERVICE), loaded=True + self.registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=None, + region_id=None, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, ) - self.services_mock.get.return_value = self.service - - self.region = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.REGION), loaded=True + self.registered_limit_with_options = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=self.description, + region_id=self.region.id, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, ) - self.regions_mock.get.return_value = self.region self.cmd = registered_limit.CreateRegisteredLimit(self.app, None) def test_registered_limit_create_without_options(self): - self.registered_limit_mock.create.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.REGISTERED_LIMIT), loaded=True + self.identity_sdk_client.create_registered_limit.return_value = ( + self.registered_limit ) - resource_name = identity_fakes.registered_limit_resource_name - default_limit = identity_fakes.registered_limit_default_limit arglist = [ '--service', - identity_fakes.service_id, + self.service.id, '--default-limit', - '10', - resource_name, + str(self.default_limit), + self.resource_name, ] verifylist = [ - ('service', identity_fakes.service_id), - ('default_limit', default_limit), - ('resource_name', resource_name), + ('service', self.service.id), + ('default_limit', self.default_limit), + ('resource_name', self.resource_name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - kwargs = {'description': None, 'region': None} - self.registered_limit_mock.create.assert_called_with( - self.service, resource_name, default_limit, **kwargs + kwargs = { + 'service_id': self.service.id, + 'default_limit': self.default_limit, + 'resource_name': self.resource_name, + } + self.identity_sdk_client.create_registered_limit.assert_called_with( + **kwargs ) collist = ( @@ -89,51 +96,52 @@ class TestRegisteredLimitCreate(TestRegisteredLimit): self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, + self.default_limit, None, - identity_fakes.registered_limit_id, + self.registered_limit.id, None, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, + self.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_registered_limit_create_with_options(self): - self.registered_limit_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGISTERED_LIMIT_OPTIONS), - loaded=True, + self.identity_sdk_client.create_registered_limit.return_value = ( + self.registered_limit_with_options ) - resource_name = identity_fakes.registered_limit_resource_name - default_limit = identity_fakes.registered_limit_default_limit - description = identity_fakes.registered_limit_description arglist = [ '--region', - identity_fakes.region_id, + self.region.id, '--description', - description, + self.description, '--service', - identity_fakes.service_id, + self.service.id, '--default-limit', - '10', - resource_name, + str(self.default_limit), + self.resource_name, ] verifylist = [ - ('region', identity_fakes.region_id), - ('description', description), - ('service', identity_fakes.service_id), - ('default_limit', default_limit), - ('resource_name', resource_name), + ('region', self.region.id), + ('description', self.description), + ('service', self.service.id), + ('default_limit', self.default_limit), + ('resource_name', self.resource_name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - kwargs = {'description': description, 'region': self.region} - self.registered_limit_mock.create.assert_called_with( - self.service, resource_name, default_limit, **kwargs + kwargs = { + 'description': self.description, + 'region_id': self.region.id, + 'service_id': self.service.id, + 'default_limit': self.default_limit, + 'resource_name': self.resource_name, + } + self.identity_sdk_client.create_registered_limit.assert_called_with( + **kwargs ) collist = ( @@ -147,44 +155,47 @@ class TestRegisteredLimitCreate(TestRegisteredLimit): self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, - description, - identity_fakes.registered_limit_id, - identity_fakes.region_id, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, + self.default_limit, + self.description, + self.registered_limit_with_options.id, + self.region.id, + self.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestRegisteredLimitDelete(TestRegisteredLimit): +class TestRegisteredLimitDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() self.cmd = registered_limit.DeleteRegisteredLimit(self.app, None) def test_registered_limit_delete(self): - self.registered_limit_mock.delete.return_value = None + self.registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + ) + self.identity_sdk_client.delete_registered_limit.return_value = None - arglist = [identity_fakes.registered_limit_id] - verifylist = [ - ('registered_limit_id', [identity_fakes.registered_limit_id]) - ] + arglist = [self.registered_limit.id] + verifylist = [('registered_limits', [self.registered_limit.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.registered_limit_mock.delete.assert_called_with( - identity_fakes.registered_limit_id + self.identity_sdk_client.delete_registered_limit.assert_called_with( + self.registered_limit.id, + ignore_missing=False, ) self.assertIsNone(result) def test_registered_limit_delete_with_exception(self): - return_value = ksa_exceptions.NotFound() - self.registered_limit_mock.delete.side_effect = return_value + self.identity_sdk_client.delete_registered_limit.side_effect = ( + sdk_exc.ResourceNotFound + ) arglist = ['fake-registered-limit-id'] - verifylist = [('registered_limit_id', ['fake-registered-limit-id'])] + verifylist = [('registered_limits', ['fake-registered-limit-id'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) try: @@ -196,27 +207,52 @@ class TestRegisteredLimitDelete(TestRegisteredLimit): ) -class TestRegisteredLimitShow(TestRegisteredLimit): +class TestRegisteredLimitShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.registered_limit_mock.get.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.REGISTERED_LIMIT), loaded=True + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + + self.description = 'default limit of foobars' + self.default_limit = 10 + self.resource_name = 'foobars' + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + + self.registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=None, + region_id=None, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, + ) + self.registered_limit_with_options = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=self.description, + region_id=self.region.id, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, ) self.cmd = registered_limit.ShowRegisteredLimit(self.app, None) def test_registered_limit_show(self): - arglist = [identity_fakes.registered_limit_id] - verifylist = [ - ('registered_limit_id', identity_fakes.registered_limit_id) - ] + self.identity_sdk_client.get_registered_limit.return_value = ( + self.registered_limit + ) + + arglist = [self.registered_limit.id] + verifylist = [('registered_limit_id', self.registered_limit.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.get.assert_called_with( - identity_fakes.registered_limit_id + self.identity_sdk_client.get_registered_limit.assert_called_with( + self.registered_limit.id ) collist = ( @@ -229,50 +265,107 @@ class TestRegisteredLimitShow(TestRegisteredLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, + self.default_limit, None, - identity_fakes.registered_limit_id, + self.registered_limit.id, None, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, + self.resource_name, + self.service.id, + ) + self.assertEqual(datalist, data) + + def test_registered_limit_show_with_options(self): + self.identity_sdk_client.get_registered_limit.return_value = ( + self.registered_limit_with_options + ) + + arglist = [self.registered_limit_with_options.id] + verifylist = [ + ('registered_limit_id', self.registered_limit_with_options.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.identity_sdk_client.get_registered_limit.assert_called_with( + self.registered_limit_with_options.id + ) + + collist = ( + 'default_limit', + 'description', + 'id', + 'region_id', + 'resource_name', + 'service_id', + ) + self.assertEqual(collist, columns) + datalist = ( + self.default_limit, + self.description, + self.registered_limit_with_options.id, + self.region.id, + self.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestRegisteredLimitSet(TestRegisteredLimit): +class TestRegisteredLimitSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() + + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + + self.default_limit = 10 + self.resource_name = 'foobars' + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + + self.registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=None, + region_id=None, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, + ) + self.cmd = registered_limit.SetRegisteredLimit(self.app, None) def test_registered_limit_set_description(self): - registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT) - registered_limit['description'] = ( - identity_fakes.registered_limit_description + updated_description = 'default limit of foobars' + updated_registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + id=self.registered_limit.id, + description=updated_description, + region_id=None, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, ) - self.registered_limit_mock.update.return_value = fakes.FakeResource( - None, registered_limit, loaded=True + self.identity_sdk_client.update_registered_limit.return_value = ( + updated_registered_limit ) arglist = [ '--description', - identity_fakes.registered_limit_description, - identity_fakes.registered_limit_id, + updated_description, + self.registered_limit.id, ] verifylist = [ - ('description', identity_fakes.registered_limit_description), - ('registered_limit_id', identity_fakes.registered_limit_id), + ('description', updated_description), + ('registered_limit_id', self.registered_limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.update.assert_called_with( - identity_fakes.registered_limit_id, - service=None, - resource_name=None, - default_limit=None, - description=identity_fakes.registered_limit_description, - region=None, + self.identity_sdk_client.update_registered_limit.assert_called_with( + self.registered_limit.id, + description=updated_description, ) collist = ( @@ -285,43 +378,46 @@ class TestRegisteredLimitSet(TestRegisteredLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, - identity_fakes.registered_limit_description, - identity_fakes.registered_limit_id, + self.default_limit, + updated_description, + self.registered_limit.id, None, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, + self.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_registered_limit_set_default_limit(self): - registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT) - default_limit = 20 - registered_limit['default_limit'] = default_limit - self.registered_limit_mock.update.return_value = fakes.FakeResource( - None, registered_limit, loaded=True + updated_default_limit = 20 + updated_registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + id=self.registered_limit.id, + description=None, + region_id=None, + service_id=self.service.id, + default_limit=updated_default_limit, + resource_name=self.resource_name, + ) + self.identity_sdk_client.update_registered_limit.return_value = ( + updated_registered_limit ) arglist = [ '--default-limit', - str(default_limit), - identity_fakes.registered_limit_id, + str(updated_default_limit), + self.registered_limit.id, ] verifylist = [ - ('default_limit', default_limit), - ('registered_limit_id', identity_fakes.registered_limit_id), + ('default_limit', updated_default_limit), + ('registered_limit_id', self.registered_limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.update.assert_called_with( - identity_fakes.registered_limit_id, - service=None, - resource_name=None, - default_limit=default_limit, - description=None, - region=None, + self.identity_sdk_client.update_registered_limit.assert_called_with( + self.registered_limit.id, + default_limit=updated_default_limit, ) collist = ( @@ -334,43 +430,46 @@ class TestRegisteredLimitSet(TestRegisteredLimit): ) self.assertEqual(collist, columns) datalist = ( - default_limit, + updated_default_limit, None, - identity_fakes.registered_limit_id, + self.registered_limit.id, None, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, + self.resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_registered_limit_set_resource_name(self): - registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT) - resource_name = 'volumes' - registered_limit['resource_name'] = resource_name - self.registered_limit_mock.update.return_value = fakes.FakeResource( - None, registered_limit, loaded=True + updated_resource_name = 'volumes' + updated_registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + id=self.registered_limit.id, + description=None, + region_id=None, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=updated_resource_name, + ) + self.identity_sdk_client.update_registered_limit.return_value = ( + updated_registered_limit ) arglist = [ '--resource-name', - resource_name, - identity_fakes.registered_limit_id, + updated_resource_name, + self.registered_limit.id, ] verifylist = [ - ('resource_name', resource_name), - ('registered_limit_id', identity_fakes.registered_limit_id), + ('resource_name', updated_resource_name), + ('registered_limit_id', self.registered_limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.update.assert_called_with( - identity_fakes.registered_limit_id, - service=None, - resource_name=resource_name, - default_limit=None, - description=None, - region=None, + self.identity_sdk_client.update_registered_limit.assert_called_with( + self.registered_limit.id, + resource_name=updated_resource_name, ) collist = ( @@ -383,40 +482,43 @@ class TestRegisteredLimitSet(TestRegisteredLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, + self.default_limit, None, - identity_fakes.registered_limit_id, + self.registered_limit.id, None, - resource_name, - identity_fakes.service_id, + updated_resource_name, + self.service.id, ) self.assertEqual(datalist, data) def test_registered_limit_set_service(self): - registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT) - service = identity_fakes.FakeService.create_one_service() - registered_limit['service_id'] = service.id - self.registered_limit_mock.update.return_value = fakes.FakeResource( - None, registered_limit, loaded=True + updated_service = sdk_fakes.generate_fake_resource(_service.Service) + self.identity_sdk_client.find_service.return_value = updated_service + updated_registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + id=self.registered_limit.id, + description=None, + region_id=None, + service_id=updated_service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, + ) + self.identity_sdk_client.update_registered_limit.return_value = ( + updated_registered_limit ) - self.services_mock.get.return_value = service - arglist = ['--service', service.id, identity_fakes.registered_limit_id] + arglist = ['--service', updated_service.id, self.registered_limit.id] verifylist = [ - ('service', service.id), - ('registered_limit_id', identity_fakes.registered_limit_id), + ('service', updated_service.id), + ('registered_limit_id', self.registered_limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.update.assert_called_with( - identity_fakes.registered_limit_id, - service=service, - resource_name=None, - default_limit=None, - description=None, - region=None, + self.identity_sdk_client.update_registered_limit.assert_called_with( + self.registered_limit.id, + service_id=updated_service.id, ) collist = ( @@ -429,42 +531,43 @@ class TestRegisteredLimitSet(TestRegisteredLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, + self.default_limit, None, - identity_fakes.registered_limit_id, + self.registered_limit.id, None, - identity_fakes.registered_limit_resource_name, - service.id, + self.resource_name, + updated_service.id, ) self.assertEqual(datalist, data) def test_registered_limit_set_region(self): - registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT) - region = identity_fakes.REGION - region['id'] = 'RegionTwo' - region = fakes.FakeResource(None, copy.deepcopy(region), loaded=True) - registered_limit['region_id'] = region.id - self.registered_limit_mock.update.return_value = fakes.FakeResource( - None, registered_limit, loaded=True + updated_region = sdk_fakes.generate_fake_resource(_region.Region) + self.identity_sdk_client.get_region.return_value = updated_region + updated_registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + id=self.registered_limit.id, + description=None, + region_id=updated_region.id, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, + ) + self.identity_sdk_client.update_registered_limit.return_value = ( + updated_registered_limit ) - self.regions_mock.get.return_value = region - arglist = ['--region', region.id, identity_fakes.registered_limit_id] + arglist = ['--region', updated_region.id, self.registered_limit.id] verifylist = [ - ('region', region.id), - ('registered_limit_id', identity_fakes.registered_limit_id), + ('region', updated_region.id), + ('registered_limit_id', self.registered_limit.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.update.assert_called_with( - identity_fakes.registered_limit_id, - service=None, - resource_name=None, - default_limit=None, - description=None, - region=region, + self.identity_sdk_client.update_registered_limit.assert_called_with( + self.registered_limit.id, + region_id=updated_region.id, ) collist = ( @@ -477,54 +580,86 @@ class TestRegisteredLimitSet(TestRegisteredLimit): ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, + self.default_limit, None, - identity_fakes.registered_limit_id, - region.id, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, + self.registered_limit.id, + updated_region.id, + self.resource_name, + self.service.id, ) self.assertEqual(datalist, data) -class TestRegisteredLimitList(TestRegisteredLimit): +class TestRegisteredLimitList(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.registered_limit_mock.get.return_value = fakes.FakeResource( - None, copy.deepcopy(identity_fakes.REGISTERED_LIMIT), loaded=True - ) + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) - self.cmd = registered_limit.ShowRegisteredLimit(self.app, None) + self.description = 'default limit of foobars' + self.default_limit = 10 + self.resource_name = 'foobars' - def test_limit_show(self): - arglist = [identity_fakes.registered_limit_id] - verifylist = [ - ('registered_limit_id', identity_fakes.registered_limit_id) + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region + + self.registered_limit = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=None, + region_id=None, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, + ) + self.registered_limit_with_options = sdk_fakes.generate_fake_resource( + resource_type=_registered_limit.RegisteredLimit, + description=self.description, + region_id=self.region.id, + service_id=self.service.id, + default_limit=self.default_limit, + resource_name=self.resource_name, + ) + self.identity_sdk_client.registered_limits.return_value = [ + self.registered_limit, + self.registered_limit_with_options, ] + + self.cmd = registered_limit.ListRegisteredLimit(self.app, None) + + def test_registered_limit_list(self): + arglist = [] + verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.registered_limit_mock.get.assert_called_with( - identity_fakes.registered_limit_id - ) - + self.identity_sdk_client.registered_limits.assert_called_with() collist = ( - 'default_limit', - 'description', - 'id', - 'region_id', - 'resource_name', - 'service_id', + "ID", + "Service ID", + "Resource Name", + "Default Limit", + "Description", + "Region ID", ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.registered_limit_default_limit, - None, - identity_fakes.registered_limit_id, - None, - identity_fakes.registered_limit_resource_name, - identity_fakes.service_id, - ) - self.assertEqual(datalist, data) + ( + self.registered_limit.id, + self.service.id, + self.resource_name, + self.default_limit, + None, + None, + ), + ( + self.registered_limit_with_options.id, + self.service.id, + self.resource_name, + self.default_limit, + self.description, + self.region.id, + ), + ) + self.assertEqual(datalist, tuple(data)) diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 3ed1d44..90b2d71 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -15,8 +15,6 @@ from unittest import mock -from osc_lib import exceptions - from openstack import exceptions as sdk_exc from openstack.identity.v3 import domain as _domain from openstack.identity.v3 import group as _group @@ -25,10 +23,10 @@ from openstack.identity.v3 import role as _role from openstack.identity.v3 import system as _system from openstack.identity.v3 import user as _user from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions from openstackclient.identity.v3 import role from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -from openstackclient.tests.unit import utils as test_utils class TestRoleInherited(identity_fakes.TestIdentityv3): @@ -36,85 +34,6 @@ class TestRoleInherited(identity_fakes.TestIdentityv3): return True -class TestFindSDKId(test_utils.TestCase): - def setUp(self): - super().setUp() - self.user = sdk_fakes.generate_fake_resource(_user.User) - self.identity_sdk_client = mock.Mock() - self.identity_sdk_client.find_user = mock.Mock() - - def test_find_sdk_id_validate(self): - self.identity_sdk_client.find_user.side_effect = [self.user] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=True, - ) - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_no_validate(self): - self.identity_sdk_client.find_user.side_effect = [self.user] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=False, - ) - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_not_found_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ResourceNotFound, - ] - - self.assertRaises( - exceptions.CommandError, - role._find_sdk_id, - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=True, - ) - - def test_find_sdk_id_not_found_no_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ResourceNotFound, - ] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=False, - ) - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_forbidden_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ForbiddenException, - ] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=True, - ) - - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_forbidden_no_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ForbiddenException, - ] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=False, - ) - - self.assertEqual(self.user.id, result) - - class TestRoleAdd(identity_fakes.TestIdentityv3): def _is_inheritance_testcase(self): return False diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 1236b86..134ba5a 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -94,7 +94,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): kwargs = { 'name': self.user.name, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -138,7 +137,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): self.user.name, ] verifylist = [ - ('password', None), ('password_prompt', True), ('enable', False), ('disable', False), @@ -171,7 +169,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): self.user.name, ] verifylist = [ - ('password', None), ('password_prompt', True), ('enable', False), ('disable', False), @@ -236,7 +233,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'email': 'barney@example.com', 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -267,7 +263,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'default_project_id': self.project.id, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -312,7 +307,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'default_project_id': self.project.id, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_once_with(**kwargs) self.identity_sdk_client.find_domain.assert_called_once_with( @@ -357,7 +351,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'domain_id': self.domain.id, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -385,7 +378,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): kwargs = { 'name': self.user.name, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -413,7 +405,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): kwargs = { 'name': self.user.name, 'is_enabled': False, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -443,7 +434,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -473,7 +463,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -503,7 +492,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_password_expiry': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -533,7 +521,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_password_expiry': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -563,7 +550,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -593,7 +579,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -623,7 +608,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'lock_password': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -653,7 +637,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'lock_password': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -683,7 +666,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -713,7 +695,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name': self.user.name, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -730,7 +711,7 @@ class TestUserCreate(identity_fakes.TestIdentityv3): ] verifylist = [ ( - 'multi_factor_auth_rule', + 'multi_factor_auth_rules', [identity_fakes.mfa_opt1, identity_fakes.mfa_opt2], ), ('enable', False), @@ -751,7 +732,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'options': { 'multi_factor_auth_rules': [["password", "totp"], ["password"]] }, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -769,7 +749,7 @@ class TestUserCreate(identity_fakes.TestIdentityv3): verifylist = [ ('ignore_password_expiry', True), ('disable_multi_factor_auth', True), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('enable', False), ('disable', False), ('name', self.user.name), @@ -790,7 +770,6 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'multi_factor_auth_enabled': False, 'multi_factor_auth_rules': [["password", "totp"]], }, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -891,7 +870,7 @@ class TestUserList(identity_fakes.TestIdentityv3): self.identity_sdk_client.find_domain.return_value = self.domain self.identity_sdk_client.find_group.return_value = self.group self.identity_sdk_client.find_project.return_value = self.project - self.identity_sdk_client.role_assignments_filter.return_value = [ + self.identity_sdk_client.role_assignments.return_value = [ self.role_assignment ] @@ -1029,12 +1008,10 @@ class TestUserList(identity_fakes.TestIdentityv3): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'project': self.project.id, + 'scope_project_id': self.project.id, } - self.identity_sdk_client.role_assignments_filter.assert_called_with( - **kwargs - ) + self.identity_sdk_client.role_assignments.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1086,7 +1063,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1107,7 +1083,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', 'qwerty'), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1138,7 +1113,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', 'qwerty'), - ('password', None), ('domain', self.domain.id), ('email', None), ('project', None), @@ -1194,7 +1168,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('password_prompt', True), ('email', None), ('project', None), @@ -1227,7 +1200,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', 'barney@example.com'), ('project', None), ('enable', False), @@ -1256,7 +1228,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', self.project.id), ('enable', False), @@ -1298,7 +1269,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', self.project.id), ('project_domain', self.project.domain_id), @@ -1332,7 +1302,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', True), @@ -1359,7 +1328,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1386,7 +1354,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_lockout_failure_attempts', True), ('project', None), @@ -1414,7 +1381,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_lockout_failure_attempts', True), ('project', None), @@ -1442,7 +1408,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_password_expiry', True), ('project', None), @@ -1470,7 +1435,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_password_expiry', True), ('project', None), @@ -1498,7 +1462,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_change_password_upon_first_use', True), ('project', None), @@ -1526,7 +1489,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_change_password_upon_first_use', True), ('project', None), @@ -1554,7 +1516,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('enable_lock_password', True), ('project', None), @@ -1582,7 +1543,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('disable_lock_password', True), ('project', None), @@ -1610,7 +1570,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('enable_multi_factor_auth', True), ('project', None), @@ -1638,7 +1597,6 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('disable_multi_factor_auth', True), ('project', None), @@ -1667,9 +1625,8 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), ('disable', False), @@ -1699,11 +1656,10 @@ class TestUserSet(identity_fakes.TestIdentityv3): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_password_expiry', True), ('enable_multi_factor_auth', True), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), ('disable', False), diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 7eb7331..c9a273b 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -1295,6 +1295,117 @@ class TestImageSet(TestImage): # the 'update membership' route. self.image_client.update_image.assert_called_with(self._image.id) + def test_image_set_membership_accept_with_project_no_owner_change(self): + """Test that --project with --accept doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--accept', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'accepted'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='accepted', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + + def test_image_set_membership_reject_with_project_no_owner_change(self): + """Test that --project with --reject doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--reject', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'rejected'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='rejected', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + + def test_image_set_membership_pending_with_project_no_owner_change(self): + """Test that --project with --pending doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--pending', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'pending'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='pending', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + def test_image_set_options(self): arglist = [ '--name', @@ -1974,7 +2085,7 @@ class TestImageImport(TestImage): remote_image_id=None, remote_service_interface=None, stores=None, - all_stores=None, + all_stores=False, all_stores_must_succeed=False, ) @@ -2004,7 +2115,7 @@ class TestImageImport(TestImage): remote_image_id=None, remote_service_interface=None, stores=None, - all_stores=None, + all_stores=False, all_stores_must_succeed=False, ) @@ -2142,7 +2253,7 @@ class TestImageImport(TestImage): remote_image_id=None, remote_service_interface=None, stores=['fast'], - all_stores=None, + all_stores=False, all_stores_must_succeed=False, ) @@ -2174,7 +2285,7 @@ class TestImageImport(TestImage): remote_image_id=None, remote_service_interface=None, stores=['fast'], - all_stores=None, + all_stores=False, all_stores_must_succeed=True, ) @@ -2209,7 +2320,7 @@ class TestImageImport(TestImage): remote_image_id='remote-image-id', remote_service_interface='private', stores=None, - all_stores=None, + all_stores=False, all_stores_must_succeed=False, ) diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index c1ad9b2..cc84c8b 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -126,17 +126,13 @@ class TestNetworkAndCompute(utils.TestCommand): # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. - self.app.client_manager.network = mock.Mock() - self.network_client = self.app.client_manager.network - self.network_client.network_action = mock.Mock( - return_value='take_action_network' - ) + self.app.client_manager.network = mock.Mock() # noqa: O401 + self.network_client = self.app.client_manager.network # noqa: O401 + self.network_client.network_action.return_value = 'take_action_network' - self.app.client_manager.compute = mock.Mock() - self.compute_client = self.app.client_manager.compute - self.compute_client.compute_action = mock.Mock( - return_value='take_action_compute' - ) + self.app.client_manager.compute = mock.Mock() # noqa: O401 + self.compute_client = self.app.client_manager.compute # noqa: O401 + self.compute_client.compute_action.return_value = 'take_action_compute' self.cmd = FakeNetworkAndComputeCommand(self.app, None) @@ -205,9 +201,9 @@ class TestNeutronCommandWithExtraArgs(utils.TestCommand): # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. - self.app.client_manager.network = mock.Mock() - self.network_client = self.app.client_manager.network - self.network_client.test_create_action = mock.Mock() + self.app.client_manager.network = mock.Mock() # noqa: O401 + self.network_client = self.app.client_manager.network # noqa: O401 + self.network_client.test_create_action = mock.Mock() # noqa: O402 # Subclasses can override the command object to test. self.cmd = FakeCreateNeutronCommandWithExtraArgs(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/taas/__init__.py b/openstackclient/tests/unit/network/v2/taas/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/__init__.py diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py new file mode 100644 index 0000000..8e4f185 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py @@ -0,0 +1,276 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import operator +import uuid + +from openstack.network.v2 import tap_flow as _tap_flow +from openstack.network.v2 import tap_service as _tap_service +from openstack.test import fakes as sdk_fakes +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_flow as osc_tap_flow +from openstackclient.network.v2.taas import tap_service as osc_tap_service +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_flow._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapFlow(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'direction', + 'id', + 'name', + 'project_id', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.CreateTapFlow(self.app, None) + + def test_create_tap_flow(self): + """Test Create Tap Flow.""" + fake_tap_service = sdk_fakes.generate_fake_resource( + _tap_service.TapService + ) + port_id = str(uuid.uuid4()) + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_flow = sdk_fakes.generate_fake_resource( + _tap_flow.TapFlow, + **{ + 'source_port': port_id, + 'tap_service_id': fake_tap_service['id'], + 'direction': 'BOTH', + }, + ) + self.app.client_manager.network.create_tap_flow.return_value = ( + fake_tap_flow + ) + self.app.client_manager.network.find_port.return_value = fake_port + self.app.client_manager.network.find_tap_service.return_value = ( + fake_tap_service + ) + arg_list = [ + '--name', + fake_tap_flow['name'], + '--port', + fake_tap_flow['source_port'], + '--tap-service', + fake_tap_flow['tap_service_id'], + '--direction', + fake_tap_flow['direction'], + ] + + verify_list = [ + ('name', fake_tap_flow['name']), + ('port', fake_tap_flow['source_port']), + ('tap_service', fake_tap_flow['tap_service_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + mock_create_t_f = self.app.client_manager.network.create_tap_flow + mock_create_t_f.assert_called_once_with( + **{ + 'name': fake_tap_flow['name'], + 'source_port': fake_tap_flow['source_port'], + 'tap_service_id': fake_tap_flow['tap_service_id'], + 'direction': fake_tap_flow['direction'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_flow, osc_tap_service._get_columns(fake_tap_flow)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapFlow(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.ListTapFlow(self.app, None) + + def test_list_tap_flows(self): + """Test List Tap Flow.""" + fake_tap_flows = list( + sdk_fakes.generate_fake_resources(_tap_flow.TapFlow, count=2) + ) + self.app.client_manager.network.tap_flows.return_value = fake_tap_flows + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_flows.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_flow, columns_long) + for fake_tap_flow in fake_tap_flows + ], + ) + + +class TestDeleteTapFlow(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + self.cmd = osc_tap_flow.DeleteTapFlow(self.app, None) + + def test_delete_tap_flow(self): + """Test Delete tap flow.""" + + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, [fake_tap_flow['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_flow = self.app.client_manager.network.delete_tap_flow + mock_delete_tap_flow.assert_called_once_with(fake_tap_flow['id']) + self.assertIsNone(result) + + +class TestShowTapFlow(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'direction', + 'id', + 'name', + 'project_id', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + self.cmd = osc_tap_flow.ShowTapFlow(self.app, None) + + def test_show_tap_flow(self): + """Test Show tap flow.""" + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) + self.app.client_manager.network.get_tap_flow.return_value = ( + fake_tap_flow + ) + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, fake_tap_flow['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.get_tap_flow.assert_called_once_with( + fake_tap_flow['id'] + ) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_flow, osc_tap_service._get_columns(fake_tap_flow)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapFlow(network_fakes.TestNetworkV2): + _new_name = 'new_name' + + # NOTE(mtomaska): The Resource class from which TapFlow inherits from + # returns duplicate `ID and `Name` keys. + columns = ( + 'Direction', + 'ID', + 'ID', + 'Name', + 'Name', + 'Status', + 'Tenant', + 'description', + 'location', + 'project_id', + 'source_port', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.UpdateTapFlow(self.app, None) + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + + def test_update_tap_flow(self): + """Test update tap service""" + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) + new_tap_flow = copy.deepcopy(fake_tap_flow) + new_tap_flow['name'] = self._new_name + + self.app.client_manager.network.update_tap_flow.return_value = ( + new_tap_flow + ) + + arg_list = [ + fake_tap_flow['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_t_f = self.app.client_manager.network.update_tap_flow + mock_update_t_f.assert_called_once_with(new_tap_flow['id'], **attrs) + self.assertEqual(self.columns, columns) + self.assertEqual(_get_data(new_tap_flow, self.columns), data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py new file mode 100644 index 0000000..10f3251 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py @@ -0,0 +1,288 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import operator +import uuid + +from openstack.network.v2 import tap_mirror +from openstack.test import fakes as sdk_fakes +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_mirror as osc_tap_mirror +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_mirror._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapMirror(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'project_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.CreateTapMirror(self.app, None) + + def test_create_tap_mirror(self): + port_id = str(uuid.uuid4()) + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror, **{'port_id': port_id, 'directions': 'IN=99'} + ) + self.app.client_manager.network.create_tap_mirror.return_value = ( + fake_tap_mirror + ) + self.app.client_manager.network.find_port.return_value = fake_port + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda _, name_or_id: {'id': name_or_id} + ) + arg_list = [ + '--name', + fake_tap_mirror['name'], + '--port', + fake_tap_mirror['port_id'], + '--directions', + fake_tap_mirror['directions'], + '--remote-ip', + fake_tap_mirror['remote_ip'], + '--mirror-type', + fake_tap_mirror['mirror_type'], + ] + + verify_directions = fake_tap_mirror['directions'].split('=') + verify_directions_dict = {verify_directions[0]: verify_directions[1]} + + verify_list = [ + ('name', fake_tap_mirror['name']), + ('port_id', fake_tap_mirror['port_id']), + ('directions', verify_directions_dict), + ('remote_ip', fake_tap_mirror['remote_ip']), + ('mirror_type', fake_tap_mirror['mirror_type']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_mirror.return_value = ( + fake_tap_mirror + ) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_m_mock = self.app.client_manager.network.create_tap_mirror + create_tap_m_mock.assert_called_once_with( + **{ + 'name': fake_tap_mirror['name'], + 'port_id': fake_tap_mirror['port_id'], + 'directions': verify_directions_dict, + 'remote_ip': fake_tap_mirror['remote_ip'], + 'mirror_type': fake_tap_mirror['mirror_type'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_mirror, osc_tap_mirror._get_columns(fake_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapMirror(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.ListTapMirror(self.app, None) + + def test_list_tap_mirror(self): + """Test List Tap Mirror.""" + fake_tap_mirrors = list( + sdk_fakes.generate_fake_resources(tap_mirror.TapMirror, count=4) + ) + self.app.client_manager.network.tap_mirrors.return_value = ( + fake_tap_mirrors + ) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_mirrors.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_mirror, columns_long) + for fake_tap_mirror in fake_tap_mirrors + ], + ) + + +class TestDeleteTapMirror(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + self.cmd = osc_tap_mirror.DeleteTapMirror(self.app, None) + + def test_delete_tap_mirror(self): + """Test Delete Tap Mirror.""" + + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror + ) + + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, [fake_tap_mirror['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_m = self.app.client_manager.network.delete_tap_mirror + mock_delete_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertIsNone(result) + + +class TestShowTapMirror(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'project_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + self.cmd = osc_tap_mirror.ShowTapMirror(self.app, None) + + def test_show_tap_mirror(self): + """Test Show Tap Mirror.""" + + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror + ) + self.app.client_manager.network.get_tap_mirror.return_value = ( + fake_tap_mirror + ) + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, fake_tap_mirror['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_m = self.app.client_manager.network.get_tap_mirror + mock_get_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_mirror, osc_tap_mirror._get_columns(fake_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapMirror(network_fakes.TestNetworkV2): + _new_name = 'new_name' + columns = ( + 'description', + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'project_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.UpdateTapMirror(self.app, None) + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + + def test_update_tap_mirror(self): + """Test update Tap Mirror""" + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror + ) + new_tap_mirror = copy.deepcopy(fake_tap_mirror) + new_tap_mirror['name'] = self._new_name + + self.app.client_manager.network.update_tap_mirror.return_value = ( + new_tap_mirror + ) + + arg_list = [ + fake_tap_mirror['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_m = self.app.client_manager.network.update_tap_mirror + mock_update_tap_m.assert_called_once_with( + fake_tap_mirror['id'], **attrs + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_mirror, osc_tap_mirror._get_columns(new_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py new file mode 100644 index 0000000..fa76689 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py @@ -0,0 +1,271 @@ +# All Rights Reserved 2020 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import operator +import uuid + +from openstack.network.v2 import tap_service +from openstack.test import fakes as sdk_fakes +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_service as osc_tap_service +from openstackclient.tests.unit.network.v2 import fakes as network_fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_service._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapService(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.CreateTapService(self.app, None) + + def test_create_tap_service(self): + """Test Create Tap Service.""" + port_id = str(uuid.uuid4()) + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService, **{'port_id': port_id} + ) + self.app.client_manager.network.create_tap_service.return_value = ( + fake_tap_service + ) + self.app.client_manager.network.find_port.return_value = fake_port + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda _, name_or_id: {'id': name_or_id} + ) + arg_list = [ + '--name', + fake_tap_service['name'], + '--port', + fake_tap_service['port_id'], + ] + + verify_list = [ + ('name', fake_tap_service['name']), + ('port_id', fake_tap_service['port_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_service.return_value = ( + fake_tap_service + ) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_s_mock = self.app.client_manager.network.create_tap_service + create_tap_s_mock.assert_called_once_with( + **{ + 'name': fake_tap_service['name'], + 'port_id': fake_tap_service['port_id'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_service, osc_tap_service._get_columns(fake_tap_service)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapService(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.ListTapService(self.app, None) + + def test_list_tap_service(self): + """Test List Tap Service.""" + fake_tap_services = list( + sdk_fakes.generate_fake_resources(tap_service.TapService, count=4) + ) + self.app.client_manager.network.tap_services.return_value = ( + fake_tap_services + ) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_services.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_service, columns_long) + for fake_tap_service in fake_tap_services + ], + ) + + +class TestDeleteTapService(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + self.cmd = osc_tap_service.DeleteTapService(self.app, None) + + def test_delete_tap_service(self): + """Test Delete tap service.""" + + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService + ) + + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, [fake_tap_service['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_s = self.app.client_manager.network.delete_tap_service + mock_delete_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertIsNone(result) + + +class TestShowTapService(network_fakes.TestNetworkV2): + columns = ( + 'description', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + self.cmd = osc_tap_service.ShowTapService(self.app, None) + + def test_show_tap_service(self): + """Test Show tap service.""" + + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService + ) + self.app.client_manager.network.get_tap_service.return_value = ( + fake_tap_service + ) + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, fake_tap_service['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_s = self.app.client_manager.network.get_tap_service + mock_get_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_service, osc_tap_service._get_columns(fake_tap_service)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapService(network_fakes.TestNetworkV2): + _new_name = 'new_name' + + columns = ( + 'description', + 'id', + 'name', + 'port_id', + 'project_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.UpdateTapService(self.app, None) + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + + def test_update_tap_service(self): + """Test update tap service""" + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService + ) + new_tap_service = copy.deepcopy(fake_tap_service) + new_tap_service['name'] = self._new_name + + self.app.client_manager.network.update_tap_service.return_value = ( + new_tap_service + ) + + arg_list = [ + fake_tap_service['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_s = self.app.client_manager.network.update_tap_service + mock_update_tap_s.assert_called_once_with( + fake_tap_service['id'], **attrs + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_service, osc_tap_service._get_columns(new_tap_service)[1] + ) + self.assertEqual(fake_data, data) diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index 411d199..48f7066 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -58,8 +57,8 @@ class TestCreateAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.create_address_group = mock.Mock( - return_value=self.new_address_group + self.network_client.create_address_group.return_value = ( + self.new_address_group ) # Get the command object to test @@ -145,7 +144,7 @@ class TestDeleteAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.delete_address_group = mock.Mock(return_value=None) + self.network_client.delete_address_group.return_value = None self.network_client.find_address_group = ( network_fakes.get_address_groups( address_groups=self._address_groups @@ -206,9 +205,7 @@ class TestDeleteAddressGroup(TestAddressGroup): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._address_groups[0], exceptions.CommandError] - self.network_client.find_address_group = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_address_group.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -251,9 +248,7 @@ class TestListAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.address_groups = mock.Mock( - return_value=self.address_groups - ) + self.network_client.address_groups.return_value = self.address_groups # Get the command object to test self.cmd = address_group.ListAddressGroup(self.app, None) @@ -333,13 +328,15 @@ class TestSetAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.update_address_group = mock.Mock(return_value=None) - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.update_address_group.return_value = None + self.network_client.find_address_group.return_value = ( + self._address_group ) - self.network_client.add_addresses_to_address_group = mock.Mock( - return_value=self._address_group + + self.network_client.add_addresses_to_address_group.return_value = ( + self._address_group ) + # Get the command object to test self.cmd = address_group.SetAddressGroup(self.app, None) @@ -442,8 +439,8 @@ class TestShowAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) # Get the command object to test @@ -486,12 +483,12 @@ class TestUnsetAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group - ) - self.network_client.remove_addresses_from_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) + + self.network_client.remove_addresses_from_address_group.return_value = self._address_group + # Get the command object to test self.cmd = address_group.UnsetAddressGroup(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py index c3eb83d..6e2c05e 100644 --- a/openstackclient/tests/unit/network/v2/test_address_scope.py +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -52,8 +51,8 @@ class TestCreateAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.create_address_scope = mock.Mock( - return_value=self.new_address_scope + self.network_client.create_address_scope.return_value = ( + self.new_address_scope ) # Get the command object to test @@ -160,7 +159,7 @@ class TestDeleteAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.delete_address_scope = mock.Mock(return_value=None) + self.network_client.delete_address_scope.return_value = None self.network_client.find_address_scope = ( network_fakes.get_address_scopes( address_scopes=self._address_scopes @@ -222,9 +221,7 @@ class TestDeleteAddressScope(TestAddressScope): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._address_scopes[0], exceptions.CommandError] - self.network_client.find_address_scope = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_address_scope.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -267,9 +264,7 @@ class TestListAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.address_scopes = mock.Mock( - return_value=self.address_scopes - ) + self.network_client.address_scopes.return_value = self.address_scopes # Get the command object to test self.cmd = address_scope.ListAddressScope(self.app, None) @@ -398,9 +393,9 @@ class TestSetAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.update_address_scope = mock.Mock(return_value=None) - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.update_address_scope.return_value = None + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test @@ -488,8 +483,8 @@ class TestShowAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py index 9b46126..c44e553 100644 --- a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -11,11 +11,9 @@ # under the License. # -from unittest import mock from unittest.mock import call import uuid -from openstack.network.v2 import _proxy from openstack.network.v2 import ( default_security_group_rule as _default_security_group_rule, ) @@ -28,18 +26,7 @@ from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit import utils as tests_utils -class TestDefaultSecurityGroupRule(network_fakes.TestNetworkV2): - def setUp(self): - super().setUp() - - self.app.client_manager.sdk_connection = mock.Mock() - self.app.client_manager.sdk_connection.network = mock.Mock( - spec=_proxy.Proxy, - ) - self.sdk_client = self.app.client_manager.sdk_connection.network - - -class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestCreateDefaultSecurityGroupRule(network_fakes.TestNetworkV2): expected_columns = ( 'description', 'direction', @@ -82,7 +69,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): **default_security_group_rule_attrs, ) - self.sdk_client.create_default_security_group_rule.return_value = ( + self.network_client.create_default_security_group_rule.return_value = ( self._default_sg_rule ) self.expected_data = ( @@ -208,7 +195,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -252,7 +239,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -302,7 +289,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -347,7 +334,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -381,7 +368,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -415,7 +402,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -449,7 +436,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -599,7 +586,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -637,7 +624,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -675,7 +662,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -713,7 +700,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -754,7 +741,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -794,7 +781,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -825,7 +812,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'description': self._default_sg_rule.description, 'direction': self._default_sg_rule.direction, @@ -840,7 +827,7 @@ class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): self.assertEqual(self.expected_data, data) -class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestDeleteDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The default security group rules to be deleted. default_security_group_rule_attrs = { 'direction': 'ingress', @@ -866,7 +853,9 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.delete_default_security_group_rule.return_value = None + self.network_client.delete_default_security_group_rule.return_value = ( + None + ) # Get the command object to test self.cmd = default_security_group_rule.DeleteDefaultSecurityGroupRule( @@ -880,7 +869,7 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): verifylist = [ ('rule', [self._default_sg_rules[0].id]), ] - self.sdk_client.find_default_security_group_rule.return_value = ( + self.network_client.find_default_security_group_rule.return_value = ( self._default_sg_rules[0] ) @@ -888,7 +877,7 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self.network_client.delete_default_security_group_rule.assert_called_once_with( self._default_sg_rules[0] ) self.assertIsNone(result) @@ -902,7 +891,7 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): verifylist = [ ('rule', arglist), ] - self.sdk_client.find_default_security_group_rule.side_effect = ( + self.network_client.find_default_security_group_rule.side_effect = ( self._default_sg_rules ) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -912,7 +901,7 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): calls = [] for s in self._default_sg_rules: calls.append(call(s)) - self.sdk_client.delete_default_security_group_rule.assert_has_calls( + self.network_client.delete_default_security_group_rule.assert_has_calls( calls ) self.assertIsNone(result) @@ -931,8 +920,8 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): self._default_sg_rules[0], exceptions.CommandError, ] - self.sdk_client.find_default_security_group_rule = mock.Mock( - side_effect=find_mock_result + self.network_client.find_default_security_group_rule.side_effect = ( + find_mock_result ) try: @@ -941,18 +930,18 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): except exceptions.CommandError as e: self.assertEqual('1 of 2 default rules failed to delete.', str(e)) - self.sdk_client.find_default_security_group_rule.assert_any_call( + self.network_client.find_default_security_group_rule.assert_any_call( self._default_sg_rules[0].id, ignore_missing=False ) - self.sdk_client.find_default_security_group_rule.assert_any_call( + self.network_client.find_default_security_group_rule.assert_any_call( 'unexist_rule', ignore_missing=False ) - self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self.network_client.delete_default_security_group_rule.assert_called_once_with( self._default_sg_rules[0] ) -class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestListDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The security group rule to be listed. _default_sg_rule_tcp = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, @@ -1001,7 +990,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.default_security_group_rules.return_value = ( + self.network_client.default_security_group_rules.return_value = ( self._default_sg_rules ) @@ -1016,7 +1005,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{} ) self.assertEqual(self.expected_columns, columns) @@ -1035,7 +1024,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'protocol': 'tcp', } @@ -1055,7 +1044,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'direction': 'ingress', } @@ -1075,7 +1064,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'direction': 'egress', } @@ -1084,7 +1073,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): self.assertEqual(self.expected_data, list(data)) -class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestShowDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The default security group rule to be shown. _default_sg_rule = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule @@ -1123,7 +1112,7 @@ class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.find_default_security_group_rule.return_value = ( + self.network_client.find_default_security_group_rule.return_value = ( self._default_sg_rule ) @@ -1148,7 +1137,7 @@ class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_default_security_group_rule.assert_called_once_with( + self.network_client.find_default_security_group_rule.assert_called_once_with( self._default_sg_rule.id, ignore_missing=False ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py index 605f4d9..ab0ec17 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from openstack.network.v2 import floating_ip as _floating_ip @@ -86,16 +85,14 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.create_ip = mock.Mock( - return_value=self.floating_ip - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_ip.return_value = self.floating_ip - self.network_client.find_network = mock.Mock( - return_value=self.floating_network - ) - self.network_client.find_subnet = mock.Mock(return_value=self.subnet) - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.set_tags.return_value = None + + self.network_client.find_network.return_value = self.floating_network + + self.network_client.find_subnet.return_value = self.subnet + self.network_client.find_port.return_value = self.port # Get the command object to test self.cmd = fip.CreateFloatingIP(self.app, None) @@ -304,7 +301,7 @@ class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.delete_ip = mock.Mock(return_value=None) + self.network_client.delete_ip.return_value = None # Get the command object to test self.cmd = fip.DeleteFloatingIP(self.app, None) @@ -470,14 +467,11 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.ips = mock.Mock(return_value=self.floating_ips) - self.network_client.find_network = mock.Mock( - return_value=self.fake_network - ) - self.network_client.find_port = mock.Mock(return_value=self.fake_port) - self.network_client.find_router = mock.Mock( - return_value=self.fake_router - ) + self.network_client.ips.return_value = self.floating_ips + self.network_client.find_network.return_value = self.fake_network + + self.network_client.find_port.return_value = self.fake_port + self.network_client.find_router.return_value = self.fake_router # Get the command object to test self.cmd = fip.ListFloatingIP(self.app, None) @@ -713,7 +707,7 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork): self.floating_ip = sdk_fakes.generate_fake_resource( _floating_ip.FloatingIP ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip self.columns = ( 'created_at', @@ -797,10 +791,10 @@ class TestSetFloatingIP(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) - self.network_client.find_port = mock.Mock(return_value=self.port) - self.network_client.update_ip = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_ip.return_value = self.floating_ip + self.network_client.find_port.return_value = self.port + self.network_client.update_ip.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = fip.SetFloatingIP(self.app, None) @@ -1044,9 +1038,9 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) - self.network_client.update_ip = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_ip.return_value = self.floating_ip + self.network_client.update_ip.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = fip.UnsetFloatingIP(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py index 2e4c93f..33b9011 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py @@ -33,7 +33,7 @@ class TestFloatingIPPortForwarding(network_fakes.TestNetworkV2): ) self.port = network_fakes.create_one_port() self.project = identity_fakes_v2.FakeProject.create_one_project() - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.find_port.return_value = self.port class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): @@ -54,11 +54,11 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): }, ) - self.network_client.create_floating_ip_port_forwarding = mock.Mock( - return_value=self.new_port_forwarding + self.network_client.create_floating_ip_port_forwarding.return_value = ( + self.new_port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.CreateFloatingIPPortForwarding( @@ -351,11 +351,11 @@ class TestDeleteFloatingIPPortForwarding(TestFloatingIPPortForwarding): }, ) ) - self.network_client.delete_floating_ip_port_forwarding = mock.Mock( - return_value=None + self.network_client.delete_floating_ip_port_forwarding.return_value = ( + None ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.DeleteFloatingIPPortForwarding( self.app, None @@ -490,10 +490,11 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding): port_forwarding.description, ) ) - self.network_client.floating_ip_port_forwardings = mock.Mock( - return_value=self.port_forwardings + self.network_client.floating_ip_port_forwardings.return_value = ( + self.port_forwardings ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.ListFloatingIPPortForwarding( self.app, None @@ -557,14 +558,15 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding): 'floatingip_id': self.floating_ip.id, } ) - self.network_client.update_floating_ip_port_forwarding = mock.Mock( - return_value=None + self.network_client.update_floating_ip_port_forwarding.return_value = ( + None ) - self.network_client.find_floating_ip_port_forwarding = mock.Mock( - return_value=self._port_forwarding + self.network_client.find_floating_ip_port_forwarding.return_value = ( + self._port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.SetFloatingIPPortForwarding( self.app, None @@ -690,10 +692,11 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding): self._port_forwarding.internal_port_range, self._port_forwarding.protocol, ) - self.network_client.find_floating_ip_port_forwarding = mock.Mock( - return_value=self._port_forwarding + self.network_client.find_floating_ip_port_forwarding.return_value = ( + self._port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.ShowFloatingIPPortForwarding( self.app, None diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index 4224d12..def3e17 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from osc_lib.cli import format_columns @@ -55,8 +54,8 @@ class TestListIPAvailability(TestIPAvailability): super().setUp() self.cmd = ip_availability.ListIPAvailability(self.app, None) - self.network_client.network_ip_availabilities = mock.Mock( - return_value=self._ip_availability + self.network_client.network_ip_availabilities.return_value = ( + self._ip_availability ) def test_list_no_options(self): @@ -134,13 +133,12 @@ class TestShowIPAvailability(TestIPAvailability): def setUp(self): super().setUp() - self.network_client.find_network_ip_availability = mock.Mock( - return_value=self._ip_availability - ) - self.network_client.find_network = mock.Mock( - return_value=self._network + self.network_client.find_network_ip_availability.return_value = ( + self._ip_availability ) + self.network_client.find_network.return_value = self._network + # Get the command object to test self.cmd = ip_availability.ShowIPAvailability(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py index b358024..0769e2e 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from osc_lib import exceptions @@ -46,8 +45,8 @@ class TestCreateL3ConntrackHelper(TestConntrackHelper): self.ct_helper.protocol, self.ct_helper.router_id, ) - self.network_client.create_conntrack_helper = mock.Mock( - return_value=self.ct_helper + self.network_client.create_conntrack_helper.return_value = ( + self.ct_helper ) # Get the command object to test @@ -119,9 +118,7 @@ class TestDeleteL3ConntrackHelper(TestConntrackHelper): attrs ) ) - self.network_client.delete_conntrack_helper = mock.Mock( - return_value=None - ) + self.network_client.delete_conntrack_helper.return_value = None # Get the command object to test self.cmd = l3_conntrack_helper.DeleteConntrackHelper(self.app, None) @@ -181,9 +178,7 @@ class TestListL3ConntrackHelper(TestConntrackHelper): ct_helper.port, ) ) - self.network_client.conntrack_helpers = mock.Mock( - return_value=ct_helpers - ) + self.network_client.conntrack_helpers.return_value = ct_helpers # Get the command object to test self.cmd = l3_conntrack_helper.ListConntrackHelper(self.app, None) @@ -216,9 +211,7 @@ class TestSetL3ConntrackHelper(TestConntrackHelper): attrs ) ) - self.network_client.update_conntrack_helper = mock.Mock( - return_value=None - ) + self.network_client.update_conntrack_helper.return_value = None # Get the command object to test self.cmd = l3_conntrack_helper.SetConntrackHelper(self.app, None) @@ -281,9 +274,7 @@ class TestShowL3ConntrackHelper(TestConntrackHelper): self.ct_helper.protocol, self.ct_helper.router_id, ) - self.network_client.get_conntrack_helper = mock.Mock( - return_value=self.ct_helper - ) + self.network_client.get_conntrack_helper.return_value = self.ct_helper # Get the command object to test self.cmd = l3_conntrack_helper.ShowConntrackHelper(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py index cf4105b..585fec7 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip.py @@ -13,7 +13,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -77,13 +76,11 @@ class TestCreateLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.create_local_ip = mock.Mock( - return_value=self.new_local_ip - ) - self.network_client.find_network = mock.Mock( - return_value=self.local_ip_network - ) - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.create_local_ip.return_value = self.new_local_ip + + self.network_client.find_network.return_value = self.local_ip_network + + self.network_client.find_port.return_value = self.port # Get the command object to test self.cmd = local_ip.CreateLocalIP(self.app, None) @@ -149,7 +146,7 @@ class TestDeleteLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.delete_local_ip = mock.Mock(return_value=None) + self.network_client.delete_local_ip.return_value = None self.network_client.find_local_ip = network_fakes.get_local_ips( local_ips=self._local_ips ) @@ -205,9 +202,7 @@ class TestDeleteLocalIP(TestLocalIP): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._local_ips[0], exceptions.CommandError] - self.network_client.find_local_ip = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_local_ip.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -258,10 +253,8 @@ class TestListLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.local_ips = mock.Mock(return_value=self.local_ips) - self.network_client.find_network = mock.Mock( - return_value=self.fake_network - ) + self.network_client.local_ips.return_value = self.local_ips + self.network_client.find_network.return_value = self.fake_network # Get the command object to test self.cmd = local_ip.ListLocalIP(self.app, None) @@ -402,10 +395,8 @@ class TestSetLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.update_local_ip = mock.Mock(return_value=None) - self.network_client.find_local_ip = mock.Mock( - return_value=self._local_ip - ) + self.network_client.update_local_ip.return_value = None + self.network_client.find_local_ip.return_value = self._local_ip # Get the command object to test self.cmd = local_ip.SetLocalIP(self.app, None) @@ -482,9 +473,7 @@ class TestShowLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.find_local_ip = mock.Mock( - return_value=self._local_ip - ) + self.network_client.find_local_ip.return_value = self._local_ip # Get the command object to test self.cmd = local_ip.ShowLocalIP(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_local_ip_association.py b/openstackclient/tests/unit/network/v2/test_local_ip_association.py index 2c1c0ff..9efdc29 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip_association.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip_association.py @@ -29,7 +29,7 @@ class TestLocalIPAssociation(network_fakes.TestNetworkV2): self.local_ip = network_fakes.create_one_local_ip() self.fixed_port = network_fakes.create_one_port() self.project = identity_fakes_v2.FakeProject.create_one_project() - self.network_client.find_port = mock.Mock(return_value=self.fixed_port) + self.network_client.find_port.return_value = self.fixed_port class TestCreateLocalIPAssociation(TestLocalIPAssociation): @@ -43,13 +43,11 @@ class TestCreateLocalIPAssociation(TestLocalIPAssociation): } ) ) - self.network_client.create_local_ip_association = mock.Mock( - return_value=self.new_local_ip_association + self.network_client.create_local_ip_association.return_value = ( + self.new_local_ip_association ) - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) + self.network_client.find_local_ip.return_value = self.local_ip # Get the command object to test self.cmd = local_ip_association.CreateLocalIPAssociation( @@ -128,13 +126,10 @@ class TestDeleteLocalIPAssociation(TestLocalIPAssociation): }, ) ) - self.network_client.delete_local_ip_association = mock.Mock( - return_value=None - ) + self.network_client.delete_local_ip_association.return_value = None + + self.network_client.find_local_ip.return_value = self.local_ip - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) # Get the command object to test self.cmd = local_ip_association.DeleteLocalIPAssociation( self.app, None @@ -262,13 +257,13 @@ class TestListLocalIPAssociation(TestLocalIPAssociation): lip_assoc.host, ) ) - self.network_client.local_ip_associations = mock.Mock( - return_value=self.local_ip_associations + self.network_client.local_ip_associations.return_value = ( + self.local_ip_associations ) - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) - self.network_client.find_port = mock.Mock(return_value=self.fixed_port) + + self.network_client.find_local_ip.return_value = self.local_ip + + self.network_client.find_port.return_value = self.fixed_port # Get the command object to test self.cmd = local_ip_association.ListLocalIPAssociation(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index f9d9a3b..0fe8740 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -31,9 +30,9 @@ class TestNDPProxy(network_fakes.TestNetworkV2): self.domains_mock = self.identity_client.domains self.router = network_fakes.create_one_router({'id': 'fake-router-id'}) - self.network_client.find_router = mock.Mock(return_value=self.router) + self.network_client.find_router.return_value = self.router self.port = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.find_port.return_value = self.port class TestCreateNDPProxy(TestNDPProxy): @@ -66,9 +65,7 @@ class TestCreateNDPProxy(TestNDPProxy): self.ndp_proxy.router_id, self.ndp_proxy.updated_at, ) - self.network_client.create_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.create_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.CreateNDPProxy(self.app, None) @@ -127,10 +124,8 @@ class TestDeleteNDPProxy(TestNDPProxy): attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxies = network_fakes.create_ndp_proxies(attrs) self.ndp_proxy = self.ndp_proxies[0] - self.network_client.delete_ndp_proxy = mock.Mock(return_value=None) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.delete_ndp_proxy.return_value = None + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.DeleteNDPProxy(self.app, None) @@ -203,7 +198,7 @@ class TestListNDPProxy(TestNDPProxy): ) ) - self.network_client.ndp_proxies = mock.Mock(return_value=ndp_proxies) + self.network_client.ndp_proxies.return_value = ndp_proxies # Get the command object to test self.cmd = ndp_proxy.ListNDPProxy(self.app, None) @@ -340,10 +335,8 @@ class TestSetNDPProxy(TestNDPProxy): super().setUp() attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxy = network_fakes.create_one_ndp_proxy(attrs) - self.network_client.update_ndp_proxy = mock.Mock(return_value=None) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.update_ndp_proxy.return_value = None + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.SetNDPProxy(self.app, None) @@ -434,12 +427,9 @@ class TestShowNDPProxy(TestNDPProxy): self.ndp_proxy.router_id, self.ndp_proxy.updated_at, ) - self.network_client.get_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.get_ndp_proxy.return_value = self.ndp_proxy + + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.ShowNDPProxy(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index f809cad..1e923d0 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -12,7 +12,6 @@ # import random -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -116,19 +115,16 @@ class TestCreateNetworkIdentityV3(TestNetwork): def setUp(self): super().setUp() - self.network_client.create_network = mock.Mock( - return_value=self._network - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_network.return_value = self._network + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_qos_policy.return_value = self.qos_policy def test_create_no_options(self): arglist = [] @@ -330,6 +326,23 @@ class TestCreateNetworkIdentityV3(TestNetwork): exceptions.CommandError, self.cmd.take_action, parsed_args ) + def test_create_with_provider_segment_without_provider_type(self): + arglist = [ + "--provider-segment", + "123", + self._network.name, + ] + verifylist = [ + ('provider_network_type', None), + ('segmentation_id', "123"), + ('name', self._network.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + class TestCreateNetworkIdentityV2( identity_fakes_v2.FakeClientMixin, @@ -406,10 +419,9 @@ class TestCreateNetworkIdentityV2( def setUp(self): super().setUp() - self.network_client.create_network = mock.Mock( - return_value=self._network - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_network.return_value = self._network + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) @@ -481,7 +493,7 @@ class TestDeleteNetwork(TestNetwork): # The networks to delete self._networks = network_fakes.create_networks(count=3) - self.network_client.delete_network = mock.Mock(return_value=None) + self.network_client.delete_network.return_value = None self.network_client.find_network = network_fakes.get_networks( networks=self._networks @@ -540,14 +552,14 @@ class TestDeleteNetwork(TestNetwork): exceptions.NotFound('404'), self._networks[1], ] - self.network_client.find_network = mock.Mock(side_effect=ret_find) + self.network_client.find_network.side_effect = ret_find # Fake exception in delete_network() ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_network = mock.Mock(side_effect=ret_delete) + self.network_client.delete_network.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -619,13 +631,13 @@ class TestListNetwork(TestNetwork): # Get the command object to test self.cmd = network.ListNetwork(self.app, None) - self.network_client.networks = mock.Mock(return_value=self._network) + self.network_client.networks.return_value = self._network self._agent = network_fakes.create_one_network_agent() self.network_client.get_agent.return_value = self._agent - self.network_client.dhcp_agent_hosting_networks = mock.Mock( - return_value=self._network + self.network_client.dhcp_agent_hosting_networks.return_value = ( + self._network ) # TestListTagMixin @@ -974,15 +986,12 @@ class TestSetNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.update_network = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_network.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_network.return_value = self._network + + self.network_client.find_qos_policy.return_value = self.qos_policy # Get the command object to test self.cmd = network.SetNetwork(self.app, None) @@ -1225,9 +1234,7 @@ class TestShowNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.find_network = mock.Mock( - return_value=self._network - ) + self.network_client.find_network.return_value = self._network # Get the command object to test self.cmd = network.ShowNetwork(self.app, None) @@ -1273,15 +1280,12 @@ class TestUnsetNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.update_network = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_network.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_network.return_value = self._network + + self.network_client.find_qos_policy.return_value = self.qos_policy # Get the command object to test self.cmd = network.UnsetNetwork(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index bd79950..48b394d 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -34,8 +33,8 @@ class TestAddNetworkToAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self.agent) - self.network_client.find_network = mock.Mock(return_value=self.net) + self.network_client.get_agent.return_value = self.agent + self.network_client.find_network.return_value = self.net self.network_client.name = self.network_client.find_network.name self.cmd = network_agent.AddNetworkToAgent(self.app, None) @@ -76,8 +75,8 @@ class TestAddRouterAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self._agent) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.get_agent.return_value = self._agent + self.network_client.find_router.return_value = self._router self.cmd = network_agent.AddRouterToAgent(self.app, None) @@ -120,7 +119,7 @@ class TestDeleteNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.delete_agent = mock.Mock(return_value=None) + self.network_client.delete_agent.return_value = None # Get the command object to test self.cmd = network_agent.DeleteNetworkAgent(self.app, None) @@ -173,9 +172,7 @@ class TestDeleteNetworkAgent(TestNetworkAgent): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [True, exceptions.CommandError] - self.network_client.delete_agent = mock.Mock( - side_effect=delete_mock_result - ) + self.network_client.delete_agent.side_effect = delete_mock_result try: self.cmd.take_action(parsed_args) @@ -357,8 +354,8 @@ class TestRemoveNetworkFromAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self.agent) - self.network_client.find_network = mock.Mock(return_value=self.net) + self.network_client.get_agent.return_value = self.agent + self.network_client.find_network.return_value = self.net self.network_client.name = self.network_client.find_network.name self.cmd = network_agent.RemoveNetworkFromAgent(self.app, None) @@ -414,8 +411,8 @@ class TestRemoveRouterAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self._agent) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.get_agent.return_value = self._agent + self.network_client.find_router.return_value = self._router self.cmd = network_agent.RemoveRouterFromAgent(self.app, None) @@ -458,10 +455,8 @@ class TestSetNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.update_agent = mock.Mock(return_value=None) - self.network_client.get_agent = mock.Mock( - return_value=self._network_agent - ) + self.network_client.update_agent.return_value = None + self.network_client.get_agent.return_value = self._network_agent # Get the command object to test self.cmd = network_agent.SetNetworkAgent(self.app, None) @@ -574,9 +569,7 @@ class TestShowNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock( - return_value=self._network_agent - ) + self.network_client.get_agent.return_value = self._network_agent # Get the command object to test self.cmd = network_agent.ShowNetworkAgent(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py index 20c645b..d13bd8c 100644 --- a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +++ b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import network_auto_allocated_topology from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -50,8 +49,8 @@ class TestCreateAutoAllocatedTopology(TestAutoAllocatedTopology): self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None ) - self.network_client.get_auto_allocated_topology = mock.Mock( - return_value=self.topology + self.network_client.get_auto_allocated_topology.return_value = ( + self.topology ) def test_create_no_options(self): @@ -155,8 +154,8 @@ class TestValidateAutoAllocatedTopology(TestAutoAllocatedTopology): self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None ) - self.network_client.validate_auto_allocated_topology = mock.Mock( - return_value=self.topology + self.network_client.validate_auto_allocated_topology.return_value = ( + self.topology ) def test_show_dry_run_no_project(self): @@ -228,9 +227,7 @@ class TestDeleteAutoAllocatedTopology(TestAutoAllocatedTopology): self.cmd = network_auto_allocated_topology.DeleteAutoAllocatedTopology( self.app, None ) - self.network_client.delete_auto_allocated_topology = mock.Mock( - return_value=None - ) + self.network_client.delete_auto_allocated_topology.return_value = None def test_delete_no_project(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py index cfbe1f7..10038e3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py @@ -41,11 +41,9 @@ class TestAddNetworkFlavorToProfile(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.network_flavor - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.service_profile + self.network_client.find_flavor.return_value = self.network_flavor + self.network_client.find_service_profile.return_value = ( + self.service_profile ) self.cmd = network_flavor.AddNetworkFlavorToProfile(self.app, None) @@ -102,8 +100,8 @@ class TestCreateNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.create_flavor = mock.Mock( - return_value=self.new_network_flavor + self.network_client.create_flavor.return_value = ( + self.new_network_flavor ) # Get the command object to test @@ -218,7 +216,7 @@ class TestDeleteNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.delete_flavor = mock.Mock(return_value=None) + self.network_client.delete_flavor.return_value = None self.network_client.find_flavor = network_fakes.get_flavor( network_flavors=self._network_flavors ) @@ -278,9 +276,7 @@ class TestDeleteNetworkFlavor(TestNetworkFlavor): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._network_flavors[0], exceptions.CommandError] - self.network_client.find_flavor = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_flavor.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -323,9 +319,7 @@ class TestListNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.flavors = mock.Mock( - return_value=self._network_flavors - ) + self.network_client.flavors.return_value = self._network_flavors # Get the command object to test self.cmd = network_flavor.ListNetworkFlavor(self.app, None) @@ -348,11 +342,9 @@ class TestRemoveNetworkFlavorFromProfile(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.network_flavor - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.service_profile + self.network_client.find_flavor.return_value = self.network_flavor + self.network_client.find_service_profile.return_value = ( + self.service_profile ) self.network_client.disassociate_flavor_from_service_profile = ( mock.Mock() @@ -412,9 +404,7 @@ class TestShowNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.new_network_flavor - ) + self.network_client.find_flavor.return_value = self.new_network_flavor # Get the command object to test self.cmd = network_flavor.ShowNetworkFlavor(self.app, None) @@ -456,10 +446,8 @@ class TestSetNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.update_flavor = mock.Mock(return_value=None) - self.network_client.find_flavor = mock.Mock( - return_value=self.new_network_flavor - ) + self.network_client.update_flavor.return_value = None + self.network_client.find_flavor.return_value = self.new_network_flavor # Get the command object to test self.cmd = network_flavor.SetNetworkFlavor(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py index d94926c..c8235bf 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -49,9 +49,10 @@ class TestCreateFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.create_service_profile = mock.Mock( - return_value=self.new_flavor_profile + self.network_client.create_service_profile.return_value = ( + self.new_flavor_profile ) + # Get the command object to test self.cmd = network_flavor_profile.CreateNetworkFlavorProfile( self.app, None @@ -195,9 +196,8 @@ class TestDeleteFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.delete_service_profile = mock.Mock( - return_value=None - ) + self.network_client.delete_service_profile.return_value = None + self.network_client.find_service_profile = ( network_fakes.get_service_profile( flavor_profile=self._network_flavor_profiles @@ -266,9 +266,7 @@ class TestDeleteFlavorProfile(TestFlavorProfile): self._network_flavor_profiles[0], exceptions.CommandError, ] - self.network_client.find_service_profile = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_service_profile.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -314,8 +312,8 @@ class TestListFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.service_profiles = mock.Mock( - return_value=self._network_flavor_profiles + self.network_client.service_profiles.return_value = ( + self._network_flavor_profiles ) # Get the command object to test @@ -355,8 +353,8 @@ class TestShowFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.find_service_profile = mock.Mock( - return_value=self.network_flavor_profile + self.network_client.find_service_profile.return_value = ( + self.network_flavor_profile ) # Get the command object to test @@ -388,11 +386,10 @@ class TestSetFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.update_service_profile = mock.Mock( - return_value=None - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.network_flavor_profile + self.network_client.update_service_profile.return_value = None + + self.network_client.find_service_profile.return_value = ( + self.network_flavor_profile ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py index a92035d..f13839a 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -55,9 +54,8 @@ class TestCreateMeter(TestMeter): def setUp(self): super().setUp() - self.network_client.create_metering_label = mock.Mock( - return_value=self.new_meter - ) + self.network_client.create_metering_label.return_value = self.new_meter + self.projects_mock.get.return_value = self.project self.cmd = network_meter.CreateMeter(self.app, None) @@ -132,9 +130,7 @@ class TestDeleteMeter(TestMeter): self.meter_list = network_fakes.FakeNetworkMeter.create_meter(count=2) - self.network_client.delete_metering_label = mock.Mock( - return_value=None - ) + self.network_client.delete_metering_label.return_value = None self.network_client.find_metering_label = ( network_fakes.FakeNetworkMeter.get_meter(meter=self.meter_list) @@ -194,15 +190,13 @@ class TestDeleteMeter(TestMeter): exceptions.NotFound('404'), self.meter_list[1], ] - self.network_client.find_meter = mock.Mock(side_effect=return_find) + self.network_client.find_metering_label.side_effect = return_find ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_metering_label = mock.Mock( - side_effect=ret_delete - ) + self.network_client.delete_metering_label.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -240,9 +234,7 @@ class TestListMeter(TestMeter): def setUp(self): super().setUp() - self.network_client.metering_labels = mock.Mock( - return_value=self.meter_list - ) + self.network_client.metering_labels.return_value = self.meter_list self.cmd = network_meter.ListMeter(self.app, None) @@ -282,9 +274,7 @@ class TestShowMeter(TestMeter): self.cmd = network_meter.ShowMeter(self.app, None) - self.network_client.find_metering_label = mock.Mock( - return_value=self.new_meter - ) + self.network_client.find_metering_label.return_value = self.new_meter def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py index e90ed05..407fb04 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -65,14 +64,13 @@ class TestCreateMeterRule(TestMeterRule): {'id': self.new_rule.metering_label_id} ) - self.network_client.create_metering_label_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_metering_label_rule.return_value = ( + self.new_rule ) + self.projects_mock.get.return_value = self.project self.cmd = network_meter_rule.CreateMeterRule(self.app, None) - self.network_client.find_metering_label = mock.Mock( - return_value=fake_meter - ) + self.network_client.find_metering_label.return_value = fake_meter def test_create_no_options(self): arglist = [] @@ -146,9 +144,7 @@ class TestDeleteMeterRule(TestMeterRule): self.rule_list = network_fakes.FakeNetworkMeterRule.create_meter_rule( count=2 ) - self.network_client.delete_metering_label_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_metering_label_rule.return_value = None self.network_client.find_metering_label_rule = ( network_fakes.FakeNetworkMeterRule.get_meter_rule( @@ -210,17 +206,13 @@ class TestDeleteMeterRule(TestMeterRule): exceptions.NotFound('404'), self.rule_list[1], ] - self.network_client.find_metering_label_rule = mock.Mock( - side_effect=return_find - ) + self.network_client.find_metering_label_rule.side_effect = return_find ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_metering_label_rule = mock.Mock( - side_effect=ret_delete - ) + self.network_client.delete_metering_label_rule.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -262,9 +254,7 @@ class TestListMeterRule(TestMeterRule): def setUp(self): super().setUp() - self.network_client.metering_label_rules = mock.Mock( - return_value=self.rule_list - ) + self.network_client.metering_label_rules.return_value = self.rule_list self.cmd = network_meter_rule.ListMeterRule(self.app, None) @@ -311,8 +301,8 @@ class TestShowMeterRule(TestMeterRule): self.cmd = network_meter_rule.ShowMeterRule(self.app, None) - self.network_client.find_metering_label_rule = mock.Mock( - return_value=self.new_rule + self.network_client.find_metering_label_rule.return_value = ( + self.new_rule ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py index fae5b69..17f40ef 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py @@ -63,8 +63,8 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.create_qos_policy = mock.Mock( - return_value=self.new_qos_policy + self.network_client.create_qos_policy.return_value = ( + self.new_qos_policy ) # Get the command object to test @@ -163,7 +163,7 @@ class TestDeleteNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.delete_qos_policy = mock.Mock(return_value=None) + self.network_client.delete_qos_policy.return_value = None self.network_client.find_qos_policy = network_fakes.get_qos_policies( qos_policies=self._qos_policies ) @@ -264,9 +264,7 @@ class TestListNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.qos_policies = mock.Mock( - return_value=self.qos_policies - ) + self.network_client.qos_policies.return_value = self.qos_policies # Get the command object to test self.cmd = network_qos_policy.ListNetworkQosPolicy(self.app, None) @@ -344,10 +342,8 @@ class TestSetNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.update_qos_policy = mock.Mock(return_value=None) - self.network_client.find_qos_policy = mock.Mock( - return_value=self._qos_policy - ) + self.network_client.update_qos_policy.return_value = None + self.network_client.find_qos_policy.return_value = self._qos_policy # Get the command object to test self.cmd = network_qos_policy.SetNetworkQosPolicy(self.app, None) @@ -447,9 +443,7 @@ class TestShowNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.find_qos_policy = mock.Mock( - return_value=self._qos_policy - ) + self.network_client.find_qos_policy.return_value = self._qos_policy # Get the command object to test self.cmd = network_qos_policy.ShowNetworkQosPolicy(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py index e658684..4300304 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -56,9 +56,7 @@ class TestNetworkQosRule(network_fakes.TestNetworkV2): def setUp(self): super().setUp() self.qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_qos_policy.return_value = self.qos_policy class TestCreateNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): @@ -87,8 +85,8 @@ class TestCreateNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -190,9 +188,7 @@ class TestCreateNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule - ) + self.network_client.create_qos_minimum_packet_rate_rule.return_value = self.new_rule # Get the command object to test self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) @@ -291,8 +287,8 @@ class TestCreateNetworkQosRuleDSCPMarking(TestNetworkQosRule): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_dscp_marking_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -389,8 +385,8 @@ class TestCreateNetworkQosRuleBandwidtLimit(TestNetworkQosRule): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -529,9 +525,10 @@ class TestDeleteNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_minimum_bandwidth_rule = mock.Mock( - return_value=None + self.network_client.delete_qos_minimum_bandwidth_rule.return_value = ( + None ) + self.network_client.find_qos_minimum_bandwidth_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -592,9 +589,8 @@ class TestDeleteNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_minimum_packet_rate_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_minimum_packet_rate_rule.return_value = None + self.network_client.find_qos_minimum_packet_rate_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -655,9 +651,8 @@ class TestDeleteNetworkQosRuleDSCPMarking(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_dscp_marking_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_dscp_marking_rule.return_value = None + self.network_client.find_qos_dscp_marking_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -718,9 +713,8 @@ class TestDeleteNetworkQosRuleBandwidthLimit(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_bandwidth_limit_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_bandwidth_limit_rule.return_value = None + self.network_client.find_qos_bandwidth_limit_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -781,16 +775,16 @@ class TestSetNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_minimum_bandwidth_rule = mock.Mock( - return_value=None + self.network_client.update_qos_minimum_bandwidth_rule.return_value = ( + None ) - self.network_client.find_qos_minimum_bandwidth_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_minimum_bandwidth_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -883,16 +877,14 @@ class TestSetNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_minimum_packet_rate_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_minimum_packet_rate_rule.return_value = None + + self.network_client.find_qos_minimum_packet_rate_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -985,16 +977,14 @@ class TestSetNetworkQosRuleDSCPMarking(TestNetworkQosRule): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_dscp_marking_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_dscp_marking_rule.return_value = None + + self.network_client.find_qos_dscp_marking_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -1351,8 +1341,8 @@ class TestShowNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): self.new_rule.type, ) - self.network_client.get_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1415,8 +1405,8 @@ class TestShowNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): self.new_rule.type, ) - self.network_client.get_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_minimum_packet_rate_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1477,8 +1467,8 @@ class TestShowNetworkQosDSCPMarking(TestNetworkQosRule): self.new_rule.type, ) - self.network_client.get_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_dscp_marking_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1543,8 +1533,8 @@ class TestShowNetworkQosBandwidthLimit(TestNetworkQosRule): self.new_rule.type, ) - self.network_client.get_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py index c0aafa0..1c50b9e 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import network_qos_rule_type as _qos_rule_type from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -35,9 +34,7 @@ class TestShowNetworkQosRuleType(TestNetworkQosRuleType): def setUp(self): super().setUp() - self.network_client.get_qos_rule_type = mock.Mock( - return_value=self.qos_rule_type - ) + self.network_client.get_qos_rule_type.return_value = self.qos_rule_type # Get the command object to test self.cmd = _qos_rule_type.ShowNetworkQosRuleType(self.app, None) @@ -84,9 +81,7 @@ class TestListNetworkQosRuleType(TestNetworkQosRuleType): def setUp(self): super().setUp() - self.network_client.qos_rule_types = mock.Mock( - return_value=self.qos_rule_types - ) + self.network_client.qos_rule_types.return_value = self.qos_rule_types # Get the command object to test self.cmd = _qos_rule_type.ListNetworkQosRuleType(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index 816e932..d3a7192 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call import ddt @@ -72,27 +71,20 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): # Get the command object to test self.cmd = network_rbac.CreateNetworkRBAC(self.app, None) - self.network_client.create_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) - self.network_client.find_network = mock.Mock( - return_value=self.network_object - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_object - ) - self.network_client.find_security_group = mock.Mock( - return_value=self.sg_object - ) - self.network_client.find_address_scope = mock.Mock( - return_value=self.as_object - ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self.snp_object - ) - self.network_client.find_address_group = mock.Mock( - return_value=self.ag_object - ) + self.network_client.create_rbac_policy.return_value = self.rbac_policy + + self.network_client.find_network.return_value = self.network_object + + self.network_client.find_qos_policy.return_value = self.qos_object + + self.network_client.find_security_group.return_value = self.sg_object + + self.network_client.find_address_scope.return_value = self.as_object + + self.network_client.find_subnet_pool.return_value = self.snp_object + + self.network_client.find_address_group.return_value = self.ag_object + self.projects_mock.get.return_value = self.project def test_network_rbac_create_no_type(self): @@ -343,7 +335,7 @@ class TestDeleteNetworkRBAC(TestNetworkRBAC): def setUp(self): super().setUp() - self.network_client.delete_rbac_policy = mock.Mock(return_value=None) + self.network_client.delete_rbac_policy.return_value = None self.network_client.find_rbac_policy = network_fakes.get_network_rbacs( rbac_policies=self.rbac_policies ) @@ -400,9 +392,7 @@ class TestDeleteNetworkRBAC(TestNetworkRBAC): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self.rbac_policies[0], exceptions.CommandError] - self.network_client.find_rbac_policy = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_rbac_policy.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -462,9 +452,7 @@ class TestListNetworkRABC(TestNetworkRBAC): # Get the command object to test self.cmd = network_rbac.ListNetworkRBAC(self.app, None) - self.network_client.rbac_policies = mock.Mock( - return_value=self.rbac_policies - ) + self.network_client.rbac_policies.return_value = self.rbac_policies self.project = identity_fakes_v3.FakeProject.create_one_project() self.projects_mock.get.return_value = self.project @@ -564,10 +552,9 @@ class TestSetNetworkRBAC(TestNetworkRBAC): # Get the command object to test self.cmd = network_rbac.SetNetworkRBAC(self.app, None) - self.network_client.find_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) - self.network_client.update_rbac_policy = mock.Mock(return_value=None) + self.network_client.find_rbac_policy.return_value = self.rbac_policy + + self.network_client.update_rbac_policy.return_value = None self.projects_mock.get.return_value = self.project def test_network_rbac_set_nothing(self): @@ -639,9 +626,7 @@ class TestShowNetworkRBAC(TestNetworkRBAC): # Get the command object to test self.cmd = network_rbac.ShowNetworkRBAC(self.app, None) - self.network_client.find_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) + self.network_client.find_rbac_policy.return_value = self.rbac_policy def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py index 936a20a..ab71c32 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -58,12 +57,9 @@ class TestCreateNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.create_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) + self.network_client.create_segment.return_value = self._network_segment + + self.network_client.find_network.return_value = self._network # Get the command object to test self.cmd = network_segment.CreateNetworkSegment(self.app, None) @@ -172,10 +168,8 @@ class TestDeleteNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.delete_segment = mock.Mock(return_value=None) - self.network_client.find_segment = mock.Mock( - side_effect=self._network_segments - ) + self.network_client.delete_segment.return_value = None + self.network_client.find_segment.side_effect = self._network_segments # Get the command object to test self.cmd = network_segment.DeleteNetworkSegment(self.app, None) @@ -224,9 +218,7 @@ class TestDeleteNetworkSegment(TestNetworkSegment): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._network_segments[0], exceptions.CommandError] - self.network_client.find_segment = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_segment.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -291,12 +283,9 @@ class TestListNetworkSegment(TestNetworkSegment): # Get the command object to test self.cmd = network_segment.ListNetworkSegment(self.app, None) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.segments = mock.Mock( - return_value=self._network_segments - ) + self.network_client.find_network.return_value = self._network + + self.network_client.segments.return_value = self._network_segments def test_list_no_option(self): arglist = [] @@ -352,12 +341,9 @@ class TestSetNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.update_segment = mock.Mock( - return_value=self._network_segment - ) + self.network_client.find_segment.return_value = self._network_segment + + self.network_client.update_segment.return_value = self._network_segment # Get the command object to test self.cmd = network_segment.SetNetworkSegment(self.app, None) @@ -432,9 +418,7 @@ class TestShowNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) + self.network_client.find_segment.return_value = self._network_segment # Get the command object to test self.cmd = network_segment.ShowNetworkSegment(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_segment_range.py b/openstackclient/tests/unit/network/v2/test_network_segment_range.py index d5e406f..9c9c900 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py @@ -78,8 +78,8 @@ class TestCreateNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.create_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.create_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test @@ -351,11 +351,10 @@ class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.delete_network_segment_range = mock.Mock( - return_value=None - ) - self.network_client.find_network_segment_range = mock.Mock( - side_effect=self._network_segment_ranges + self.network_client.delete_network_segment_range.return_value = None + + self.network_client.find_network_segment_range.side_effect = ( + self._network_segment_ranges ) # Get the command object to test @@ -412,8 +411,8 @@ class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange): self._network_segment_ranges[0], exceptions.CommandError, ] - self.network_client.find_network_segment_range = mock.Mock( - side_effect=find_mock_result + self.network_client.find_network_segment_range.side_effect = ( + find_mock_result ) try: @@ -493,8 +492,8 @@ class TestListNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.network_segment_ranges = mock.Mock( - return_value=self._network_segment_ranges + self.network_client.network_segment_ranges.return_value = ( + self._network_segment_ranges ) # Get the command object to test @@ -567,8 +566,8 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.find_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.find_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test @@ -583,9 +582,10 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.update_network_segment_range.return_value = ( + self._network_segment_range ) + result = self.cmd.take_action(parsed_args) self.network_client.update_network_segment_range.assert_called_once_with( @@ -611,9 +611,10 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_network_segment_range = mock.Mock( - return_value=self._network_segment_range_updated + self.network_client.update_network_segment_range.return_value = ( + self._network_segment_range_updated ) + result = self.cmd.take_action(parsed_args) attrs = { @@ -662,8 +663,8 @@ class TestShowNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.find_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.find_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_service_provider.py b/openstackclient/tests/unit/network/v2/test_network_service_provider.py index 9f61bce..f84bcd3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_service_provider.py +++ b/openstackclient/tests/unit/network/v2/test_network_service_provider.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import ( network_service_provider as service_provider, @@ -52,9 +51,7 @@ class TestListNetworkServiceProvider(TestNetworkServiceProvider): def setUp(self): super().setUp() - self.network_client.service_providers = mock.Mock( - return_value=self.provider_list - ) + self.network_client.service_providers.return_value = self.provider_list self.cmd = service_provider.ListNetworkServiceProvider(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 180e80d..1056c21 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -11,7 +11,6 @@ # under the License. import copy -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -84,12 +83,12 @@ class TestCreateNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.create_trunk = mock.Mock( - return_value=self.new_trunk - ) - self.network_client.find_port = mock.Mock( - side_effect=[self.parent_port, self.sub_port] - ) + self.network_client.create_trunk.return_value = self.new_trunk + + self.network_client.find_port.side_effect = [ + self.parent_port, + self.sub_port, + ] # Get the command object to test self.cmd = network_trunk.CreateNetworkTrunk(self.app, None) @@ -313,13 +312,16 @@ class TestDeleteNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock( - side_effect=[self.new_trunks[0], self.new_trunks[1]] - ) - self.network_client.delete_trunk = mock.Mock(return_value=None) - self.network_client.find_port = mock.Mock( - side_effect=[self.parent_port, self.sub_port] - ) + self.network_client.find_trunk.side_effect = [ + self.new_trunks[0], + self.new_trunks[1], + ] + + self.network_client.delete_trunk.return_value = None + self.network_client.find_port.side_effect = [ + self.parent_port, + self.sub_port, + ] self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -371,9 +373,11 @@ class TestDeleteNetworkTrunk(TestNetworkTrunk): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.find_trunk = mock.Mock( - side_effect=[self.new_trunks[0], exceptions.CommandError] - ) + self.network_client.find_trunk.side_effect = [ + self.new_trunks[0], + exceptions.CommandError, + ] + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual('1 of 2 trunks failed to delete.', str(e)) @@ -412,8 +416,8 @@ class TestShowNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self.new_trunk) - self.network_client.get_trunk = mock.Mock(return_value=self.new_trunk) + self.network_client.find_trunk.return_value = self.new_trunk + self.network_client.get_trunk.return_value = self.new_trunk self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -485,7 +489,7 @@ class TestListNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.trunks = mock.Mock(return_value=self.new_trunks) + self.network_client.trunks.return_value = self.new_trunks self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -564,14 +568,14 @@ class TestSetNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.update_trunk = mock.Mock(return_value=self._trunk) - self.network_client.add_trunk_subports = mock.Mock( - return_value=self._trunk - ) - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.find_port = mock.Mock( - side_effect=[self.sub_port, self.sub_port] - ) + self.network_client.update_trunk.return_value = self._trunk + self.network_client.add_trunk_subports.return_value = self._trunk + + self.network_client.find_trunk.return_value = self._trunk + self.network_client.find_port.side_effect = [ + self.sub_port, + self.sub_port, + ] self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -763,9 +767,8 @@ class TestSetNetworkTrunk(TestNetworkTrunk): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_trunk = mock.Mock( - side_effect=exceptions.CommandError - ) + self.network_client.update_trunk.side_effect = exceptions.CommandError + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( @@ -790,12 +793,14 @@ class TestSetNetworkTrunk(TestNetworkTrunk): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.add_trunk_subports = mock.Mock( - side_effect=exceptions.CommandError - ) - self.network_client.find_port = mock.Mock( - return_value={'id': 'invalid_subport'} + self.network_client.add_trunk_subports.side_effect = ( + exceptions.CommandError ) + + self.network_client.find_port.side_effect = [ + network_fakes.create_one_port({'id': 'invalid_subport'}) + ] + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( @@ -832,10 +837,10 @@ class TestListNetworkSubport(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.get_trunk_subports = mock.Mock( - return_value={network_trunk.SUB_PORTS: self._subports} - ) + self.network_client.find_trunk.return_value = self._trunk + self.network_client.get_trunk_subports.return_value = { + network_trunk.SUB_PORTS: self._subports + } # Get the command object to test self.cmd = network_trunk.ListNetworkSubport(self.app, None) @@ -902,13 +907,13 @@ class TestUnsetNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.find_port = mock.Mock( - side_effect=[self.sub_port, self.sub_port] - ) - self.network_client.delete_trunk_subports = mock.Mock( - return_value=None - ) + self.network_client.find_trunk.return_value = self._trunk + self.network_client.find_port.side_effect = [ + self.sub_port, + self.sub_port, + ] + + self.network_client.delete_trunk_subports.return_value = None # Get the command object to test self.cmd = network_trunk.UnsetNetworkTrunk(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 0611be4..11a8711 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -136,19 +136,18 @@ class TestCreatePort(TestPort): def setUp(self): super().setUp() - self.network_client.create_port = mock.Mock(return_value=self._port) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_port.return_value = self._port + self.network_client.set_tags.return_value = None fake_net = network_fakes.create_one_network( { 'id': self._port.network_id, } ) - self.network_client.find_network = mock.Mock(return_value=fake_net) + self.network_client.find_network.return_value = fake_net self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_extension = mock.Mock(return_value=[]) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_extension.return_value = [] # Get the command object to test self.cmd = port.CreatePort(self.app, None) @@ -321,9 +320,8 @@ class TestCreatePort(TestPort): def test_create_with_security_group(self): secgroup = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - return_value=secgroup - ) + self.network_client.find_security_group.return_value = secgroup + arglist = [ '--network', self._port.network_id, @@ -393,9 +391,8 @@ class TestCreatePort(TestPort): def test_create_with_security_groups(self): sg_1 = network_fakes.create_one_security_group() sg_2 = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - side_effect=[sg_1, sg_2] - ) + self.network_client.find_security_group.side_effect = [sg_1, sg_2] + arglist = [ '--network', self._port.network_id, @@ -583,9 +580,8 @@ class TestCreatePort(TestPort): def test_create_port_with_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ '--network', self._port.network_id, @@ -701,9 +697,7 @@ class TestCreatePort(TestPort): else: verifylist.append(('no_tag', True)) - self.network_client.find_extension = mock.Mock( - return_value=add_tags_in_post - ) + self.network_client.find_extension.return_value = add_tags_in_post parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1170,7 +1164,7 @@ class TestDeletePort(TestPort): def setUp(self): super().setUp() - self.network_client.delete_port = mock.Mock(return_value=None) + self.network_client.delete_port.return_value = None self.network_client.find_port = network_fakes.get_ports( ports=self._ports ) @@ -1223,7 +1217,7 @@ class TestDeletePort(TestPort): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._ports[0], exceptions.CommandError] - self.network_client.find_port = mock.Mock(side_effect=find_mock_result) + self.network_client.find_port.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -1324,7 +1318,7 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): def setUp(self): super().setUp() - self.network_client.ports = mock.Mock(return_value=self._ports) + self.network_client.ports.return_value = self._ports fake_router = network_fakes.create_one_router( { 'id': 'fake-router-id', @@ -1335,8 +1329,8 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): 'id': 'fake-network-id', } ) - self.network_client.find_router = mock.Mock(return_value=fake_router) - self.network_client.find_network = mock.Mock(return_value=fake_network) + self.network_client.find_router.return_value = fake_router + self.network_client.find_network.return_value = fake_network # Get the command object to test self.cmd = port.ListPort(self.app, None) @@ -1550,9 +1544,8 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': subnet_id} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1579,9 +1572,8 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': subnet_id} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1616,9 +1608,8 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): 'fields': LIST_FIELDS_TO_RETRIEVE, } ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1825,12 +1816,11 @@ class TestSetPort(TestPort): def setUp(self): super().setUp() self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_port = mock.Mock(return_value=self._port) - self.network_client.update_port = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_port.return_value = self._port + self.network_client.update_port.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = port.SetPort(self.app, None) @@ -1853,7 +1843,7 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'fixed_ips': [{'ip_address': '0.0.0.1'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--fixed-ip', 'ip-address=10.0.0.12', @@ -1881,7 +1871,7 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'fixed_ips': [{'ip_address': '0.0.0.1'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--fixed-ip', 'ip-address=10.0.0.12', @@ -1931,7 +1921,7 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'binding_profile': {'lok_i': 'visi_on'}} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--binding-profile', 'lok_i=than_os', @@ -1956,7 +1946,7 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'mac_address': '11:22:33:44:55:66'} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--mac-address', '66:55:44:33:22:11', @@ -2097,7 +2087,7 @@ class TestSetPort(TestPort): def test_set_port_security_group(self): sg = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock(return_value=sg) + self.network_client.find_security_group.return_value = sg arglist = [ '--security-group', sg.id, @@ -2122,13 +2112,12 @@ class TestSetPort(TestPort): sg_1 = network_fakes.create_one_security_group() sg_2 = network_fakes.create_one_security_group() sg_3 = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - side_effect=[sg_2, sg_3] - ) + self.network_client.find_security_group.side_effect = [sg_2, sg_3] + _testport = network_fakes.create_one_port( {'security_group_ids': [sg_1.id]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--security-group', sg_2.id, @@ -2177,8 +2166,8 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'security_group_ids': [sg1.id]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) - self.network_client.find_security_group = mock.Mock(return_value=sg2) + self.network_client.find_port.return_value = _testport + self.network_client.find_security_group.return_value = sg2 arglist = [ '--security-group', sg2.id, @@ -2226,7 +2215,7 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2255,7 +2244,7 @@ class TestSetPort(TestPort): _testport = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2372,11 +2361,10 @@ class TestSetPort(TestPort): def test_set_port_with_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + _testport = network_fakes.create_one_port({'qos_policy_id': None}) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--qos-policy', qos_policy.id, @@ -2400,7 +2388,7 @@ class TestSetPort(TestPort): def test_set_port_data_plane_status(self): _testport = network_fakes.create_one_port({'data_plane_status': None}) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--data-plane-status', 'ACTIVE', @@ -2546,10 +2534,12 @@ class TestSetPort(TestPort): def test_set_hints_valid_alias_value(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) - self.network_client.find_extension = mock.Mock( - return_value=['port-hints', 'port-hint-ovs-tx-steering'] - ) + self.network_client.find_port.return_value = testport + self.network_client.find_extension.return_value = [ + 'port-hints', + 'port-hint-ovs-tx-steering', + ] + arglist = [ '--hint', 'ovs-tx-steering=hash', @@ -2574,10 +2564,12 @@ class TestSetPort(TestPort): def test_set_hints_valid_json(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) - self.network_client.find_extension = mock.Mock( - return_value=['port-hints', 'port-hint-ovs-tx-steering'] - ) + self.network_client.find_port.return_value = testport + self.network_client.find_extension.return_value = [ + 'port-hints', + 'port-hint-ovs-tx-steering', + ] + arglist = [ '--hint', '{"openvswitch": {"other_config": {"tx-steering": "hash"}}}', @@ -2670,7 +2662,7 @@ class TestShowPort(TestPort): def setUp(self): super().setUp() - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_port.return_value = self._port # Get the command object to test self.cmd = port.ShowPort(self.app, None) @@ -2731,12 +2723,11 @@ class TestUnsetPort(TestPort): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': '042eb10a-3a18-4658-ab-cf47c8d03152'} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_port = mock.Mock(return_value=self._testport) - self.network_client.update_port = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_port.return_value = self._testport + self.network_client.update_port.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = port.UnsetPort(self.app, None) @@ -2826,10 +2817,9 @@ class TestUnsetPort(TestPort): _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id, _fake_sg2.id]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) - self.network_client.find_security_group = mock.Mock( - return_value=_fake_sg2 - ) + self.network_client.find_port.return_value = _fake_port + self.network_client.find_security_group.return_value = _fake_sg2 + arglist = [ '--security-group', _fake_sg2.id, @@ -2854,9 +2844,8 @@ class TestUnsetPort(TestPort): _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id]} ) - self.network_client.find_security_group = mock.Mock( - return_value=_fake_sg2 - ) + self.network_client.find_security_group.return_value = _fake_sg2 + arglist = [ '--security-group', _fake_sg2.id, @@ -2875,7 +2864,7 @@ class TestUnsetPort(TestPort): _fake_port = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--allowed-address', 'ip-address=192.168.1.123', @@ -2901,7 +2890,7 @@ class TestUnsetPort(TestPort): _fake_port = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2920,7 +2909,7 @@ class TestUnsetPort(TestPort): _fake_port = network_fakes.create_one_port( {'data_plane_status': 'ACTIVE'} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--data-plane-status', _fake_port.name, @@ -2973,7 +2962,7 @@ class TestUnsetPort(TestPort): _fake_port = network_fakes.create_one_port( {'numa_affinity_policy': 'required'} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--numa-policy', _fake_port.name, @@ -2997,7 +2986,7 @@ class TestUnsetPort(TestPort): def test_unset_hints(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--hints', testport.name, @@ -3017,7 +3006,7 @@ class TestUnsetPort(TestPort): def test_unset_device(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--device', testport.name, @@ -3037,7 +3026,7 @@ class TestUnsetPort(TestPort): def test_unset_device_owner(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--device-owner', testport.name, diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 3536583..6ebb780 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -39,8 +39,8 @@ class TestAddPortToRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_router.return_value = self._router + self.network_client.find_port.return_value = self._port self.cmd = router.AddPortToRouter(self.app, None) @@ -87,8 +87,8 @@ class TestAddSubnetToRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_router.return_value = self._router + self.network_client.find_subnet.return_value = self._subnet self.cmd = router.AddSubnetToRouter(self.app, None) @@ -172,12 +172,11 @@ class TestCreateRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.create_router = mock.Mock( - return_value=self.new_router - ) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.create_router.return_value = self.new_router + + self.network_client.set_tags.return_value = None + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) # Get the command object to test self.cmd = router.CreateRouter(self.app, None) @@ -222,8 +221,8 @@ class TestCreateRouter(TestRouter): def test_create_with_gateway(self): _network = network_fakes.create_one_network() _subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=_network) - self.network_client.find_subnet = mock.Mock(return_value=_subnet) + self.network_client.find_network.return_value = _network + self.network_client.find_subnet.return_value = _subnet arglist = [ self.new_router.name, '--external-gateway', @@ -392,7 +391,7 @@ class TestCreateRouter(TestRouter): def test_create_with_flavor_id_id(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [ self.new_router.name, '--flavor-id', @@ -419,7 +418,7 @@ class TestCreateRouter(TestRouter): def test_create_with_flavor_id_name(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [self.new_router.name, '--flavor-id', _flavor.name] verifylist = [ ('name', self.new_router.name), @@ -442,7 +441,7 @@ class TestCreateRouter(TestRouter): def test_create_with_flavor_id(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [ self.new_router.name, '--flavor', @@ -469,7 +468,7 @@ class TestCreateRouter(TestRouter): def test_create_with_flavor_name(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [self.new_router.name, '--flavor', _flavor.name] verifylist = [ ('name', self.new_router.name), @@ -491,11 +490,12 @@ class TestCreateRouter(TestRouter): self.assertCountEqual(self.data, data) def test_create_with_enable_default_route_bfd(self): - self.network_client.find_extension = mock.Mock( - return_value=network_fakes.create_one_extension( + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( attrs={'name': 'external-gateway-multihoming'} - ) - ) + ), + } + arglist = [self.new_router.name, '--enable-default-route-bfd'] verifylist = [ ('name', self.new_router.name), @@ -525,11 +525,12 @@ class TestCreateRouter(TestRouter): ) def test_create_with_enable_default_route_ecmp(self): - self.network_client.find_extension = mock.Mock( - return_value=network_fakes.create_one_extension( + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( attrs={'name': 'external-gateway-multihoming'} - ) - ) + ), + } + arglist = [self.new_router.name, '--enable-default-route-ecmp'] verifylist = [ ('name', self.new_router.name), @@ -560,11 +561,10 @@ class TestCreateRouter(TestRouter): def test_create_with_qos_policy(self): _network = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock(return_value=_network) + self.network_client.find_network.return_value = _network _qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=_qos_policy - ) + self.network_client.find_qos_policy.return_value = _qos_policy + arglist = [ self.new_router.name, '--external-gateway', @@ -595,9 +595,8 @@ class TestCreateRouter(TestRouter): def test_create_with_qos_policy_no_external_gateway(self): _qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=_qos_policy - ) + self.network_client.find_qos_policy.return_value = _qos_policy + arglist = [ self.new_router.name, '--qos-policy', @@ -625,7 +624,7 @@ class TestDeleteRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.delete_router = mock.Mock(return_value=None) + self.network_client.delete_router.return_value = None self.network_client.find_router = network_fakes.get_routers( self._routers @@ -679,9 +678,7 @@ class TestDeleteRouter(TestRouter): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._routers[0], exceptions.CommandError] - self.network_client.find_router = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_router.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -786,21 +783,16 @@ class TestListRouter(TestRouter): # Get the command object to test self.cmd = router.ListRouter(self.app, None) - self.network_client.agent_hosted_routers = mock.Mock( - return_value=self.routers - ) - self.network_client.routers = mock.Mock(return_value=self.routers) - self.network_client.find_extension = mock.Mock( - return_value=self.extensions - ) - self.network_client.find_router = mock.Mock( - return_value=self.routers[0] - ) + self.network_client.agent_hosted_routers.return_value = self.routers + + self.network_client.routers.return_value = self.routers + self.network_client.find_extension.return_value = self.extensions + + self.network_client.find_router.return_value = self.routers[0] + self._testagent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=self._testagent) - self.network_client.get_router = mock.Mock( - return_value=self.routers[0] - ) + self.network_client.get_agent.return_value = self._testagent + self.network_client.get_router.return_value = self.routers[0] def test_router_list_no_options(self): arglist = [] @@ -865,7 +857,7 @@ class TestListRouter(TestRouter): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # to mock, that no availability zone - self.network_client.find_extension = mock.Mock(return_value=None) + self.network_client.find_extension.return_value = None # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable @@ -1044,8 +1036,8 @@ class TestRemovePortFromRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_router.return_value = self._router + self.network_client.find_port.return_value = self._port self.cmd = router.RemovePortFromRouter(self.app, None) @@ -1089,8 +1081,8 @@ class TestRemoveSubnetFromRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_router.return_value = self._router + self.network_client.find_subnet.return_value = self._subnet self.cmd = router.RemoveSubnetFromRouter(self.app, None) @@ -1129,11 +1121,12 @@ class TestAddExtraRoutesToRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.add_extra_routes_to_router = mock.Mock( - return_value=self._router + self.network_client.add_extra_routes_to_router.return_value = ( + self._router ) + self.cmd = router.AddExtraRoutesToRouter(self.app, None) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def test_add_no_extra_route(self): arglist = [ @@ -1218,11 +1211,12 @@ class TestRemoveExtraRoutesFromRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.remove_extra_routes_from_router = mock.Mock( - return_value=self._router + self.network_client.remove_extra_routes_from_router.return_value = ( + self._router ) + self.cmd = router.RemoveExtraRoutesFromRouter(self.app, None) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def test_remove_no_extra_route(self): arglist = [ @@ -1316,15 +1310,14 @@ class TestSetRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.update_router.return_value = None + self.network_client.set_tags.return_value = None + self.network_client.find_router.return_value = self._router + self.network_client.find_network.return_value = self._network + + self.network_client.find_subnet.return_value = self._subnet + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) # Get the command object to test self.cmd = router.SetRouter(self.app, None) @@ -1459,7 +1452,7 @@ class TestSetRouter(TestRouter): _testrouter = network_fakes.create_one_router( {'routes': [{"destination": "10.0.0.2", "nexthop": "1.1.1.1"}]} ) - self.network_client.find_router = mock.Mock(return_value=_testrouter) + self.network_client.find_router.return_value = _testrouter arglist = [ _testrouter.name, '--route', @@ -1667,9 +1660,8 @@ class TestSetRouter(TestRouter): def test_set_gateway_ip_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ "--external-gateway", self._network.id, @@ -1724,9 +1716,8 @@ class TestSetRouter(TestRouter): def test_set_unset_gateway_ip_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ "--external-gateway", self._network.id, @@ -1752,11 +1743,10 @@ class TestSetRouter(TestRouter): def test_set_gateway_ip_qos_no_gateway(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", qos_policy.id, @@ -1774,11 +1764,10 @@ class TestSetRouter(TestRouter): def test_unset_gateway_ip_qos_no_gateway(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--no-qos-policy", router.id, @@ -1857,8 +1846,8 @@ class TestShowRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.ports = mock.Mock(return_value=[self._port]) + self.network_client.find_router.return_value = self._router + self.network_client.ports.return_value = [self._port] # Get the command object to test self.cmd = router.ShowRouter(self.app, None) @@ -1961,18 +1950,16 @@ class TestUnsetRouter(TestRouter): } ) self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_router = mock.Mock( - return_value=self._testrouter - ) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_router.return_value = self._testrouter + + self.network_client.update_router.return_value = None + self.network_client.set_tags.return_value = None self._extensions = {'fake': network_fakes.create_one_extension()} - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) - ) - self.network_client.remove_external_gateways = mock.Mock( - return_value=None + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) + self.network_client.remove_external_gateways.return_value = None + # Get the command object to test self.cmd = router.UnsetRouter(self.app, None) @@ -2109,11 +2096,10 @@ class TestUnsetRouter(TestRouter): def test_unset_gateway_ip_qos_no_network(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", router.id, @@ -2129,13 +2115,12 @@ class TestUnsetRouter(TestRouter): def test_unset_gateway_ip_qos_no_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router( {"external_gateway_info": {"network_id": "fake-id"}} ) - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", router.id, @@ -2172,10 +2157,10 @@ class TestGatewayOps(TestRouter): attrs={'name': 'external-gateway-multihoming'} ) } - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.find_extension.side_effect = ( + lambda name, ignore_missing=True: self._extensions.get(name) ) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def _find_network(name_or_id, ignore_missing): for network in self._networks: @@ -2185,15 +2170,12 @@ class TestGatewayOps(TestRouter): return None raise Exception('Test resource not found') - self.network_client.find_network = mock.Mock(side_effect=_find_network) + self.network_client.find_network.side_effect = _find_network - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.network_client.add_external_gateways = mock.Mock( - return_value=None - ) - self.network_client.remove_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.find_subnet.return_value = self._subnet + self.network_client.add_external_gateways.return_value = None + + self.network_client.remove_external_gateways.return_value = None class TestCreateMultipleGateways(TestGatewayOps): @@ -2223,13 +2205,10 @@ class TestCreateMultipleGateways(TestGatewayOps): self._second_network = network_fakes.create_one_network() self._networks.append(self._second_network) - self.network_client.create_router = mock.Mock( - return_value=self._router - ) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.update_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.create_router.return_value = self._router + + self.network_client.update_router.return_value = None + self.network_client.update_external_gateways.return_value = None self._data = ( router.AdminStateColumn(self._router.is_admin_state_up), @@ -2353,10 +2332,9 @@ class TestUpdateMultipleGateways(TestGatewayOps): self._second_network = network_fakes.create_one_network() self._networks.append(self._second_network) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.update_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.update_router.return_value = None + self.network_client.update_external_gateways.return_value = None + self.cmd = router.SetRouter(self.app, None) def test_update_one_gateway(self): diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py index 9bf1f67..c5ccf62 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -69,13 +68,13 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.create_security_group = mock.Mock( - return_value=self._security_group + self.network_client.create_security_group.return_value = ( + self._security_group ) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.CreateSecurityGroup(self.app, None) @@ -185,9 +184,7 @@ class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.delete_security_group = mock.Mock( - return_value=None - ) + self.network_client.delete_security_group.return_value = None self.network_client.find_security_group = ( network_fakes.get_security_groups(self._security_groups) @@ -245,9 +242,7 @@ class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._security_groups[0], exceptions.CommandError] - self.network_client.find_security_group = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_security_group.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -295,8 +290,8 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.security_groups = mock.Mock( - return_value=self._security_groups + self.network_client.security_groups.return_value = ( + self._security_groups ) # Get the command object to test @@ -423,14 +418,13 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.update_security_group = mock.Mock( - return_value=None - ) + self.network_client.update_security_group.return_value = None - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.set_tags = mock.Mock(return_value=None) + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.SetSecurityGroup(self.app, None) @@ -557,8 +551,8 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) # Get the command object to test @@ -596,14 +590,13 @@ class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.update_security_group = mock.Mock( - return_value=None - ) + self.network_client.update_security_group.return_value = None - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.set_tags = mock.Mock(return_value=None) + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.UnsetSecurityGroup(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py index d5630c3..920e891 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -69,9 +68,10 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self._security_group_rule = ( network_fakes.create_one_security_group_rule(attrs) ) - self.network_client.create_security_group_rule = mock.Mock( - return_value=self._security_group_rule + self.network_client.create_security_group_rule.return_value = ( + self._security_group_rule ) + self.expected_data = ( self._security_group_rule.created_at, self._security_group_rule.description, @@ -93,12 +93,12 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) self.projects_mock.get.return_value = self.project @@ -970,9 +970,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.delete_security_group_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_security_group_rule.return_value = None self.network_client.find_security_group_rule = ( network_fakes.get_security_group_rules(self._security_group_rules) @@ -1030,8 +1028,8 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self._security_group_rules[0], exceptions.CommandError, ] - self.network_client.find_security_group_rule = mock.Mock( - side_effect=find_mock_result + self.network_client.find_security_group_rule.side_effect = ( + find_mock_result ) try: @@ -1134,11 +1132,12 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.security_group_rules = mock.Mock( - return_value=self._security_group_rules + + self.network_client.security_group_rules.return_value = ( + self._security_group_rules ) # Get the command object to test @@ -1344,8 +1343,8 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group_rule = mock.Mock( - return_value=self._security_group_rule + self.network_client.find_security_group_rule.return_value = ( + self._security_group_rule ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 8b82074..e59168e 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -266,19 +265,14 @@ class TestCreateSubnet(TestSubnet): self.domains_mock.get.return_value = self.domain # Mock SDK calls for all tests. - self.network_client.create_subnet = mock.Mock( - return_value=self._subnet - ) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.create_subnet.return_value = self._subnet + + self.network_client.set_tags.return_value = None + self.network_client.find_network.return_value = self._network + + self.network_client.find_segment.return_value = self._network_segment + + self.network_client.find_subnet_pool.return_value = self._subnet_pool def test_create_no_options(self): arglist = [] @@ -333,7 +327,7 @@ class TestCreateSubnet(TestSubnet): def test_create_from_subnet_pool_options(self): # Mock SDK calls for this test. self.network_client.create_subnet.return_value = self._subnet_from_pool - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.set_tags.return_value = None self._network.id = self._subnet_from_pool.network_id arglist = [ @@ -721,7 +715,7 @@ class TestDeleteSubnet(TestSubnet): def setUp(self): super().setUp() - self.network_client.delete_subnet = mock.Mock(return_value=None) + self.network_client.delete_subnet.return_value = None self.network_client.find_subnet = network_fakes.FakeSubnet.get_subnets( self._subnets @@ -775,9 +769,7 @@ class TestDeleteSubnet(TestSubnet): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._subnets[0], exceptions.CommandError] - self.network_client.find_subnet = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_subnet.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -855,7 +847,7 @@ class TestListSubnet(TestSubnet): # Get the command object to test self.cmd = subnet_v2.ListSubnet(self.app, None) - self.network_client.subnets = mock.Mock(return_value=self._subnet) + self.network_client.subnets.return_value = self._subnet def test_subnet_list_no_options(self): arglist = [] @@ -1019,7 +1011,7 @@ class TestListSubnet(TestSubnet): def test_subnet_list_network(self): network = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock(return_value=network) + self.network_client.find_network.return_value = network arglist = [ '--network', network.id, @@ -1038,7 +1030,7 @@ class TestListSubnet(TestSubnet): def test_subnet_list_gateway(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--gateway', subnet.gateway_ip, @@ -1057,7 +1049,7 @@ class TestListSubnet(TestSubnet): def test_subnet_list_name(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--name', subnet.name, @@ -1076,7 +1068,7 @@ class TestListSubnet(TestSubnet): def test_subnet_list_subnet_range(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--subnet-range', subnet.cidr, @@ -1098,10 +1090,9 @@ class TestListSubnet(TestSubnet): subnet = network_fakes.FakeSubnet.create_one_subnet( {'subnetpool_id': subnet_pool.id} ) - self.network_client.find_network = mock.Mock(return_value=subnet) - self.network_client.find_subnet_pool = mock.Mock( - return_value=subnet_pool - ) + self.network_client.find_network.return_value = subnet + self.network_client.find_subnet_pool.return_value = subnet_pool + arglist = [ '--subnet-pool', subnet_pool.name, @@ -1123,10 +1114,9 @@ class TestListSubnet(TestSubnet): subnet = network_fakes.FakeSubnet.create_one_subnet( {'subnetpool_id': subnet_pool.id} ) - self.network_client.find_network = mock.Mock(return_value=subnet) - self.network_client.find_subnet_pool = mock.Mock( - return_value=subnet_pool - ) + self.network_client.find_network.return_value = subnet + self.network_client.find_subnet_pool.return_value = subnet_pool + arglist = [ '--subnet-pool', subnet_pool.id, @@ -1182,9 +1172,9 @@ class TestSetSubnet(TestSubnet): def setUp(self): super().setUp() - self.network_client.update_subnet = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.update_subnet.return_value = None + self.network_client.set_tags.return_value = None + self.network_client.find_subnet.return_value = self._subnet self.cmd = subnet_v2.SetSubnet(self.app, None) def test_set_this(self): @@ -1263,7 +1253,7 @@ class TestSetSubnet(TestSubnet): 'service_types': ["network:router_gateway"], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--dns-nameserver', '10.0.0.2', @@ -1329,7 +1319,7 @@ class TestSetSubnet(TestSubnet): 'dns_nameservers': ["10.0.0.1"], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', @@ -1379,7 +1369,7 @@ class TestSetSubnet(TestSubnet): 'dns_nameservers': ['10.0.0.1'], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--no-host-route', '--no-allocation-pool', @@ -1448,8 +1438,8 @@ class TestSetSubnet(TestSubnet): 'segment_id': None, } ) - self.network_client.find_subnet = mock.Mock(return_value=_subnet) - self.network_client.find_segment = mock.Mock(return_value=_segment) + self.network_client.find_subnet.return_value = _subnet + self.network_client.find_segment.return_value = _segment arglist = ['--network-segment', _segment.id, _subnet.name] verifylist = [('network_segment', _segment.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1514,7 +1504,7 @@ class TestShowSubnet(TestSubnet): # Get the command object to test self.cmd = subnet_v2.ShowSubnet(self.app, None) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_subnet.return_value = self._subnet def test_show_no_options(self): arglist = [] @@ -1572,11 +1562,10 @@ class TestUnsetSubnet(TestSubnet): 'tags': ['green', 'red'], } ) - self.network_client.find_subnet = mock.Mock( - return_value=self._testsubnet - ) - self.network_client.update_subnet = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self._testsubnet + + self.network_client.update_subnet.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_v2.UnsetSubnet(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 40aa1e9..013550e 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -76,16 +75,15 @@ class TestCreateSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.create_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_subnet_pool.return_value = self._subnet_pool + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_pool.CreateSubnetPool(self.app, None) - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) self.projects_mock.get.return_value = self.project @@ -387,7 +385,7 @@ class TestDeleteSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.delete_subnet_pool = mock.Mock(return_value=None) + self.network_client.delete_subnet_pool.return_value = None self.network_client.find_subnet_pool = ( network_fakes.FakeSubnetPool.get_subnet_pools(self._subnet_pools) @@ -445,9 +443,7 @@ class TestDeleteSubnetPool(TestSubnetPool): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._subnet_pools[0], exceptions.CommandError] - self.network_client.find_subnet_pool = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_subnet_pool.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -514,9 +510,7 @@ class TestListSubnetPool(TestSubnetPool): # Get the command object to test self.cmd = subnet_pool.ListSubnetPool(self.app, None) - self.network_client.subnet_pools = mock.Mock( - return_value=self._subnet_pools - ) + self.network_client.subnet_pools.return_value = self._subnet_pools def test_subnet_pool_list_no_option(self): arglist = [] @@ -653,7 +647,7 @@ class TestListSubnetPool(TestSubnetPool): def test_subnet_pool_list_name(self): subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() - self.network_client.find_network = mock.Mock(return_value=subnet_pool) + self.network_client.find_network.return_value = subnet_pool arglist = [ '--name', subnet_pool.name, @@ -672,9 +666,8 @@ class TestListSubnetPool(TestSubnetPool): def test_subnet_pool_list_address_scope(self): addr_scope = network_fakes.create_one_address_scope() - self.network_client.find_address_scope = mock.Mock( - return_value=addr_scope - ) + self.network_client.find_address_scope.return_value = addr_scope + arglist = [ '--address-scope', addr_scope.id, @@ -734,15 +727,13 @@ class TestSetSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.update_subnet_pool = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_subnet_pool.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.find_subnet_pool.return_value = self._subnet_pool - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test @@ -1079,9 +1070,7 @@ class TestShowSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.find_subnet_pool.return_value = self._subnet_pool # Get the command object to test self.cmd = subnet_pool.ShowSubnetPool(self.app, None) @@ -1122,11 +1111,10 @@ class TestUnsetSubnetPool(TestSubnetPool): self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool( {'tags': ['green', 'red']} ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnetpool - ) - self.network_client.update_subnet_pool = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet_pool.return_value = self._subnetpool + + self.network_client.update_subnet_pool.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_pool.UnsetSubnetPool(self.app, None) diff --git a/openstackclient/tests/unit/volume/test_find_resource.py b/openstackclient/tests/unit/volume/test_find_resource.py index df087dd..614fa9a 100644 --- a/openstackclient/tests/unit/volume/test_find_resource.py +++ b/openstackclient/tests/unit/volume/test_find_resource.py @@ -21,15 +21,6 @@ from osc_lib import exceptions from osc_lib import utils from openstackclient.tests.unit import utils as test_utils -from openstackclient.volume import client # noqa - - -# Monkey patch for v1 cinderclient -# NOTE(dtroyer): Do here because openstackclient.volume.client -# doesn't do it until the client object is created now. -volumes.Volume.NAME_ATTR = 'display_name' -volume_snapshots.Snapshot.NAME_ATTR = 'display_name' - ID = '1after909' NAME = 'PhilSpector' @@ -42,14 +33,14 @@ class TestFindResourceVolumes(test_utils.TestCase): api.client = mock.Mock() api.client.get = mock.Mock() resp = mock.Mock() - body = {"volumes": [{"id": ID, 'display_name': NAME}]} + body = {"volumes": [{"id": ID, 'name': NAME}]} api.client.get.side_effect = [Exception("Not found"), (resp, body)] self.manager = volumes.VolumeManager(api) def test_find(self): result = utils.find_resource(self.manager, NAME) self.assertEqual(ID, result.id) - self.assertEqual(NAME, result.display_name) + self.assertEqual(NAME, result.name) def test_not_find(self): self.assertRaises( @@ -67,14 +58,14 @@ class TestFindResourceVolumeSnapshots(test_utils.TestCase): api.client = mock.Mock() api.client.get = mock.Mock() resp = mock.Mock() - body = {"snapshots": [{"id": ID, 'display_name': NAME}]} + body = {"snapshots": [{"id": ID, 'name': NAME}]} api.client.get.side_effect = [Exception("Not found"), (resp, body)] self.manager = volume_snapshots.SnapshotManager(api) def test_find(self): result = utils.find_resource(self.manager, NAME) self.assertEqual(ID, result.id) - self.assertEqual(NAME, result.display_name) + self.assertEqual(NAME, result.name) def test_not_find(self): self.assertRaises( diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py index dc62e5e..68021c8 100644 --- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py @@ -129,7 +129,10 @@ class TestConsistencyGroupAddVolume(TestConsistencyGroup): utils, 'find_resource', side_effect=find_mock_result ) as find_mock: result = self.cmd.take_action(parsed_args) - mock_error.assert_called_with("1 of 2 volumes failed to add.") + mock_error.assert_called_with( + '%(result)s of %(total)s volumes failed to add.', + {'result': 1, 'total': 2}, + ) self.assertIsNone(result) find_mock.assert_any_call( self.consistencygroups_mock, self._consistency_group.id @@ -602,7 +605,10 @@ class TestConsistencyGroupRemoveVolume(TestConsistencyGroup): utils, 'find_resource', side_effect=find_mock_result ) as find_mock: result = self.cmd.take_action(parsed_args) - mock_error.assert_called_with("1 of 2 volumes failed to remove.") + mock_error.assert_called_with( + '%(result)s of %(total)s volumes failed to remove.', + {'result': 1, 'total': 2}, + ) self.assertIsNone(result) find_mock.assert_any_call( self.consistencygroups_mock, self._consistency_group.id diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index b68020f..8a9f777 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -655,7 +655,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): arglist = [self.volumes[0].id] verifylist = [ ("force", False), - ("purge", False), + ("cascade", False), ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -674,7 +674,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): arglist = [v.id for v in self.volumes] verifylist = [ ('force', False), - ('purge', False), + ('cascade', False), ('volumes', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -701,7 +701,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): ] verifylist = [ ('force', False), - ('purge', False), + ('cascade', False), ('volumes', [self.volumes[0].id, 'unexist_volume']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -732,7 +732,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): ] verifylist = [ ('force', False), - ('purge', True), + ('cascade', True), ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -754,7 +754,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): ] verifylist = [ ('force', True), - ('purge', False), + ('cascade', False), ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1618,7 +1618,8 @@ class TestVolumeSet(TestVolume): result = self.cmd.take_action(parsed_args) self.volumes_mock.retype.assert_not_called() mock_warning.assert_called_with( - "'--retype-policy' option will not work without '--type' option" + "'%s' option will not work without '--type' option", + '--retype-policy', ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index 7d4fba9..e7bbb69 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -565,7 +565,9 @@ class TestBackupShow(volume_fakes.TestVolume): parsed_args = self.check_parser(self.cmd, arglist, verifylist) 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) diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 5fb35a0..3338ccf 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -37,6 +37,7 @@ class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', + 'backup_id', 'bootable', 'cluster_name', 'consistencygroup_id', @@ -78,6 +79,7 @@ class TestVolumeCreate(volume_fakes.TestVolume): self.datalist = ( self.volume.attachments, self.volume.availability_zone, + self.volume.backup_id, self.volume.is_bootable, self.volume.cluster_name, self.volume.consistency_group_id, @@ -910,7 +912,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): arglist = [self.volumes[0].id] verifylist = [ ("force", False), - ("purge", False), + ("cascade", False), ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -929,7 +931,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): arglist = [v.id for v in self.volumes] verifylist = [ ('force', False), - ('purge', False), + ('cascade', False), ('volumes', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -956,7 +958,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): ] verifylist = [ ('force', False), - ('purge', False), + ('cascade', False), ('volumes', [self.volumes[0].id, 'unexist_volume']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -987,7 +989,29 @@ class TestVolumeDelete(volume_fakes.TestVolume): ] verifylist = [ ('force', False), - ('purge', True), + ('cascade', True), + ('volumes', [self.volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=True, force=False + ) + + def test_volume_delete_with_cascade(self): + arglist = [ + '--cascade', + self.volumes[0].id, + ] + verifylist = [ + ('force', False), + ('cascade', True), ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1009,7 +1033,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): ] verifylist = [ ('force', True), - ('purge', False), + ('cascade', False), ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1029,7 +1053,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): verifylist = [ ("remote", True), ("force", False), - ("purge", False), + ("cascade", False), ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1050,7 +1074,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): verifylist = [ ('remote', True), ('force', False), - ('purge', False), + ('cascade', False), ('volumes', arglist[1:]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1075,7 +1099,6 @@ class TestVolumeDelete(volume_fakes.TestVolume): verifylist = [ ('remote', True), ('force', False), - ('purge', True), ('volumes', [self.volumes[0].id]), ] @@ -1084,7 +1107,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn( - "The --force and --purge options are not supported with the " + "The --force and --cascade options are not supported with the " "--remote parameter.", str(exc), ) @@ -1102,7 +1125,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): verifylist = [ ('remote', True), ('force', True), - ('purge', False), + ('cascade', False), ('volumes', [self.volumes[0].id]), ] @@ -1111,7 +1134,7 @@ class TestVolumeDelete(volume_fakes.TestVolume): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn( - "The --force and --purge options are not supported with the " + "The --force and --cascade options are not supported with the " "--remote parameter.", str(exc), ) @@ -1996,7 +2019,8 @@ class TestVolumeSet(volume_fakes.TestVolume): result = self.cmd.take_action(parsed_args) self.volumes_mock.retype.assert_not_called() mock_warning.assert_called_with( - "'--retype-policy' option will not work without '--type' option" + "'%s' option will not work without '--type' option", + '--retype-policy', ) self.assertIsNone(result) @@ -2011,6 +2035,7 @@ class TestVolumeShow(volume_fakes.TestVolume): self.columns = ( 'attachments', 'availability_zone', + 'backup_id', 'bootable', 'cluster_name', 'consistencygroup_id', @@ -2045,6 +2070,7 @@ class TestVolumeShow(volume_fakes.TestVolume): self.data = ( self.volume.attachments, self.volume.availability_zone, + self.volume.backup_id, self.volume.is_bootable, self.volume.cluster_name, self.volume.consistency_group_id, diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index 5be0985..86bde78 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -17,6 +17,7 @@ from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import snapshot as _snapshot from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project from openstack.test import fakes as sdk_fakes from osc_lib import exceptions @@ -381,6 +382,7 @@ class TestBackupList(volume_fakes.TestVolume): ("marker", None), ("limit", None), ('all_projects', False), + ("project", None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -395,11 +397,14 @@ class TestBackupList(volume_fakes.TestVolume): all_tenants=False, marker=None, limit=None, + project_id=None, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) def test_backup_list_with_options(self): + project = sdk_fakes.generate_fake_resource(_project.Project) + self.identity_sdk_client.find_project.return_value = project arglist = [ "--long", "--name", @@ -413,6 +418,8 @@ class TestBackupList(volume_fakes.TestVolume): "--all-projects", "--limit", "3", + "--project", + project.id, ] verifylist = [ ("long", True), @@ -422,6 +429,7 @@ class TestBackupList(volume_fakes.TestVolume): ("marker", self.backups[0].id), ('all_projects', True), ("limit", 3), + ("project", project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -440,6 +448,7 @@ class TestBackupList(volume_fakes.TestVolume): all_tenants=True, marker=self.backups[0].id, limit=3, + project_id=project.id, ) self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, list(data)) diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index d3a3406..dbef055 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -20,16 +20,14 @@ from osc_lib import utils from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '3' API_VERSION_OPTION = 'os_volume_api_version' -API_NAME = "volume" +API_NAME = 'volume' API_VERSIONS = { - "1": "cinderclient.v1.client.Client", - "2": "cinderclient.v2.client.Client", - "3": "cinderclient.v3.client.Client", + '2': 'cinderclient.v2.client.Client', + '3': 'cinderclient.v3.client.Client', } # Save the microversion if in use @@ -45,11 +43,6 @@ def make_client(instance): from cinderclient.v3 import volume_snapshots from cinderclient.v3 import volumes - # Check whether the available cinderclient supports v1 or v2 - try: - from cinderclient.v1 import services # noqa - except Exception: - del API_VERSIONS['1'] try: from cinderclient.v2 import services # noqa except Exception: @@ -127,21 +120,18 @@ def check_api_version(check_version): global _volume_api_version - # Copy some logic from novaclient 3.3.0 for basic version detection - # NOTE(dtroyer): This is only enough to resume operations using API - # version 3.0 or any valid version supplied by the user. _volume_api_version = api_versions.get_api_version(check_version) # Bypass X.latest format microversion if not _volume_api_version.is_latest(): - if _volume_api_version > api_versions.APIVersion("3.0"): + if _volume_api_version > api_versions.APIVersion('3.0'): if not _volume_api_version.matches( api_versions.MIN_VERSION, api_versions.MAX_VERSION, ): - msg = _("versions supported by client: %(min)s - %(max)s") % { - "min": api_versions.MIN_VERSION, - "max": api_versions.MAX_VERSION, + msg = _('versions supported by client: %(min)s - %(max)s') % { + 'min': api_versions.MIN_VERSION, + 'max': api_versions.MAX_VERSION, } raise exceptions.CommandError(msg) diff --git a/openstackclient/volume/v2/backup_record.py b/openstackclient/volume/v2/backup_record.py index 98f7c71..93492f8 100644 --- a/openstackclient/volume/v2/backup_record.py +++ b/openstackclient/volume/v2/backup_record.py @@ -16,9 +16,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py index 347724f..31959c7 100644 --- a/openstackclient/volume/v2/consistency_group.py +++ b/openstackclient/volume/v2/consistency_group.py @@ -18,10 +18,10 @@ import argparse import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -38,8 +38,8 @@ def _find_volumes(parsed_args_volumes, volume_client): except Exception as e: result += 1 LOG.error( - _("Failed to find volume with name or ID '%(volume)s':%(e)s") - % {'volume': volume, 'e': e} + _("Failed to find volume with name or ID '%(volume)s':%(e)s"), + {'volume': volume, 'e': e}, ) return result, uuid @@ -73,8 +73,8 @@ class AddVolumeToConsistencyGroup(command.Command): if result > 0: total = len(parsed_args.volumes) LOG.error( - _("%(result)s of %(total)s volumes failed to add.") - % {'result': result, 'total': total} + _("%(result)s of %(total)s volumes failed to add."), + {'result': result, 'total': total}, ) if add_uuid: @@ -226,8 +226,8 @@ class DeleteConsistencyGroup(command.Command): _( "Failed to delete consistency group with " "name or ID '%(consistency_group)s':%(e)s" - ) - % {'consistency_group': i, 'e': e} + ), + {'consistency_group': i, 'e': e}, ) if result > 0: @@ -317,8 +317,8 @@ class RemoveVolumeFromConsistencyGroup(command.Command): if result > 0: total = len(parsed_args.volumes) LOG.error( - _("%(result)s of %(total)s volumes failed to remove.") - % {'result': result, 'total': total} + _("%(result)s of %(total)s volumes failed to remove."), + {'result': result, 'total': total}, ) if remove_uuid: diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py index 3c582d6..4adf4a2 100644 --- a/openstackclient/volume/v2/consistency_group_snapshot.py +++ b/openstackclient/volume/v2/consistency_group_snapshot.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -101,8 +101,8 @@ class DeleteConsistencyGroupSnapshot(command.Command): _( "Failed to delete consistency group snapshot " "with name or ID '%(snapshot)s': %(e)s" - ) - % {'snapshot': snapshot, 'e': e} + ), + {'snapshot': snapshot, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index 72706a7..6dcc23f 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -19,10 +19,10 @@ import logging from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -146,8 +146,8 @@ class DeleteQos(command.Command): _( "Failed to delete QoS specification with " "name or ID '%(qos)s': %(e)s" - ) - % {'qos': i, 'e': e} + ), + {'qos': i, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index 48cdba7..7777e7e 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -14,10 +14,10 @@ """Service action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index fa76d77..ef4ff05 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -25,11 +25,11 @@ from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from openstackclient.api import volume_v2 +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -61,7 +61,7 @@ class KeyValueHintAction(argparse.Action): ) -class AttachmentsColumn(cliff_columns.FormattableColumn): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[ty.Any]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the @@ -390,12 +390,19 @@ class DeleteVolume(command.Command): ), ) group.add_argument( - "--purge", + "--cascade", action="store_true", help=_( "Remove any snapshots along with volume(s) (defaults to False)" ), ) + group.add_argument( + # now called "cascade", accept old arg for compatibility + "--purge", + action="store_true", + help=argparse.SUPPRESS, + dest='cascade', + ) return parser def take_action(self, parsed_args): @@ -410,7 +417,7 @@ class DeleteVolume(command.Command): volume_client.delete_volume( volume_obj.id, force=parsed_args.force, - cascade=parsed_args.purge, + cascade=parsed_args.cascade, ) except Exception as e: result += 1 @@ -910,12 +917,12 @@ class SetVolume(command.Command): elif policy: # If the "--migration-policy" is specified without "--type" LOG.warning( - _("'%s' option will not work without '--type' option") - % ( + _("'%s' option will not work without '--type' option"), + ( '--migration-policy' if parsed_args.migration_policy else '--retype-policy' - ) + ), ) kwargs = {} diff --git a/openstackclient/volume/v2/volume_backend.py b/openstackclient/volume/v2/volume_backend.py index 2fbbed6..e51e37b 100644 --- a/openstackclient/volume/v2/volume_backend.py +++ b/openstackclient/volume/v2/volume_backend.py @@ -15,9 +15,9 @@ """Storage backend action implementations""" from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 55cde32..7f36652 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -18,17 +18,17 @@ import functools import logging from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -175,8 +175,8 @@ class DeleteVolumeBackup(command.Command): _( "Failed to delete backup with " "name or ID '%(backup)s': %(e)s" - ) - % {'backup': backup, 'e': e} + ), + {'backup': backup, 'e': e}, ) if result > 0: @@ -453,7 +453,9 @@ class ShowVolumeBackup(command.ShowOne): def take_action(self, parsed_args): 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, ...] = ( "availability_zone", "container", diff --git a/openstackclient/volume/v2/volume_host.py b/openstackclient/volume/v2/volume_host.py index 2b0df0a..44fd58a 100644 --- a/openstackclient/volume/v2/volume_host.py +++ b/openstackclient/volume/v2/volume_host.py @@ -14,8 +14,7 @@ """Volume v2 host action implementations""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index fb77461..9533a1a 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -22,10 +22,10 @@ from cliff import columns as cliff_columns from openstack.block_storage.v2 import snapshot as _snapshot from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -34,7 +34,7 @@ from openstackclient.identity import common as identity_common LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -228,8 +228,8 @@ class DeleteVolumeSnapshot(command.Command): _( "Failed to delete snapshot with " "name or ID '%(snapshot)s': %(e)s" - ) - % {'snapshot': snapshot, 'e': e} + ), + {'snapshot': snapshot, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index 3bd91d0..2f1bee8 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -128,8 +128,8 @@ class DeleteTransferRequest(command.Command): _( "Failed to delete volume transfer request " "with name or ID '%(transfer)s': %(e)s" - ) - % {'transfer': t, 'e': e} + ), + {'transfer': t, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 9e2ac20..9c80461 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -16,14 +16,15 @@ import functools import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -31,7 +32,7 @@ from openstackclient.identity import common as identity_common LOG = logging.getLogger(__name__) -class EncryptionInfoColumn(cliff_columns.FormattableColumn): +class EncryptionInfoColumn(cliff_columns.FormattableColumn[ty.Any]): """Formattable column for encryption info column. Unlike the parent FormattableColumn class, the initializer of the @@ -171,7 +172,8 @@ class CreateVolumeType(command.ShowOne): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled=<is> True') " + "(this is an alias for " + "'--property replication_enabled=<is> True') " "(requires driver support)" ), ) @@ -181,7 +183,8 @@ class CreateVolumeType(command.ShowOne): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:<az>') " + "(this is an alias for " + "'--property RESKEY:availability_zones:<az>') " "(repeat option to set multiple availability zones)" ), ) @@ -274,7 +277,7 @@ class CreateVolumeType(command.ShowOne): msg = _( "Failed to add project %(project)s access to type: %(e)s" ) - LOG.error(msg % {'project': parsed_args.project, 'e': e}) + LOG.error(msg, {'project': parsed_args.project, 'e': e}) properties = {} if parsed_args.properties: @@ -355,8 +358,8 @@ class DeleteVolumeType(command.Command): _( "Failed to delete volume type with " "name or ID '%(volume_type)s': %(e)s" - ) - % {'volume_type': volume_type, 'e': e} + ), + {'volume_type': volume_type, 'e': e}, ) if result > 0: @@ -463,7 +466,7 @@ class ListVolumeType(command.Lister): _EncryptionInfoColumn = functools.partial( EncryptionInfoColumn, encryption_data=encryption ) - formatters['id'] = _EncryptionInfoColumn + formatters['id'] = _EncryptionInfoColumn # type: ignore return ( column_headers, @@ -534,7 +537,8 @@ class SetVolumeType(command.Command): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled=<is> True') " + "(this is an alias for " + "'--property replication_enabled=<is> True') " "(requires driver support)" ), ) @@ -544,7 +548,8 @@ class SetVolumeType(command.Command): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:<az>') " + "(this is an alias for " + "'--property RESKEY:availability_zones:<az>') " "(repeat option to set multiple availability zones)" ), ) @@ -758,7 +763,7 @@ class ShowVolumeType(command.ShowOne): 'Failed to get access project list for volume type ' '%(type)s: %(e)s' ) - LOG.error(msg % {'type': volume_type.id, 'e': e}) + LOG.error(msg, {'type': volume_type.id, 'e': e}) volume_type._info.update({'access_project_ids': access_project_ids}) if parsed_args.encryption_type: # show encryption type information for this volume type diff --git a/openstackclient/volume/v3/block_storage_cleanup.py b/openstackclient/volume/v3/block_storage_cleanup.py index 361440c..5208504 100644 --- a/openstackclient/volume/v3/block_storage_cleanup.py +++ b/openstackclient/volume/v3/block_storage_cleanup.py @@ -11,9 +11,9 @@ # under the License. from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_cluster.py b/openstackclient/volume/v3/block_storage_cluster.py index 8cf01b2..d99ec52 100644 --- a/openstackclient/volume/v3/block_storage_cluster.py +++ b/openstackclient/volume/v3/block_storage_cluster.py @@ -11,10 +11,10 @@ # under the License. from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_log_level.py b/openstackclient/volume/v3/block_storage_log_level.py index 1f8d5b3..2e2fdc5 100644 --- a/openstackclient/volume/v3/block_storage_log_level.py +++ b/openstackclient/volume/v3/block_storage_log_level.py @@ -15,9 +15,9 @@ """Block Storage Service action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_manage.py b/openstackclient/volume/v3/block_storage_manage.py index 17c699b..7875638 100644 --- a/openstackclient/volume/v3/block_storage_manage.py +++ b/openstackclient/volume/v3/block_storage_manage.py @@ -16,10 +16,10 @@ import argparse from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py index 494d9cb..fc56438 100644 --- a/openstackclient/volume/v3/block_storage_resource_filter.py +++ b/openstackclient/volume/v3/block_storage_resource_filter.py @@ -14,10 +14,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index 0f14e47..eecd8e0 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -15,10 +15,10 @@ """Service action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 54dd5c7..4284600 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -26,11 +26,11 @@ from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from openstackclient.api import volume_v3 +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -62,7 +62,7 @@ class KeyValueHintAction(argparse.Action): ) -class AttachmentsColumn(cliff_columns.FormattableColumn): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[ty.Any]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the @@ -515,12 +515,19 @@ class DeleteVolume(command.Command): ), ) group.add_argument( - "--purge", + "--cascade", action="store_true", help=_( "Remove any snapshots along with volume(s) (defaults to False)" ), ) + group.add_argument( + # now called "cascade", accept old arg for compatibility + "--purge", + action="store_true", + help=argparse.SUPPRESS, + dest='cascade', + ) parser.add_argument( '--remote', action='store_true', @@ -532,9 +539,9 @@ class DeleteVolume(command.Command): volume_client = self.app.client_manager.sdk_connection.volume result = 0 - if parsed_args.remote and (parsed_args.force or parsed_args.purge): + if parsed_args.remote and (parsed_args.force or parsed_args.cascade): msg = _( - "The --force and --purge options are not " + "The --force and --cascade options are not " "supported with the --remote parameter." ) raise exceptions.CommandError(msg) @@ -550,7 +557,7 @@ class DeleteVolume(command.Command): volume_client.delete_volume( volume_obj.id, force=parsed_args.force, - cascade=parsed_args.purge, + cascade=parsed_args.cascade, ) except Exception as e: result += 1 @@ -1071,12 +1078,12 @@ class SetVolume(command.Command): elif policy: # If the "--migration-policy" is specified without "--type" LOG.warning( - _("'%s' option will not work without '--type' option") - % ( + _("'%s' option will not work without '--type' option"), + ( '--migration-policy' if parsed_args.migration_policy else '--retype-policy' - ) + ), ) kwargs = {} diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index 5773a12..3201da3 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -11,13 +11,14 @@ # under the License. import logging +import typing as ty from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ @@ -56,12 +57,12 @@ def _format_attachment(attachment): # VolumeAttachmentManager.create returns a dict while everything else # returns a VolumeAttachment object if isinstance(attachment, dict): - data = [] + data: tuple[ty.Any, ...] = () for column in columns: if column == 'connection_info': - data.append(format_columns.DictColumn(attachment[column])) + data += (format_columns.DictColumn(attachment[column]),) continue - data.append(attachment[column]) + data += (attachment[column],) else: data = utils.get_item_properties( attachment, @@ -458,7 +459,7 @@ class ListVolumeAttachment(command.Lister): } # Update search option with `filters` # if AppendFilters.filters: - # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) + # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) # noqa: E501 # TODO(stephenfin): Implement sorting attachments = volume_client.attachments( diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 7df46f8..2ab24f3 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -21,17 +21,17 @@ import logging from cliff import columns as cliff_columns from openstack import utils as sdk_utils from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -218,8 +218,8 @@ class DeleteVolumeBackup(command.Command): _( "Failed to delete backup with " "name or ID '%(backup)s': %(e)s" - ) - % {'backup': backup, 'e': e} + ), + {'backup': backup, 'e': e}, ) if result > 0: @@ -237,6 +237,11 @@ class ListVolumeBackup(command.Lister): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( + '--project', + metavar='<project>', + help=_('Filter results by project (name or ID) (admin only)'), + ) + parser.add_argument( "--long", action="store_true", default=False, @@ -296,6 +301,7 @@ class ListVolumeBackup(command.Lister): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume + identity_client = self.app.client_manager.sdk_connection.identity columns: tuple[str, ...] = ( 'id', @@ -332,6 +338,14 @@ class ListVolumeBackup(command.Lister): VolumeIdColumn, volume_cache=volume_cache ) + all_tenants = parsed_args.all_projects + project_id = None + if parsed_args.project: + all_tenants = True + project_id = identity_client.find_project( + parsed_args.project, ignore_missing=False + ).id + filter_volume_id = None if parsed_args.volume: try: @@ -360,9 +374,10 @@ class ListVolumeBackup(command.Lister): name=parsed_args.name, status=parsed_args.status, volume_id=filter_volume_id, - all_tenants=parsed_args.all_projects, + all_tenants=all_tenants, marker=marker_backup_id, limit=parsed_args.limit, + project_id=project_id, ) return ( diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index 8433e57..1810fee 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -13,10 +13,10 @@ import argparse from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import envvars from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py index 5a760ac..530b7d5 100644 --- a/openstackclient/volume/v3/volume_group_snapshot.py +++ b/openstackclient/volume/v3/volume_group_snapshot.py @@ -13,10 +13,10 @@ import logging from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import envvars from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_group_type.py b/openstackclient/volume/v3/volume_group_type.py index f1ef617..bdedd25 100644 --- a/openstackclient/volume/v3/volume_group_type.py +++ b/openstackclient/volume/v3/volume_group_type.py @@ -15,10 +15,10 @@ import logging from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/volume/v3/volume_message.py b/openstackclient/volume/v3/volume_message.py index 0fc724a..b39c579 100644 --- a/openstackclient/volume/v3/volume_message.py +++ b/openstackclient/volume/v3/volume_message.py @@ -17,10 +17,10 @@ import logging as LOG from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 9cbc198..6eddb5a 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -22,10 +22,10 @@ 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 from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -33,7 +33,7 @@ from openstackclient.identity import common as identity_common LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the @@ -246,8 +246,8 @@ class DeleteVolumeSnapshot(command.Command): _( "Failed to delete snapshot with " "name or ID '%(snapshot)s': %(e)s" - ) - % {'snapshot': snapshot, 'e': e} + ), + {'snapshot': snapshot, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v3/volume_transfer_request.py b/openstackclient/volume/v3/volume_transfer_request.py index b7add12..42fd37c 100644 --- a/openstackclient/volume/v3/volume_transfer_request.py +++ b/openstackclient/volume/v3/volume_transfer_request.py @@ -17,10 +17,10 @@ import logging from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ @@ -163,8 +163,8 @@ class DeleteTransferRequest(command.Command): _( "Failed to delete volume transfer request " "with name or ID '%(transfer)s': %(e)s" - ) - % {'transfer': t, 'e': e} + ), + {'transfer': t, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index b5b328c..ba69252 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -10,21 +10,21 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Volume v3 Type action implementations""" import functools import logging +import typing as ty from cinderclient import api_versions from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -32,7 +32,7 @@ from openstackclient.identity import common as identity_common LOG = logging.getLogger(__name__) -class EncryptionInfoColumn(cliff_columns.FormattableColumn): +class EncryptionInfoColumn(cliff_columns.FormattableColumn[ty.Any]): """Formattable column for encryption info column. Unlike the parent FormattableColumn class, the initializer of the @@ -172,7 +172,8 @@ class CreateVolumeType(command.ShowOne): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled=<is> True') " + "(this is an alias for " + "'--property replication_enabled=<is> True') " "(requires driver support)" ), ) @@ -182,7 +183,8 @@ class CreateVolumeType(command.ShowOne): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:<az>') " + "(this is an alias for " + "'--property RESKEY:availability_zones:<az>') " "(repeat option to set multiple availability zones)" ), ) @@ -275,7 +277,7 @@ class CreateVolumeType(command.ShowOne): msg = _( "Failed to add project %(project)s access to type: %(e)s" ) - LOG.error(msg % {'project': parsed_args.project, 'e': e}) + LOG.error(msg, {'project': parsed_args.project, 'e': e}) properties = {} if parsed_args.properties: @@ -356,8 +358,8 @@ class DeleteVolumeType(command.Command): _( "Failed to delete volume type with " "name or ID '%(volume_type)s': %(e)s" - ) - % {'volume_type': volume_type, 'e': e} + ), + {'volume_type': volume_type, 'e': e}, ) if result > 0: @@ -447,7 +449,8 @@ class ListVolumeType(command.Lister): default=False, help=_( "List only volume types with replication enabled " - "(this is an alias for '--property replication_enabled=<is> True') " + "(this is an alias for " + "'--property replication_enabled=<is> True') " "(supported by --os-volume-api-version 3.52 or above)" ), ) @@ -457,7 +460,8 @@ class ListVolumeType(command.Lister): dest='availability_zones', help=_( "List only volume types with this availability configured " - "(this is an alias for '--property RESKEY:availability_zones:<az>') " + "(this is an alias for " + "'--property RESKEY:availability_zones:<az>') " "(repeat option to filter on multiple availability zones)" ), ) @@ -545,7 +549,7 @@ class ListVolumeType(command.Lister): _EncryptionInfoColumn = functools.partial( EncryptionInfoColumn, encryption_data=encryption ) - formatters['id'] = _EncryptionInfoColumn + formatters['id'] = _EncryptionInfoColumn # type: ignore return ( column_headers, @@ -616,7 +620,8 @@ class SetVolumeType(command.Command): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled=<is> True') " + "(this is an alias for " + "'--property replication_enabled=<is> True') " "(requires driver support)" ), ) @@ -626,7 +631,8 @@ class SetVolumeType(command.Command): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:<az>') " + "(this is an alias for " + "'--property RESKEY:availability_zones:<az>') " "(repeat option to set multiple availability zones)" ), ) @@ -840,7 +846,7 @@ class ShowVolumeType(command.ShowOne): 'Failed to get access project list for volume type ' '%(type)s: %(e)s' ) - LOG.error(msg % {'type': volume_type.id, 'e': e}) + LOG.error(msg, {'type': volume_type.id, 'e': e}) volume_type._info.update({'access_project_ids': access_project_ids}) if parsed_args.encryption_type: # show encryption type information for this volume type diff --git a/pyproject.toml b/pyproject.toml index 74f93cc..b23877e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -540,6 +540,23 @@ subnet_pool_set = "openstackclient.network.v2.subnet_pool:SetSubnetPool" subnet_pool_show = "openstackclient.network.v2.subnet_pool:ShowSubnetPool" subnet_pool_unset = "openstackclient.network.v2.subnet_pool:UnsetSubnetPool" +# Tap-as-a-Service +tap_flow_create = "openstackclient.network.v2.taas.tap_flow:CreateTapFlow" +tap_flow_delete = "openstackclient.network.v2.taas.tap_flow:DeleteTapFlow" +tap_flow_list = "openstackclient.network.v2.taas.tap_flow:ListTapFlow" +tap_flow_show = "openstackclient.network.v2.taas.tap_flow:ShowTapFlow" +tap_flow_update = "openstackclient.network.v2.taas.tap_flow:UpdateTapFlow" +tap_mirror_create = "openstackclient.network.v2.taas.tap_mirror:CreateTapMirror" +tap_mirror_delete = "openstackclient.network.v2.taas.tap_mirror:DeleteTapMirror" +tap_mirror_list = "openstackclient.network.v2.taas.tap_mirror:ListTapMirror" +tap_mirror_show = "openstackclient.network.v2.taas.tap_mirror:ShowTapMirror" +tap_mirror_update = "openstackclient.network.v2.taas.tap_mirror:UpdateTapMirror" +tap_service_create = "openstackclient.network.v2.taas.tap_service:CreateTapService" +tap_service_delete = "openstackclient.network.v2.taas.tap_service:DeleteTapService" +tap_service_list = "openstackclient.network.v2.taas.tap_service:ListTapService" +tap_service_show = "openstackclient.network.v2.taas.tap_service:ShowTapService" +tap_service_update = "openstackclient.network.v2.taas.tap_service:UpdateTapService" + [project.entry-points."openstack.object_store.v1"] object_store_account_set = "openstackclient.object.v1.account:SetAccount" object_store_account_show = "openstackclient.object.v1.account:ShowAccount" @@ -727,14 +744,7 @@ follow_imports = "normal" incremental = true check_untyped_defs = true warn_unused_ignores = true -# keep this in-sync with 'mypy.exclude' in '.pre-commit-config.yaml' -exclude = ''' -(?x)( - doc - | examples - | releasenotes - ) -''' +exclude = '(?x)(doc | examples | hacking | releasenotes)' [[tool.mypy.overrides]] module = ["openstackclient.tests.unit.*"] @@ -748,7 +758,7 @@ quote-style = "preserve" docstring-code-format = true [tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "S", "U"] +select = ["E4", "E5", "E7", "E9", "F", "G", "S", "UP"] [tool.ruff.lint.per-file-ignores] -"openstackclient/tests/*" = ["S"] +"openstackclient/tests/*" = ["E501", "S"] diff --git a/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml b/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml new file mode 100644 index 0000000..06bc7a4 --- /dev/null +++ b/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--project`` option to ``volume backup list`` command, + to allow filtering for projects when listing volume backups. diff --git a/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml b/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml new file mode 100644 index 0000000..87fe689 --- /dev/null +++ b/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Running ``openstack application credential show`` on + a non-existent application credential does not + raise an exception. + + [Bug `2126565 <https://bugs.launchpad.net/python-openstackclient/+bug/2126565>`_] diff --git a/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml b/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml new file mode 100644 index 0000000..818e29a --- /dev/null +++ b/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix ``openstack quota show --usage`` to correctly display resource usage + and reservation. diff --git a/releasenotes/notes/bug-2138903-f75c7348f22db195.yaml b/releasenotes/notes/bug-2138903-f75c7348f22db195.yaml new file mode 100644 index 0000000..2492421 --- /dev/null +++ b/releasenotes/notes/bug-2138903-f75c7348f22db195.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The ``--all-stores`` of the ``image import`` command is now correctly + treated as a boolean flag. diff --git a/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml b/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml new file mode 100644 index 0000000..4cd755b --- /dev/null +++ b/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + Fix a bug where using ``openstack image set --project <project> + --accept <image>`` incorrectly changed the image owner to the specified + project instead of only updating the member status. The ``--project`` + parameter when used with ``--accept``, ``--reject``, or ``--pending`` + should only identify which member's status to update, not change the + image ownership. + [Bug `2136795 <https://bugs.launchpad.net/python-openstackclient/+bug/2136795>`_] diff --git a/releasenotes/notes/fix-resize-server-args-required-2e9013bcbf207f6a.yaml b/releasenotes/notes/fix-resize-server-args-required-2e9013bcbf207f6a.yaml new file mode 100644 index 0000000..e1faf7e --- /dev/null +++ b/releasenotes/notes/fix-resize-server-args-required-2e9013bcbf207f6a.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The ``openstack server resize`` command now requires the ``--flavor`` + option or one of the deprecated ``--confirm`` or ``--revert`` options + to be provided. + Previously, the command would silently exit successfully without + performing any action if no option was provided. diff --git a/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml b/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml new file mode 100644 index 0000000..7cd6acb --- /dev/null +++ b/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + [Bug `2136148 <https://bugs.launchpad.net/bugs/2136148>`_] Keystone allows + users to be created with no password but no value should be submitted for + the password instead of a ``null`` value. diff --git a/releasenotes/notes/limits-project-domain-option-84bfbb0e30e21b73.yaml b/releasenotes/notes/limits-project-domain-option-84bfbb0e30e21b73.yaml new file mode 100644 index 0000000..ab94680 --- /dev/null +++ b/releasenotes/notes/limits-project-domain-option-84bfbb0e30e21b73.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--project-domain`` option for the ``limit list`` and ``limit create`` + commands. diff --git a/releasenotes/notes/migrate-federation-protocol-to-sdk-43dc2b50fb277da6.yaml b/releasenotes/notes/migrate-federation-protocol-to-sdk-43dc2b50fb277da6.yaml new file mode 100644 index 0000000..ea3808a --- /dev/null +++ b/releasenotes/notes/migrate-federation-protocol-to-sdk-43dc2b50fb277da6.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Migrate ``federation protocol`` commands from keystoneclient to SDK. +upgrade: + - | + Filtering in ``federation protocol`` commands is now case sensitive. diff --git a/releasenotes/notes/migrate-limit-to-sdk-378037ec2b79e302.yaml b/releasenotes/notes/migrate-limit-to-sdk-378037ec2b79e302.yaml new file mode 100644 index 0000000..d4a5732 --- /dev/null +++ b/releasenotes/notes/migrate-limit-to-sdk-378037ec2b79e302.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Migrate ``limit`` commands from keystoneclient to SDK. +upgrade: + - | + Filtering in ``limit`` commands is now case sensitive. + - | + Specifying ``--region None`` is no longer supported for ``limit`` commands. diff --git a/releasenotes/notes/migrate-project-to-sdk-9201efd2804371de.yaml b/releasenotes/notes/migrate-project-to-sdk-9201efd2804371de.yaml new file mode 100644 index 0000000..90c6031 --- /dev/null +++ b/releasenotes/notes/migrate-project-to-sdk-9201efd2804371de.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Migrate ``project`` commands from keystoneclient to SDK. +upgrade: + - | + Filtering in ``project`` commands is now case sensitive. diff --git a/releasenotes/notes/migrate-registered-limit-to-sdk-36b6451e3a799a43.yaml b/releasenotes/notes/migrate-registered-limit-to-sdk-36b6451e3a799a43.yaml new file mode 100644 index 0000000..77f404a --- /dev/null +++ b/releasenotes/notes/migrate-registered-limit-to-sdk-36b6451e3a799a43.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Migrate ``registered limit`` commands from keystoneclient to SDK. +upgrade: + - | + Filtering in ``registered limit`` commands is now case sensitive. + - | + Specifying ``--region None`` is no longer supported for ``registered limit`` + commands. diff --git a/releasenotes/notes/use-project-domain-for-parent-cb29ee3f5adeb647.yaml b/releasenotes/notes/use-project-domain-for-parent-cb29ee3f5adeb647.yaml new file mode 100644 index 0000000..acbf2ba --- /dev/null +++ b/releasenotes/notes/use-project-domain-for-parent-cb29ee3f5adeb647.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + When creating or listing projects, parent project will now be searched from + the same domain as the child project. diff --git a/releasenotes/notes/volume-delete-cascade-384003efc8896096.yaml b/releasenotes/notes/volume-delete-cascade-384003efc8896096.yaml new file mode 100644 index 0000000..679964f --- /dev/null +++ b/releasenotes/notes/volume-delete-cascade-384003efc8896096.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The ``--purge`` argument to the ``volume delete`` command has been renamed + to ``--cascade`` to better match the Cinder API and the meaning of what + this argument does. An alias is provided for backwards compatibility. diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst index 4977a4f..6896656 100644 --- a/releasenotes/source/2024.1.rst +++ b/releasenotes/source/2024.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2024.1 + :branch: unmaintained/2024.1 diff --git a/releasenotes/source/2025.2.rst b/releasenotes/source/2025.2.rst new file mode 100644 index 0000000..4dae18d --- /dev/null +++ b/releasenotes/source/2025.2.rst @@ -0,0 +1,6 @@ +=========================== +2025.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index a9c0579..2b28cfb 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + 2025.2 2025.1 2024.2 2024.1 diff --git a/requirements.txt b/requirements.txt index 9a9c79a..fc31d78 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 -cliff>=4.8.0 # Apache-2.0 +cliff>=4.13.0 # Apache-2.0 iso8601>=0.1.11 # MIT openstacksdk>=4.6.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 @@ -4,118 +4,128 @@ envlist = py3,pep8 [testenv] description = - Run unit tests. + Run unit tests. usedevelop = true setenv = - OS_STDOUT_CAPTURE=1 - OS_STDERR_CAPTURE=1 - OS_TEST_TIMEOUT=60 + OS_STDOUT_CAPTURE=1 + OS_STDERR_CAPTURE=1 + OS_TEST_TIMEOUT=60 deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/test-requirements.txt - -r{toxinidir}/requirements.txt + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/test-requirements.txt + -r{toxinidir}/requirements.txt commands = - stestr run {posargs} + stestr run {posargs} [testenv:pep8] description = - Run style checks. -skip_install = true + Run style checks. deps = - pre-commit + pre-commit + {[testenv:mypy]deps} commands = - pre-commit run --all-files --show-diff-on-failure + pre-commit run --all-files --show-diff-on-failure + {[testenv:mypy]commands} -[testenv:bandit] +[testenv:mypy] description = - Run bandit security checks. -skip_install = true + Run type checks. deps = - pre-commit + {[testenv]deps} + mypy + types-requests commands = - pre-commit run --all-files --show-diff-on-failure bandit + mypy --cache-dir="{envdir}/mypy_cache" {posargs:openstackclient} [testenv:unit-tips] commands = - python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff - python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth - python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib - python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk - python -m pip freeze - stestr run {posargs} + python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff + python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth + python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib + python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk + python -m pip freeze + stestr run {posargs} -[testenv:functional{,-tips,-py38,-py39,-py310,-py311,-py312}] +[testenv:functional{,-tips,-py310,-py311,-py312,-py313,-py314}] description = - Run functional tests. + Run functional tests. setenv = - OS_TEST_PATH=./openstackclient/tests/functional + OS_TEST_PATH=./openstackclient/tests/functional passenv = - OS_* + OS_* commands = - tips: python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff - tips: python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth1 - tips: python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib - tips: python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk - tips: python -m pip freeze - {[testenv]commands} + tips: python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff + tips: python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth1 + tips: python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib + tips: python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk + tips: python -m pip freeze + {[testenv]commands} [testenv:venv] description = - Run specified command in a virtual environment with all dependencies installed. + Run specified command in a virtual environment with all dependencies installed. deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/requirements.txt - -r{toxinidir}/doc/requirements.txt + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/requirements.txt + -r{toxinidir}/doc/requirements.txt commands = - {posargs} + {posargs} [testenv:cover] description = - Run unit tests and generate coverage report. + Run unit tests and generate coverage report. setenv = - {[testenv]setenv} - PYTHON=coverage run --source openstackclient --parallel-mode + {[testenv]setenv} + PYTHON=coverage run --source openstackclient --parallel-mode commands = - stestr run {posargs} - coverage combine - coverage html -d cover - coverage xml -o cover/coverage.xml + stestr run {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml [testenv:debug] description = - Run specified tests through oslo_debug_helper, which allows use of pdb. + Run specified tests through oslo_debug_helper, which allows use of pdb. passenv = - OS_* + OS_* commands = - oslo_debug_helper -t openstackclient/tests {posargs} + oslo_debug_helper -t openstackclient/tests {posargs} [testenv:docs] description = - Build documentation in HTML format. + Build documentation in HTML format. deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/doc/requirements.txt + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/doc/requirements.txt commands = - sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html - sphinx-build -a -E -W -d doc/build/doctrees -b man doc/source doc/build/man - # Validate redirects (must be done after the docs build - whereto doc/build/html/.htaccess doc/test/redirect-tests.txt + sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html + sphinx-build -a -E -W -d doc/build/doctrees -b man doc/source doc/build/man + # Validate redirects (must be done after the docs build + whereto doc/build/html/.htaccess doc/test/redirect-tests.txt [testenv:releasenotes] description = - Build release note documentation in HTML format. + Build release note documentation in HTML format. deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/doc/requirements.txt + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/doc/requirements.txt commands = - sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html + sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes -# We only enable the hacking (H) checks -select = H +# We only enable the hacking (H) and openstackclient (O) checks +select = H,O # H301 Black will put commas after imports that can't fit on one line ignore = H301 import-order-style = pep8 application_import_names = openstackclient + +[flake8:local-plugins] +extension = + O400 = checks:assert_no_oslo + O401 = checks:assert_no_duplicated_setup + O402 = checks:assert_use_of_client_aliases + O403 = checks:assert_find_ignore_missing_kwargs +paths = ./hacking |
