summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/utils.py84
-rw-r--r--openstackclient/common/clientmanager.py7
-rw-r--r--openstackclient/common/commandmanager.py59
-rw-r--r--openstackclient/compute/v2/aggregate.py2
-rw-r--r--openstackclient/compute/v2/server.py162
-rw-r--r--openstackclient/identity/v2_0/project.py2
-rw-r--r--openstackclient/identity/v3/endpoint_group.py6
-rw-r--r--openstackclient/image/v1/image.py2
-rw-r--r--openstackclient/image/v2/image.py2
-rw-r--r--openstackclient/shell.py2
-rw-r--r--openstackclient/tests/functional/compute/v2/test_aggregate.py148
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server.py78
-rw-r--r--openstackclient/tests/unit/api/test_utils.py115
-rw-r--r--openstackclient/tests/unit/common/test_commandmanager.py107
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py447
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py62
-rw-r--r--openstackclient/tests/unit/identity/v3/test_endpoint_group.py495
-rw-r--r--openstackclient/tests/unit/image/v1/test_image.py2
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py2
19 files changed, 1295 insertions, 489 deletions
diff --git a/openstackclient/api/utils.py b/openstackclient/api/utils.py
deleted file mode 100644
index 6407cd44..00000000
--- a/openstackclient/api/utils.py
+++ /dev/null
@@ -1,84 +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.
-#
-
-"""API Utilities Library"""
-
-
-def simple_filter(
- data=None,
- attr=None,
- value=None,
- property_field=None,
-):
- """Filter a list of dicts
-
- :param list data:
- The list to be filtered. The list is modified in-place and will
- be changed if any filtering occurs.
- :param string attr:
- The name of the attribute to filter. If attr does not exist no
- match will succeed and no rows will be returned. If attr is
- None no filtering will be performed and all rows will be returned.
- :param string value:
- The value to filter. None is considered to be a 'no filter' value.
- '' matches against a Python empty string.
- :param string property_field:
- The name of the data field containing a property dict to filter.
- If property_field is None, attr is a field name. If property_field
- is not None, attr is a property key name inside the named property
- field.
-
- :returns:
- Returns the filtered list
- :rtype list:
-
- This simple filter (one attribute, one exact-match value) searches a
- list of dicts to select items. It first searches the item dict for a
- matching ``attr`` then does an exact-match on the ``value``. If
- ``property_field`` is given, it will look inside that field (if it
- exists and is a dict) for a matching ``value``.
- """
-
- # Take the do-nothing case shortcut
- if not data or not attr or value is None:
- return data
-
- # NOTE:(dtroyer): This filter modifies the provided list in-place using
- # list.remove() so we need to start at the end so the loop pointer does
- # not skip any items after a deletion.
- for d in reversed(data):
- if attr in d:
- # Searching data fields
- search_value = d[attr]
- elif (property_field and property_field in d and
- isinstance(d[property_field], dict)):
- # Searching a properties field - do this separately because
- # we don't want to fail over to checking the fields if a
- # property name is given.
- if attr in d[property_field]:
- search_value = d[property_field][attr]
- else:
- search_value = None
- else:
- search_value = None
-
- # could do regex here someday...
- if not search_value or search_value != value:
- # remove from list
- try:
- data.remove(d)
- except ValueError:
- # it's already gone!
- pass
-
- return data
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index aa1045e4..c1118ad3 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -91,13 +91,6 @@ class ClientManager(clientmanager.ClientManager):
return super(ClientManager, self).setup_auth()
- @property
- def auth_ref(self):
- if not self._auth_required:
- return None
- else:
- return super(ClientManager, self).auth_ref
-
def _fallback_load_auth_plugin(self, e):
# NOTES(RuiChen): Hack to avoid auth plugins choking on data they don't
# expect, delete fake token and endpoint, then try to
diff --git a/openstackclient/common/commandmanager.py b/openstackclient/common/commandmanager.py
deleted file mode 100644
index c190e33e..00000000
--- a/openstackclient/common/commandmanager.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright 2012-2013 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-"""Modify cliff.CommandManager"""
-
-import pkg_resources
-
-import cliff.commandmanager
-
-
-class CommandManager(cliff.commandmanager.CommandManager):
- """Add additional functionality to cliff.CommandManager
-
- Load additional command groups after initialization
- Add _command_group() methods
- """
-
- def __init__(self, namespace, convert_underscores=True):
- self.group_list = []
- super(CommandManager, self).__init__(namespace, convert_underscores)
-
- def load_commands(self, namespace):
- self.group_list.append(namespace)
- return super(CommandManager, self).load_commands(namespace)
-
- def add_command_group(self, group=None):
- """Adds another group of command entrypoints"""
- if group:
- self.load_commands(group)
-
- def get_command_groups(self):
- """Returns a list of the loaded command groups"""
- return self.group_list
-
- def get_command_names(self, group=None):
- """Returns a list of commands loaded for the specified group"""
- group_list = []
- if group is not None:
- for ep in pkg_resources.iter_entry_points(group):
- cmd_name = (
- ep.name.replace('_', ' ')
- if self.convert_underscores
- else ep.name
- )
- group_list.append(cmd_name)
- return group_list
- return list(self.commands.keys())
diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py
index 7f9161a9..fa646478 100644
--- a/openstackclient/compute/v2/aggregate.py
+++ b/openstackclient/compute/v2/aggregate.py
@@ -101,6 +101,8 @@ class CreateAggregate(command.ShowOne):
parsed_args.property,
)._info)
+ # TODO(dtroyer): re-format metadata field to properites as
+ # in the set command
return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index cb9f8d43..3e1deed5 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -540,6 +540,12 @@ class CreateServer(command.ShowOne):
help=_('User data file to serve from the metadata server'),
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Set description for the server (supported by '
+ '--os-compute-api-version 2.19 or above)'),
+ )
+ parser.add_argument(
'--availability-zone',
metavar='<zone-name>',
help=_('Select an availability zone for the server'),
@@ -749,6 +755,12 @@ class CreateServer(command.ShowOne):
"exception": e}
)
+ if parsed_args.description:
+ if compute_client.api_version < api_versions.APIVersion("2.19"):
+ msg = _("Description is not supported for "
+ "--os-compute-api-version less than 2.19")
+ raise exceptions.CommandError(msg)
+
block_device_mapping_v2 = []
if volume:
block_device_mapping_v2 = [{'uuid': volume,
@@ -909,6 +921,9 @@ class CreateServer(command.ShowOne):
scheduler_hints=hints,
config_drive=config_drive)
+ if parsed_args.description:
+ boot_kwargs['description'] = parsed_args.description
+
LOG.debug('boot_args: %s', boot_args)
LOG.debug('boot_kwargs: %s', boot_kwargs)
@@ -1129,12 +1144,21 @@ class ListServer(command.Lister):
help=_('Only display deleted servers (Admin only).')
)
parser.add_argument(
+ '--changes-before',
+ metavar='<changes-before>',
+ default=None,
+ help=_("List only servers changed before a certain point of time. "
+ "The provided time should be an ISO 8061 formatted time "
+ "(e.g., 2016-03-05T06:27:59Z). "
+ "(Supported by API versions '2.66' - '2.latest')")
+ )
+ parser.add_argument(
'--changes-since',
metavar='<changes-since>',
default=None,
help=_("List only servers changed after a certain point of time."
- " The provided time should be an ISO 8061 formatted time."
- " ex 2016-03-04T06:27:59Z .")
+ " The provided time should be an ISO 8061 formatted time"
+ " (e.g., 2016-03-04T06:27:59Z).")
)
return parser
@@ -1188,10 +1212,24 @@ class ListServer(command.Lister):
'all_tenants': parsed_args.all_projects,
'user_id': user_id,
'deleted': parsed_args.deleted,
+ 'changes-before': parsed_args.changes_before,
'changes-since': parsed_args.changes_since,
}
LOG.debug('search options: %s', search_opts)
+ if search_opts['changes-before']:
+ if compute_client.api_version < api_versions.APIVersion('2.66'):
+ msg = _('--os-compute-api-version 2.66 or later is required')
+ raise exceptions.CommandError(msg)
+
+ try:
+ timeutils.parse_isotime(search_opts['changes-before'])
+ except ValueError:
+ raise exceptions.CommandError(
+ _('Invalid changes-before value: %s') %
+ search_opts['changes-before']
+ )
+
if search_opts['changes-since']:
try:
timeutils.parse_isotime(search_opts['changes-since'])
@@ -1407,9 +1445,38 @@ class MigrateServer(command.Command):
help=_('Server (name or ID)'),
)
parser.add_argument(
+ '--live-migration',
+ dest='live_migration',
+ action='store_true',
+ help=_('Live migrate the server. Use the ``--host`` option to '
+ 'specify a target host for the migration which will be '
+ 'validated by the scheduler.'),
+ )
+ # The --live and --host options are mutually exclusive ways of asking
+ # for a target host during a live migration.
+ host_group = parser.add_mutually_exclusive_group()
+ # TODO(mriedem): Remove --live in the next major version bump after
+ # the Train release.
+ host_group.add_argument(
'--live',
metavar='<hostname>',
- help=_('Target hostname'),
+ help=_('**Deprecated** This option is problematic in that it '
+ 'requires a host and prior to compute API version 2.30, '
+ 'specifying a host during live migration will bypass '
+ 'validation by the scheduler which could result in '
+ 'failures to actually migrate the server to the specified '
+ 'host or over-subscribe the host. Use the '
+ '``--live-migration`` option instead. If both this option '
+ 'and ``--live-migration`` are used, ``--live-migration`` '
+ 'takes priority.'),
+ )
+ # TODO(mriedem): Add support for --os-compute-api-version >= 2.56 where
+ # you can cold migrate to a specified target host.
+ host_group.add_argument(
+ '--host',
+ metavar='<hostname>',
+ help=_('Live migrate the server to the specified host. Requires '
+ '``--os-compute-api-version`` 2.30 or greater.'),
)
migration_group = parser.add_mutually_exclusive_group()
migration_group.add_argument(
@@ -1447,6 +1514,15 @@ class MigrateServer(command.Command):
)
return parser
+ def _log_warning_for_live(self, parsed_args):
+ if parsed_args.live:
+ # NOTE(mriedem): The --live option requires a host and if
+ # --os-compute-api-version is less than 2.30 it will forcefully
+ # bypass the scheduler which is dangerous.
+ self.log.warning(_(
+ 'The --live option has been deprecated. Please use the '
+ '--live-migration option instead.'))
+
def take_action(self, parsed_args):
def _show_progress(progress):
@@ -1460,19 +1536,45 @@ class MigrateServer(command.Command):
compute_client.servers,
parsed_args.server,
)
- if parsed_args.live:
+ # Check for live migration.
+ if parsed_args.live or parsed_args.live_migration:
+ # Always log a warning if --live is used.
+ self._log_warning_for_live(parsed_args)
kwargs = {
- 'host': parsed_args.live,
'block_migration': parsed_args.block_migration
}
+ # Prefer --live-migration over --live if both are specified.
+ if parsed_args.live_migration:
+ # Technically we could pass a non-None host with
+ # --os-compute-api-version < 2.30 but that is the same thing
+ # as the --live option bypassing the scheduler which we don't
+ # want to support, so if the user is using --live-migration
+ # and --host, we want to enforce that they are using version
+ # 2.30 or greater.
+ if (parsed_args.host and
+ compute_client.api_version <
+ api_versions.APIVersion('2.30')):
+ raise exceptions.CommandError(
+ '--os-compute-api-version 2.30 or greater is required '
+ 'when using --host')
+ # The host parameter is required in the API even if None.
+ kwargs['host'] = parsed_args.host
+ else:
+ kwargs['host'] = parsed_args.live
+
if compute_client.api_version < api_versions.APIVersion('2.25'):
kwargs['disk_over_commit'] = parsed_args.disk_overcommit
server.live_migrate(**kwargs)
else:
- if parsed_args.block_migration or parsed_args.disk_overcommit:
- raise exceptions.CommandError("--live must be specified if "
- "--block-migration or "
- "--disk-overcommit is specified")
+ if (parsed_args.block_migration or parsed_args.disk_overcommit or
+ parsed_args.host):
+ # TODO(mriedem): Allow --host for cold migration if
+ # --os-compute-api-version >= 2.56.
+ raise exceptions.CommandError(
+ "--live-migration must be specified if "
+ "--block-migration, --disk-overcommit or --host is "
+ "specified")
+
server.migrate()
if parsed_args.wait:
@@ -1601,6 +1703,12 @@ class RebuildServer(command.ShowOne):
'(repeat option to set multiple values)'),
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('New description for the server (supported by '
+ '--os-compute-api-version 2.19 or above'),
+ )
+ parser.add_argument(
'--wait',
action='store_true',
help=_('Wait for rebuild to complete'),
@@ -1644,6 +1752,12 @@ class RebuildServer(command.ShowOne):
kwargs = {}
if parsed_args.property:
kwargs['meta'] = parsed_args.property
+ if parsed_args.description:
+ if server.api_version < api_versions.APIVersion("2.19"):
+ msg = _("Description is not supported for "
+ "--os-compute-api-version less than 2.19")
+ raise exceptions.CommandError(msg)
+ kwargs['description'] = parsed_args.description
if parsed_args.key_name or parsed_args.key_unset:
if compute_client.api_version < api_versions.APIVersion('2.54'):
@@ -2065,6 +2179,12 @@ class SetServer(command.Command):
choices=['active', 'error'],
help=_('New server state (valid value: active, error)'),
)
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('New server description (supported by '
+ '--os-compute-api-version 2.19 or above)'),
+ )
return parser
def take_action(self, parsed_args):
@@ -2096,6 +2216,13 @@ class SetServer(command.Command):
msg = _("Passwords do not match, password unchanged")
raise exceptions.CommandError(msg)
+ if parsed_args.description:
+ if server.api_version < api_versions.APIVersion("2.19"):
+ msg = _("Description is not supported for "
+ "--os-compute-api-version less than 2.19")
+ raise exceptions.CommandError(msg)
+ server.update(description=parsed_args.description)
+
class ShelveServer(command.Command):
_description = _("Shelve server(s)")
@@ -2455,6 +2582,13 @@ class UnsetServer(command.Command):
help=_('Property key to remove from server '
'(repeat option to remove multiple values)'),
)
+ parser.add_argument(
+ '--description',
+ dest='description',
+ action='store_true',
+ help=_('Unset server description (supported by '
+ '--os-compute-api-version 2.19 or above)'),
+ )
return parser
def take_action(self, parsed_args):
@@ -2470,6 +2604,16 @@ class UnsetServer(command.Command):
parsed_args.property,
)
+ if parsed_args.description:
+ if compute_client.api_version < api_versions.APIVersion("2.19"):
+ msg = _("Description is not supported for "
+ "--os-compute-api-version less than 2.19")
+ raise exceptions.CommandError(msg)
+ compute_client.servers.update(
+ server,
+ description="",
+ )
+
class UnshelveServer(command.Command):
_description = _("Unshelve server(s)")
diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py
index 04d422ec..9bb5fc4d 100644
--- a/openstackclient/identity/v2_0/project.py
+++ b/openstackclient/identity/v2_0/project.py
@@ -289,7 +289,7 @@ class ShowProject(command.ShowOne):
# the API has and handle the extra top level properties.
reserved = ('name', 'id', 'enabled', 'description')
properties = {}
- for k, v in info.items():
+ for k, v in list(info.items()):
if k not in reserved:
# If a key is not in `reserved` it's a property, pop it
info.pop(k)
diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py
index e254973b..66bd164d 100644
--- a/openstackclient/identity/v3/endpoint_group.py
+++ b/openstackclient/identity/v3/endpoint_group.py
@@ -204,11 +204,11 @@ class ListEndpointGroup(command.Lister):
if endpointgroup:
# List projects associated to the endpoint group
- columns = ('ID', 'Name')
+ columns = ('ID', 'Name', 'Description')
data = client.endpoint_filter.list_projects_for_endpoint_group(
endpoint_group=endpointgroup.id)
elif project:
- columns = ('ID', 'Name')
+ columns = ('ID', 'Name', 'Description')
data = client.endpoint_filter.list_endpoint_groups_for_project(
project=project.id)
else:
@@ -251,7 +251,7 @@ class RemoveProjectFromEndpointGroup(command.Command):
parsed_args.project,
parsed_args.project_domain)
- client.endpoint_filter.delete_endpoint_group_to_project(
+ client.endpoint_filter.delete_endpoint_group_from_project(
endpoint_group=endpointgroup.id,
project=project.id)
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index 7ecaa3ef..64c4049c 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -22,12 +22,12 @@ import os
import sys
from glanceclient.common import utils as gc_utils
+from osc_lib.api import utils as api_utils
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import utils
import six
-from openstackclient.api import utils as api_utils
from openstackclient.i18n import _
if os.name == "nt":
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 3efde808..bdec99d7 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -21,13 +21,13 @@ import logging
from glanceclient.common import utils as gc_utils
from openstack.image import image_signer
+from osc_lib.api import utils as api_utils
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
import six
-from openstackclient.api import utils as api_utils
from openstackclient.i18n import _
from openstackclient.identity import common
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 58a77120..22d8412c 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -20,13 +20,13 @@ import locale
import sys
from osc_lib.api import auth
+from osc_lib.command import commandmanager
from osc_lib import shell
import six
import openstackclient
from openstackclient.common import client_config as cloud_config
from openstackclient.common import clientmanager
-from openstackclient.common import commandmanager
DEFAULT_DOMAIN = 'default'
diff --git a/openstackclient/tests/functional/compute/v2/test_aggregate.py b/openstackclient/tests/functional/compute/v2/test_aggregate.py
index 590d28cb..c591dd61 100644
--- a/openstackclient/tests/functional/compute/v2/test_aggregate.py
+++ b/openstackclient/tests/functional/compute/v2/test_aggregate.py
@@ -11,7 +11,6 @@
# under the License.
import json
-import time
import uuid
from openstackclient.tests.functional import base
@@ -20,12 +19,13 @@ from openstackclient.tests.functional import base
class AggregateTests(base.TestCase):
"""Functional tests for aggregate"""
- def test_aggregate_create_and_delete(self):
+ def test_aggregate_crud(self):
"""Test create, delete multiple"""
name1 = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'aggregate create -f json ' +
'--zone nova ' +
+ '--property a=b ' +
name1
))
self.assertEqual(
@@ -36,11 +36,17 @@ class AggregateTests(base.TestCase):
'nova',
cmd_output['availability_zone']
)
+ # TODO(dtroyer): enable the following once the properties field
+ # is correctly formatted in the create output
+ # self.assertIn(
+ # "a='b'",
+ # cmd_output['properties']
+ # )
name2 = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'aggregate create -f json ' +
- '--zone nova ' +
+ '--zone external ' +
name2
))
self.assertEqual(
@@ -48,102 +54,15 @@ class AggregateTests(base.TestCase):
cmd_output['name']
)
self.assertEqual(
- 'nova',
+ 'external',
cmd_output['availability_zone']
)
- # Loop a few times since this is timing-sensitive
- # Just hard-code it for now, since there is no pause and it is
- # racy we shouldn't have to wait too long, a minute or two
- # seems reasonable
- wait_time = 0
- while wait_time <= 120:
- cmd_output = json.loads(self.openstack(
- 'aggregate show -f json ' +
- name2
- ))
- if cmd_output['name'] != name2:
- # Hang out for a bit and try again
- print('retrying aggregate check')
- wait_time += 10
- time.sleep(10)
- else:
- break
-
- del_output = self.openstack(
- 'aggregate delete ' +
- name1 + ' ' +
- name2
- )
- self.assertOutput('', del_output)
-
- def test_aggregate_list(self):
- """Test aggregate list"""
- name1 = uuid.uuid4().hex
- self.openstack(
- 'aggregate create ' +
- '--zone nova ' +
- '--property a=b ' +
- name1
- )
- self.addCleanup(
- self.openstack,
- 'aggregate delete ' + name1,
- fail_ok=True,
- )
-
- name2 = uuid.uuid4().hex
- self.openstack(
- 'aggregate create ' +
- '--zone internal ' +
- '--property c=d ' +
- name2
- )
- self.addCleanup(
- self.openstack,
- 'aggregate delete ' + name2,
- fail_ok=True,
- )
-
- cmd_output = json.loads(self.openstack(
- 'aggregate list -f json'
- ))
- names = [x['Name'] for x in cmd_output]
- self.assertIn(name1, names)
- self.assertIn(name2, names)
- zones = [x['Availability Zone'] for x in cmd_output]
- self.assertIn('nova', zones)
- self.assertIn('internal', zones)
-
- # Test aggregate list --long
- cmd_output = json.loads(self.openstack(
- 'aggregate list --long -f json'
- ))
- names = [x['Name'] for x in cmd_output]
- self.assertIn(name1, names)
- self.assertIn(name2, names)
- zones = [x['Availability Zone'] for x in cmd_output]
- self.assertIn('nova', zones)
- self.assertIn('internal', zones)
- properties = [x['Properties'] for x in cmd_output]
- self.assertIn({'a': 'b'}, properties)
- self.assertIn({'c': 'd'}, properties)
-
- def test_aggregate_set_and_unset(self):
- """Test aggregate set, show and unset"""
- name1 = uuid.uuid4().hex
- name2 = uuid.uuid4().hex
- self.openstack(
- 'aggregate create ' +
- '--zone nova ' +
- '--property a=b ' +
- name1
- )
- self.addCleanup(self.openstack, 'aggregate delete ' + name2)
-
+ # Test aggregate set
+ name3 = uuid.uuid4().hex
raw_output = self.openstack(
'aggregate set ' +
- '--name ' + name2 + ' ' +
+ '--name ' + name3 + ' ' +
'--zone internal ' +
'--no-property ' +
'--property c=d ' +
@@ -153,10 +72,10 @@ class AggregateTests(base.TestCase):
cmd_output = json.loads(self.openstack(
'aggregate show -f json ' +
- name2
+ name3
))
self.assertEqual(
- name2,
+ name3,
cmd_output['name']
)
self.assertEqual(
@@ -172,23 +91,56 @@ class AggregateTests(base.TestCase):
cmd_output['properties']
)
+ # Test aggregate list
+ cmd_output = json.loads(self.openstack(
+ 'aggregate list -f json'
+ ))
+ names = [x['Name'] for x in cmd_output]
+ self.assertIn(name3, names)
+ self.assertIn(name2, names)
+ zones = [x['Availability Zone'] for x in cmd_output]
+ self.assertIn('external', zones)
+ self.assertIn('internal', zones)
+
+ # Test aggregate list --long
+ cmd_output = json.loads(self.openstack(
+ 'aggregate list --long -f json'
+ ))
+ names = [x['Name'] for x in cmd_output]
+ self.assertIn(name3, names)
+ self.assertIn(name2, names)
+ zones = [x['Availability Zone'] for x in cmd_output]
+ self.assertIn('external', zones)
+ self.assertIn('internal', zones)
+ properties = [x['Properties'] for x in cmd_output]
+ self.assertNotIn({'a': 'b'}, properties)
+ self.assertIn({'c': 'd'}, properties)
+
# Test unset
raw_output = self.openstack(
'aggregate unset ' +
'--property c ' +
- name2
+ name3
)
self.assertOutput('', raw_output)
cmd_output = json.loads(self.openstack(
'aggregate show -f json ' +
- name2
+ name3
))
self.assertNotIn(
"c='d'",
cmd_output['properties']
)
+ # test aggregate delete
+ del_output = self.openstack(
+ 'aggregate delete ' +
+ name3 + ' ' +
+ name2
+ )
+ self.assertOutput('', del_output)
+
def test_aggregate_add_and_remove_host(self):
"""Test aggregate add and remove host"""
# Get a host
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index c8fb44d3..6330ac98 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -63,6 +63,84 @@ class ServerTests(common.ComputeTestCase):
self.assertNotIn(name1, col_name)
self.assertIn(name2, col_name)
+ def test_server_list_with_changes_before(self):
+ """Test server list.
+
+ Getting the servers list with updated_at time equal or
+ before than changes-before.
+ """
+ cmd_output = self.server_create()
+ server_name1 = cmd_output['name']
+
+ cmd_output = self.server_create()
+ server_name2 = cmd_output['name']
+ updated_at2 = cmd_output['updated']
+
+ cmd_output = self.server_create()
+ server_name3 = cmd_output['name']
+
+ cmd_output = json.loads(self.openstack(
+ '--os-compute-api-version 2.66 ' +
+ 'server list -f json '
+ '--changes-before ' + updated_at2
+ ))
+
+ col_updated = [server["Name"] for server in cmd_output]
+ self.assertIn(server_name1, col_updated)
+ self.assertIn(server_name2, col_updated)
+ self.assertNotIn(server_name3, col_updated)
+
+ def test_server_list_with_changes_since(self):
+ """Test server list.
+
+ Getting the servers list with updated_at time equal or
+ later than changes-since.
+ """
+ cmd_output = self.server_create()
+ server_name1 = cmd_output['name']
+ cmd_output = self.server_create()
+ server_name2 = cmd_output['name']
+ updated_at2 = cmd_output['updated']
+ cmd_output = self.server_create()
+ server_name3 = cmd_output['name']
+
+ cmd_output = json.loads(self.openstack(
+ 'server list -f json '
+ '--changes-since ' + updated_at2
+ ))
+
+ col_updated = [server["Name"] for server in cmd_output]
+ self.assertNotIn(server_name1, col_updated)
+ self.assertIn(server_name2, col_updated)
+ self.assertIn(server_name3, col_updated)
+
+ def test_server_list_with_changes_before_and_changes_since(self):
+ """Test server list.
+
+ Getting the servers list with updated_at time equal or before than
+ changes-before and equal or later than changes-since.
+ """
+ cmd_output = self.server_create()
+ server_name1 = cmd_output['name']
+ cmd_output = self.server_create()
+ server_name2 = cmd_output['name']
+ updated_at2 = cmd_output['updated']
+ cmd_output = self.server_create()
+ server_name3 = cmd_output['name']
+ updated_at3 = cmd_output['updated']
+
+ cmd_output = json.loads(self.openstack(
+ '--os-compute-api-version 2.66 ' +
+ 'server list -f json ' +
+ '--changes-since ' + updated_at2 +
+ ' --changes-before ' + updated_at3
+ ))
+
+ col_updated = [server["Name"] for server in cmd_output]
+ self.assertNotIn(server_name1, col_updated)
+ self.assertIn(server_name2, col_updated)
+ self.assertIn(server_name3, col_updated)
+
def test_server_set(self):
"""Test server create, delete, set, show"""
cmd_output = self.server_create()
diff --git a/openstackclient/tests/unit/api/test_utils.py b/openstackclient/tests/unit/api/test_utils.py
deleted file mode 100644
index 1f528558..00000000
--- a/openstackclient/tests/unit/api/test_utils.py
+++ /dev/null
@@ -1,115 +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.
-#
-
-"""API Utilities Library Tests"""
-
-import copy
-
-from openstackclient.api import api
-from openstackclient.api import utils as api_utils
-from openstackclient.tests.unit.api import fakes as api_fakes
-
-
-class TestBaseAPIFilter(api_fakes.TestSession):
- """The filters can be tested independently"""
-
- def setUp(self):
- super(TestBaseAPIFilter, self).setUp()
- self.api = api.BaseAPI(
- session=self.sess,
- endpoint=self.BASE_URL,
- )
-
- self.input_list = [
- api_fakes.RESP_ITEM_1,
- api_fakes.RESP_ITEM_2,
- api_fakes.RESP_ITEM_3,
- ]
-
- def test_simple_filter_none(self):
- output = api_utils.simple_filter(
- )
- self.assertIsNone(output)
-
- def test_simple_filter_no_attr(self):
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- )
- self.assertEqual(self.input_list, output)
-
- def test_simple_filter_attr_only(self):
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='status',
- )
- self.assertEqual(self.input_list, output)
-
- def test_simple_filter_attr_value(self):
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='status',
- value='',
- )
- self.assertEqual([], output)
-
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='status',
- value='UP',
- )
- self.assertEqual(
- [api_fakes.RESP_ITEM_1, api_fakes.RESP_ITEM_3],
- output,
- )
-
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='fred',
- value='UP',
- )
- self.assertEqual([], output)
-
- def test_simple_filter_prop_attr_only(self):
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='b',
- property_field='props',
- )
- self.assertEqual(self.input_list, output)
-
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='status',
- property_field='props',
- )
- self.assertEqual(self.input_list, output)
-
- def test_simple_filter_prop_attr_value(self):
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='b',
- value=2,
- property_field='props',
- )
- self.assertEqual(
- [api_fakes.RESP_ITEM_1, api_fakes.RESP_ITEM_2],
- output,
- )
-
- output = api_utils.simple_filter(
- copy.deepcopy(self.input_list),
- attr='b',
- value=9,
- property_field='props',
- )
- self.assertEqual([], output)
diff --git a/openstackclient/tests/unit/common/test_commandmanager.py b/openstackclient/tests/unit/common/test_commandmanager.py
deleted file mode 100644
index 0c6c99c0..00000000
--- a/openstackclient/tests/unit/common/test_commandmanager.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright 2012-2013 OpenStack Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-import mock
-
-from openstackclient.common import commandmanager
-from openstackclient.tests.unit import utils
-
-
-class FakeCommand(object):
-
- @classmethod
- def load(cls):
- return cls
-
- def __init__(self):
- return
-
-FAKE_CMD_ONE = FakeCommand
-FAKE_CMD_TWO = FakeCommand
-FAKE_CMD_ALPHA = FakeCommand
-FAKE_CMD_BETA = FakeCommand
-
-
-class FakeCommandManager(commandmanager.CommandManager):
- commands = {}
-
- def load_commands(self, namespace):
- if namespace == 'test':
- self.commands['one'] = FAKE_CMD_ONE
- self.commands['two'] = FAKE_CMD_TWO
- self.group_list.append(namespace)
- elif namespace == 'greek':
- self.commands['alpha'] = FAKE_CMD_ALPHA
- self.commands['beta'] = FAKE_CMD_BETA
- self.group_list.append(namespace)
-
-
-class TestCommandManager(utils.TestCase):
-
- def test_add_command_group(self):
- mgr = FakeCommandManager('test')
-
- # Make sure add_command() still functions
- mock_cmd_one = mock.Mock()
- mgr.add_command('mock', mock_cmd_one)
- cmd_mock, name, args = mgr.find_command(['mock'])
- self.assertEqual(mock_cmd_one, cmd_mock)
-
- # Find a command added in initialization
- cmd_one, name, args = mgr.find_command(['one'])
- self.assertEqual(FAKE_CMD_ONE, cmd_one)
-
- # Load another command group
- mgr.add_command_group('greek')
-
- # Find a new command
- cmd_alpha, name, args = mgr.find_command(['alpha'])
- self.assertEqual(FAKE_CMD_ALPHA, cmd_alpha)
-
- # Ensure that the original commands were not overwritten
- cmd_two, name, args = mgr.find_command(['two'])
- self.assertEqual(FAKE_CMD_TWO, cmd_two)
-
- def test_get_command_groups(self):
- mgr = FakeCommandManager('test')
-
- # Make sure add_command() still functions
- mock_cmd_one = mock.Mock()
- mgr.add_command('mock', mock_cmd_one)
- cmd_mock, name, args = mgr.find_command(['mock'])
- self.assertEqual(mock_cmd_one, cmd_mock)
-
- # Load another command group
- mgr.add_command_group('greek')
-
- gl = mgr.get_command_groups()
- self.assertEqual(['test', 'greek'], gl)
-
- def test_get_command_names(self):
- mock_cmd_one = mock.Mock()
- mock_cmd_one.name = 'one'
- mock_cmd_two = mock.Mock()
- mock_cmd_two.name = 'cmd two'
- mock_pkg_resources = mock.Mock(
- return_value=[mock_cmd_one, mock_cmd_two],
- )
- with mock.patch(
- 'pkg_resources.iter_entry_points',
- mock_pkg_resources,
- ) as iter_entry_points:
- mgr = commandmanager.CommandManager('test')
- iter_entry_points.assert_called_once_with('test')
- cmds = mgr.get_command_names('test')
- self.assertEqual(['one', 'cmd two'], cmds)
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index c30af8fb..4a37a812 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -23,6 +23,7 @@ from openstack import exceptions as sdk_exceptions
from osc_lib import exceptions
from osc_lib import utils as common_utils
from oslo_utils import timeutils
+import six
from openstackclient.compute.v2 import server
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -1834,6 +1835,90 @@ class TestServerCreate(TestServer):
self.cmd.take_action,
parsed_args)
+ def test_server_create_with_description_api_newer(self):
+
+ # Description is supported for nova api version 2.19 or above
+ self.app.client_manager.compute.api_version = 2.19
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--description', 'description1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('description', 'description1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ # In base command class ShowOne in cliff, abstract method
+ # take_action() returns a two-part tuple with a tuple of
+ # column names and a tuple of data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ meta=None,
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='auto',
+ scheduler_hints={},
+ config_drive=None,
+ description='description1',
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ self.image,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+ self.assertFalse(self.images_mock.called)
+ self.assertFalse(self.flavors_mock.called)
+
+ def test_server_create_with_description_api_older(self):
+
+ # Description is not supported for nova api version below 2.19
+ self.app.client_manager.compute.api_version = 2.18
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--description', 'description1',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('description', 'description1'),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
class TestServerDelete(TestServer):
@@ -1991,6 +2076,7 @@ class TestServerList(TestServer):
'user_id': None,
'deleted': False,
'changes-since': None,
+ 'changes-before': None,
}
# Default params of the core function of the command in the case of no
@@ -2272,6 +2358,71 @@ class TestServerList(TestServer):
'Invalid time value'
)
+ def test_server_list_v266_with_changes_before(self):
+ self.app.client_manager.compute.api_version = (
+ api_versions.APIVersion('2.66'))
+ arglist = [
+ '--changes-before', '2016-03-05T06:27:59Z',
+ '--deleted'
+ ]
+ verifylist = [
+ ('changes_before', '2016-03-05T06:27:59Z'),
+ ('deleted', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['changes-before'] = '2016-03-05T06:27:59Z'
+ self.search_opts['deleted'] = True
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ @mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
+ def test_server_list_v266_with_invalid_changes_before(
+ self, mock_parse_isotime):
+ self.app.client_manager.compute.api_version = (
+ api_versions.APIVersion('2.66'))
+
+ arglist = [
+ '--changes-before', 'Invalid time value',
+ ]
+ verifylist = [
+ ('changes_before', 'Invalid time value'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('Invalid changes-before value: Invalid time '
+ 'value', str(e))
+ mock_parse_isotime.assert_called_once_with(
+ 'Invalid time value'
+ )
+
+ def test_server_with_changes_before_older_version(self):
+ self.app.client_manager.compute.api_version = (
+ api_versions.APIVersion('2.65'))
+
+ arglist = [
+ '--changes-before', '2016-03-05T06:27:59Z',
+ '--deleted'
+ ]
+ verifylist = [
+ ('changes_before', '2016-03-05T06:27:59Z'),
+ ('deleted', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+
def test_server_list_v269_with_partial_constructs(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.69')
@@ -2415,12 +2566,40 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
self.assertNotCalled(self.servers_mock.migrate)
+ def test_server_migrate_with_host(self):
+ # Tests that --host is not allowed for a cold migration.
+ arglist = [
+ '--host', 'fakehost', self.server.id,
+ ]
+ verifylist = [
+ ('live', None),
+ ('live_migration', False),
+ ('host', 'fakehost'),
+ ('block_migration', False),
+ ('disk_overcommit', False),
+ ('wait', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ # Make sure it's the error we expect.
+ self.assertIn("--live-migration must be specified if "
+ "--block-migration, --disk-overcommit or --host is "
+ "specified", six.text_type(ex))
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.assertNotCalled(self.servers_mock.live_migrate)
+ self.assertNotCalled(self.servers_mock.migrate)
+
def test_server_live_migrate(self):
arglist = [
'--live', 'fakehost', self.server.id,
]
verifylist = [
('live', 'fakehost'),
+ ('live_migration', False),
+ ('host', None),
('block_migration', False),
('disk_overcommit', False),
('wait', False),
@@ -2430,7 +2609,8 @@ class TestServerMigrate(TestServer):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.24')
- result = self.cmd.take_action(parsed_args)
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id)
self.server.live_migrate.assert_called_with(block_migration=False,
@@ -2438,6 +2618,132 @@ class TestServerMigrate(TestServer):
host='fakehost')
self.assertNotCalled(self.servers_mock.migrate)
self.assertIsNone(result)
+ # A warning should have been logged for using --live.
+ mock_warning.assert_called_once()
+ self.assertIn('The --live option has been deprecated.',
+ six.text_type(mock_warning.call_args[0][0]))
+
+ def test_server_live_migrate_host_pre_2_30(self):
+ # Tests that the --host option is not supported for --live-migration
+ # before microversion 2.30 (the test defaults to 2.1).
+ arglist = [
+ '--live-migration', '--host', 'fakehost', self.server.id,
+ ]
+ verifylist = [
+ ('live', None),
+ ('live_migration', True),
+ ('host', 'fakehost'),
+ ('block_migration', False),
+ ('disk_overcommit', False),
+ ('wait', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ # Make sure it's the error we expect.
+ self.assertIn('--os-compute-api-version 2.30 or greater is required '
+ 'when using --host', six.text_type(ex))
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.assertNotCalled(self.servers_mock.live_migrate)
+ self.assertNotCalled(self.servers_mock.migrate)
+
+ def test_server_live_migrate_no_host(self):
+ # Tests the --live-migration option without --host or --live.
+ arglist = [
+ '--live-migration', self.server.id,
+ ]
+ verifylist = [
+ ('live', None),
+ ('live_migration', True),
+ ('host', None),
+ ('block_migration', False),
+ ('disk_overcommit', False),
+ ('wait', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.live_migrate.assert_called_with(block_migration=False,
+ disk_over_commit=False,
+ host=None)
+ self.assertNotCalled(self.servers_mock.migrate)
+ self.assertIsNone(result)
+ # Since --live wasn't used a warning shouldn't have been logged.
+ mock_warning.assert_not_called()
+
+ def test_server_live_migrate_with_host(self):
+ # Tests the --live-migration option with --host but no --live.
+ # This requires --os-compute-api-version >= 2.30 so the test uses 2.30.
+ arglist = [
+ '--live-migration', '--host', 'fakehost', self.server.id,
+ ]
+ verifylist = [
+ ('live', None),
+ ('live_migration', True),
+ ('host', 'fakehost'),
+ ('block_migration', False),
+ ('disk_overcommit', False),
+ ('wait', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.30')
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ # No disk_overcommit with microversion >= 2.25.
+ self.server.live_migrate.assert_called_with(block_migration=False,
+ host='fakehost')
+ self.assertNotCalled(self.servers_mock.migrate)
+ self.assertIsNone(result)
+
+ def test_server_live_migrate_without_host_override_live(self):
+ # Tests the --live-migration option without --host and with --live.
+ # The --live-migration option will take precedence and a warning is
+ # logged for using --live.
+ arglist = [
+ '--live', 'fakehost', '--live-migration', self.server.id,
+ ]
+ verifylist = [
+ ('live', 'fakehost'),
+ ('live_migration', True),
+ ('host', None),
+ ('block_migration', False),
+ ('disk_overcommit', False),
+ ('wait', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(self.cmd.log, 'warning') as mock_warning:
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.server.live_migrate.assert_called_with(block_migration=False,
+ disk_over_commit=False,
+ host=None)
+ self.assertNotCalled(self.servers_mock.migrate)
+ self.assertIsNone(result)
+ # A warning should have been logged for using --live.
+ mock_warning.assert_called_once()
+ self.assertIn('The --live option has been deprecated.',
+ six.text_type(mock_warning.call_args[0][0]))
+
+ def test_server_live_migrate_live_and_host_mutex(self):
+ # Tests specifying both the --live and --host options which are in a
+ # mutex group so argparse should fail.
+ arglist = [
+ '--live', 'fakehost', '--host', 'fakehost', self.server.id,
+ ]
+ self.assertRaises(utils.ParserException,
+ self.check_parser, self.cmd, arglist, verify_args=[])
def test_server_block_live_migrate(self):
arglist = [
@@ -2663,6 +2969,55 @@ class TestServerRebuild(TestServer):
self.images_mock.get.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, password)
+ def test_rebuild_with_description_api_older(self):
+
+ # Description is not supported for nova api version below 2.19
+ self.server.api_version = 2.18
+
+ description = 'description1'
+ arglist = [
+ self.server.id,
+ '--description', description
+ ]
+ verifylist = [
+ ('server', self.server.id),
+ ('description', description)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_rebuild_with_description_api_newer(self):
+
+ # Description is supported for nova api version 2.19 or above
+ self.server.api_version = 2.19
+
+ description = 'description1'
+ arglist = [
+ self.server.id,
+ '--description', description
+ ]
+ verifylist = [
+ ('server', self.server.id),
+ ('description', description)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ # Get the command object to test
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.images_mock.get.assert_called_with(self.image.id)
+ self.server.rebuild.assert_called_with(self.image, None,
+ description=description)
+
@mock.patch.object(common_utils, 'wait_for_status', return_value=True)
def test_rebuild_with_wait_ok(self, mock_wait_for_status):
arglist = [
@@ -3400,6 +3755,10 @@ class TestServerSet(TestServer):
def setUp(self):
super(TestServerSet, self).setUp()
+ self.attrs = {
+ 'api_version': None,
+ }
+
self.methods = {
'update': None,
'reset_state': None,
@@ -3502,6 +3861,48 @@ class TestServerSet(TestServer):
mock.sentinel.fake_pass)
self.assertIsNone(result)
+ def test_server_set_with_description_api_newer(self):
+
+ # Description is supported for nova api version 2.19 or above
+ self.fake_servers[0].api_version = 2.19
+
+ arglist = [
+ '--description', 'foo_description',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('description', 'foo_description'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ result = self.cmd.take_action(parsed_args)
+ self.fake_servers[0].update.assert_called_once_with(
+ description='foo_description')
+ self.assertIsNone(result)
+
+ def test_server_set_with_description_api_older(self):
+
+ # Description is not supported for nova api version below 2.19
+ self.fake_servers[0].api_version = 2.18
+
+ arglist = [
+ '--description', 'foo_description',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('description', 'foo_description'),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
class TestServerShelve(TestServer):
@@ -3783,6 +4184,50 @@ class TestServerUnset(TestServer):
self.fake_server, ['key1', 'key2'])
self.assertIsNone(result)
+ def test_server_unset_with_description_api_newer(self):
+
+ # Description is supported for nova api version 2.19 or above
+ self.app.client_manager.compute.api_version = 2.19
+
+ arglist = [
+ '--description',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('description', True),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ result = self.cmd.take_action(parsed_args)
+ self.servers_mock.update.assert_called_once_with(
+ self.fake_server, description="")
+ self.assertIsNone(result)
+
+ def test_server_unset_with_description_api_older(self):
+
+ # Description is not supported for nova api version below 2.19
+ self.app.client_manager.compute.api_version = 2.18
+
+ arglist = [
+ '--description',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('description', True),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch.object(api_versions,
+ 'APIVersion',
+ return_value=2.19):
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
class TestServerUnshelve(TestServer):
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index 27ee9fd0..e5727a6a 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -235,6 +235,10 @@ endpoint_group_filters = {
'service_id': service_id,
'region_id': endpoint_region,
}
+endpoint_group_filters_2 = {
+ 'region_id': endpoint_region,
+}
+endpoint_group_file_path = '/tmp/path/to/file'
ENDPOINT_GROUP = {
'id': endpoint_group_id,
@@ -1044,6 +1048,64 @@ class FakeEndpoint(object):
return endpoint_filter
+class FakeEndpointGroup(object):
+ """Fake one or more endpoint group."""
+
+ @staticmethod
+ def create_one_endpointgroup(attrs=None):
+ """Create a fake endpoint group.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id, url, and so on
+ """
+
+ attrs = attrs or {}
+
+ # set default attributes.
+ endpointgroup_info = {
+ 'id': 'endpoint-group-id-' + uuid.uuid4().hex,
+ 'name': 'endpoint-group-name-' + uuid.uuid4().hex,
+ 'filters': {
+ 'region': 'region-' + uuid.uuid4().hex,
+ 'service_id': 'service-id-' + uuid.uuid4().hex,
+ },
+ 'description': 'endpoint-group-description-' + uuid.uuid4().hex,
+ 'links': 'links-' + uuid.uuid4().hex,
+ }
+ endpointgroup_info.update(attrs)
+
+ endpoint = fakes.FakeResource(info=copy.deepcopy(endpointgroup_info),
+ loaded=True)
+ return endpoint
+
+ @staticmethod
+ def create_one_endpointgroup_filter(attrs=None):
+ """Create a fake endpoint project relationship.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of endpointgroup filter
+ :return:
+ A FakeResource object with project, endpointgroup and so on
+ """
+ attrs = attrs or {}
+
+ # Set default attribute
+ endpointgroup_filter_info = {
+ 'project': 'project-id-' + uuid.uuid4().hex,
+ 'endpointgroup': 'endpointgroup-id-' + uuid.uuid4().hex,
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ endpointgroup_filter_info.update(attrs)
+
+ endpointgroup_filter = fakes.FakeModel(
+ copy.deepcopy(endpointgroup_filter_info))
+
+ return endpointgroup_filter
+
+
class FakeService(object):
"""Fake one or more service."""
diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint_group.py b/openstackclient/tests/unit/identity/v3/test_endpoint_group.py
new file mode 100644
index 00000000..6e9da9c7
--- /dev/null
+++ b/openstackclient/tests/unit/identity/v3/test_endpoint_group.py
@@ -0,0 +1,495 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+import mock
+
+from openstackclient.identity.v3 import endpoint_group
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
+
+
+class TestEndpointGroup(identity_fakes.TestIdentityv3):
+
+ def setUp(self):
+ super(TestEndpointGroup, self).setUp()
+
+ # Get a shortcut to the EndpointManager Mock
+ self.endpoint_groups_mock = (
+ self.app.client_manager.identity.endpoint_groups
+ )
+ self.endpoint_groups_mock.reset_mock()
+ self.epf_mock = (
+ self.app.client_manager.identity.endpoint_filter
+ )
+ self.epf_mock.reset_mock()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.services_mock = self.app.client_manager.identity.services
+ self.services_mock.reset_mock()
+
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.app.client_manager.identity.domains
+ self.domains_mock.reset_mock()
+
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock.reset_mock()
+
+
+class TestEndpointGroupCreate(TestEndpointGroup):
+
+ columns = (
+ 'description',
+ 'filters',
+ 'id',
+ 'name',
+ )
+
+ def setUp(self):
+ super(TestEndpointGroupCreate, self).setUp()
+
+ self.endpoint_group = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup(
+ attrs={'filters': identity_fakes.endpoint_group_filters}))
+
+ self.endpoint_groups_mock.create.return_value = self.endpoint_group
+
+ # Get the command object to test
+ self.cmd = endpoint_group.CreateEndpointGroup(self.app, None)
+
+ def test_endpointgroup_create_no_options(self):
+ arglist = [
+ '--description', self.endpoint_group.description,
+ self.endpoint_group.name,
+ identity_fakes.endpoint_group_file_path,
+ ]
+ verifylist = [
+ ('name', self.endpoint_group.name),
+ ('filters', identity_fakes.endpoint_group_file_path),
+ ('description', self.endpoint_group.description),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ mocker = mock.Mock()
+ mocker.return_value = identity_fakes.endpoint_group_filters
+ with mock.patch("openstackclient.identity.v3.endpoint_group."
+ "CreateEndpointGroup._read_filters", mocker):
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.endpoint_group.name,
+ 'filters': identity_fakes.endpoint_group_filters,
+ 'description': self.endpoint_group.description,
+ }
+
+ self.endpoint_groups_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ datalist = (
+ self.endpoint_group.description,
+ identity_fakes.endpoint_group_filters,
+ self.endpoint_group.id,
+ self.endpoint_group.name,
+ )
+ self.assertEqual(datalist, data)
+
+
+class TestEndpointGroupDelete(TestEndpointGroup):
+
+ endpoint_group = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup())
+
+ def setUp(self):
+ super(TestEndpointGroupDelete, self).setUp()
+
+ # This is the return value for utils.find_resource(endpoint)
+ self.endpoint_groups_mock.get.return_value = self.endpoint_group
+ self.endpoint_groups_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = endpoint_group.DeleteEndpointGroup(self.app, None)
+
+ def test_endpointgroup_delete(self):
+ arglist = [
+ self.endpoint_group.id,
+ ]
+ verifylist = [
+ ('endpointgroup', [self.endpoint_group.id]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.endpoint_groups_mock.delete.assert_called_with(
+ self.endpoint_group.id,
+ )
+ self.assertIsNone(result)
+
+
+class TestEndpointGroupList(TestEndpointGroup):
+
+ endpoint_group = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup())
+ project = identity_fakes.FakeProject.create_one_project()
+ domain = identity_fakes.FakeDomain.create_one_domain()
+
+ columns = (
+ 'ID',
+ 'Name',
+ 'Description',
+ )
+
+ def setUp(self):
+ super(TestEndpointGroupList, self).setUp()
+
+ self.endpoint_groups_mock.list.return_value = [self.endpoint_group]
+ self.endpoint_groups_mock.get.return_value = self.endpoint_group
+ self.epf_mock.list_projects_for_endpoint_group.return_value = [
+ self.project]
+ self.epf_mock.list_endpoint_groups_for_project.return_value = [
+ self.endpoint_group]
+
+ # Get the command object to test
+ self.cmd = endpoint_group.ListEndpointGroup(self.app, None)
+
+ def test_endpoint_group_list_no_options(self):
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+ self.endpoint_groups_mock.list.assert_called_with()
+
+ self.assertEqual(self.columns, columns)
+ datalist = (
+ (
+ self.endpoint_group.id,
+ self.endpoint_group.name,
+ self.endpoint_group.description,
+ ),
+ )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_group_list_projects_by_endpoint_group(self):
+ arglist = [
+ '--endpointgroup', self.endpoint_group.id,
+ ]
+ verifylist = [
+ ('endpointgroup', self.endpoint_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+ self.epf_mock.list_projects_for_endpoint_group.assert_called_with(
+ endpoint_group=self.endpoint_group.id
+ )
+
+ self.assertEqual(self.columns, columns)
+ datalist = (
+ (
+ self.project.id,
+ self.project.name,
+ self.project.description,
+ ),
+ )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_group_list_by_project(self):
+ self.epf_mock.list_endpoints_for_project.return_value = [
+ self.endpoint_group
+ ]
+ self.projects_mock.get.return_value = self.project
+
+ arglist = [
+ '--project', self.project.name,
+ '--domain', self.domain.name
+ ]
+ verifylist = [
+ ('project', self.project.name),
+ ('domain', self.domain.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+ self.epf_mock.list_endpoint_groups_for_project.assert_called_with(
+ project=self.project.id
+ )
+
+ self.assertEqual(self.columns, columns)
+ datalist = (
+ (
+ self.endpoint_group.id,
+ self.endpoint_group.name,
+ self.endpoint_group.description,
+ ),
+ )
+ self.assertEqual(datalist, tuple(data))
+
+
+class TestEndpointGroupSet(TestEndpointGroup):
+
+ endpoint_group = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup())
+
+ def setUp(self):
+ super(TestEndpointGroupSet, self).setUp()
+
+ # This is the return value for utils.find_resource(endpoint)
+ self.endpoint_groups_mock.get.return_value = self.endpoint_group
+
+ self.endpoint_groups_mock.update.return_value = self.endpoint_group
+
+ # Get the command object to test
+ self.cmd = endpoint_group.SetEndpointGroup(self.app, None)
+
+ def test_endpoint_group_set_no_options(self):
+ arglist = [
+ self.endpoint_group.id,
+ ]
+ verifylist = [
+ ('endpointgroup', self.endpoint_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ kwargs = {
+ 'name': None,
+ 'filters': None,
+ 'description': ''
+ }
+ self.endpoint_groups_mock.update.assert_called_with(
+ self.endpoint_group.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_endpoint_group_set_name(self):
+ arglist = [
+ '--name', 'qwerty',
+ self.endpoint_group.id
+ ]
+ verifylist = [
+ ('name', 'qwerty'),
+ ('endpointgroup', self.endpoint_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'qwerty',
+ 'filters': None,
+ 'description': ''
+ }
+ self.endpoint_groups_mock.update.assert_called_with(
+ self.endpoint_group.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_endpoint_group_set_filters(self):
+ arglist = [
+ '--filters', identity_fakes.endpoint_group_file_path,
+ self.endpoint_group.id,
+ ]
+ verifylist = [
+ ('filters', identity_fakes.endpoint_group_file_path),
+ ('endpointgroup', self.endpoint_group.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ mocker = mock.Mock()
+ mocker.return_value = identity_fakes.endpoint_group_filters_2
+ with mock.patch("openstackclient.identity.v3.endpoint_group."
+ "SetEndpointGroup._read_filters", mocker):
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': None,
+ 'filters': identity_fakes.endpoint_group_filters_2,
+ 'description': '',
+ }
+
+ self.endpoint_groups_mock.update.assert_called_with(
+ self.endpoint_group.id,
+ **kwargs
+ )
+
+ self.assertIsNone(result)
+
+ def test_endpoint_group_set_description(self):
+ arglist = [
+ '--description', 'qwerty',
+ self.endpoint_group.id
+ ]
+ verifylist = [
+ ('description', 'qwerty'),
+ ('endpointgroup', self.endpoint_group.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': None,
+ 'filters': None,
+ 'description': 'qwerty',
+ }
+ self.endpoint_groups_mock.update.assert_called_with(
+ self.endpoint_group.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+
+class TestAddProjectToEndpointGroup(TestEndpointGroup):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ domain = identity_fakes.FakeDomain.create_one_domain()
+ endpoint_group = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup())
+
+ new_ep_filter = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup_filter(
+ attrs={'endpointgroup': endpoint_group.id,
+ 'project': project.id}))
+
+ def setUp(self):
+ super(TestAddProjectToEndpointGroup, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.endpoint_groups_mock.get.return_value = self.endpoint_group
+
+ # Update the image_id in the MEMBER dict
+ self.epf_mock.create.return_value = self.new_ep_filter
+ self.projects_mock.get.return_value = self.project
+ self.domains_mock.get.return_value = self.domain
+
+ # Get the command object to test
+ self.cmd = endpoint_group.AddProjectToEndpointGroup(self.app, None)
+
+ def test_add_project_to_endpoint_no_option(self):
+ arglist = [
+ self.endpoint_group.id,
+ self.project.id,
+ ]
+ verifylist = [
+ ('endpointgroup', self.endpoint_group.id),
+ ('project', self.project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.epf_mock.add_endpoint_group_to_project.assert_called_with(
+ project=self.project.id,
+ endpoint_group=self.endpoint_group.id,
+ )
+ self.assertIsNone(result)
+
+ def test_add_project_to_endpoint_with_option(self):
+ arglist = [
+ self.endpoint_group.id,
+ self.project.id,
+ '--project-domain', self.domain.id,
+ ]
+ verifylist = [
+ ('endpointgroup', self.endpoint_group.id),
+ ('project', self.project.id),
+ ('project_domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.epf_mock.add_endpoint_group_to_project.assert_called_with(
+ project=self.project.id,
+ endpoint_group=self.endpoint_group.id,
+ )
+ self.assertIsNone(result)
+
+
+class TestRemoveProjectEndpointGroup(TestEndpointGroup):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ domain = identity_fakes.FakeDomain.create_one_domain()
+ endpoint_group = (
+ identity_fakes.FakeEndpointGroup.create_one_endpointgroup())
+
+ def setUp(self):
+ super(TestRemoveProjectEndpointGroup, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.endpoint_groups_mock.get.return_value = self.endpoint_group
+
+ self.projects_mock.get.return_value = self.project
+ self.domains_mock.get.return_value = self.domain
+ self.epf_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = endpoint_group.RemoveProjectFromEndpointGroup(
+ self.app, None)
+
+ def test_remove_project_endpoint_no_options(self):
+ arglist = [
+ self.endpoint_group.id,
+ self.project.id,
+ ]
+ verifylist = [
+ ('endpointgroup', self.endpoint_group.id),
+ ('project', self.project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.epf_mock.delete_endpoint_group_from_project.assert_called_with(
+ project=self.project.id,
+ endpoint_group=self.endpoint_group.id,
+ )
+ self.assertIsNone(result)
+
+ def test_remove_project_endpoint_with_options(self):
+ arglist = [
+ self.endpoint_group.id,
+ self.project.id,
+ '--project-domain', self.domain.id,
+ ]
+ verifylist = [
+ ('endpointgroup', self.endpoint_group.id),
+ ('project', self.project.id),
+ ('project_domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.epf_mock.delete_endpoint_group_from_project.assert_called_with(
+ project=self.project.id,
+ endpoint_group=self.endpoint_group.id,
+ )
+ self.assertIsNone(result)
diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py
index ae578d91..6babd4ff 100644
--- a/openstackclient/tests/unit/image/v1/test_image.py
+++ b/openstackclient/tests/unit/image/v1/test_image.py
@@ -417,7 +417,7 @@ class TestImageList(TestImage):
), )
self.assertEqual(datalist, tuple(data))
- @mock.patch('openstackclient.api.utils.simple_filter')
+ @mock.patch('osc_lib.api.utils.simple_filter')
def test_image_list_property_option(self, sf_mock):
sf_mock.side_effect = [
[self.image_info], [],
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index 16a393df..4cc8f448 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -734,7 +734,7 @@ class TestImageList(TestImage):
), )
self.assertEqual(datalist, tuple(data))
- @mock.patch('openstackclient.api.utils.simple_filter')
+ @mock.patch('osc_lib.api.utils.simple_filter')
def test_image_list_property_option(self, sf_mock):
sf_mock.return_value = [copy.deepcopy(self._image)]