summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/object_store_v1.py14
-rw-r--r--openstackclient/common/quota.py13
-rw-r--r--openstackclient/common/sdk_utils.py60
-rw-r--r--openstackclient/compute/v2/server.py38
-rw-r--r--openstackclient/compute/v2/server_backup.py7
-rw-r--r--openstackclient/compute/v2/server_image.py7
-rw-r--r--openstackclient/identity/common.py24
-rw-r--r--openstackclient/identity/v3/domain.py9
-rw-r--r--openstackclient/identity/v3/project.py17
-rw-r--r--openstackclient/identity/v3/role.py22
-rw-r--r--openstackclient/identity/v3/user.py120
-rw-r--r--openstackclient/image/client.py50
-rw-r--r--openstackclient/image/v1/image.py138
-rw-r--r--openstackclient/image/v2/image.py285
-rw-r--r--openstackclient/network/sdk_utils.py5
-rw-r--r--openstackclient/network/v2/address_scope.py10
-rw-r--r--openstackclient/network/v2/floating_ip.py10
-rw-r--r--openstackclient/network/v2/floating_ip_port_forwarding.py20
-rw-r--r--openstackclient/network/v2/ip_availability.py2
-rw-r--r--openstackclient/network/v2/network.py8
-rw-r--r--openstackclient/network/v2/network_agent.py1
-rw-r--r--openstackclient/network/v2/network_auto_allocated_topology.py17
-rw-r--r--openstackclient/network/v2/network_flavor.py10
-rw-r--r--openstackclient/network/v2/network_flavor_profile.py10
-rw-r--r--openstackclient/network/v2/network_meter.py10
-rw-r--r--openstackclient/network/v2/network_meter_rule.py10
-rw-r--r--openstackclient/network/v2/network_qos_policy.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule.py10
-rw-r--r--openstackclient/network/v2/network_qos_rule_type.py8
-rw-r--r--openstackclient/network/v2/network_rbac.py32
-rw-r--r--openstackclient/network/v2/network_segment.py10
-rw-r--r--openstackclient/network/v2/network_segment_range.py12
-rw-r--r--openstackclient/network/v2/port.py7
-rw-r--r--openstackclient/network/v2/router.py98
-rw-r--r--openstackclient/network/v2/security_group.py41
-rw-r--r--openstackclient/network/v2/security_group_rule.py10
-rw-r--r--openstackclient/network/v2/subnet.py1
-rw-r--r--openstackclient/network/v2/subnet_pool.py1
-rw-r--r--openstackclient/tests/functional/identity/v3/common.py2
-rw-r--r--openstackclient/tests/functional/identity/v3/test_project.py1
-rw-r--r--openstackclient/tests/functional/identity/v3/test_role.py25
-rw-r--r--openstackclient/tests/functional/network/v2/test_router.py43
-rw-r--r--openstackclient/tests/functional/network/v2/test_security_group.py4
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_snapshot.py18
-rw-r--r--openstackclient/tests/unit/api/test_object_store_v1.py60
-rw-r--r--openstackclient/tests/unit/common/test_parseractions.py4
-rw-r--r--openstackclient/tests/unit/common/test_quota.py69
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py129
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_backup.py54
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_image.py19
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py4
-rw-r--r--openstackclient/tests/unit/identity/v3/test_access_rule.py2
-rw-r--r--openstackclient/tests/unit/identity/v3/test_domain.py110
-rw-r--r--openstackclient/tests/unit/identity/v3/test_project.py281
-rw-r--r--openstackclient/tests/unit/identity/v3/test_role.py241
-rw-r--r--openstackclient/tests/unit/identity/v3/test_user.py852
-rw-r--r--openstackclient/tests/unit/image/v1/fakes.py10
-rw-r--r--openstackclient/tests/unit/image/v1/test_image.py173
-rw-r--r--openstackclient/tests/unit/image/v2/fakes.py24
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py547
-rw-r--r--openstackclient/tests/unit/integ/cli/test_shell.py20
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py4
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_network.py26
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py15
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py33
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py74
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_segment_range.py14
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py16
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py140
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_network.py27
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py38
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py8
-rw-r--r--openstackclient/volume/client.py4
-rw-r--r--openstackclient/volume/v1/volume_snapshot.py4
-rw-r--r--openstackclient/volume/v2/consistency_group_snapshot.py2
-rw-r--r--openstackclient/volume/v2/volume.py5
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py6
77 files changed, 3219 insertions, 1046 deletions
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py
index c8514a57..8092abd0 100644
--- a/openstackclient/api/object_store_v1.py
+++ b/openstackclient/api/object_store_v1.py
@@ -87,7 +87,7 @@ class APIv1(api.BaseAPI):
def container_list(
self,
- all_data=False,
+ full_listing=False,
limit=None,
marker=None,
end_marker=None,
@@ -96,7 +96,7 @@ class APIv1(api.BaseAPI):
):
"""Get containers in an account
- :param boolean all_data:
+ :param boolean full_listing:
if True, return a full listing, else returns a max of
10000 listings
:param integer limit:
@@ -113,7 +113,7 @@ class APIv1(api.BaseAPI):
params['format'] = 'json'
- if all_data:
+ if full_listing:
data = listing = self.container_list(
limit=limit,
marker=marker,
@@ -299,7 +299,7 @@ class APIv1(api.BaseAPI):
def object_list(
self,
container=None,
- all_data=False,
+ full_listing=False,
limit=None,
marker=None,
end_marker=None,
@@ -311,7 +311,7 @@ class APIv1(api.BaseAPI):
:param string container:
container name to get a listing for
- :param boolean all_data:
+ :param boolean full_listing:
if True, return a full listing, else returns a max of
10000 listings
:param integer limit:
@@ -328,11 +328,11 @@ class APIv1(api.BaseAPI):
headers will be a dict and all header names will be lowercase.
"""
- if container is None or object is None:
+ if container is None:
return None
params['format'] = 'json'
- if all_data:
+ if full_listing:
data = listing = self.object_list(
container=container,
limit=limit,
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index 7a0dda14..11de986b 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -274,9 +274,18 @@ class ListQuota(command.Lister, BaseQuota):
return parser
def take_action(self, parsed_args):
- projects = self.app.client_manager.identity.projects.list()
result = []
- project_ids = [getattr(p, 'id', '') for p in projects]
+ project_ids = []
+ if parsed_args.project is None:
+ for p in self.app.client_manager.identity.projects.list():
+ project_ids.append(getattr(p, 'id', ''))
+ else:
+ identity_client = self.app.client_manager.identity
+ project = utils.find_resource(
+ identity_client.projects,
+ parsed_args.project,
+ )
+ project_ids.append(getattr(project, 'id', ''))
if parsed_args.compute:
if parsed_args.detail:
diff --git a/openstackclient/common/sdk_utils.py b/openstackclient/common/sdk_utils.py
new file mode 100644
index 00000000..9f085617
--- /dev/null
+++ b/openstackclient/common/sdk_utils.py
@@ -0,0 +1,60 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import six
+
+
+def get_osc_show_columns_for_sdk_resource(
+ sdk_resource,
+ osc_column_map,
+ invisible_columns=None
+):
+ """Get and filter the display and attribute columns for an SDK resource.
+
+ Common utility function for preparing the output of an OSC show command.
+ Some of the columns may need to get renamed, others made invisible.
+
+ :param sdk_resource: An SDK resource
+ :param osc_column_map: A hash of mappings for display column names
+ :param invisible_columns: A list of invisible column names
+
+ :returns: Two tuples containing the names of the display and attribute
+ columns
+ """
+
+ if getattr(sdk_resource, 'allow_get', None) is not None:
+ resource_dict = sdk_resource.to_dict(
+ body=True, headers=False, ignore_none=False)
+ else:
+ resource_dict = sdk_resource
+
+ # Build the OSC column names to display for the SDK resource.
+ attr_map = {}
+ display_columns = list(resource_dict.keys())
+ invisible_columns = [] if invisible_columns is None else invisible_columns
+ for col_name in invisible_columns:
+ if col_name in display_columns:
+ display_columns.remove(col_name)
+ for sdk_attr, osc_attr in six.iteritems(osc_column_map):
+ if sdk_attr in display_columns:
+ attr_map[osc_attr] = sdk_attr
+ display_columns.remove(sdk_attr)
+ if osc_attr not in display_columns:
+ display_columns.append(osc_attr)
+ sorted_display_columns = sorted(display_columns)
+
+ # Build the SDK attribute names for the OSC column names.
+ attr_columns = []
+ for column in sorted_display_columns:
+ new_column = attr_map[column] if column in attr_map else column
+ attr_columns.append(new_column)
+ return tuple(sorted_display_columns), tuple(attr_columns)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 5cc73284..93e9f966 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -143,7 +143,7 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
if image_info:
image_id = image_info.get('id', '')
try:
- image = utils.find_resource(image_client.images, image_id)
+ image = image_client.get_image(image_id)
info['image'] = "%s (%s)" % (image.name, image_id)
except Exception:
info['image'] = image_id
@@ -735,10 +735,8 @@ class CreateServer(command.ShowOne):
# Lookup parsed_args.image
image = None
if parsed_args.image:
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False)
if not image and parsed_args.image_property:
def emit_duplicated_warning(img, image_property):
@@ -749,7 +747,7 @@ class CreateServer(command.ShowOne):
'chosen_one': img_uuid_list[0]})
def _match_image(image_api, wanted_properties):
- image_list = image_api.image_list()
+ image_list = image_api.images()
images_matched = []
for img in image_list:
img_dict = {}
@@ -768,7 +766,7 @@ class CreateServer(command.ShowOne):
return []
return images_matched
- images = _match_image(image_client.api, parsed_args.image_property)
+ images = _match_image(image_client, parsed_args.image_property)
if len(images) > 1:
emit_duplicated_warning(images,
parsed_args.image_property)
@@ -890,8 +888,8 @@ class CreateServer(command.ShowOne):
# one specified by --image, then the compute service will
# create a volume from the image and attach it to the
# server as a non-root volume.
- image_id = utils.find_resource(
- image_client.images, dev_map[0]).id
+ image_id = image_client.find_image(dev_map[0],
+ ignore_missing=False).id
mapping['uuid'] = image_id
# 3. append size and delete_on_termination if exist
if len(dev_map) > 2 and dev_map[2]:
@@ -1324,8 +1322,8 @@ class ListServer(command.Lister):
# image name is given, map it to ID.
image_id = None
if parsed_args.image:
- image_id = utils.find_resource(image_client.images,
- parsed_args.image).id
+ image_id = image_client.find_image(parsed_args.image,
+ ignore_missing=False).id
search_opts = {
'reservation_id': parsed_args.reservation_id,
@@ -1476,12 +1474,12 @@ class ListServer(command.Lister):
(s.image.get('id') for s in data
if s.image))):
try:
- images[i_id] = image_client.images.get(i_id)
+ images[i_id] = image_client.get_image(i_id)
except Exception:
pass
else:
try:
- images_list = image_client.images.list()
+ images_list = image_client.images()
for i in images_list:
images[i.id] = i
except Exception:
@@ -1923,9 +1921,12 @@ class RebuildServer(command.ShowOne):
compute_client.servers, parsed_args.server)
# If parsed_args.image is not set, default to the currently used one.
- image_id = parsed_args.image or server.to_dict().get(
- 'image', {}).get('id')
- image = utils.find_resource(image_client.images, image_id)
+ if parsed_args.image:
+ image = image_client.find_image(
+ parsed_args.image, ignore_missing=False)
+ else:
+ image_id = server.to_dict().get('image', {}).get('id')
+ image = image_client.get_image(image_id)
kwargs = {}
if parsed_args.property:
@@ -2195,10 +2196,7 @@ class RescueServer(command.Command):
image = None
if parsed_args.image:
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
+ image = image_client.find_image(parsed_args.image)
utils.find_resource(
compute_client.servers,
diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py
index 1d560dc0..a5d43fc6 100644
--- a/openstackclient/compute/v2/server_backup.py
+++ b/openstackclient/compute/v2/server_backup.py
@@ -100,14 +100,11 @@ class CreateServerBackup(command.ShowOne):
)
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- backup_name,
- )
+ image = image_client.find_image(backup_name, ignore_missing=False)
if parsed_args.wait:
if utils.wait_for_status(
- image_client.images.get,
+ image_client.get_image,
image.id,
callback=_show_progress,
):
diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py
index b93cd4d8..fea87af8 100644
--- a/openstackclient/compute/v2/server_image.py
+++ b/openstackclient/compute/v2/server_image.py
@@ -79,14 +79,11 @@ class CreateServerImage(command.ShowOne):
)
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- image_id,
- )
+ image = image_client.find_image(image_id)
if parsed_args.wait:
if utils.wait_for_status(
- image_client.images.get,
+ image_client.get_image,
image_id,
callback=_show_progress,
):
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index 7be2a17b..e70d87d2 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -213,6 +213,15 @@ def _find_identity_resource(identity_client_manager, name_or_id,
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
+def get_immutable_options(parsed_args):
+ options = {}
+ if parsed_args.immutable:
+ options['immutable'] = True
+ if parsed_args.no_immutable:
+ options['immutable'] = False
+ return options
+
+
def add_user_domain_option_to_parser(parser):
parser.add_argument(
'--user-domain',
@@ -261,3 +270,18 @@ def add_inherited_option_to_parser(parser):
help=_('Specifies if the role grant is inheritable to the sub '
'projects'),
)
+
+
+def add_resource_option_to_parser(parser):
+ enable_group = parser.add_mutually_exclusive_group()
+ enable_group.add_argument(
+ '--immutable',
+ action='store_true',
+ help=_('Make resource immutable. An immutable project may not '
+ 'be deleted or modified except to remove the immutable flag'),
+ )
+ enable_group.add_argument(
+ '--no-immutable',
+ action='store_true',
+ help=_('Make resource mutable (default)'),
+ )
diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py
index dbcc97f6..e33fce05 100644
--- a/openstackclient/identity/v3/domain.py
+++ b/openstackclient/identity/v3/domain.py
@@ -60,6 +60,7 @@ class CreateDomain(command.ShowOne):
action='store_true',
help=_('Return existing domain'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -69,10 +70,13 @@ class CreateDomain(command.ShowOne):
if parsed_args.disable:
enabled = False
+ options = common.get_immutable_options(parsed_args)
+
try:
domain = identity_client.domains.create(
name=parsed_args.name,
description=parsed_args.description,
+ options=options,
enabled=enabled,
)
except ks_exc.Conflict:
@@ -163,6 +167,7 @@ class SetDomain(command.Command):
action='store_true',
help=_('Disable domain'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -180,6 +185,10 @@ class SetDomain(command.Command):
if parsed_args.disable:
kwargs['enabled'] = False
+ options = common.get_immutable_options(parsed_args)
+ if options:
+ kwargs['options'] = options
+
identity_client.domains.update(domain.id, **kwargs)
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index 9ecc70ef..5e8ce829 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -78,6 +78,7 @@ class CreateProject(command.ShowOne):
action='store_true',
help=_('Return existing project'),
)
+ common.add_resource_option_to_parser(parser)
tag.add_tag_option_to_parser_for_create(parser, _('project'))
return parser
@@ -99,9 +100,20 @@ class CreateProject(command.ShowOne):
enabled = True
if parsed_args.disable:
enabled = False
+
+ options = common.get_immutable_options(parsed_args)
+
kwargs = {}
if parsed_args.property:
kwargs = parsed_args.property.copy()
+ if 'is_domain' in kwargs.keys():
+ if kwargs['is_domain'].lower() == "true":
+ kwargs['is_domain'] = True
+ elif kwargs['is_domain'].lower() == "false":
+ kwargs['is_domain'] = False
+ elif kwargs['is_domain'].lower() == "none":
+ kwargs['is_domain'] = None
+
kwargs['tags'] = list(set(parsed_args.tags))
try:
@@ -111,6 +123,7 @@ class CreateProject(command.ShowOne):
parent=parent,
description=parsed_args.description,
enabled=enabled,
+ options=options,
**kwargs
)
except ks_exc.Conflict:
@@ -317,6 +330,7 @@ class SetProject(command.Command):
help=_('Set a property on <project> '
'(repeat option to set multiple properties)'),
)
+ common.add_resource_option_to_parser(parser)
tag.add_tag_option_to_parser_for_set(parser, _('project'))
return parser
@@ -336,6 +350,9 @@ class SetProject(command.Command):
kwargs['enabled'] = True
if parsed_args.disable:
kwargs['enabled'] = False
+ options = common.get_immutable_options(parsed_args)
+ if options:
+ kwargs['options'] = options
if parsed_args.property:
kwargs.update(parsed_args.property)
tag.update_tags_in_args(parsed_args, project, kwargs)
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 986f823f..980ebf11 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -177,6 +177,11 @@ class CreateRole(command.ShowOne):
help=_('New role name'),
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Add description about the role'),
+ )
+ parser.add_argument(
'--domain',
metavar='<domain>',
help=_('Domain the role belongs to (name or ID)'),
@@ -186,6 +191,7 @@ class CreateRole(command.ShowOne):
action='store_true',
help=_('Return existing role'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -196,9 +202,12 @@ class CreateRole(command.ShowOne):
domain_id = common.find_domain(identity_client,
parsed_args.domain).id
+ options = common.get_immutable_options(parsed_args)
+
try:
role = identity_client.roles.create(
- name=parsed_args.name, domain=domain_id)
+ name=parsed_args.name, domain=domain_id,
+ description=parsed_args.description, options=options)
except ks_exc.Conflict:
if parsed_args.or_show:
@@ -346,6 +355,11 @@ class SetRole(command.Command):
help=_('Role to modify (name or ID)'),
)
parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Add description about the role'),
+ )
+ parser.add_argument(
'--domain',
metavar='<domain>',
help=_('Domain the role belongs to (name or ID)'),
@@ -355,6 +369,7 @@ class SetRole(command.Command):
metavar='<name>',
help=_('Set role name'),
)
+ common.add_resource_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
@@ -365,11 +380,14 @@ class SetRole(command.Command):
domain_id = common.find_domain(identity_client,
parsed_args.domain).id
+ options = common.get_immutable_options(parsed_args)
role = utils.find_resource(identity_client.roles,
parsed_args.role,
domain_id=domain_id)
- identity_client.roles.update(role.id, name=parsed_args.name)
+ identity_client.roles.update(role.id, name=parsed_args.name,
+ description=parsed_args.description,
+ options=options)
class ShowRole(command.ShowOne):
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index ca85c5d8..cbc112a0 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -30,6 +30,114 @@ from openstackclient.identity import common
LOG = logging.getLogger(__name__)
+def _get_options_for_user(identity_client, parsed_args):
+ options = {}
+ if parsed_args.ignore_lockout_failure_attempts:
+ options['ignore_lockout_failure_attempts'] = True
+ if parsed_args.no_ignore_lockout_failure_attempts:
+ options['ignore_lockout_failure_attempts'] = False
+ if parsed_args.ignore_password_expiry:
+ options['ignore_password_expiry'] = True
+ if parsed_args.no_ignore_password_expiry:
+ options['ignore_password_expiry'] = False
+ if parsed_args.ignore_change_password_upon_first_use:
+ options['ignore_change_password_upon_first_use'] = True
+ if parsed_args.no_ignore_change_password_upon_first_use:
+ options['ignore_change_password_upon_first_use'] = False
+ if parsed_args.enable_lock_password:
+ options['lock_password'] = True
+ if parsed_args.disable_lock_password:
+ options['lock_password'] = False
+ if parsed_args.enable_multi_factor_auth:
+ 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:
+ auth_rules = [rule.split(",") for rule in
+ parsed_args.multi_factor_auth_rule]
+ if auth_rules:
+ options['multi_factor_auth_rules'] = auth_rules
+ return options
+
+
+def _add_user_options(parser):
+ # Add additional user options
+
+ parser.add_argument(
+ '--ignore-lockout-failure-attempts',
+ action="store_true",
+ help=_('Opt into ignoring the number of times a user has '
+ 'authenticated and locking out the user as a result'),
+ )
+ parser.add_argument(
+ '--no-ignore-lockout-failure-attempts',
+ action="store_true",
+ help=_('Opt out of ignoring the number of times a user has '
+ 'authenticated and locking out the user as a result'),
+ )
+ parser.add_argument(
+ '--ignore-password-expiry',
+ action="store_true",
+ help=_('Opt into allowing user to continue using passwords that '
+ 'may be expired'),
+ )
+ parser.add_argument(
+ '--no-ignore-password-expiry',
+ action="store_true",
+ help=_('Opt out of allowing user to continue using passwords '
+ 'that may be expired'),
+ )
+ parser.add_argument(
+ '--ignore-change-password-upon-first-use',
+ action="store_true",
+ help=_('Control if a user should be forced to change their password '
+ 'immediately after they log into keystone for the first time. '
+ 'Opt into ignoring the user to change their password during '
+ 'first time login in keystone'),
+ )
+ parser.add_argument(
+ '--no-ignore-change-password-upon-first-use',
+ action="store_true",
+ help=_('Control if a user should be forced to change their password '
+ 'immediately after they log into keystone for the first time. '
+ 'Opt out of ignoring the user to change their password during '
+ 'first time login in keystone'),
+ )
+ parser.add_argument(
+ '--enable-lock-password',
+ action="store_true",
+ help=_('Disables the ability for a user to change its password '
+ 'through self-service APIs'),
+ )
+ parser.add_argument(
+ '--disable-lock-password',
+ action="store_true",
+ help=_('Enables the ability for a user to change its password '
+ 'through self-service APIs'),
+ )
+ parser.add_argument(
+ '--enable-multi-factor-auth',
+ action="store_true",
+ help=_('Enables the MFA (Multi Factor Auth)'),
+ )
+ parser.add_argument(
+ '--disable-multi-factor-auth',
+ action="store_true",
+ help=_('Disables the MFA (Multi Factor Auth)'),
+ )
+ parser.add_argument(
+ '--multi-factor-auth-rule',
+ metavar='<rule>',
+ action="append",
+ default=[],
+ help=_('Set multi-factor auth rules. For example, to set a rule '
+ 'requiring the "password" and "totp" auth methods to be '
+ 'provided, use: "--multi-factor-auth-rule password,totp". '
+ 'May be provided multiple times to set different rule '
+ 'combinations.')
+ )
+
+
class CreateUser(command.ShowOne):
_description = _("Create new user")
@@ -72,6 +180,8 @@ class CreateUser(command.ShowOne):
metavar='<description>',
help=_('User description'),
)
+ _add_user_options(parser)
+
enable_group = parser.add_mutually_exclusive_group()
enable_group.add_argument(
'--enable',
@@ -113,6 +223,7 @@ class CreateUser(command.ShowOne):
if not parsed_args.password:
LOG.warning(_("No password was supplied, authentication will fail "
"when a user does not have a password."))
+ options = _get_options_for_user(identity_client, parsed_args)
try:
user = identity_client.users.create(
@@ -122,7 +233,8 @@ class CreateUser(command.ShowOne):
password=parsed_args.password,
email=parsed_args.email,
description=parsed_args.description,
- enabled=enabled
+ enabled=enabled,
+ options=options,
)
except ks_exc.Conflict:
if parsed_args.or_show:
@@ -333,6 +445,8 @@ class SetUser(command.Command):
metavar='<description>',
help=_('Set user description'),
)
+ _add_user_options(parser)
+
enable_group = parser.add_mutually_exclusive_group()
enable_group.add_argument(
'--enable',
@@ -390,6 +504,10 @@ class SetUser(command.Command):
if parsed_args.disable:
kwargs['enabled'] = False
+ options = _get_options_for_user(identity_client, parsed_args)
+ if options:
+ kwargs['options'] = options
+
identity_client.users.update(user.id, **kwargs)
diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py
index b67c291f..9a0d7bac 100644
--- a/openstackclient/image/client.py
+++ b/openstackclient/image/client.py
@@ -26,56 +26,18 @@ DEFAULT_API_VERSION = '2'
API_VERSION_OPTION = 'os_image_api_version'
API_NAME = "image"
API_VERSIONS = {
- "1": "glanceclient.v1.client.Client",
- "2": "glanceclient.v2.client.Client",
-}
-
-IMAGE_API_TYPE = 'image'
-IMAGE_API_VERSIONS = {
- '1': 'openstackclient.api.image_v1.APIv1',
- '2': 'openstackclient.api.image_v2.APIv2',
+ "1": "openstack.connection.Connection",
+ "2": "openstack.connection.Connection",
}
def make_client(instance):
- """Returns an image service client"""
- image_client = utils.get_client_class(
- API_NAME,
- instance._api_version[API_NAME],
- API_VERSIONS)
- LOG.debug('Instantiating image client: %s', image_client)
- endpoint = instance.get_endpoint_for_service_type(
- API_NAME,
- region_name=instance.region_name,
- interface=instance.interface,
+ LOG.debug(
+ 'Image client initialized using OpenStack SDK: %s',
+ instance.sdk_connection.image,
)
-
- client = image_client(
- endpoint,
- token=instance.auth.get_token(instance.session),
- cacert=instance.cacert,
- insecure=not instance.verify,
- )
-
- # Create the low-level API
-
- image_api = utils.get_client_class(
- API_NAME,
- instance._api_version[API_NAME],
- IMAGE_API_VERSIONS)
- LOG.debug('Instantiating image api: %s', image_api)
-
- client.api = image_api(
- session=instance.session,
- endpoint=instance.get_endpoint_for_service_type(
- IMAGE_API_TYPE,
- region_name=instance.region_name,
- interface=instance.interface,
- )
- )
-
- return client
+ return instance.sdk_connection.image
def build_option_parser(parser):
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index a711a128..cf1d6817 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -22,13 +22,13 @@ import os
import sys
from cliff import columns as cliff_columns
-from glanceclient.common import utils as gc_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 utils
+from openstackclient.common import sdk_utils
from openstackclient.i18n import _
if os.name == "nt":
@@ -47,6 +47,36 @@ DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx",
LOG = logging.getLogger(__name__)
+def _get_columns(item):
+ # Trick sdk_utils to return URI attribute
+ column_map = {
+ 'is_protected': 'protected',
+ 'owner_id': 'owner'
+ }
+ hidden_columns = ['location', 'checksum',
+ 'copy_from', 'created_at', 'status', 'updated_at']
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(
+ item.to_dict(), column_map, hidden_columns)
+
+
+_formatters = {
+}
+
+
+class HumanReadableSizeColumn(cliff_columns.FormattableColumn):
+ def human_readable(self):
+ """Return a formatted visibility string
+
+ :rtype:
+ A string formatted to public/private
+ """
+
+ if self._value:
+ return utils.format_size(self._value)
+ else:
+ return ''
+
+
class VisibilityColumn(cliff_columns.FormattableColumn):
def human_readable(self):
"""Return a formatted visibility string
@@ -210,7 +240,7 @@ class CreateImage(command.ShowOne):
# Special case project option back to API attribute name 'owner'
val = getattr(parsed_args, 'project', None)
if val:
- kwargs['owner'] = val
+ kwargs['owner_id'] = val
# Handle exclusive booleans with care
# Avoid including attributes in kwargs if an option is not
@@ -219,9 +249,9 @@ class CreateImage(command.ShowOne):
# to do nothing when no options are present as opposed to always
# setting a default.
if parsed_args.protected:
- kwargs['protected'] = True
+ kwargs['is_protected'] = True
if parsed_args.unprotected:
- kwargs['protected'] = False
+ kwargs['is_protected'] = False
if parsed_args.public:
kwargs['is_public'] = True
if parsed_args.private:
@@ -250,27 +280,35 @@ class CreateImage(command.ShowOne):
kwargs["data"] = io.open(parsed_args.file, "rb")
else:
# Read file from stdin
- if sys.stdin.isatty() is not True:
+ if not sys.stdin.isatty():
if msvcrt:
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
- # Send an open file handle to glanceclient so it will
- # do a chunked transfer
- kwargs["data"] = sys.stdin
+ if hasattr(sys.stdin, 'buffer'):
+ kwargs['data'] = sys.stdin.buffer
+ else:
+ kwargs["data"] = sys.stdin
if not parsed_args.volume:
# Wrap the call to catch exceptions in order to close files
try:
- image = image_client.images.create(**kwargs)
+ image = image_client.create_image(**kwargs)
finally:
# Clean up open files - make sure data isn't a string
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
kwargs['data'] != sys.stdin):
kwargs['data'].close()
+ if image:
+ display_columns, columns = _get_columns(image)
+ _formatters['properties'] = format_columns.DictColumn
+ data = utils.get_item_properties(image, columns,
+ formatters=_formatters)
+ return (display_columns, data)
+ elif info:
info.update(image._info)
info['properties'] = format_columns.DictColumn(
info.get('properties', {}))
- return zip(*sorted(info.items()))
+ return zip(*sorted(info.items()))
class DeleteImage(command.Command):
@@ -289,11 +327,8 @@ class DeleteImage(command.Command):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
for image in parsed_args.images:
- image_obj = utils.find_resource(
- image_client.images,
- image,
- )
- image_client.images.delete(image_obj.id)
+ image_obj = image_client.find_image(image)
+ image_client.delete_image(image_obj.id)
class ListImage(command.Lister):
@@ -359,15 +394,9 @@ class ListImage(command.Lister):
kwargs = {}
if parsed_args.public:
- kwargs['public'] = True
+ kwargs['is_public'] = True
if parsed_args.private:
- kwargs['private'] = True
- # Note: We specifically need to do that below to get the 'status'
- # column.
- #
- # Always set kwargs['detailed'] to True, and then filter the columns
- # according to whether the --long option is specified or not.
- kwargs['detailed'] = True
+ kwargs['is_private'] = True
if parsed_args.long:
columns = (
@@ -379,8 +408,8 @@ class ListImage(command.Lister):
'Checksum',
'Status',
'is_public',
- 'protected',
- 'owner',
+ 'is_protected',
+ 'owner_id',
'properties',
)
column_headers = (
@@ -401,16 +430,7 @@ class ListImage(command.Lister):
column_headers = columns
# List of image data received
- data = []
- # No pages received yet, so start the page marker at None.
- marker = None
- while True:
- page = image_client.api.image_list(marker=marker, **kwargs)
- if not page:
- break
- data.extend(page)
- # Set the marker to the id of the last item we received
- marker = page[-1]['id']
+ data = list(image_client.images(**kwargs))
if parsed_args.property:
# NOTE(dtroyer): coerce to a list to subscript it in py3
@@ -426,7 +446,7 @@ class ListImage(command.Lister):
return (
column_headers,
- (utils.get_dict_properties(
+ (utils.get_item_properties(
s,
columns,
formatters={
@@ -456,13 +476,9 @@ class SaveImage(command.Command):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
- data = image_client.images.data(image)
+ image = image_client.find_image(parsed_args.image)
- gc_utils.save_image(data, parsed_args.file)
+ image_client.download_image(image.id, output=parsed_args.file)
class SetImage(command.Command):
@@ -621,22 +637,17 @@ class SetImage(command.Command):
# to do nothing when no options are present as opposed to always
# setting a default.
if parsed_args.protected:
- kwargs['protected'] = True
+ kwargs['is_protected'] = True
if parsed_args.unprotected:
- kwargs['protected'] = False
+ kwargs['is_protected'] = False
if parsed_args.public:
kwargs['is_public'] = True
if parsed_args.private:
kwargs['is_public'] = False
- if parsed_args.force:
- kwargs['force'] = True
# Wrap the call to catch exceptions in order to close files
try:
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
+ image = image_client.find_image(parsed_args.image)
if not parsed_args.location and not parsed_args.copy_from:
if parsed_args.volume:
@@ -666,9 +677,10 @@ class SetImage(command.Command):
if parsed_args.stdin:
if msvcrt:
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
- # Send an open file handle to glanceclient so it
- # will do a chunked transfer
- kwargs["data"] = sys.stdin
+ if hasattr(sys.stdin, 'buffer'):
+ kwargs['data'] = sys.stdin.buffer
+ else:
+ kwargs["data"] = sys.stdin
else:
LOG.warning(_('Use --stdin to enable read image '
'data from standard input'))
@@ -677,7 +689,7 @@ class SetImage(command.Command):
image.properties.update(kwargs['properties'])
kwargs['properties'] = image.properties
- image = image_client.images.update(image.id, **kwargs)
+ image = image_client.update_image(image.id, **kwargs)
finally:
# Clean up open files - make sure data isn't a string
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
@@ -705,16 +717,12 @@ class ShowImage(command.ShowOne):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
+ image = image_client.find_image(parsed_args.image)
- info = {}
- info.update(image._info)
if parsed_args.human_readable:
- if 'size' in info:
- info['size'] = utils.format_size(info['size'])
- info['properties'] = format_columns.DictColumn(
- info.get('properties', {}))
- return zip(*sorted(info.items()))
+ _formatters['size'] = HumanReadableSizeColumn
+ display_columns, columns = _get_columns(image)
+ _formatters['properties'] = format_columns.DictColumn
+ data = utils.get_item_properties(image, columns,
+ formatters=_formatters)
+ return (display_columns, data)
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index feeb2567..53ce560d 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -18,8 +18,10 @@
import argparse
from base64 import b64encode
import logging
+import os
+import sys
-from glanceclient.common import utils as gc_utils
+import openstack.cloud._utils
from openstack.image import image_signer
from osc_lib.api import utils as api_utils
from osc_lib.cli import format_columns
@@ -27,11 +29,16 @@ 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.common import sdk_utils
from openstackclient.i18n import _
from openstackclient.identity import common
+if os.name == "nt":
+ import msvcrt
+else:
+ msvcrt = None
+
CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"]
DEFAULT_CONTAINER_FORMAT = 'bare'
@@ -44,7 +51,7 @@ MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"]
LOG = logging.getLogger(__name__)
-def _format_image(image):
+def _format_image(image, human_readable=False):
"""Format an image to make it more consistent with OSC operations."""
info = {}
@@ -56,15 +63,25 @@ def _format_image(image):
'min_disk', 'protected', 'id', 'file', 'checksum',
'owner', 'virtual_size', 'min_ram', 'schema']
+ # TODO(gtema/anybody): actually it should be possible to drop this method,
+ # since SDK already delivers a proper object
+ image = image.to_dict(ignore_none=True, original_names=True)
+
# split out the usual key and the properties which are top-level
- for key in six.iterkeys(image):
+ for key in image:
if key in fields_to_show:
info[key] = image.get(key)
elif key == 'tags':
continue # handle this later
- else:
+ elif key == 'properties':
+ # NOTE(gtema): flatten content of properties
+ properties.update(image.get(key))
+ elif key != 'location':
properties[key] = image.get(key)
+ if human_readable:
+ info['size'] = utils.format_size(image['size'])
+
# format the tags if they are there
info['tags'] = format_columns.ListColumn(image.get('tags'))
@@ -75,6 +92,51 @@ def _format_image(image):
return info
+_formatters = {
+ 'tags': format_columns.ListColumn,
+}
+
+
+def _get_member_columns(item):
+ # Trick sdk_utils to return URI attribute
+ column_map = {
+ 'image_id': 'image_id'
+ }
+ hidden_columns = ['id', 'location', 'name']
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(
+ item.to_dict(), column_map, hidden_columns)
+
+
+def get_data_file(args):
+ if args.file:
+ return (open(args.file, 'rb'), args.file)
+ else:
+ # distinguish cases where:
+ # (1) stdin is not valid (as in cron jobs):
+ # openstack ... <&-
+ # (2) image data is provided through stdin:
+ # openstack ... < /tmp/file
+ # (3) no image data provided
+ # openstack ...
+ try:
+ os.fstat(0)
+ except OSError:
+ # (1) stdin is not valid
+ return (None, None)
+ if not sys.stdin.isatty():
+ # (2) image data is provided through stdin
+ image = sys.stdin
+ if hasattr(sys.stdin, 'buffer'):
+ image = sys.stdin.buffer
+ if msvcrt:
+ msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
+
+ return (image, None)
+ else:
+ # (3)
+ return (None, None)
+
+
class AddProjectToImage(command.ShowOne):
_description = _("Associate project with image")
@@ -97,20 +159,26 @@ class AddProjectToImage(command.ShowOne):
image_client = self.app.client_manager.image
identity_client = self.app.client_manager.identity
- project_id = common.find_project(identity_client,
- parsed_args.project,
- parsed_args.project_domain).id
+ if openstack.cloud._utils._is_uuid_like(parsed_args.project):
+ project_id = parsed_args.project
+ else:
+ project_id = common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain).id
- image_id = utils.find_resource(
- image_client.images,
- parsed_args.image).id
+ image = image_client.find_image(parsed_args.image,
+ ignore_missing=False)
- image_member = image_client.image_members.create(
- image_id,
- project_id,
+ obj = image_client.add_member(
+ image=image.id,
+ member_id=project_id,
)
- return zip(*sorted(image_member.items()))
+ display_columns, columns = _get_member_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters={})
+
+ return (display_columns, data)
class CreateImage(command.ShowOne):
@@ -302,9 +370,9 @@ class CreateImage(command.ShowOne):
# to do nothing when no options are present as opposed to always
# setting a default.
if parsed_args.protected:
- kwargs['protected'] = True
+ kwargs['is_protected'] = True
if parsed_args.unprotected:
- kwargs['protected'] = False
+ kwargs['is_protected'] = False
if parsed_args.public:
kwargs['visibility'] = 'public'
if parsed_args.private:
@@ -314,24 +382,30 @@ class CreateImage(command.ShowOne):
if parsed_args.shared:
kwargs['visibility'] = 'shared'
if parsed_args.project:
- kwargs['owner'] = common.find_project(
+ kwargs['owner_id'] = common.find_project(
identity_client,
parsed_args.project,
parsed_args.project_domain,
).id
# open the file first to ensure any failures are handled before the
- # image is created
- fp = gc_utils.get_data_file(parsed_args)
+ # image is created. Get the file name (if it is file, and not stdin)
+ # for easier further handling.
+ (fp, fname) = get_data_file(parsed_args)
info = {}
+
if fp is not None and parsed_args.volume:
raise exceptions.CommandError(_("Uploading data and using "
"container are not allowed at "
"the same time"))
-
if fp is None and parsed_args.file:
LOG.warning(_("Failed to get an image file."))
return {}, {}
+ elif fname:
+ kwargs['filename'] = fname
+ elif fp:
+ kwargs['validate_checksum'] = False
+ kwargs['data'] = fp
# sign an image using a given local private key file
if parsed_args.sign_key_path or parsed_args.sign_cert_id:
@@ -361,8 +435,8 @@ class CreateImage(command.ShowOne):
sign_key_path,
password=pw)
except Exception:
- msg = (_("Error during sign operation: private key could "
- "not be loaded."))
+ msg = (_("Error during sign operation: private key "
+ "could not be loaded."))
raise exceptions.CommandError(msg)
signature = signer.generate_signature(fp)
@@ -371,7 +445,8 @@ class CreateImage(command.ShowOne):
kwargs['img_signature_certificate_uuid'] = sign_cert_id
kwargs['img_signature_hash_method'] = signer.hash_method
if signer.padding_method:
- kwargs['img_signature_key_type'] = signer.padding_method
+ kwargs['img_signature_key_type'] = \
+ signer.padding_method
# If a volume is specified.
if parsed_args.volume:
@@ -393,26 +468,7 @@ class CreateImage(command.ShowOne):
except TypeError:
info['volume_type'] = None
else:
- image = image_client.images.create(**kwargs)
-
- if fp is not None:
- with fp:
- try:
- image_client.images.upload(image.id, fp)
- except Exception:
- # If the upload fails for some reason attempt to remove the
- # dangling queued image made by the create() call above but
- # only if the user did not specify an id which indicates
- # the Image already exists and should be left alone.
- try:
- if 'id' not in kwargs:
- image_client.images.delete(image.id)
- except Exception:
- pass # we don't care about this one
- raise # now, throw the upload exception again
-
- # update the image after the data has been uploaded
- image = image_client.images.get(image.id)
+ image = image_client.create_image(**kwargs)
if not info:
info = _format_image(image)
@@ -439,11 +495,9 @@ class DeleteImage(command.Command):
image_client = self.app.client_manager.image
for image in parsed_args.images:
try:
- image_obj = utils.find_resource(
- image_client.images,
- image,
- )
- image_client.images.delete(image_obj.id)
+ image_obj = image_client.find_image(image,
+ ignore_missing=False)
+ image_client.delete_image(image_obj.id)
except Exception as e:
del_result += 1
LOG.error(_("Failed to delete image with name or "
@@ -569,18 +623,17 @@ class ListImage(command.Lister):
kwargs = {}
if parsed_args.public:
- kwargs['public'] = True
+ kwargs['visibility'] = 'public'
if parsed_args.private:
- kwargs['private'] = True
+ kwargs['visibility'] = 'private'
if parsed_args.community:
- kwargs['community'] = True
+ kwargs['visibility'] = 'community'
if parsed_args.shared:
- kwargs['shared'] = True
+ kwargs['visibility'] = 'shared'
if parsed_args.limit:
kwargs['limit'] = parsed_args.limit
if parsed_args.marker:
- kwargs['marker'] = utils.find_resource(image_client.images,
- parsed_args.marker).id
+ kwargs['marker'] = image_client.find_image(parsed_args.marker).id
if parsed_args.name:
kwargs['name'] = parsed_args.name
if parsed_args.status:
@@ -599,8 +652,8 @@ class ListImage(command.Lister):
'Checksum',
'Status',
'visibility',
- 'protected',
- 'owner',
+ 'is_protected',
+ 'owner_id',
'tags',
)
column_headers = (
@@ -621,24 +674,10 @@ class ListImage(command.Lister):
column_headers = columns
# List of image data received
- data = []
- limit = None
if 'limit' in kwargs:
- limit = kwargs['limit']
- if 'marker' in kwargs:
- data = image_client.api.image_list(**kwargs)
- else:
- # No pages received yet, so start the page marker at None.
- marker = None
- while True:
- page = image_client.api.image_list(marker=marker, **kwargs)
- if not page:
- break
- data.extend(page)
- # Set the marker to the id of the last item we received
- marker = page[-1]['id']
- if limit:
- break
+ # Disable automatic pagination in SDK
+ kwargs['paginated'] = False
+ data = list(image_client.images(**kwargs))
if parsed_args.property:
for attr, value in parsed_args.property.items():
@@ -653,12 +692,10 @@ class ListImage(command.Lister):
return (
column_headers,
- (utils.get_dict_properties(
+ (utils.get_item_properties(
s,
columns,
- formatters={
- 'tags': format_columns.ListColumn,
- },
+ formatters=_formatters,
) for s in data)
)
@@ -684,11 +721,9 @@ class ListImageProjects(command.Lister):
"Status"
)
- image_id = utils.find_resource(
- image_client.images,
- parsed_args.image).id
+ image_id = image_client.find_image(parsed_args.image).id
- data = image_client.image_members.list(image_id)
+ data = image_client.members(image=image_id)
return (columns,
(utils.get_item_properties(
@@ -722,11 +757,12 @@ class RemoveProjectImage(command.Command):
parsed_args.project,
parsed_args.project_domain).id
- image_id = utils.find_resource(
- image_client.images,
- parsed_args.image).id
+ image = image_client.find_image(parsed_args.image,
+ ignore_missing=False)
- image_client.image_members.delete(image_id, project_id)
+ image_client.remove_member(
+ member=project_id,
+ image=image.id)
class SaveImage(command.Command):
@@ -748,19 +784,9 @@ class SaveImage(command.Command):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
- data = image_client.images.data(image.id)
+ image = image_client.find_image(parsed_args.image)
- if data.wrapped is None:
- msg = _('Image %s has no data.') % image.id
- LOG.error(msg)
- self.app.stdout.write(msg + '\n')
- raise SystemExit
-
- gc_utils.save_image(data, parsed_args.file)
+ image_client.download_image(image.id, output=parsed_args.file)
class SetImage(command.Command):
@@ -979,9 +1005,9 @@ class SetImage(command.Command):
# to do nothing when no options are present as opposed to always
# setting a default.
if parsed_args.protected:
- kwargs['protected'] = True
+ kwargs['is_protected'] = True
if parsed_args.unprotected:
- kwargs['protected'] = False
+ kwargs['is_protected'] = False
if parsed_args.public:
kwargs['visibility'] = 'public'
if parsed_args.private:
@@ -997,17 +1023,20 @@ class SetImage(command.Command):
parsed_args.project,
parsed_args.project_domain,
).id
- kwargs['owner'] = project_id
+ kwargs['owner_id'] = project_id
+
+ image = image_client.find_image(parsed_args.image,
+ ignore_missing=False)
- image = utils.find_resource(
- image_client.images, parsed_args.image)
+ # image = utils.find_resource(
+ # image_client.images, parsed_args.image)
activation_status = None
if parsed_args.deactivate:
- image_client.images.deactivate(image.id)
+ image_client.deactivate_image(image.id)
activation_status = "deactivated"
if parsed_args.activate:
- image_client.images.reactivate(image.id)
+ image_client.reactivate_image(image.id)
activation_status = "activated"
membership_group_args = ('accept', 'reject', 'pending')
@@ -1022,15 +1051,15 @@ class SetImage(command.Command):
# most one item in the membership_status list.
if membership_status[0] != 'pending':
membership_status[0] += 'ed' # Glance expects the past form
- image_client.image_members.update(
- image.id, project_id, membership_status[0])
+ image_client.update_member(
+ image=image.id, member=project_id, status=membership_status[0])
if parsed_args.tags:
# Tags should be extended, but duplicates removed
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
try:
- image = image_client.images.update(image.id, **kwargs)
+ image = image_client.update_image(image.id, **kwargs)
except Exception:
if activation_status is not None:
LOG.info(_("Image %(id)s was %(status)s."),
@@ -1058,14 +1087,11 @@ class ShowImage(command.ShowOne):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
- if parsed_args.human_readable:
- image['size'] = utils.format_size(image['size'])
- info = _format_image(image)
+ image = image_client.find_image(parsed_args.image,
+ ignore_missing=False)
+
+ info = _format_image(image, parsed_args.human_readable)
return zip(*sorted(info.items()))
@@ -1101,10 +1127,8 @@ class UnsetImage(command.Command):
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
- image = utils.find_resource(
- image_client.images,
- parsed_args.image,
- )
+ image = image_client.find_image(parsed_args.image,
+ ignore_missing=False)
kwargs = {}
tagret = 0
@@ -1112,7 +1136,7 @@ class UnsetImage(command.Command):
if parsed_args.tags:
for k in parsed_args.tags:
try:
- image_client.image_tags.delete(image.id, k)
+ image_client.remove_tag(image.id, k)
except Exception:
LOG.error(_("tag unset failed, '%s' is a "
"nonexistent tag "), k)
@@ -1120,13 +1144,26 @@ class UnsetImage(command.Command):
if parsed_args.properties:
for k in parsed_args.properties:
- if k not in image:
+ if k in image:
+ kwargs[k] = None
+ elif k in image.properties:
+ # Since image is an "evil" object from SDK POV we need to
+ # pass modified properties object, so that SDK can figure
+ # out, what was changed inside
+ # NOTE: ping gtema to improve that in SDK
+ new_props = kwargs.get('properties',
+ image.get('properties').copy())
+ new_props.pop(k, None)
+ kwargs['properties'] = new_props
+ else:
LOG.error(_("property unset failed, '%s' is a "
"nonexistent property "), k)
propret += 1
- image_client.images.update(
- image.id,
- parsed_args.properties,
+
+ # We must give to update a current image for the reference on what
+ # has changed
+ image_client.update_image(
+ image,
**kwargs)
tagtotal = len(parsed_args.tags)
diff --git a/openstackclient/network/sdk_utils.py b/openstackclient/network/sdk_utils.py
index af9c74f9..cff30713 100644
--- a/openstackclient/network/sdk_utils.py
+++ b/openstackclient/network/sdk_utils.py
@@ -10,6 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import munch
+
def get_osc_show_columns_for_sdk_resource(
sdk_resource,
@@ -38,6 +40,9 @@ def get_osc_show_columns_for_sdk_resource(
# Build the OSC column names to display for the SDK resource.
attr_map = {}
display_columns = list(resource_dict.keys())
+ for col_name in display_columns:
+ if isinstance(resource_dict[col_name], munch.Munch):
+ display_columns.remove(col_name)
invisible_columns = [] if invisible_columns is None else invisible_columns
for col_name in invisible_columns:
if col_name in display_columns:
diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 7efbb631..71c1a9af 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -15,7 +15,6 @@
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
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -106,7 +100,7 @@ class CreateAddressScope(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_address_scope(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -295,6 +289,6 @@ class ShowAddressScope(command.ShowOne):
parsed_args.address_scope,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index 4525913f..a2765cd1 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -13,7 +13,6 @@
"""IP Floating action implementations"""
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
from osc_lib.utils import tags as _tag
@@ -25,7 +24,6 @@ from openstackclient.network import sdk_utils
_formatters = {
- 'location': format_columns.DictColumn,
'port_details': utils.format_dict,
}
@@ -412,6 +410,11 @@ class SetFloatingIP(command.Command):
help=_("Fixed IP of the port "
"(required only if port has multiple IPs)")
)
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Set floating IP description')
+ )
qos_policy_group = parser.add_mutually_exclusive_group()
qos_policy_group.add_argument(
'--qos-policy',
@@ -443,6 +446,9 @@ class SetFloatingIP(command.Command):
if parsed_args.fixed_ip_address:
attrs['fixed_ip_address'] = parsed_args.fixed_ip_address
+ if parsed_args.description:
+ attrs['description'] = parsed_args.description
+
if parsed_args.qos_policy:
attrs['qos_policy_id'] = client.find_qos_policy(
parsed_args.qos_policy, ignore_missing=False).id
diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py
index f94bcc06..06b3df8b 100644
--- a/openstackclient/network/v2/floating_ip_port_forwarding.py
+++ b/openstackclient/network/v2/floating_ip_port_forwarding.py
@@ -75,6 +75,12 @@ class CreateFloatingIPPortForwarding(command.ShowOne):
required=True,
help=_("The protocol used in the floating IP "
"port forwarding, for instance: TCP, UDP")
+ ),
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("A text to describe/contextualize the use of the "
+ "port forwarding configuration")
)
parser.add_argument(
'floating_ip',
@@ -113,6 +119,9 @@ class CreateFloatingIPPortForwarding(command.ShowOne):
attrs['internal_ip_address'] = parsed_args.internal_ip_address
attrs['protocol'] = parsed_args.protocol
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+
obj = client.create_floating_ip_port_forwarding(
floating_ip.id,
**attrs
@@ -212,6 +221,7 @@ class ListFloatingIPPortForwarding(command.Lister):
'internal_port',
'external_port',
'protocol',
+ 'description',
)
headers = (
'ID',
@@ -220,6 +230,7 @@ class ListFloatingIPPortForwarding(command.Lister):
'Internal Port',
'External Port',
'Protocol',
+ 'Description',
)
query = {}
@@ -296,6 +307,12 @@ class SetFloatingIPPortForwarding(command.Command):
metavar='<protocol>',
choices=['tcp', 'udp'],
help=_("The IP protocol used in the floating IP port forwarding")
+ ),
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("A text to describe/contextualize the use of "
+ "the port forwarding configuration")
)
return parser
@@ -332,6 +349,9 @@ class SetFloatingIPPortForwarding(command.Command):
if parsed_args.protocol:
attrs['protocol'] = parsed_args.protocol
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+
client.update_floating_ip_port_forwarding(
floating_ip.id, parsed_args.port_forwarding_id, **attrs)
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index c026baa0..ddc88e55 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -21,9 +21,7 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import sdk_utils
-
_formatters = {
- 'location': format_columns.DictColumn,
'subnet_ip_availability': format_columns.ListDictColumn,
}
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 3f579b6d..7a12d523 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -16,7 +16,6 @@
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 osc_lib.utils import tags as _tag
@@ -41,7 +40,6 @@ _formatters = {
'subnet_ids': format_columns.ListColumn,
'admin_state_up': AdminStateColumn,
'is_admin_state_up': AdminStateColumn,
- 'location': format_columns.DictColumn,
'router:external': RouterExternalColumn,
'is_router_external': RouterExternalColumn,
'availability_zones': format_columns.ListColumn,
@@ -126,9 +124,6 @@ def _get_attrs_network(client_manager, parsed_args):
attrs['is_default'] = False
if parsed_args.default:
attrs['is_default'] = True
- if attrs.get('is_default') and not attrs.get('router:external'):
- msg = _("Cannot set default for internal network")
- raise exceptions.CommandError(msg)
# Update Provider network options
if parsed_args.provider_network_type:
attrs['provider:network_type'] = parsed_args.provider_network_type
@@ -706,8 +701,7 @@ class SetNetwork(command.Command):
default_router_grp.add_argument(
'--default',
action='store_true',
- help=_("Set the network as the default external network "
- "(cannot be used with internal network).")
+ help=_("Set the network as the default external network")
)
default_router_grp.add_argument(
'--no-default',
diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py
index a6ed3629..16784854 100644
--- a/openstackclient/network/v2/network_agent.py
+++ b/openstackclient/network/v2/network_agent.py
@@ -43,7 +43,6 @@ _formatters = {
'alive': AliveColumn,
'admin_state_up': AdminStateColumn,
'is_admin_state_up': AdminStateColumn,
- 'location': format_columns.DictColumn,
'configurations': format_columns.DictColumn,
}
diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py
index f6070a02..36f39200 100644
--- a/openstackclient/network/v2/network_auto_allocated_topology.py
+++ b/openstackclient/network/v2/network_auto_allocated_topology.py
@@ -15,7 +15,6 @@
import logging
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -26,11 +25,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -99,17 +93,16 @@ class CreateAutoAllocatedTopology(command.ShowOne):
obj = client.validate_auto_allocated_topology(parsed_args.project)
columns = _format_check_resource_columns()
- data = utils.get_item_properties(
- _format_check_resource(obj),
- columns,
- formatters=_formatters,
- )
+ data = utils.get_item_properties(_format_check_resource(obj),
+ columns,
+ formatters={})
+
return (columns, data)
def get_topology(self, client, parsed_args):
obj = client.get_auto_allocated_topology(parsed_args.project)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
def take_action(self, parsed_args):
diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py
index 355d04c1..c9d368bf 100644
--- a/openstackclient/network/v2/network_flavor.py
+++ b/openstackclient/network/v2/network_flavor.py
@@ -15,7 +15,6 @@
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
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
@@ -142,7 +136,7 @@ class CreateNetworkFlavor(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_flavor(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -306,5 +300,5 @@ class ShowNetworkFlavor(command.ShowOne):
client = self.app.client_manager.network
obj = client.find_flavor(parsed_args.flavor, ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py
index 492fd432..6cf0c412 100644
--- a/openstackclient/network/v2/network_flavor_profile.py
+++ b/openstackclient/network/v2/network_flavor_profile.py
@@ -13,7 +13,6 @@
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
@@ -26,11 +25,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_enabled': 'enabled',
@@ -116,7 +110,7 @@ class CreateNetworkFlavorProfile(command.ShowOne):
obj = client.create_service_profile(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -252,5 +246,5 @@ class ShowNetworkFlavorProfile(command.ShowOne):
obj = client.find_service_profile(parsed_args.flavor_profile,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py
index cde7a304..df0e1da1 100644
--- a/openstackclient/network/v2/network_meter.py
+++ b/openstackclient/network/v2/network_meter.py
@@ -15,7 +15,6 @@
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
@@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -108,7 +102,7 @@ class CreateMeter(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_metering_label(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -192,5 +186,5 @@ class ShowMeter(command.ShowOne):
obj = client.find_metering_label(parsed_args.meter,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py
index 5f31255a..49ff9e1b 100644
--- a/openstackclient/network/v2/network_meter_rule.py
+++ b/openstackclient/network/v2/network_meter_rule.py
@@ -15,7 +15,6 @@
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
@@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -122,7 +116,7 @@ class CreateMeterRule(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_metering_label_rule(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -205,5 +199,5 @@ class ShowMeterRule(command.ShowOne):
obj = client.find_metering_label_rule(parsed_args.meter_rule_id,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py
index 1622de4a..fd5ff937 100644
--- a/openstackclient/network/v2/network_qos_policy.py
+++ b/openstackclient/network/v2/network_qos_policy.py
@@ -15,7 +15,6 @@
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
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'is_shared': 'shared',
@@ -125,7 +119,7 @@ class CreateNetworkQosPolicy(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_qos_policy(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns, formatters={})
return (display_columns, data)
@@ -285,5 +279,5 @@ class ShowNetworkQosPolicy(command.ShowOne):
obj = client.find_qos_policy(parsed_args.policy,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
index d74beda7..28c5600a 100644
--- a/openstackclient/network/v2/network_qos_rule.py
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -15,7 +15,6 @@
import itertools
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -47,11 +46,6 @@ ACTION_SET = 'update'
ACTION_SHOW = 'get'
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
@@ -214,7 +208,7 @@ class CreateNetworkQosRule(command.ShowOne):
msg = (_('Failed to create Network QoS rule: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
@@ -364,5 +358,5 @@ class ShowNetworkQosRule(command.ShowOne):
{'rule': rule_id, 'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py
index e842944c..7b92c8ad 100644
--- a/openstackclient/network/v2/network_qos_rule_type.py
+++ b/openstackclient/network/v2/network_qos_rule_type.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
@@ -21,11 +20,6 @@ from openstackclient.i18n import _
from openstackclient.network import sdk_utils
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
"type": "rule_type_name",
@@ -71,5 +65,5 @@ class ShowNetworkQosRuleType(command.ShowOne):
client = self.app.client_manager.network
obj = client.get_qos_rule_type(parsed_args.rule_type)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index 1781193f..b88ef019 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -15,7 +15,6 @@
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
@@ -28,11 +27,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
column_map = {
'target_tenant': 'target_project_id',
@@ -58,6 +52,15 @@ def _get_attrs(client_manager, parsed_args):
object_id = network_client.find_security_group(
parsed_args.rbac_object,
ignore_missing=False).id
+ if parsed_args.type == 'address_scope':
+ object_id = network_client.find_address_scope(
+ parsed_args.rbac_object,
+ ignore_missing=False).id
+ if parsed_args.type == 'subnetpool':
+ object_id = network_client.find_subnet_pool(
+ parsed_args.rbac_object,
+ ignore_missing=False).id
+
attrs['object_id'] = object_id
identity_client = client_manager.identity
@@ -97,9 +100,11 @@ class CreateNetworkRBAC(command.ShowOne):
'--type',
metavar="<type>",
required=True,
- choices=['security_group', 'qos_policy', 'network'],
+ choices=['address_scope', 'security_group', 'subnetpool',
+ 'qos_policy', 'network'],
help=_('Type of the object that RBAC policy '
- 'affects ("security_group", "qos_policy" or "network")')
+ 'affects ("address_scope", "security_group", "subnetpool",'
+ ' "qos_policy" or "network")')
)
parser.add_argument(
'--action',
@@ -142,7 +147,7 @@ class CreateNetworkRBAC(command.ShowOne):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_rbac_policy(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
@@ -188,10 +193,11 @@ class ListNetworkRBAC(command.Lister):
parser.add_argument(
'--type',
metavar='<type>',
- choices=['security_group', 'qos_policy', 'network'],
+ choices=['address_scope', 'security_group', 'subnetpool',
+ 'qos_policy', 'network'],
help=_('List network RBAC policies according to '
- 'given object type ("security_group", "qos_policy" '
- 'or "network")')
+ 'given object type ("address_scope", "security_group", '
+ '"subnetpool", "qos_policy" or "network")')
)
parser.add_argument(
'--action',
@@ -299,5 +305,5 @@ class ShowNetworkRBAC(command.ShowOne):
obj = client.find_rbac_policy(parsed_args.rbac_policy,
ignore_missing=False)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return display_columns, data
diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py
index 5899dc69..c1a672e2 100644
--- a/openstackclient/network/v2/network_segment.py
+++ b/openstackclient/network/v2/network_segment.py
@@ -15,7 +15,6 @@
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
@@ -27,11 +26,6 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
@@ -96,7 +90,7 @@ class CreateNetworkSegment(command.ShowOne):
attrs['segmentation_id'] = parsed_args.segment
obj = client.create_segment(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
@@ -248,5 +242,5 @@ class ShowNetworkSegment(command.ShowOne):
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py
index 2cdae642..6229995a 100644
--- a/openstackclient/network/v2/network_segment_range.py
+++ b/openstackclient/network/v2/network_segment_range.py
@@ -19,11 +19,9 @@
import itertools
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
-import six
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -32,17 +30,13 @@ from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
def _get_columns(item):
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {})
def _get_ranges(item):
- item = [int(i) if isinstance(i, six.string_types) else i for i in item]
+ item = sorted([int(i) for i in item])
for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]):
b = list(b)
yield "%s-%s" % (b[0][1], b[-1][1]) if b[0][1] != b[-1][1] else \
@@ -217,7 +211,7 @@ class CreateNetworkSegmentRange(command.ShowOne):
attrs['physical_network'] = parsed_args.physical_network
obj = network_client.create_network_segment_range(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
@@ -456,6 +450,6 @@ class ShowNetworkSegmentRange(command.ShowOne):
ignore_missing=False
)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
data = _update_additional_fields_from_props(columns, props=data)
return (display_columns, data)
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index a22bcafb..a21324ae 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -51,7 +51,6 @@ _formatters = {
'dns_assignment': format_columns.ListDictColumn,
'extra_dhcp_opts': format_columns.ListDictColumn,
'fixed_ips': format_columns.ListDictColumn,
- 'location': format_columns.DictColumn,
'security_group_ids': format_columns.ListColumn,
'tags': format_columns.ListColumn,
}
@@ -516,6 +515,10 @@ class ListPort(command.Lister):
"network:dhcp).")
)
parser.add_argument(
+ '--host',
+ metavar='<host-id>',
+ help=_("List only ports bound to this host ID"))
+ parser.add_argument(
'--network',
metavar='<network>',
help=_("List only ports connected to this network (name or ID)"))
@@ -603,6 +606,8 @@ class ListPort(command.Lister):
server = utils.find_resource(compute_client.servers,
parsed_args.server)
filters['device_id'] = server.id
+ if parsed_args.host:
+ filters['binding:host_id'] = parsed_args.host
if parsed_args.network:
network = network_client.find_network(parsed_args.network,
ignore_missing=False)
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 464dbbec..e3e8accd 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -61,7 +61,6 @@ _formatters = {
'external_gateway_info': RouterInfoColumn,
'availability_zones': format_columns.ListColumn,
'availability_zone_hints': format_columns.ListColumn,
- 'location': format_columns.DictColumn,
'routes': RoutesColumn,
'tags': format_columns.ListColumn,
}
@@ -168,6 +167,93 @@ class AddSubnetToRouter(command.Command):
subnet_id=subnet.id)
+class AddExtraRoutesToRouter(command.ShowOne):
+ _description = _("Add extra static routes to a router's routing table.")
+
+ def get_parser(self, prog_name):
+ parser = super(AddExtraRoutesToRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router to which extra static routes "
+ "will be added (name or ID).")
+ )
+ parser.add_argument(
+ '--route',
+ metavar='destination=<subnet>,gateway=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ dest='routes',
+ default=[],
+ required_keys=['destination', 'gateway'],
+ help=_("Add extra static route to the router. "
+ "destination: destination subnet (in CIDR notation), "
+ "gateway: nexthop IP address. "
+ "Repeat option to add multiple routes. "
+ "Trying to add a route that's already present "
+ "(exactly, including destination and nexthop) "
+ "in the routing table is allowed and is considered "
+ "a successful operation.")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ if parsed_args.routes is not None:
+ for route in parsed_args.routes:
+ route['nexthop'] = route.pop('gateway')
+ client = self.app.client_manager.network
+ router_obj = client.add_extra_routes_to_router(
+ client.find_router(parsed_args.router, ignore_missing=False),
+ body={'router': {'routes': parsed_args.routes}})
+ display_columns, columns = _get_columns(router_obj)
+ data = utils.get_item_properties(
+ router_obj, columns, formatters=_formatters)
+ return (display_columns, data)
+
+
+class RemoveExtraRoutesFromRouter(command.ShowOne):
+ _description = _(
+ "Remove extra static routes from a router's routing table.")
+
+ def get_parser(self, prog_name):
+ parser = super(RemoveExtraRoutesFromRouter, self).get_parser(prog_name)
+ parser.add_argument(
+ 'router',
+ metavar='<router>',
+ help=_("Router from which extra static routes "
+ "will be removed (name or ID).")
+ )
+ parser.add_argument(
+ '--route',
+ metavar='destination=<subnet>,gateway=<ip-address>',
+ action=parseractions.MultiKeyValueAction,
+ dest='routes',
+ default=[],
+ required_keys=['destination', 'gateway'],
+ help=_("Remove extra static route from the router. "
+ "destination: destination subnet (in CIDR notation), "
+ "gateway: nexthop IP address. "
+ "Repeat option to remove multiple routes. "
+ "Trying to remove a route that's already missing "
+ "(fully, including destination and nexthop) "
+ "from the routing table is allowed and is considered "
+ "a successful operation.")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ if parsed_args.routes is not None:
+ for route in parsed_args.routes:
+ route['nexthop'] = route.pop('gateway')
+ client = self.app.client_manager.network
+ router_obj = client.remove_extra_routes_from_router(
+ client.find_router(parsed_args.router, ignore_missing=False),
+ body={'router': {'routes': parsed_args.routes}})
+ display_columns, columns = _get_columns(router_obj)
+ data = utils.get_item_properties(
+ router_obj, columns, formatters=_formatters)
+ return (display_columns, data)
+
+
# TODO(yanxing'an): Use the SDK resource mapped attribute names once the
# OSC minimum requirements include SDK 1.0.
class CreateRouter(command.ShowOne):
@@ -540,17 +626,21 @@ class SetRouter(command.Command):
dest='routes',
default=None,
required_keys=['destination', 'gateway'],
- help=_("Routes associated with the router "
+ help=_("Add routes to the router "
"destination: destination subnet (in CIDR notation) "
"gateway: nexthop IP address "
- "(repeat option to set multiple routes)")
+ "(repeat option to add multiple routes). "
+ "This is deprecated in favor of 'router add/remove route' "
+ "since it is prone to race conditions between concurrent "
+ "clients when not used together with --no-route to "
+ "overwrite the current value of 'routes'.")
)
parser.add_argument(
'--no-route',
action='store_true',
help=_("Clear routes associated with the router. "
"Specify both --route and --no-route to overwrite "
- "current value of route.")
+ "current value of routes.")
)
routes_ha = parser.add_mutually_exclusive_group()
routes_ha.add_argument(
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index f8153fa8..0732c23e 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -16,7 +16,6 @@
import argparse
from cliff import columns as cliff_columns
-from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import utils
from osc_lib.utils import tags as _tag
@@ -77,13 +76,11 @@ class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn):
_formatters_network = {
- 'location': format_columns.DictColumn,
'security_group_rules': NetworkSecurityGroupRulesColumn,
}
_formatters_compute = {
- 'location': format_columns.DictColumn,
'rules': ComputeSecurityGroupRulesColumn,
}
@@ -120,6 +117,19 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
metavar='<project>',
help=self.enhance_help_neutron(_("Owner's project (name or ID)"))
)
+ stateful_group = parser.add_mutually_exclusive_group()
+ stateful_group.add_argument(
+ "--stateful",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateful (Default)")
+ )
+ stateful_group.add_argument(
+ "--stateless",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateless")
+ )
identity_common.add_project_domain_option_to_parser(
parser, enhance_help=self.enhance_help_neutron)
_tag.add_tag_option_to_parser_for_create(
@@ -138,6 +148,10 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne):
attrs = {}
attrs['name'] = parsed_args.name
attrs['description'] = self._get_description(parsed_args)
+ if parsed_args.stateful:
+ attrs['stateful'] = True
+ if parsed_args.stateless:
+ attrs['stateful'] = False
if parsed_args.project is not None:
identity_client = self.app.client_manager.identity
project_id = identity_common.find_project(
@@ -202,6 +216,7 @@ class DeleteSecurityGroup(common.NetworkAndComputeDelete):
# the OSC minimum requirements include SDK 1.0.
class ListSecurityGroup(common.NetworkAndComputeLister):
_description = _("List security groups")
+ FIELDS_TO_RETRIEVE = ['id', 'name', 'description', 'project_id', 'tags']
def update_parser_network(self, parser):
if not self.is_docs_build:
@@ -251,7 +266,8 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
filters['project_id'] = project_id
_tag.get_tag_filtering_args(parsed_args, filters)
- data = client.security_groups(**filters)
+ data = client.security_groups(fields=self.FIELDS_TO_RETRIEVE,
+ **filters)
columns = (
"ID",
@@ -313,6 +329,19 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
metavar="<description>",
help=_("New security group description")
)
+ stateful_group = parser.add_mutually_exclusive_group()
+ stateful_group.add_argument(
+ "--stateful",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateful (Default)")
+ )
+ stateful_group.add_argument(
+ "--stateless",
+ action='store_true',
+ default=None,
+ help=_("Security group is stateless")
+ )
return parser
def update_parser_network(self, parser):
@@ -329,6 +358,10 @@ class SetSecurityGroup(common.NetworkAndComputeCommand):
attrs['name'] = parsed_args.name
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
+ if parsed_args.stateful:
+ attrs['stateful'] = True
+ if parsed_args.stateless:
+ attrs['stateful'] = False
# NOTE(rtheis): Previous behavior did not raise a CommandError
# if there were no updates. Maintain this behavior and issue
# the update.
diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index f48478ea..1fbd97ab 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -16,7 +16,6 @@
import argparse
import logging
-from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib import exceptions
from osc_lib import utils
@@ -31,11 +30,6 @@ from openstackclient.network import utils as network_utils
LOG = logging.getLogger(__name__)
-_formatters = {
- 'location': format_columns.DictColumn,
-}
-
-
def _format_security_group_rule_show(obj):
data = network_utils.transform_compute_security_group_rule(obj)
return zip(*sorted(data.items()))
@@ -353,7 +347,7 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
# Create and show the security group rule.
obj = client.create_security_group_rule(**attrs)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
def take_action_compute(self, client, parsed_args):
@@ -620,7 +614,7 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
if not obj['remote_ip_prefix']:
obj['remote_ip_prefix'] = _format_remote_ip_prefix(obj)
display_columns, columns = _get_columns(obj)
- data = utils.get_item_properties(obj, columns, formatters=_formatters)
+ data = utils.get_item_properties(obj, columns)
return (display_columns, data)
def take_action_compute(self, client, parsed_args):
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index f6844065..f87f7abe 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -61,7 +61,6 @@ _formatters = {
'allocation_pools': AllocationPoolsColumn,
'dns_nameservers': format_columns.ListColumn,
'host_routes': HostRoutesColumn,
- 'location': format_columns.DictColumn,
'service_types': format_columns.ListColumn,
'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 2750574a..56cf6152 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -42,7 +42,6 @@ def _get_columns(item):
_formatters = {
- 'location': format_columns.DictColumn,
'prefixes': format_columns.ListColumn,
'tags': format_columns.ListColumn,
}
diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py
index 86f090bc..a5edd9a5 100644
--- a/openstackclient/tests/functional/identity/v3/common.py
+++ b/openstackclient/tests/functional/identity/v3/common.py
@@ -33,7 +33,7 @@ class IdentityTests(base.TestCase):
'password_expires_at']
PROJECT_FIELDS = ['description', 'id', 'domain_id', 'is_domain',
'enabled', 'name', 'parent_id']
- ROLE_FIELDS = ['id', 'name', 'domain_id']
+ ROLE_FIELDS = ['id', 'name', 'domain_id', 'description']
SERVICE_FIELDS = ['id', 'enabled', 'name', 'type', 'description']
REGION_FIELDS = ['description', 'enabled', 'parent_region', 'region']
ENDPOINT_FIELDS = ['id', 'region', 'region_id', 'service_id',
diff --git a/openstackclient/tests/functional/identity/v3/test_project.py b/openstackclient/tests/functional/identity/v3/test_project.py
index 96d41c3a..27cf4481 100644
--- a/openstackclient/tests/functional/identity/v3/test_project.py
+++ b/openstackclient/tests/functional/identity/v3/test_project.py
@@ -79,7 +79,6 @@ class ProjectTests(common.IdentityTests):
'--disable '
'--property k0=v0 '
'%(name)s' % {'new_name': new_project_name,
- 'domain': self.domain_name,
'name': project_name})
self.assertEqual(0, len(raw_output))
# check project details
diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py
index 38bfff71..3954c4e3 100644
--- a/openstackclient/tests/functional/identity/v3/test_role.py
+++ b/openstackclient/tests/functional/identity/v3/test_role.py
@@ -20,6 +20,21 @@ class RoleTests(common.IdentityTests):
def test_role_create(self):
self._create_dummy_role()
+ def test_role_create_with_description(self):
+ role_name = data_utils.rand_name('TestRole')
+ description = data_utils.rand_name('description')
+ raw_output = self.openstack(
+ 'role create '
+ '--description %(description)s '
+ '%(name)s' % {'description': description,
+ 'name': role_name})
+ role = self.parse_show_as_object(raw_output)
+ self.addCleanup(self.openstack, 'role delete %s' % role['id'])
+ items = self.parse_show(raw_output)
+ self.assert_show_fields(items, self.ROLE_FIELDS)
+ self.assertEqual(description, role['description'])
+ return role_name
+
def test_role_delete(self):
role_name = self._create_dummy_role(add_clean_up=False)
raw_output = self.openstack('role delete %s' % role_name)
@@ -47,6 +62,16 @@ class RoleTests(common.IdentityTests):
role = self.parse_show_as_object(raw_output)
self.assertEqual(new_role_name, role['name'])
+ def test_role_set_description(self):
+ role_name = self._create_dummy_role()
+ description = data_utils.rand_name("NewDescription")
+ raw_output = self.openstack('role set --description %s %s'
+ % (description, role_name))
+ self.assertEqual(0, len(raw_output))
+ raw_output = self.openstack('role show %s' % role_name)
+ role = self.parse_show_as_object(raw_output)
+ self.assertEqual(description, role['description'])
+
def test_role_add(self):
role_name = self._create_dummy_role()
username = self._create_dummy_user()
diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py
index 05aad7a0..0769dca6 100644
--- a/openstackclient/tests/functional/network/v2/test_router.py
+++ b/openstackclient/tests/functional/network/v2/test_router.py
@@ -261,3 +261,46 @@ class RouterTests(common.NetworkTagTests):
new_name
))
self.assertIsNone(cmd_output["external_gateway_info"])
+
+ def test_router_add_remove_route(self):
+ network_name = uuid.uuid4().hex
+ subnet_name = uuid.uuid4().hex
+ router_name = uuid.uuid4().hex
+
+ self.openstack('network create %s' % network_name)
+ self.addCleanup(self.openstack, 'network delete %s' % network_name)
+
+ self.openstack(
+ 'subnet create %s '
+ '--network %s --subnet-range 10.0.0.0/24' % (
+ subnet_name, network_name))
+
+ self.openstack('router create %s' % router_name)
+ self.addCleanup(self.openstack, 'router delete %s' % router_name)
+
+ self.openstack('router add subnet %s %s' % (router_name, subnet_name))
+ self.addCleanup(self.openstack, 'router remove subnet %s %s' % (
+ router_name, subnet_name))
+
+ out1 = json.loads(self.openstack(
+ 'router add route -f json %s '
+ '--route destination=10.0.10.0/24,gateway=10.0.0.10' %
+ router_name)),
+ self.assertEqual(1, len(out1[0]['routes']))
+
+ self.addCleanup(
+ self.openstack, 'router set %s --no-route' % router_name)
+
+ out2 = json.loads(self.openstack(
+ 'router add route -f json %s '
+ '--route destination=10.0.10.0/24,gateway=10.0.0.10 '
+ '--route destination=10.0.11.0/24,gateway=10.0.0.11' %
+ router_name)),
+ self.assertEqual(2, len(out2[0]['routes']))
+
+ out3 = json.loads(self.openstack(
+ 'router remove route -f json %s '
+ '--route destination=10.0.11.0/24,gateway=10.0.0.11 '
+ '--route destination=10.0.12.0/24,gateway=10.0.0.12' %
+ router_name)),
+ self.assertEqual(1, len(out3[0]['routes']))
diff --git a/openstackclient/tests/functional/network/v2/test_security_group.py b/openstackclient/tests/functional/network/v2/test_security_group.py
index 8ae24b72..d46f8db7 100644
--- a/openstackclient/tests/functional/network/v2/test_security_group.py
+++ b/openstackclient/tests/functional/network/v2/test_security_group.py
@@ -42,7 +42,7 @@ class SecurityGroupTests(common.NetworkTests):
def test_security_group_set(self):
other_name = uuid.uuid4().hex
raw_output = self.openstack(
- 'security group set --description NSA --name ' +
+ 'security group set --description NSA --stateless --name ' +
other_name + ' ' + self.NAME
)
self.assertEqual('', raw_output)
@@ -50,8 +50,10 @@ class SecurityGroupTests(common.NetworkTests):
cmd_output = json.loads(self.openstack(
'security group show -f json ' + other_name))
self.assertEqual('NSA', cmd_output['description'])
+ self.assertFalse(cmd_output['stateful'])
def test_security_group_show(self):
cmd_output = json.loads(self.openstack(
'security group show -f json ' + self.NAME))
self.assertEqual(self.NAME, cmd_output['name'])
+ self.assertTrue(cmd_output['stateful'])
diff --git a/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py
index 8d32d997..4977a73e 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py
@@ -121,6 +121,24 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
cmd_output["size"],
)
self.wait_for_status('volume snapshot', name2, 'available')
+
+ raw_output = self.openstack(
+ 'volume snapshot set ' +
+ '--state error_deleting ' +
+ name2
+ )
+ self.assertOutput('', raw_output)
+
+ # Test list --long, --status
+ cmd_output = json.loads(self.openstack(
+ 'volume snapshot list -f json ' +
+ '--long ' +
+ '--status error_deleting'
+ ))
+ names = [x["Name"] for x in cmd_output]
+ self.assertNotIn(name1, names)
+ self.assertIn(name2, names)
+
raw_output = self.openstack(
'volume snapshot set ' +
'--state error ' +
diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py
index 96c68d5a..b9e0740c 100644
--- a/openstackclient/tests/unit/api/test_object_store_v1.py
+++ b/openstackclient/tests/unit/api/test_object_store_v1.py
@@ -30,8 +30,10 @@ FAKE_CONTAINER = 'rainbarrel'
FAKE_OBJECT = 'spigot'
LIST_CONTAINER_RESP = [
- 'qaz',
- 'fred',
+ {"name": "qaz", "count": 0, "bytes": 0,
+ "last_modified": "2020-05-16T05:52:07.377550"},
+ {"name": "fred", "count": 0, "bytes": 0,
+ "last_modified": "2020-05-16T05:55:07.377550"},
]
LIST_OBJECT_RESP = [
@@ -117,34 +119,32 @@ class TestContainer(TestObjectAPIv1):
)
self.assertEqual(LIST_CONTAINER_RESP, ret)
-# def test_container_list_full_listing(self):
-# sess = self.app.client_manager.session
-#
-# def side_effect(*args, **kwargs):
-# rv = sess.get().json.return_value
-# sess.get().json.return_value = []
-# sess.get().json.side_effect = None
-# return rv
-#
-# resp = [{'name': 'is-name'}]
-# sess.get().json.return_value = resp
-# sess.get().json.side_effect = side_effect
-#
-# data = lib_container.list_containers(
-# self.app.client_manager.session,
-# fake_url,
-# full_listing=True,
-# )
-#
-# # Check expected values
-# sess.get.assert_called_with(
-# fake_url,
-# params={
-# 'format': 'json',
-# 'marker': 'is-name',
-# }
-# )
-# self.assertEqual(resp, data)
+ def test_container_list_full_listing(self):
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '?limit=1&format=json',
+ json=[LIST_CONTAINER_RESP[0]],
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL +
+ '?marker=%s&limit=1&format=json' % LIST_CONTAINER_RESP[0]['name'],
+ json=[LIST_CONTAINER_RESP[1]],
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL +
+ '?marker=%s&limit=1&format=json' % LIST_CONTAINER_RESP[1]['name'],
+ json=[],
+ status_code=200,
+ )
+ ret = self.api.container_list(
+ limit=1,
+ full_listing=True,
+ )
+ self.assertEqual(LIST_CONTAINER_RESP, ret)
def test_container_show(self):
headers = {
diff --git a/openstackclient/tests/unit/common/test_parseractions.py b/openstackclient/tests/unit/common/test_parseractions.py
index d015da43..736cd0b6 100644
--- a/openstackclient/tests/unit/common/test_parseractions.py
+++ b/openstackclient/tests/unit/common/test_parseractions.py
@@ -92,7 +92,7 @@ class TestMultiKeyValueAction(utils.TestCase):
{'req1': 'aaa', 'req2': 'bbb'},
{'req1': '', 'req2': ''},
]
- self.assertItemsEqual(expect, actual)
+ self.assertCountEqual(expect, actual)
def test_empty_required_optional(self):
self.parser.add_argument(
@@ -116,7 +116,7 @@ class TestMultiKeyValueAction(utils.TestCase):
{'req1': 'aaa', 'req2': 'bbb'},
{'req1': '', 'req2': ''},
]
- self.assertItemsEqual(expect, actual)
+ self.assertCountEqual(expect, actual)
def test_error_values_with_comma(self):
self.assertRaises(
diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py
index 0018e067..6504c5b0 100644
--- a/openstackclient/tests/unit/common/test_quota.py
+++ b/openstackclient/tests/unit/common/test_quota.py
@@ -392,6 +392,29 @@ class TestQuotaList(TestQuota):
parsed_args,
)
+ def test_quota_list_compute_by_project(self):
+ # Two projects with non-default quotas
+ self.compute.quotas.get = mock.Mock(
+ side_effect=self.compute_quotas,
+ )
+
+ arglist = [
+ '--compute',
+ '--project', self.projects[0].name,
+ ]
+ verifylist = [
+ ('compute', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ ret_quotas = list(data)
+
+ self.assertEqual(self.compute_column_header, columns)
+ self.assertEqual(self.compute_reference_data, ret_quotas[0])
+ self.assertEqual(1, len(ret_quotas))
+
def test_quota_list_network(self):
# Two projects with non-default quotas
self.network.get_quota = mock.Mock(
@@ -461,6 +484,29 @@ class TestQuotaList(TestQuota):
self.assertEqual(self.network_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
+ def test_quota_list_network_by_project(self):
+ # Two projects with non-default quotas
+ self.network.get_quota = mock.Mock(
+ side_effect=self.network_quotas,
+ )
+
+ arglist = [
+ '--network',
+ '--project', self.projects[0].name,
+ ]
+ verifylist = [
+ ('network', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ ret_quotas = list(data)
+
+ self.assertEqual(self.network_column_header, columns)
+ self.assertEqual(self.network_reference_data, ret_quotas[0])
+ self.assertEqual(1, len(ret_quotas))
+
def test_quota_list_volume(self):
# Two projects with non-default quotas
self.volume.quotas.get = mock.Mock(
@@ -530,6 +576,29 @@ class TestQuotaList(TestQuota):
self.assertEqual(self.volume_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
+ def test_quota_list_volume_by_project(self):
+ # Two projects with non-default quotas
+ self.volume.quotas.get = mock.Mock(
+ side_effect=self.volume_quotas,
+ )
+
+ arglist = [
+ '--volume',
+ '--project', self.projects[0].name,
+ ]
+ verifylist = [
+ ('volume', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ ret_quotas = list(data)
+
+ self.assertEqual(self.volume_column_header, columns)
+ self.assertEqual(self.volume_reference_data, ret_quotas[0])
+ self.assertEqual(1, len(ret_quotas))
+
class TestQuotaSet(TestQuota):
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 27eefd85..7e4c71c5 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -55,6 +55,12 @@ class TestServer(compute_fakes.TestComputev2):
self.images_mock = self.app.client_manager.image.images
self.images_mock.reset_mock()
+ self.find_image_mock = self.app.client_manager.image.find_image
+ self.find_image_mock.reset_mock()
+
+ self.get_image_mock = self.app.client_manager.image.get_image
+ self.get_image_mock.reset_mock()
+
# Get a shortcut to the volume client VolumeManager Mock
self.volumes_mock = self.app.client_manager.volume.volumes
self.volumes_mock.reset_mock()
@@ -770,7 +776,8 @@ class TestServerCreate(TestServer):
self.servers_mock.create.return_value = self.new_server
self.image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = self.image
+ self.find_image_mock.return_value = self.image
+ self.get_image_mock.return_value = self.image
self.flavor = compute_fakes.FakeFlavor.create_one_flavor()
self.flavors_mock.get.return_value = self.flavor
@@ -1916,19 +1923,13 @@ class TestServerCreate(TestServer):
('config_drive', False),
('server_name', self.new_server.name),
]
- _image = image_fakes.FakeImage.create_one_image()
# create a image_info as the side_effect of the fake image_list()
image_info = {
- 'id': _image.id,
- 'name': _image.name,
- 'owner': _image.owner,
'hypervisor_type': 'qemu',
}
- self.api_mock = mock.Mock()
- self.api_mock.image_list.side_effect = [
- [image_info], [],
- ]
- self.app.client_manager.image.api = self.api_mock
+
+ _image = image_fakes.FakeImage.create_one_image(image_info)
+ self.images_mock.return_value = [_image]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1953,7 +1954,7 @@ class TestServerCreate(TestServer):
# ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with(
self.new_server.name,
- image_info,
+ _image,
self.flavor,
**kwargs
)
@@ -1977,20 +1978,13 @@ class TestServerCreate(TestServer):
('config_drive', False),
('server_name', self.new_server.name),
]
- _image = image_fakes.FakeImage.create_one_image()
# create a image_info as the side_effect of the fake image_list()
image_info = {
- 'id': _image.id,
- 'name': _image.name,
- 'owner': _image.owner,
'hypervisor_type': 'qemu',
'hw_disk_bus': 'ide',
}
- self.api_mock = mock.Mock()
- self.api_mock.image_list.side_effect = [
- [image_info], [],
- ]
- self.app.client_manager.image.api = self.api_mock
+ _image = image_fakes.FakeImage.create_one_image(image_info)
+ self.images_mock.return_value = [_image]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -2015,7 +2009,7 @@ class TestServerCreate(TestServer):
# ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with(
self.new_server.name,
- image_info,
+ _image,
self.flavor,
**kwargs
)
@@ -2039,20 +2033,14 @@ class TestServerCreate(TestServer):
('config_drive', False),
('server_name', self.new_server.name),
]
- _image = image_fakes.FakeImage.create_one_image()
# create a image_info as the side_effect of the fake image_list()
image_info = {
- 'id': _image.id,
- 'name': _image.name,
- 'owner': _image.owner,
'hypervisor_type': 'qemu',
'hw_disk_bus': 'ide',
}
- self.api_mock = mock.Mock()
- self.api_mock.image_list.side_effect = [
- [image_info], [],
- ]
- self.app.client_manager.image.api = self.api_mock
+
+ _image = image_fakes.FakeImage.create_one_image(image_info)
+ self.images_mock.return_value = [_image]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -2585,7 +2573,10 @@ class TestServerList(TestServer):
self.servers_mock.list.return_value = self.servers
self.image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = self.image
+
+ # self.images_mock.return_value = [self.image]
+ self.find_image_mock.return_value = self.image
+ self.get_image_mock.return_value = self.image
self.flavor = compute_fakes.FakeFlavor.create_one_flavor()
self.flavors_mock.get.return_value = self.flavor
@@ -2599,7 +2590,7 @@ class TestServerList(TestServer):
self.data_no_name_lookup = []
Image = collections.namedtuple('Image', 'id name')
- self.images_mock.list.return_value = [
+ self.images_mock.return_value = [
Image(id=s.image['id'], name=self.image.name)
# Image will be an empty string if boot-from-volume
for s in self.servers if s.image
@@ -2662,11 +2653,11 @@ class TestServerList(TestServer):
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
- self.images_mock.list.assert_called()
+ self.images_mock.assert_called()
self.flavors_mock.list.assert_called()
# we did not pass image or flavor, so gets on those must be absent
self.assertFalse(self.flavors_mock.get.call_count)
- self.assertFalse(self.images_mock.get.call_count)
+ self.assertFalse(self.get_image_mock.call_count)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@@ -2753,7 +2744,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertFalse(self.images_mock.list.call_count)
self.assertFalse(self.flavors_mock.list.call_count)
- self.images_mock.get.assert_called()
+ self.get_image_mock.assert_called()
self.flavors_mock.get.assert_called()
self.assertEqual(self.columns, columns)
@@ -2771,7 +2762,8 @@ class TestServerList(TestServer):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.images_mock.get.assert_any_call(self.image.id)
+ self.find_image_mock.assert_called_with(self.image.id,
+ ignore_missing=False)
self.search_opts['image'] = self.image.id
self.servers_mock.list.assert_called_with(**self.kwargs)
@@ -3558,7 +3550,7 @@ class TestServerRebuild(TestServer):
# Return value for utils.find_resource for image
self.image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = self.image
+ self.get_image_mock.return_value = self.image
# Fake the rebuilt new server.
attrs = {
@@ -3585,6 +3577,41 @@ class TestServerRebuild(TestServer):
self.cmd = server.RebuildServer(self.app, None)
+ def test_rebuild_with_image_name(self):
+ image_name = 'my-custom-image'
+ user_image = image_fakes.FakeImage.create_one_image(
+ attrs={'name': image_name})
+ self.find_image_mock.return_value = user_image
+
+ attrs = {
+ 'image': {
+ 'id': user_image.id
+ },
+ 'networks': {},
+ 'adminPass': 'passw0rd',
+ }
+ new_server = compute_fakes.FakeServer.create_one_server(attrs=attrs)
+ self.server.rebuild.return_value = new_server
+
+ arglist = [
+ self.server.id,
+ '--image', image_name
+ ]
+ verifylist = [
+ ('server', self.server.id),
+ ('image', image_name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # Get the command object to test.
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(self.server.id)
+ self.find_image_mock.assert_called_with(
+ image_name, ignore_missing=False)
+ self.get_image_mock.assert_called_with(user_image.id)
+ self.server.rebuild.assert_called_with(user_image, None)
+
def test_rebuild_with_current_image(self):
arglist = [
self.server.id,
@@ -3598,7 +3625,8 @@ class TestServerRebuild(TestServer):
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.find_image_mock.assert_not_called()
+ self.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, None)
def test_rebuild_with_current_image_and_password(self):
@@ -3617,7 +3645,7 @@ class TestServerRebuild(TestServer):
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.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, password)
def test_rebuild_with_description_api_older(self):
@@ -3665,7 +3693,7 @@ class TestServerRebuild(TestServer):
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.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, None,
description=description)
@@ -3694,7 +3722,7 @@ class TestServerRebuild(TestServer):
)
self.servers_mock.get.assert_called_with(self.server.id)
- self.images_mock.get.assert_called_with(self.image.id)
+ self.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, None)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
@@ -3718,7 +3746,7 @@ class TestServerRebuild(TestServer):
)
self.servers_mock.get.assert_called_with(self.server.id)
- self.images_mock.get.assert_called_with(self.image.id)
+ self.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(self.image, None)
def test_rebuild_with_property(self):
@@ -3738,7 +3766,7 @@ class TestServerRebuild(TestServer):
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.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(
self.image, None, meta=expected_property)
@@ -3767,7 +3795,7 @@ class TestServerRebuild(TestServer):
key_name=self.server.key_name,
)
self.servers_mock.get.assert_called_with(self.server.id)
- self.images_mock.get.assert_called_with(self.image.id)
+ self.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(*args, **kwargs)
def test_rebuild_with_keypair_name_older_version(self):
@@ -3814,7 +3842,7 @@ class TestServerRebuild(TestServer):
key_name=None,
)
self.servers_mock.get.assert_called_with(self.server.id)
- self.images_mock.get.assert_called_with(self.image.id)
+ self.get_image_mock.assert_called_with(self.image.id)
self.server.rebuild.assert_called_with(*args, **kwargs)
def test_rebuild_with_key_name_and_unset(self):
@@ -3872,7 +3900,7 @@ class TestServerRescue(TestServer):
# Return value for utils.find_resource for image
self.image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = self.image
+ self.get_image_mock.return_value = self.image
new_server = compute_fakes.FakeServer.create_one_server()
attrs = {
@@ -3913,7 +3941,7 @@ class TestServerRescue(TestServer):
def test_rescue_with_new_image(self):
new_image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = new_image
+ self.find_image_mock.return_value = new_image
arglist = [
'--image', new_image.id,
self.server.id,
@@ -3928,7 +3956,7 @@ class TestServerRescue(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id)
- self.images_mock.get.assert_called_with(new_image.id)
+ self.find_image_mock.assert_called_with(new_image.id)
self.server.rescue.assert_called_with(image=new_image, password=None)
def test_rescue_with_current_image_and_password(self):
@@ -4679,7 +4707,7 @@ class TestServerShow(TestServer):
# This is the return value for utils.find_resource()
self.servers_mock.get.return_value = self.server
- self.images_mock.get.return_value = self.image
+ self.get_image_mock.return_value = self.image
self.flavors_mock.get.return_value = self.flavor
# Get the command object to test
@@ -5140,7 +5168,8 @@ class TestServerGeneral(TestServer):
'links': u'http://xxx.yyy.com',
}
_server = compute_fakes.FakeServer.create_one_server(attrs=server_info)
- find_resource.side_effect = [_server, _image, _flavor]
+ find_resource.side_effect = [_server, _flavor]
+ self.get_image_mock.return_value = _image
# Prepare result data.
info = {
diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py
index 7dd459d8..5cdc2080 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_backup.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py
@@ -32,8 +32,8 @@ class TestServerBackup(compute_fakes.TestComputev2):
self.servers_mock.reset_mock()
# Get a shortcut to the image client ImageManager Mock
- self.images_mock = self.app.client_manager.image.images
- self.images_mock.reset_mock()
+ self.images_mock = self.app.client_manager.image
+ self.images_mock.find_image.reset_mock()
# Set object attributes to be tested. Could be overwritten in subclass.
self.attrs = {}
@@ -60,15 +60,18 @@ class TestServerBackupCreate(TestServerBackup):
# Just return whatever Image is testing with these days
def image_columns(self, image):
- columnlist = tuple(sorted(image.keys()))
+ # columnlist = tuple(sorted(image.keys()))
+ columnlist = (
+ 'id', 'name', 'owner', 'protected', 'status', 'tags', 'visibility'
+ )
return columnlist
def image_data(self, image):
datalist = (
image['id'],
image['name'],
- image['owner'],
- image['protected'],
+ image['owner_id'],
+ image['is_protected'],
'active',
format_columns.ListColumn(image.get('tags')),
image['visibility'],
@@ -102,7 +105,8 @@ class TestServerBackupCreate(TestServerBackup):
count=count,
)
- self.images_mock.get = mock.Mock(side_effect=images)
+ # self.images_mock.get = mock.Mock(side_effect=images)
+ self.images_mock.find_image = mock.Mock(side_effect=images)
return images
def test_server_backup_defaults(self):
@@ -174,16 +178,18 @@ class TestServerBackupCreate(TestServerBackup):
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_backup_wait_fail(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1)
- images = image_fakes.FakeImage.create_images(
- attrs={
- 'name': servers[0].name,
- 'status': 'active',
- },
- count=5,
- )
-
- self.images_mock.get = mock.Mock(
- side_effect=images,
+ images = self.setup_images_mock(count=1, servers=servers)
+# images = image_fakes.FakeImage.create_images(
+# attrs={
+# 'name': servers[0].name,
+# 'status': 'active',
+# },
+# count=1,
+# )
+#
+# self.images_mock.find_image.return_value = images[0]
+ self.images_mock.get_image = mock.Mock(
+ side_effect=images[0],
)
arglist = [
@@ -215,7 +221,7 @@ class TestServerBackupCreate(TestServerBackup):
)
mock_wait_for_status.assert_called_once_with(
- self.images_mock.get,
+ self.images_mock.get_image,
images[0].id,
callback=mock.ANY
)
@@ -223,16 +229,10 @@ class TestServerBackupCreate(TestServerBackup):
@mock.patch.object(common_utils, 'wait_for_status', return_value=True)
def test_server_backup_wait_ok(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1)
- images = image_fakes.FakeImage.create_images(
- attrs={
- 'name': servers[0].name,
- 'status': 'active',
- },
- count=5,
- )
+ images = self.setup_images_mock(count=1, servers=servers)
- self.images_mock.get = mock.Mock(
- side_effect=images,
+ self.images_mock.get_image = mock.Mock(
+ side_effect=images[0],
)
arglist = [
@@ -263,7 +263,7 @@ class TestServerBackupCreate(TestServerBackup):
)
mock_wait_for_status.assert_called_once_with(
- self.images_mock.get,
+ self.images_mock.get_image,
images[0].id,
callback=mock.ANY
)
diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py
index f9d7b10e..1cec5b68 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_image.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_image.py
@@ -31,8 +31,8 @@ class TestServerImage(compute_fakes.TestComputev2):
self.servers_mock.reset_mock()
# Get a shortcut to the image client ImageManager Mock
- self.images_mock = self.app.client_manager.image.images
- self.images_mock.reset_mock()
+ self.images_mock = self.app.client_manager.image
+ self.images_mock.find_image.reset_mock()
# Set object attributes to be tested. Could be overwritten in subclass.
self.attrs = {}
@@ -58,15 +58,18 @@ class TestServerImage(compute_fakes.TestComputev2):
class TestServerImageCreate(TestServerImage):
def image_columns(self, image):
- columnlist = tuple(sorted(image.keys()))
+ # columnlist = tuple(sorted(image.keys()))
+ columnlist = (
+ 'id', 'name', 'owner', 'protected', 'status', 'tags', 'visibility'
+ )
return columnlist
def image_data(self, image):
datalist = (
image['id'],
image['name'],
- image['owner'],
- image['protected'],
+ image['owner_id'],
+ image['is_protected'],
'active',
format_columns.ListColumn(image.get('tags')),
image['visibility'],
@@ -100,7 +103,7 @@ class TestServerImageCreate(TestServerImage):
count=count,
)
- self.images_mock.get = mock.Mock(side_effect=images)
+ self.images_mock.find_image = mock.Mock(side_effect=images)
self.servers_mock.create_image = mock.Mock(
return_value=images[0].id,
)
@@ -188,7 +191,7 @@ class TestServerImageCreate(TestServerImage):
)
mock_wait_for_status.assert_called_once_with(
- self.images_mock.get,
+ self.images_mock.get_image,
images[0].id,
callback=mock.ANY
)
@@ -220,7 +223,7 @@ class TestServerImageCreate(TestServerImage):
)
mock_wait_for_status.assert_called_once_with(
- self.images_mock.get,
+ self.images_mock.get_image,
images[0].id,
callback=mock.ANY
)
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index fc4a48e3..58d5d14d 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -108,6 +108,9 @@ MAPPING_RESPONSE_2 = {
"rules": MAPPING_RULES_2
}
+mfa_opt1 = 'password,totp'
+mfa_opt2 = 'password'
+
project_id = '8-9-64'
project_name = 'beatles'
project_description = 'Fab Four'
@@ -176,6 +179,7 @@ ids_for_children = [PROJECT_WITH_GRANDPARENT['id']]
role_id = 'r1'
role_name = 'roller'
+role_description = 'role description'
ROLE = {
'id': role_id,
diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py
index f8b6093a..904fe323 100644
--- a/openstackclient/tests/unit/identity/v3/test_access_rule.py
+++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py
@@ -14,8 +14,8 @@
#
import copy
+from unittest import mock
-import mock
from osc_lib import exceptions
from osc_lib import utils
diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py
index 014986e5..46f389e8 100644
--- a/openstackclient/tests/unit/identity/v3/test_domain.py
+++ b/openstackclient/tests/unit/identity/v3/test_domain.py
@@ -68,6 +68,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': None,
+ 'options': {},
'enabled': True,
}
self.domains_mock.create.assert_called_with(
@@ -97,6 +98,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': 'new desc',
+ 'options': {},
'enabled': True,
}
self.domains_mock.create.assert_called_with(
@@ -126,6 +128,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': None,
+ 'options': {},
'enabled': True,
}
self.domains_mock.create.assert_called_with(
@@ -155,6 +158,7 @@ class TestDomainCreate(TestDomain):
kwargs = {
'name': self.domain.name,
'description': None,
+ 'options': {},
'enabled': False,
}
self.domains_mock.create.assert_called_with(
@@ -164,6 +168,66 @@ class TestDomainCreate(TestDomain):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_domain_create_with_immutable(self):
+ arglist = [
+ '--immutable',
+ self.domain.name,
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('name', self.domain.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.domain.name,
+ 'description': None,
+ 'options': {'immutable': True},
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_domain_create_with_no_immutable(self):
+ arglist = [
+ '--no-immutable',
+ self.domain.name,
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('name', self.domain.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.domain.name,
+ 'description': None,
+ 'options': {'immutable': False},
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
class TestDomainDelete(TestDomain):
@@ -354,6 +418,52 @@ class TestDomainSet(TestDomain):
)
self.assertIsNone(result)
+ def test_domain_set_immutable_option(self):
+ arglist = [
+ '--immutable',
+ self.domain.id,
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': True},
+ }
+ self.domains_mock.update.assert_called_with(
+ self.domain.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_domain_set_no_immutable_option(self):
+ arglist = [
+ '--no-immutable',
+ self.domain.id,
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': False},
+ }
+ self.domains_mock.update.assert_called_with(
+ self.domain.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestDomainShow(TestDomain):
diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py
index 466bea18..dfd0805b 100644
--- a/openstackclient/tests/unit/identity/v3/test_project.py
+++ b/openstackclient/tests/unit/identity/v3/test_project.py
@@ -98,7 +98,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -156,7 +157,8 @@ class TestProjectCreate(TestProject):
'description': 'new desc',
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -194,7 +196,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -232,7 +235,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
self.projects_mock.create.assert_called_with(
**kwargs
@@ -266,7 +270,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -302,7 +307,8 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': False,
'parent': None,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=,
# description=, enabled=, **kwargs)
@@ -339,7 +345,8 @@ class TestProjectCreate(TestProject):
'parent': None,
'fee': 'fi',
'fo': 'fum',
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -350,6 +357,126 @@ class TestProjectCreate(TestProject):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_project_create_is_domain_false_property(self):
+ arglist = [
+ '--property', 'is_domain=false',
+ self.project.name,
+ ]
+ verifylist = [
+ ('parent', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('tags', []),
+ ('property', {'is_domain': 'false'}),
+ ('name', self.project.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'is_domain': False,
+ 'tags': [],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_is_domain_true_property(self):
+ arglist = [
+ '--property', 'is_domain=true',
+ self.project.name,
+ ]
+ verifylist = [
+ ('parent', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('tags', []),
+ ('property', {'is_domain': 'true'}),
+ ('name', self.project.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'is_domain': True,
+ 'tags': [],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_is_domain_none_property(self):
+ arglist = [
+ '--property', 'is_domain=none',
+ self.project.name,
+ ]
+ verifylist = [
+ ('parent', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('tags', []),
+ ('property', {'is_domain': 'none'}),
+ ('name', self.project.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'is_domain': None,
+ 'tags': [],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
def test_project_create_parent(self):
self.parent = identity_fakes.FakeProject.create_one_project()
self.project = identity_fakes.FakeProject.create_one_project(
@@ -380,7 +507,8 @@ class TestProjectCreate(TestProject):
'parent': self.parent.id,
'description': None,
'enabled': True,
- 'tags': []
+ 'tags': [],
+ 'options': {},
}
self.projects_mock.create.assert_called_with(
@@ -465,8 +593,89 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
- 'tags': ['foo']
+ 'tags': ['foo'],
+ 'options': {},
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_with_immutable_option(self):
+ arglist = [
+ '--immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('description', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('parent', None),
+ ('tags', [])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'tags': [],
+ 'options': {'immutable': True},
}
+ # ProjectManager.create(name=, domain=, description=,
+ # enabled=, **kwargs)
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_project_create_with_no_immutable_option(self):
+ arglist = [
+ '--no-immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('description', None),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('parent', None),
+ ('tags', [])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': None,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'tags': [],
+ 'options': {'immutable': False},
+ }
+ # ProjectManager.create(name=, domain=, description=,
+ # enabled=, **kwargs)
self.projects_mock.create.assert_called_with(
**kwargs
)
@@ -927,6 +1136,60 @@ class TestProjectSet(TestProject):
)
self.assertIsNone(result)
+ def test_project_set_with_immutable_option(self):
+ arglist = [
+ '--domain', self.project.domain_id,
+ '--immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('domain', self.project.domain_id),
+ ('immutable', True),
+ ('enable', False),
+ ('disable', False),
+ ('project', self.project.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': True},
+ }
+ self.projects_mock.update.assert_called_with(
+ self.project.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_project_set_with_no_immutable_option(self):
+ arglist = [
+ '--domain', self.project.domain_id,
+ '--no-immutable',
+ self.project.name,
+ ]
+ verifylist = [
+ ('domain', self.project.domain_id),
+ ('no_immutable', True),
+ ('enable', False),
+ ('disable', False),
+ ('project', self.project.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'options': {'immutable': False},
+ }
+ self.projects_mock.update.assert_called_with(
+ self.project.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestProjectShow(TestProject):
diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py
index ead2cb58..544da7c1 100644
--- a/openstackclient/tests/unit/identity/v3/test_role.py
+++ b/openstackclient/tests/unit/identity/v3/test_role.py
@@ -332,6 +332,8 @@ class TestRoleCreate(TestRole):
kwargs = {
'domain': None,
'name': identity_fakes.role_name,
+ 'description': None,
+ 'options': {},
}
# RoleManager.create(name=, domain=)
@@ -375,6 +377,8 @@ class TestRoleCreate(TestRole):
kwargs = {
'domain': identity_fakes.domain_id,
'name': identity_fakes.ROLE_2['name'],
+ 'description': None,
+ 'options': {},
}
# RoleManager.create(name=, domain=)
@@ -391,6 +395,140 @@ class TestRoleCreate(TestRole):
)
self.assertEqual(datalist, data)
+ def test_role_create_with_description(self):
+
+ self.roles_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--description', identity_fakes.role_description,
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('description', identity_fakes.role_description),
+ ('name', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'description': identity_fakes.role_description,
+ 'name': identity_fakes.ROLE_2['name'],
+ 'domain': None,
+ 'options': {},
+ }
+
+ # RoleManager.create(name=, domain=)
+ self.roles_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('domain', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ 'd1',
+ identity_fakes.ROLE_2['id'],
+ identity_fakes.ROLE_2['name'],
+ )
+ self.assertEqual(datalist, data)
+
+ def test_role_create_with_immutable_option(self):
+
+ self.roles_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('immutable', True),
+ ('name', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+
+ 'options': {'immutable': True},
+ 'description': None,
+ 'name': identity_fakes.ROLE_2['name'],
+ 'domain': None,
+ }
+
+ # RoleManager.create(name=, domain=)
+ self.roles_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('domain', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ 'd1',
+ identity_fakes.ROLE_2['id'],
+ identity_fakes.ROLE_2['name'],
+ )
+ self.assertEqual(datalist, data)
+
+ def test_role_create_with_no_immutable_option(self):
+
+ self.roles_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--no-immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('no_immutable', True),
+ ('name', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+
+ 'options': {'immutable': False},
+ 'description': None,
+ 'name': identity_fakes.ROLE_2['name'],
+ 'domain': None,
+ }
+
+ # RoleManager.create(name=, domain=)
+ self.roles_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('domain', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ 'd1',
+ identity_fakes.ROLE_2['id'],
+ identity_fakes.ROLE_2['name'],
+ )
+ self.assertEqual(datalist, data)
+
class TestRoleDelete(TestRole):
@@ -825,6 +963,8 @@ class TestRoleSet(TestRole):
# Set expected values
kwargs = {
'name': 'over',
+ 'description': None,
+ 'options': {},
}
# RoleManager.update(role, name=)
self.roles_mock.update.assert_called_with(
@@ -856,6 +996,107 @@ class TestRoleSet(TestRole):
# Set expected values
kwargs = {
'name': 'over',
+ 'description': None,
+ 'options': {},
+ }
+ # RoleManager.update(role, name=)
+ self.roles_mock.update.assert_called_with(
+ identity_fakes.ROLE_2['id'],
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_role_set_description(self):
+ self.roles_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--name', 'over',
+ '--description', identity_fakes.role_description,
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('name', 'over'),
+ ('description', identity_fakes.role_description),
+ ('role', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'over',
+ 'description': identity_fakes.role_description,
+ 'options': {},
+ }
+ # RoleManager.update(role, name=)
+ self.roles_mock.update.assert_called_with(
+ identity_fakes.ROLE_2['id'],
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_role_set_with_immutable(self):
+ self.roles_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--name', 'over',
+ '--immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('name', 'over'),
+ ('immutable', True),
+ ('role', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'over',
+ 'description': None,
+ 'options': {'immutable': True},
+ }
+ # RoleManager.update(role, name=)
+ self.roles_mock.update.assert_called_with(
+ identity_fakes.ROLE_2['id'],
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_role_set_with_no_immutable(self):
+ self.roles_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE_2),
+ loaded=True,
+ )
+ arglist = [
+ '--name', 'over',
+ '--no-immutable',
+ identity_fakes.ROLE_2['name'],
+ ]
+ verifylist = [
+ ('name', 'over'),
+ ('no_immutable', True),
+ ('role', identity_fakes.ROLE_2['name']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'over',
+ 'description': None,
+ 'options': {'immutable': False},
}
# RoleManager.update(role, name=)
self.roles_mock.update.assert_called_with(
diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py
index 4b14bca0..c71435ba 100644
--- a/openstackclient/tests/unit/identity/v3/test_user.py
+++ b/openstackclient/tests/unit/identity/v3/test_user.py
@@ -111,6 +111,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': None,
'email': None,
+ 'options': {},
'enabled': True,
'password': None,
}
@@ -150,6 +151,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': None,
'email': None,
+ 'options': {},
'enabled': True,
'password': 'secret',
}
@@ -190,6 +192,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': None,
'email': None,
+ 'options': {},
'enabled': True,
'password': 'abc123',
}
@@ -228,6 +231,7 @@ class TestUserCreate(TestUser):
'domain': None,
'email': 'barney@example.com',
'enabled': True,
+ 'options': {},
'password': None,
}
# UserManager.create(name=, domain=, project=, password=, email=,
@@ -265,6 +269,7 @@ class TestUserCreate(TestUser):
'domain': None,
'email': None,
'enabled': True,
+ 'options': {},
'password': None,
}
# UserManager.create(name=, domain=, project=, password=, email=,
@@ -311,6 +316,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': None,
'email': None,
+ 'options': {},
'enabled': True,
'password': None,
}
@@ -356,6 +362,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': self.domain.id,
'email': None,
+ 'options': {},
'enabled': True,
'password': None,
}
@@ -392,6 +399,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': None,
'email': None,
+ 'options': {},
'enabled': True,
'password': None,
}
@@ -428,6 +436,7 @@ class TestUserCreate(TestUser):
'description': None,
'domain': None,
'email': None,
+ 'options': {},
'enabled': False,
'password': None,
}
@@ -438,6 +447,471 @@ class TestUserCreate(TestUser):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
+ def test_user_create_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('ignore_lockout_failure_attempts', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_lockout_failure_attempts': True},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_no_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--no-ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('no_ignore_lockout_failure_attempts', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_lockout_failure_attempts': False},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_ignore_password_expiry(self):
+ arglist = [
+ '--ignore-password-expiry',
+ self.user.name,
+ ]
+ verifylist = [
+ ('ignore_password_expiry', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_password_expiry': True},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_no_ignore_password_expiry(self):
+ arglist = [
+ '--no-ignore-password-expiry',
+ self.user.name,
+ ]
+ verifylist = [
+ ('no_ignore_password_expiry', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_password_expiry': False},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_ignore_change_password_upon_first_use(self):
+ arglist = [
+ '--ignore-change-password-upon-first-use',
+ self.user.name,
+ ]
+ verifylist = [
+ ('ignore_change_password_upon_first_use', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_change_password_upon_first_use': True},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_no_ignore_change_password_upon_first_use(self):
+ arglist = [
+ '--no-ignore-change-password-upon-first-use',
+ self.user.name,
+ ]
+ verifylist = [
+ ('no_ignore_change_password_upon_first_use', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_change_password_upon_first_use': False},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_enables_lock_password(self):
+ arglist = [
+ '--enable-lock-password',
+ self.user.name,
+ ]
+ verifylist = [
+ ('enable_lock_password', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'lock_password': True},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_disables_lock_password(self):
+ arglist = [
+ '--disable-lock-password',
+ self.user.name,
+ ]
+ verifylist = [
+ ('disable_lock_password', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'lock_password': False},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_enable_multi_factor_auth(self):
+ arglist = [
+ '--enable-multi-factor-auth',
+ self.user.name,
+ ]
+ verifylist = [
+ ('enable_multi_factor_auth', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'multi_factor_auth_enabled': True},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_disable_multi_factor_auth(self):
+ arglist = [
+ '--disable-multi-factor-auth',
+ self.user.name,
+ ]
+ verifylist = [
+ ('disable_multi_factor_auth', True),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'multi_factor_auth_enabled': False},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_option_with_multi_factor_auth_rule(self):
+ arglist = [
+ '--multi-factor-auth-rule', identity_fakes.mfa_opt1,
+ '--multi-factor-auth-rule', identity_fakes.mfa_opt2,
+ self.user.name,
+ ]
+ verifylist = [
+ ('multi_factor_auth_rule', [identity_fakes.mfa_opt1,
+ identity_fakes.mfa_opt2]),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'multi_factor_auth_rules': [["password", "totp"],
+ ["password"]]},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
+ def test_user_create_with_multiple_options(self):
+ arglist = [
+ '--ignore-password-expiry',
+ '--disable-multi-factor-auth',
+ '--multi-factor-auth-rule', identity_fakes.mfa_opt1,
+ self.user.name,
+ ]
+ verifylist = [
+ ('ignore_password_expiry', True),
+ ('disable_multi_factor_auth', True),
+ ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.user.name,
+ 'default_project': None,
+ 'description': None,
+ 'domain': None,
+ 'email': None,
+ 'enabled': True,
+ 'options': {'ignore_password_expiry': True,
+ 'multi_factor_auth_enabled': False,
+ 'multi_factor_auth_rules': [["password", "totp"]]},
+ 'password': None,
+ }
+ # UserManager.create(name=, domain=, project=, password=, email=,
+ # description=, enabled=, default_project=)
+ self.users_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
class TestUserDelete(TestUser):
@@ -1007,6 +1481,384 @@ class TestUserSet(TestUser):
)
self.assertIsNone(result)
+ def test_user_set_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('ignore_lockout_failure_attempts', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_lockout_failure_attempts': True},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_no_ignore_lockout_failure_attempts(self):
+ arglist = [
+ '--no-ignore-lockout-failure-attempts',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('no_ignore_lockout_failure_attempts', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_lockout_failure_attempts': False},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_ignore_password_expiry(self):
+ arglist = [
+ '--ignore-password-expiry',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('ignore_password_expiry', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_password_expiry': True},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_no_ignore_password_expiry(self):
+ arglist = [
+ '--no-ignore-password-expiry',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('no_ignore_password_expiry', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_password_expiry': False},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_ignore_change_password_upon_first_use(self):
+ arglist = [
+ '--ignore-change-password-upon-first-use',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('ignore_change_password_upon_first_use', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_change_password_upon_first_use': True},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_no_ignore_change_password_upon_first_use(self):
+ arglist = [
+ '--no-ignore-change-password-upon-first-use',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('no_ignore_change_password_upon_first_use', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_change_password_upon_first_use': False},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_enable_lock_password(self):
+ arglist = [
+ '--enable-lock-password',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('enable_lock_password', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'lock_password': True},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_disable_lock_password(self):
+ arglist = [
+ '--disable-lock-password',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('disable_lock_password', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'lock_password': False},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_enable_multi_factor_auth(self):
+ arglist = [
+ '--enable-multi-factor-auth',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('enable_multi_factor_auth', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'multi_factor_auth_enabled': True},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_disable_multi_factor_auth(self):
+ arglist = [
+ '--disable-multi-factor-auth',
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('disable_multi_factor_auth', True),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'multi_factor_auth_enabled': False},
+ }
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_option_multi_factor_auth_rule(self):
+ arglist = [
+ '--multi-factor-auth-rule', identity_fakes.mfa_opt1,
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'multi_factor_auth_rules': [["password", "totp"]]}}
+
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
+ def test_user_set_with_multiple_options(self):
+ arglist = [
+ '--ignore-password-expiry',
+ '--enable-multi-factor-auth',
+ '--multi-factor-auth-rule', identity_fakes.mfa_opt1,
+ self.user.name,
+ ]
+ verifylist = [
+ ('name', None),
+ ('password', None),
+ ('email', None),
+ ('ignore_password_expiry', True),
+ ('enable_multi_factor_auth', True),
+ ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]),
+ ('project', None),
+ ('enable', False),
+ ('disable', False),
+ ('user', self.user.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'options': {'ignore_password_expiry': True,
+ 'multi_factor_auth_enabled': True,
+ 'multi_factor_auth_rules': [["password", "totp"]]}}
+
+ # UserManager.update(user, name=, domain=, project=, password=,
+ # email=, description=, enabled=, default_project=)
+ self.users_mock.update.assert_called_with(
+ self.user.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestUserSetPassword(TestUser):
diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py
index de232235..add3978d 100644
--- a/openstackclient/tests/unit/image/v1/fakes.py
+++ b/openstackclient/tests/unit/image/v1/fakes.py
@@ -13,10 +13,11 @@
# under the License.
#
-import copy
from unittest import mock
import uuid
+from openstack.image.v1 import image
+
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit import utils
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
@@ -111,13 +112,10 @@ class FakeImage(object):
'Alpha': 'a',
'Beta': 'b',
'Gamma': 'g'},
+ 'status': 'status' + uuid.uuid4().hex
}
# Overwrite default attributes if there are some attributes set
image_info.update(attrs)
- image = fakes.FakeResource(
- info=copy.deepcopy(image_info),
- loaded=True)
-
- return image
+ return image.Image(**image_info)
diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py
index 970b36c6..2f190a7a 100644
--- a/openstackclient/tests/unit/image/v1/test_image.py
+++ b/openstackclient/tests/unit/image/v1/test_image.py
@@ -17,7 +17,6 @@ import copy
from unittest import mock
from osc_lib.cli import format_columns
-from osc_lib import exceptions
from openstackclient.image.v1 import image
from openstackclient.tests.unit import fakes
@@ -29,9 +28,8 @@ class TestImage(image_fakes.TestImagev1):
def setUp(self):
super(TestImage, self).setUp()
- # Get a shortcut to the ServerManager Mock
- self.images_mock = self.app.client_manager.image.images
- self.images_mock.reset_mock()
+ self.app.client_manager.image = mock.Mock()
+ self.client = self.app.client_manager.image
class TestImageCreate(TestImage):
@@ -48,6 +46,7 @@ class TestImageCreate(TestImage):
'owner',
'properties',
'protected',
+ 'size'
)
data = (
new_image.container_format,
@@ -57,28 +56,24 @@ class TestImageCreate(TestImage):
new_image.min_disk,
new_image.min_ram,
new_image.name,
- new_image.owner,
+ new_image.owner_id,
format_columns.DictColumn(new_image.properties),
- new_image.protected,
+ new_image.is_protected,
+ new_image.size
)
def setUp(self):
super(TestImageCreate, self).setUp()
- self.images_mock.create.return_value = self.new_image
- # This is the return value for utils.find_resource()
- self.images_mock.get.return_value = self.new_image
- self.images_mock.update.return_value = self.new_image
+ self.client.create_image = mock.Mock(return_value=self.new_image)
+ self.client.find_image = mock.Mock(return_value=self.new_image)
+ self.client.update_image = mock.Mock(return_image=self.new_image)
# Get the command object to test
self.cmd = image.CreateImage(self.app, None)
- def test_image_reserve_no_options(self):
- mock_exception = {
- 'find.side_effect': exceptions.CommandError('x'),
- 'get.side_effect': exceptions.CommandError('x'),
- }
- self.images_mock.configure_mock(**mock_exception)
+ @mock.patch('sys.stdin', side_effect=[None])
+ def test_image_reserve_no_options(self, raw_input):
arglist = [
self.new_image.name,
]
@@ -95,25 +90,20 @@ class TestImageCreate(TestImage):
columns, data = self.cmd.take_action(parsed_args)
# ImageManager.create(name=, **)
- self.images_mock.create.assert_called_with(
+ self.client.create_image.assert_called_with(
name=self.new_image.name,
container_format=image.DEFAULT_CONTAINER_FORMAT,
- disk_format=image.DEFAULT_DISK_FORMAT,
- data=mock.ANY,
+ disk_format=image.DEFAULT_DISK_FORMAT
)
# Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
+ self.assertEqual(self.client.update_image.call_args_list, [])
self.assertEqual(self.columns, columns)
self.assertItemEqual(self.data, data)
- def test_image_reserve_options(self):
- mock_exception = {
- 'find.side_effect': exceptions.CommandError('x'),
- 'get.side_effect': exceptions.CommandError('x'),
- }
- self.images_mock.configure_mock(**mock_exception)
+ @mock.patch('sys.stdin', side_effect=[None])
+ def test_image_reserve_options(self, raw_input):
arglist = [
'--container-format', 'ovf',
'--disk-format', 'ami',
@@ -144,20 +134,19 @@ class TestImageCreate(TestImage):
columns, data = self.cmd.take_action(parsed_args)
# ImageManager.create(name=, **)
- self.images_mock.create.assert_called_with(
+ self.client.create_image.assert_called_with(
name=self.new_image.name,
container_format='ovf',
disk_format='ami',
min_disk=10,
min_ram=4,
- protected=True,
+ is_protected=True,
is_public=False,
- owner='q',
- data=mock.ANY,
+ owner_id='q',
)
# Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
+ self.assertEqual(self.client.update_image.call_args_list, [])
self.assertEqual(self.columns, columns)
self.assertItemEqual(self.data, data)
@@ -167,11 +156,6 @@ class TestImageCreate(TestImage):
mock_file = mock.Mock(name='File')
mock_open.return_value = mock_file
mock_open.read.return_value = self.data
- mock_exception = {
- 'find.side_effect': exceptions.CommandError('x'),
- 'get.side_effect': exceptions.CommandError('x'),
- }
- self.images_mock.configure_mock(**mock_exception)
arglist = [
'--file', 'filer',
@@ -203,15 +187,12 @@ class TestImageCreate(TestImage):
# Ensure the input file is closed
mock_file.close.assert_called_with()
- # ImageManager.get(name) not to be called since update action exists
- self.images_mock.get.assert_not_called()
-
# ImageManager.create(name=, **)
- self.images_mock.create.assert_called_with(
+ self.client.create_image.assert_called_with(
name=self.new_image.name,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
- protected=False,
+ is_protected=False,
is_public=True,
properties={
'Alpha': '1',
@@ -221,7 +202,7 @@ class TestImageCreate(TestImage):
)
# Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
+ self.assertEqual(self.client.update_image.call_args_list, [])
self.assertEqual(self.columns, columns)
self.assertItemEqual(self.data, data)
@@ -235,8 +216,8 @@ class TestImageDelete(TestImage):
super(TestImageDelete, self).setUp()
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = self._image
- self.images_mock.delete.return_value = None
+ self.client.find_image = mock.Mock(return_value=self._image)
+ self.client.delete_image = mock.Mock(return_value=None)
# Get the command object to test
self.cmd = image.DeleteImage(self.app, None)
@@ -252,7 +233,7 @@ class TestImageDelete(TestImage):
result = self.cmd.take_action(parsed_args)
- self.images_mock.delete.assert_called_with(self._image.id)
+ self.client.delete_image.assert_called_with(self._image.id)
self.assertIsNone(result)
@@ -269,7 +250,7 @@ class TestImageList(TestImage):
(
_image.id,
_image.name,
- '',
+ _image.status
),
)
@@ -277,13 +258,13 @@ class TestImageList(TestImage):
info = {
'id': _image.id,
'name': _image.name,
- 'owner': _image.owner,
+ 'owner': _image.owner_id,
'container_format': _image.container_format,
'disk_format': _image.disk_format,
'min_disk': _image.min_disk,
'min_ram': _image.min_ram,
'is_public': _image.is_public,
- 'protected': _image.protected,
+ 'protected': _image.is_protected,
'properties': _image.properties,
}
image_info = copy.deepcopy(info)
@@ -291,11 +272,10 @@ class TestImageList(TestImage):
def setUp(self):
super(TestImageList, self).setUp()
- self.api_mock = mock.Mock()
- self.api_mock.image_list.side_effect = [
- [self.image_info], [],
+ self.client.images = mock.Mock()
+ self.client.images.side_effect = [
+ [self._image], [],
]
- self.app.client_manager.image.api = self.api_mock
# Get the command object to test
self.cmd = image.ListImage(self.app, None)
@@ -313,10 +293,7 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- detailed=True,
- marker=self._image.id,
- )
+ self.client.images.assert_called_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
@@ -336,10 +313,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- detailed=True,
- public=True,
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ is_public=True,
)
self.assertEqual(self.columns, columns)
@@ -360,10 +335,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- detailed=True,
- private=True,
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ is_private=True,
)
self.assertEqual(self.columns, columns)
@@ -382,10 +355,7 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- detailed=True,
- marker=self._image.id,
- )
+ self.client.images.assert_called_with()
collist = (
'ID',
@@ -405,14 +375,14 @@ class TestImageList(TestImage):
datalist = ((
self._image.id,
self._image.name,
- '',
- '',
- '',
- '',
- '',
- image.VisibilityColumn(True),
- False,
- self._image.owner,
+ self._image.disk_format,
+ self._image.container_format,
+ self._image.size,
+ self._image.checksum,
+ self._image.status,
+ image.VisibilityColumn(self._image.is_public),
+ self._image.is_protected,
+ self._image.owner_id,
format_columns.DictColumn(
{'Alpha': 'a', 'Beta': 'b', 'Gamma': 'g'}),
), )
@@ -436,12 +406,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- detailed=True,
- marker=self._image.id,
- )
+ self.client.images.assert_called_with()
sf_mock.assert_called_with(
- [self.image_info],
+ [self._image],
attr='a',
value='1',
property_field='properties',
@@ -453,7 +420,7 @@ class TestImageList(TestImage):
@mock.patch('osc_lib.utils.sort_items')
def test_image_list_sort_option(self, si_mock):
si_mock.side_effect = [
- [self.image_info], [],
+ [self._image], [],
]
arglist = ['--sort', 'name:asc']
@@ -464,12 +431,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- detailed=True,
- marker=self._image.id,
- )
+ self.client.images.assert_called_with()
si_mock.assert_called_with(
- [self.image_info],
+ [self._image],
'name:asc'
)
@@ -485,8 +449,8 @@ class TestImageSet(TestImage):
super(TestImageSet, self).setUp()
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = self._image
- self.images_mock.update.return_value = self._image
+ self.client.find_image = mock.Mock(return_value=self._image)
+ self.client.update_image = mock.Mock(return_value=self._image)
# Get the command object to test
self.cmd = image.SetImage(self.app, None)
@@ -502,8 +466,7 @@ class TestImageSet(TestImage):
result = self.cmd.take_action(parsed_args)
- self.images_mock.update.assert_called_with(self._image.id,
- **{})
+ self.client.update_image.assert_called_with(self._image.id, **{})
self.assertIsNone(result)
def test_image_set_options(self):
@@ -541,7 +504,7 @@ class TestImageSet(TestImage):
'size': 35165824
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
+ self.client.update_image.assert_called_with(
self._image.id,
**kwargs
)
@@ -565,11 +528,11 @@ class TestImageSet(TestImage):
result = self.cmd.take_action(parsed_args)
kwargs = {
- 'protected': True,
+ 'is_protected': True,
'is_public': False,
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
+ self.client.update_image.assert_called_with(
self._image.id,
**kwargs
)
@@ -593,11 +556,11 @@ class TestImageSet(TestImage):
result = self.cmd.take_action(parsed_args)
kwargs = {
- 'protected': False,
+ 'is_protected': False,
'is_public': True,
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
+ self.client.update_image.assert_called_with(
self._image.id,
**kwargs
)
@@ -625,7 +588,7 @@ class TestImageSet(TestImage):
},
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
+ self.client.update_image.assert_called_with(
self._image.id,
**kwargs
)
@@ -683,7 +646,7 @@ class TestImageSet(TestImage):
'',
)
# ImageManager.update(image_id, remove_props=, **)
- self.images_mock.update.assert_called_with(
+ self.client.update_image.assert_called_with(
self._image.id,
name='updated_image',
volume='volly',
@@ -710,7 +673,7 @@ class TestImageSet(TestImage):
'min_ram': 0,
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
+ self.client.update_image.assert_called_with(
self._image.id,
**kwargs
)
@@ -742,16 +705,16 @@ class TestImageShow(TestImage):
_image.min_disk,
_image.min_ram,
_image.name,
- _image.owner,
+ _image.owner_id,
format_columns.DictColumn(_image.properties),
- _image.protected,
+ _image.is_protected,
_image.size,
)
def setUp(self):
super(TestImageShow, self).setUp()
- self.images_mock.get.return_value = self._image
+ self.client.find_image = mock.Mock(return_value=self._image)
# Get the command object to test
self.cmd = image.ShowImage(self.app, None)
@@ -769,7 +732,7 @@ class TestImageShow(TestImage):
# 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.images_mock.get.assert_called_with(
+ self.client.find_image.assert_called_with(
self._image.id,
)
@@ -791,9 +754,9 @@ class TestImageShow(TestImage):
# 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.images_mock.get.assert_called_with(
+ self.client.find_image.assert_called_with(
self._image.id,
)
size_index = columns.index('size')
- self.assertEqual(data[size_index], '2K')
+ self.assertEqual(data[size_index].human_readable(), '2K')
diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py
index 655ae341..516d5630 100644
--- a/openstackclient/tests/unit/image/v2/fakes.py
+++ b/openstackclient/tests/unit/image/v2/fakes.py
@@ -18,9 +18,9 @@ import random
from unittest import mock
import uuid
-from glanceclient.v2 import schemas
+from openstack.image.v2 import image
+from openstack.image.v2 import member
from osc_lib.cli import format_columns
-import warlock
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -154,6 +154,12 @@ class FakeImagev2Client(object):
self.image_members.resource_class = fakes.FakeResource(None, {})
self.image_tags = mock.Mock()
self.image_tags.resource_class = fakes.FakeResource(None, {})
+
+ self.find_image = mock.Mock()
+ self.find_image.resource_class = fakes.FakeResource(None, {})
+
+ self.get_image = mock.Mock()
+ self.get_image.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
self.version = 2.0
@@ -197,8 +203,8 @@ class FakeImage(object):
image_info = {
'id': str(uuid.uuid4()),
'name': 'image-name' + uuid.uuid4().hex,
- 'owner': 'image-owner' + uuid.uuid4().hex,
- 'protected': bool(random.choice([0, 1])),
+ 'owner_id': 'image-owner' + uuid.uuid4().hex,
+ 'is_protected': bool(random.choice([0, 1])),
'visibility': random.choice(['public', 'private']),
'tags': [uuid.uuid4().hex for r in range(2)],
}
@@ -206,13 +212,7 @@ class FakeImage(object):
# Overwrite default attributes if there are some attributes set
image_info.update(attrs)
- # Set up the schema
- model = warlock.model_factory(
- IMAGE_schema,
- schemas.SchemaBasedModel,
- )
-
- return model(**image_info)
+ return image.Image(**image_info)
@staticmethod
def create_images(attrs=None, count=2):
@@ -307,6 +307,8 @@ class FakeImage(object):
# Overwrite default attributes if there are some attributes set
image_member_info.update(attrs)
+ return member.Member(**image_member_info)
+
image_member = fakes.FakeModel(
copy.deepcopy(image_member_info))
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index 78d857e2..a021cfc7 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -14,13 +14,14 @@
#
import copy
+import io
+import os
+import tempfile
from unittest import mock
-from glanceclient.common import utils as glanceclient_utils
-from glanceclient.v2 import schemas
+from openstack import exceptions as sdk_exceptions
from osc_lib.cli import format_columns
from osc_lib import exceptions
-import warlock
from openstackclient.image.v2 import image
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -33,12 +34,16 @@ class TestImage(image_fakes.TestImagev2):
super(TestImage, self).setUp()
# Get shortcuts to the Mocks in image client
- self.images_mock = self.app.client_manager.image.images
- self.images_mock.reset_mock()
+ # SDK proxy mock
+ self.app.client_manager.image = mock.Mock()
+ self.client = self.app.client_manager.image
+
+ self.client.remove_member = mock.Mock()
+
+ self.client.create_image = mock.Mock()
+ self.client.update_image = mock.Mock()
self.image_members_mock = self.app.client_manager.image.image_members
- self.image_members_mock.reset_mock()
self.image_tags_mock = self.app.client_manager.image.image_tags
- self.image_tags_mock.reset_mock()
# Get shortcut to the Mocks in identity client
self.project_mock = self.app.client_manager.identity.projects
@@ -49,9 +54,6 @@ class TestImage(image_fakes.TestImagev2):
def setup_images_mock(self, count):
images = image_fakes.FakeImage.create_images(count=count)
- self.images_mock.get = image_fakes.FakeImage.get_images(
- images,
- 0)
return images
@@ -64,26 +66,22 @@ class TestImageCreate(TestImage):
super(TestImageCreate, self).setUp()
self.new_image = image_fakes.FakeImage.create_one_image()
- self.images_mock.create.return_value = self.new_image
+ self.client.create_image.return_value = self.new_image
self.project_mock.get.return_value = self.project
self.domain_mock.get.return_value = self.domain
- # This is the return value for utils.find_resource()
- self.images_mock.get.return_value = copy.deepcopy(
- self.new_image
- )
- self.images_mock.update.return_value = self.new_image
+ self.client.update_image.return_value = self.new_image
+
+ (self.expected_columns, self.expected_data) = zip(
+ *sorted(image._format_image(self.new_image).items()))
# Get the command object to test
self.cmd = image.CreateImage(self.app, None)
- def test_image_reserve_no_options(self):
- mock_exception = {
- 'find.side_effect': exceptions.CommandError('x'),
- }
- self.images_mock.configure_mock(**mock_exception)
+ @mock.patch("sys.stdin", side_effect=[None])
+ def test_image_reserve_no_options(self, raw_input):
arglist = [
self.new_image.name
]
@@ -100,45 +98,34 @@ class TestImageCreate(TestImage):
columns, data = self.cmd.take_action(parsed_args)
# ImageManager.create(name=, **)
- self.images_mock.create.assert_called_with(
+ self.client.create_image.assert_called_with(
name=self.new_image.name,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
)
# Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
-
- self.images_mock.upload.assert_called_with(
- mock.ANY, mock.ANY,
- )
+ self.assertEqual(self.client.update_image.call_args_list, [])
self.assertEqual(
- image_fakes.FakeImage.get_image_columns(self.new_image),
+ self.expected_columns,
columns)
self.assertItemEqual(
- image_fakes.FakeImage.get_image_data(self.new_image),
+ self.expected_data,
data)
- @mock.patch('glanceclient.common.utils.get_data_file', name='Open')
- def test_image_reserve_options(self, mock_open):
- mock_file = mock.MagicMock(name='File')
- mock_open.return_value = mock_file
- mock_open.read.return_value = None
- mock_exception = {
- 'find.side_effect': exceptions.CommandError('x'),
- }
- self.images_mock.configure_mock(**mock_exception)
+ @mock.patch('sys.stdin', side_effect=[None])
+ def test_image_reserve_options(self, raw_input):
arglist = [
'--container-format', 'ovf',
'--disk-format', 'ami',
'--min-disk', '10',
'--min-ram', '4',
('--protected'
- if self.new_image.protected else '--unprotected'),
+ if self.new_image.is_protected else '--unprotected'),
('--private'
if self.new_image.visibility == 'private' else '--public'),
- '--project', self.new_image.owner,
+ '--project', self.new_image.owner_id,
'--project-domain', self.domain.id,
self.new_image.name,
]
@@ -147,11 +134,11 @@ class TestImageCreate(TestImage):
('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
- ('protected', self.new_image.protected),
- ('unprotected', not self.new_image.protected),
+ ('protected', self.new_image.is_protected),
+ ('unprotected', not self.new_image.is_protected),
('public', self.new_image.visibility == 'public'),
('private', self.new_image.visibility == 'private'),
- ('project', self.new_image.owner),
+ ('project', self.new_image.owner_id),
('project_domain', self.domain.id),
('name', self.new_image.name),
]
@@ -163,29 +150,22 @@ class TestImageCreate(TestImage):
columns, data = self.cmd.take_action(parsed_args)
# ImageManager.create(name=, **)
- self.images_mock.create.assert_called_with(
+ self.client.create_image.assert_called_with(
name=self.new_image.name,
container_format='ovf',
disk_format='ami',
min_disk=10,
min_ram=4,
- owner=self.project.id,
- protected=self.new_image.protected,
+ owner_id=self.project.id,
+ is_protected=self.new_image.is_protected,
visibility=self.new_image.visibility,
)
- # Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
-
- self.images_mock.upload.assert_called_with(
- mock.ANY, mock.ANY,
- )
-
self.assertEqual(
- image_fakes.FakeImage.get_image_columns(self.new_image),
+ self.expected_columns,
columns)
self.assertItemEqual(
- image_fakes.FakeImage.get_image_data(self.new_image),
+ self.expected_data,
data)
def test_image_create_with_unexist_project(self):
@@ -222,21 +202,15 @@ class TestImageCreate(TestImage):
parsed_args,
)
- @mock.patch('glanceclient.common.utils.get_data_file', name='Open')
- def test_image_create_file(self, mock_open):
- mock_file = mock.MagicMock(name='File')
- mock_open.return_value = mock_file
- mock_open.read.return_value = (
- image_fakes.FakeImage.get_image_data(self.new_image))
- mock_exception = {
- 'find.side_effect': exceptions.CommandError('x'),
- }
- self.images_mock.configure_mock(**mock_exception)
+ def test_image_create_file(self):
+ imagefile = tempfile.NamedTemporaryFile(delete=False)
+ imagefile.write(b'\0')
+ imagefile.close()
arglist = [
- '--file', 'filer',
+ '--file', imagefile.name,
('--unprotected'
- if not self.new_image.protected else '--protected'),
+ if not self.new_image.is_protected else '--protected'),
('--public'
if self.new_image.visibility == 'public' else '--private'),
'--property', 'Alpha=1',
@@ -246,9 +220,9 @@ class TestImageCreate(TestImage):
self.new_image.name,
]
verifylist = [
- ('file', 'filer'),
- ('protected', self.new_image.protected),
- ('unprotected', not self.new_image.protected),
+ ('file', imagefile.name),
+ ('protected', self.new_image.is_protected),
+ ('unprotected', not self.new_image.is_protected),
('public', self.new_image.visibility == 'public'),
('private', self.new_image.visibility == 'private'),
('properties', {'Alpha': '1', 'Beta': '2'}),
@@ -263,29 +237,23 @@ class TestImageCreate(TestImage):
columns, data = self.cmd.take_action(parsed_args)
# ImageManager.create(name=, **)
- self.images_mock.create.assert_called_with(
+ self.client.create_image.assert_called_with(
name=self.new_image.name,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
- protected=self.new_image.protected,
+ is_protected=self.new_image.is_protected,
visibility=self.new_image.visibility,
Alpha='1',
Beta='2',
tags=self.new_image.tags,
- )
-
- # Verify update() was not called, if it was show the args
- self.assertEqual(self.images_mock.update.call_args_list, [])
-
- self.images_mock.upload.assert_called_with(
- mock.ANY, mock.ANY,
+ filename=imagefile.name
)
self.assertEqual(
- image_fakes.FakeImage.get_image_columns(self.new_image),
+ self.expected_columns,
columns)
self.assertItemEqual(
- image_fakes.FakeImage.get_image_data(self.new_image),
+ self.expected_data,
data)
def test_image_create_dead_options(self):
@@ -315,25 +283,31 @@ class TestAddProjectToImage(TestImage):
)
columns = (
+ 'created_at',
'image_id',
'member_id',
+ 'schema',
'status',
+ 'updated_at'
)
datalist = (
+ new_member.created_at,
_image.id,
new_member.member_id,
+ new_member.schema,
new_member.status,
+ new_member.updated_at
)
def setUp(self):
super(TestAddProjectToImage, self).setUp()
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = self._image
+ self.client.find_image.return_value = self._image
# Update the image_id in the MEMBER dict
- self.image_members_mock.create.return_value = self.new_member
+ self.client.add_member.return_value = self.new_member
self.project_mock.get.return_value = self.project
self.domain_mock.get.return_value = self.domain
# Get the command object to test
@@ -354,9 +328,9 @@ class TestAddProjectToImage(TestImage):
# 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.image_members_mock.create.assert_called_with(
- self._image.id,
- self.project.id
+ self.client.add_member.assert_called_with(
+ image=self._image.id,
+ member_id=self.project.id
)
self.assertEqual(self.columns, columns)
@@ -379,10 +353,11 @@ class TestAddProjectToImage(TestImage):
# 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.image_members_mock.create.assert_called_with(
- self._image.id,
- self.project.id
+ self.client.add_member.assert_called_with(
+ image=self._image.id,
+ member_id=self.project.id
)
+
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
@@ -392,7 +367,7 @@ class TestImageDelete(TestImage):
def setUp(self):
super(TestImageDelete, self).setUp()
- self.images_mock.delete.return_value = None
+ self.client.delete_image.return_value = None
# Get the command object to test
self.cmd = image.DeleteImage(self.app, None)
@@ -408,9 +383,11 @@ class TestImageDelete(TestImage):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.client.find_image.side_effect = images
+
result = self.cmd.take_action(parsed_args)
- self.images_mock.delete.assert_called_with(images[0].id)
+ self.client.delete_image.assert_called_with(images[0].id)
self.assertIsNone(result)
def test_image_delete_multi_images(self):
@@ -422,10 +399,12 @@ class TestImageDelete(TestImage):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.client.find_image.side_effect = images
+
result = self.cmd.take_action(parsed_args)
calls = [mock.call(i.id) for i in images]
- self.images_mock.delete.assert_has_calls(calls)
+ self.client.delete_image.assert_has_calls(calls)
self.assertIsNone(result)
def test_image_delete_multi_images_exception(self):
@@ -449,15 +428,15 @@ class TestImageDelete(TestImage):
ret_find = [
images[0],
images[1],
- exceptions.NotFound('404'),
+ sdk_exceptions.ResourceNotFound()
]
- self.images_mock.get = Exception()
- self.images_mock.find.side_effect = ret_find
+ self.client.find_image.side_effect = ret_find
+
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
calls = [mock.call(i.id) for i in images]
- self.images_mock.delete.assert_has_calls(calls)
+ self.client.delete_image.assert_has_calls(calls)
class TestImageList(TestImage):
@@ -473,17 +452,17 @@ class TestImageList(TestImage):
datalist = (
_image.id,
_image.name,
- '',
+ None,
),
def setUp(self):
super(TestImageList, self).setUp()
self.api_mock = mock.Mock()
- self.api_mock.image_list.side_effect = [
+ self.api_mock.side_effect = [
[self._image], [],
]
- self.app.client_manager.image.api = self.api_mock
+ self.client.images = self.api_mock
# Get the command object to test
self.cmd = image.ListImage(self.app, None)
@@ -503,8 +482,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ # marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -527,9 +506,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- public=True,
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ visibility='public',
)
self.assertEqual(self.columns, columns)
@@ -552,9 +530,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- private=True,
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ visibility='private',
)
self.assertEqual(self.columns, columns)
@@ -577,9 +554,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- community=True,
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ visibility='community',
)
self.assertEqual(self.columns, columns)
@@ -602,9 +578,8 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- shared=True,
- marker=self._image.id,
+ self.client.images.assert_called_with(
+ visibility='shared',
)
self.assertEqual(self.columns, columns)
@@ -629,10 +604,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- shared=True,
+ self.client.images.assert_called_with(
+ visibility='shared',
member_status='all',
- marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -666,8 +640,7 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- marker=self._image.id,
+ self.client.images.assert_called_with(
)
collist = (
@@ -688,14 +661,14 @@ class TestImageList(TestImage):
datalist = ((
self._image.id,
self._image.name,
- '',
- '',
- '',
- '',
- '',
+ None,
+ None,
+ None,
+ None,
+ None,
self._image.visibility,
- self._image.protected,
- self._image.owner,
+ self._image.is_protected,
+ self._image.owner_id,
format_columns.ListColumn(self._image.tags),
), )
self.assertListItemEqual(datalist, tuple(data))
@@ -716,8 +689,7 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- marker=self._image.id,
+ self.client.images.assert_called_with(
)
sf_mock.assert_called_with(
[self._image],
@@ -741,8 +713,7 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with(
- marker=self._image.id,
+ self.client.images.assert_called_with(
)
si_mock.assert_called_with(
[self._image],
@@ -763,8 +734,10 @@ class TestImageList(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with(
- limit=ret_limit, marker=None
+ self.client.images.assert_called_with(
+ limit=ret_limit,
+ paginated=False
+ # marker=None
)
self.assertEqual(self.columns, columns)
@@ -775,8 +748,9 @@ class TestImageList(TestImage):
# tangchen: Since image_fakes.IMAGE is a dict, it cannot offer a .id
# operation. Will fix this by using FakeImage class instead
# of IMAGE dict.
- fr_mock.return_value = mock.Mock()
- fr_mock.return_value.id = image_fakes.image_id
+ self.client.find_image = mock.Mock(return_value=self._image)
+# fr_mock.return_value = mock.Mock()
+# fr_mock.return_value.id = image_fakes.image_id
arglist = [
'--marker', image_fakes.image_name,
@@ -787,10 +761,12 @@ class TestImageList(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with(
- marker=image_fakes.image_id,
+ self.client.images.assert_called_with(
+ marker=self._image.id,
)
+ self.client.find_image.assert_called_with(image_fakes.image_name)
+
def test_image_list_name_option(self):
arglist = [
'--name', 'abc',
@@ -801,8 +777,9 @@ class TestImageList(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with(
- name='abc', marker=self._image.id
+ self.client.images.assert_called_with(
+ name='abc',
+ # marker=self._image.id
)
def test_image_list_status_option(self):
@@ -815,8 +792,8 @@ class TestImageList(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with(
- status='active', marker=self._image.id
+ self.client.images.assert_called_with(
+ status='active'
)
def test_image_list_tag_option(self):
@@ -829,8 +806,8 @@ class TestImageList(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with(
- tag='abc', marker=self._image.id
+ self.client.images.assert_called_with(
+ tag='abc'
)
@@ -849,17 +826,17 @@ class TestListImageProjects(TestImage):
"Status"
)
- datalist = ((
+ datalist = [(
_image.id,
member.member_id,
member.status,
- ))
+ )]
def setUp(self):
super(TestListImageProjects, self).setUp()
- self.images_mock.get.return_value = self._image
- self.image_members_mock.list.return_value = self.datalist
+ self.client.find_image.return_value = self._image
+ self.client.members.return_value = [self.member]
self.cmd = image.ListImageProjects(self.app, None)
@@ -874,10 +851,10 @@ class TestListImageProjects(TestImage):
columns, data = self.cmd.take_action(parsed_args)
- self.image_members_mock.list.assert_called_with(self._image.id)
+ self.client.members.assert_called_with(image=self._image.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(len(self.datalist), len(tuple(data)))
+ self.assertEqual(self.datalist, list(data))
class TestRemoveProjectImage(TestImage):
@@ -890,11 +867,11 @@ class TestRemoveProjectImage(TestImage):
self._image = image_fakes.FakeImage.create_one_image()
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = self._image
+ self.client.find_image.return_value = self._image
self.project_mock.get.return_value = self.project
self.domain_mock.get.return_value = self.domain
- self.image_members_mock.delete.return_value = None
+ self.client.remove_member.return_value = None
# Get the command object to test
self.cmd = image.RemoveProjectImage(self.app, None)
@@ -911,9 +888,13 @@ class TestRemoveProjectImage(TestImage):
result = self.cmd.take_action(parsed_args)
- self.image_members_mock.delete.assert_called_with(
+ self.client.find_image.assert_called_with(
self._image.id,
- self.project.id,
+ ignore_missing=False)
+
+ self.client.remove_member.assert_called_with(
+ member=self.project.id,
+ image=self._image.id,
)
self.assertIsNone(result)
@@ -932,9 +913,9 @@ class TestRemoveProjectImage(TestImage):
result = self.cmd.take_action(parsed_args)
- self.image_members_mock.delete.assert_called_with(
- self._image.id,
- self.project.id,
+ self.client.remove_member.assert_called_with(
+ member=self.project.id,
+ image=self._image.id,
)
self.assertIsNone(result)
@@ -943,21 +924,16 @@ class TestImageSet(TestImage):
project = identity_fakes.FakeProject.create_one_project()
domain = identity_fakes.FakeDomain.create_one_domain()
+ _image = image_fakes.FakeImage.create_one_image({'tags': []})
def setUp(self):
super(TestImageSet, self).setUp()
- # Set up the schema
- self.model = warlock.model_factory(
- image_fakes.IMAGE_schema,
- schemas.SchemaBasedModel,
- )
self.project_mock.get.return_value = self.project
self.domain_mock.get.return_value = self.domain
- self.images_mock.get.return_value = self.model(**image_fakes.IMAGE)
- self.images_mock.update.return_value = self.model(**image_fakes.IMAGE)
+ self.client.find_image.return_value = self._image
self.app.client_manager.auth_ref = mock.Mock(
project_id=self.project.id,
@@ -986,38 +962,38 @@ class TestImageSet(TestImage):
attrs={'image_id': image_fakes.image_id,
'member_id': self.project.id}
)
- self.image_members_mock.update.return_value = membership
+ self.client.update_member.return_value = membership
arglist = [
'--accept',
- image_fakes.image_id,
+ self._image.id,
]
verifylist = [
('accept', True),
('reject', False),
('pending', False),
- ('image', image_fakes.image_id)
+ ('image', self._image.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.image_members_mock.update.assert_called_once_with(
- image_fakes.image_id,
- self.app.client_manager.auth_ref.project_id,
- 'accepted',
+ self.client.update_member.assert_called_once_with(
+ image=self._image.id,
+ member=self.app.client_manager.auth_ref.project_id,
+ status='accepted',
)
# Assert that the 'update image" route is also called, in addition to
# the 'update membership' route.
- self.images_mock.update.assert_called_with(image_fakes.image_id)
+ self.client.update_image.assert_called_with(self._image.id)
def test_image_set_membership_option_reject(self):
membership = image_fakes.FakeImage.create_one_image_member(
attrs={'image_id': image_fakes.image_id,
'member_id': self.project.id}
)
- self.image_members_mock.update.return_value = membership
+ self.client.update_member.return_value = membership
arglist = [
'--reject',
@@ -1033,22 +1009,22 @@ class TestImageSet(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.image_members_mock.update.assert_called_once_with(
- image_fakes.image_id,
- self.app.client_manager.auth_ref.project_id,
- 'rejected',
+ self.client.update_member.assert_called_once_with(
+ image=self._image.id,
+ member=self.app.client_manager.auth_ref.project_id,
+ status='rejected',
)
# Assert that the 'update image" route is also called, in addition to
# the 'update membership' route.
- self.images_mock.update.assert_called_with(image_fakes.image_id)
+ self.client.update_image.assert_called_with(self._image.id)
def test_image_set_membership_option_pending(self):
membership = image_fakes.FakeImage.create_one_image_member(
attrs={'image_id': image_fakes.image_id,
'member_id': self.project.id}
)
- self.image_members_mock.update.return_value = membership
+ self.client.update_member.return_value = membership
arglist = [
'--pending',
@@ -1064,15 +1040,15 @@ class TestImageSet(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.image_members_mock.update.assert_called_once_with(
- image_fakes.image_id,
- self.app.client_manager.auth_ref.project_id,
- 'pending',
+ self.client.update_member.assert_called_once_with(
+ image=self._image.id,
+ member=self.app.client_manager.auth_ref.project_id,
+ status='pending',
)
# Assert that the 'update image" route is also called, in addition to
# the 'update membership' route.
- self.images_mock.update.assert_called_with(image_fakes.image_id)
+ self.client.update_image.assert_called_with(self._image.id)
def test_image_set_options(self):
arglist = [
@@ -1083,7 +1059,7 @@ class TestImageSet(TestImage):
'--disk-format', 'vmdk',
'--project', self.project.name,
'--project-domain', self.domain.id,
- image_fakes.image_id,
+ self._image.id,
]
verifylist = [
('name', 'new-name'),
@@ -1093,7 +1069,7 @@ class TestImageSet(TestImage):
('disk_format', 'vmdk'),
('project', self.project.name),
('project_domain', self.domain.id),
- ('image', image_fakes.image_id),
+ ('image', self._image.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1101,15 +1077,15 @@ class TestImageSet(TestImage):
kwargs = {
'name': 'new-name',
- 'owner': self.project.id,
+ 'owner_id': self.project.id,
'min_disk': 2,
'min_ram': 4,
'container_format': 'ovf',
'disk_format': 'vmdk',
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id, **kwargs)
+ self.client.update_image.assert_called_with(
+ self._image.id, **kwargs)
self.assertIsNone(result)
def test_image_set_with_unexist_project(self):
@@ -1148,12 +1124,12 @@ class TestImageSet(TestImage):
result = self.cmd.take_action(parsed_args)
kwargs = {
- 'protected': True,
+ 'is_protected': True,
'visibility': 'private',
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1176,12 +1152,12 @@ class TestImageSet(TestImage):
result = self.cmd.take_action(parsed_args)
kwargs = {
- 'protected': False,
+ 'is_protected': False,
'visibility': 'public',
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1205,8 +1181,8 @@ class TestImageSet(TestImage):
'Beta': '2',
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1243,8 +1219,8 @@ class TestImageSet(TestImage):
'ramdisk_id': 'xyzpdq',
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1266,8 +1242,8 @@ class TestImageSet(TestImage):
'tags': ['test-tag'],
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1290,12 +1266,12 @@ class TestImageSet(TestImage):
'tags': ['test-tag'],
}
- self.images_mock.reactivate.assert_called_with(
- image_fakes.image_id,
+ self.client.reactivate_image.assert_called_with(
+ self._image.id,
)
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1318,20 +1294,20 @@ class TestImageSet(TestImage):
'tags': ['test-tag'],
}
- self.images_mock.deactivate.assert_called_with(
- image_fakes.image_id,
+ self.client.deactivate_image.assert_called_with(
+ self._image.id,
)
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
def test_image_set_tag_merge(self):
- old_image = copy.copy(image_fakes.IMAGE)
+ old_image = self._image
old_image['tags'] = ['old1', 'new2']
- self.images_mock.get.return_value = self.model(**old_image)
+ self.client.find_image.return_value = old_image
arglist = [
'--tag', 'test-tag',
image_fakes.image_name,
@@ -1348,16 +1324,16 @@ class TestImageSet(TestImage):
'tags': ['old1', 'new2', 'test-tag'],
}
# ImageManager.update(image, **kwargs)
- a, k = self.images_mock.update.call_args
- self.assertEqual(image_fakes.image_id, a[0])
+ a, k = self.client.update_image.call_args
+ self.assertEqual(self._image.id, a[0])
self.assertIn('tags', k)
self.assertEqual(set(kwargs['tags']), set(k['tags']))
self.assertIsNone(result)
def test_image_set_tag_merge_dupe(self):
- old_image = copy.copy(image_fakes.IMAGE)
+ old_image = self._image
old_image['tags'] = ['old1', 'new2']
- self.images_mock.get.return_value = self.model(**old_image)
+ self.client.find_image.return_value = old_image
arglist = [
'--tag', 'old1',
image_fakes.image_name,
@@ -1374,8 +1350,8 @@ class TestImageSet(TestImage):
'tags': ['new2', 'old1'],
}
# ImageManager.update(image, **kwargs)
- a, k = self.images_mock.update.call_args
- self.assertEqual(image_fakes.image_id, a[0])
+ a, k = self.client.update_image.call_args
+ self.assertEqual(self._image.id, a[0])
self.assertIn('tags', k)
self.assertEqual(set(kwargs['tags']), set(k['tags']))
self.assertIsNone(result)
@@ -1416,8 +1392,8 @@ class TestImageSet(TestImage):
'min_ram': 0,
}
# ImageManager.update(image, **kwargs)
- self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self.client.update_image.assert_called_with(
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -1428,16 +1404,25 @@ class TestImageShow(TestImage):
new_image = image_fakes.FakeImage.create_one_image(
attrs={'size': 1000})
+ _data = image_fakes.FakeImage.create_one_image()
+
+ columns = (
+ 'id', 'name', 'owner', 'protected', 'tags', 'visibility'
+ )
+
+ data = (
+ _data.id,
+ _data.name,
+ _data.owner_id,
+ _data.is_protected,
+ format_columns.ListColumn(_data.tags),
+ _data.visibility
+ )
+
def setUp(self):
super(TestImageShow, self).setUp()
- # Set up the schema
- self.model = warlock.model_factory(
- image_fakes.IMAGE_schema,
- schemas.SchemaBasedModel,
- )
-
- self.images_mock.get.return_value = self.model(**image_fakes.IMAGE)
+ self.client.find_image = mock.Mock(return_value=self._data)
# Get the command object to test
self.cmd = image.ShowImage(self.app, None)
@@ -1455,15 +1440,16 @@ class TestImageShow(TestImage):
# 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.images_mock.get.assert_called_with(
+ self.client.find_image.assert_called_with(
image_fakes.image_id,
+ ignore_missing=False
)
- self.assertEqual(image_fakes.IMAGE_columns, columns)
- self.assertItemEqual(image_fakes.IMAGE_SHOW_data, data)
+ self.assertEqual(self.columns, columns)
+ self.assertItemEqual(self.data, data)
def test_image_show_human_readable(self):
- self.images_mock.get.return_value = self.new_image
+ self.client.find_image.return_value = self.new_image
arglist = [
'--human-readable',
self.new_image.id,
@@ -1478,8 +1464,9 @@ class TestImageShow(TestImage):
# 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.images_mock.get.assert_called_with(
+ self.client.find_image.assert_called_with(
self.new_image.id,
+ ignore_missing=False
)
size_index = columns.index('size')
@@ -1491,19 +1478,15 @@ class TestImageUnset(TestImage):
attrs = {}
attrs['tags'] = ['test']
attrs['prop'] = 'test'
+ attrs['prop2'] = 'fake'
image = image_fakes.FakeImage.create_one_image(attrs)
def setUp(self):
super(TestImageUnset, self).setUp()
- # Set up the schema
- self.model = warlock.model_factory(
- image_fakes.IMAGE_schema,
- schemas.SchemaBasedModel,
- )
-
- self.images_mock.get.return_value = self.image
- self.image_tags_mock.delete.return_value = self.image
+ self.client.find_image.return_value = self.image
+ self.client.remove_tag.return_value = self.image
+ self.client.update_image.return_value = self.image
# Get the command object to test
self.cmd = image.UnsetImage(self.app, None)
@@ -1535,7 +1518,7 @@ class TestImageUnset(TestImage):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.image_tags_mock.delete.assert_called_with(
+ self.client.remove_tag.assert_called_with(
self.image.id, 'test'
)
self.assertIsNone(result)
@@ -1555,9 +1538,9 @@ class TestImageUnset(TestImage):
result = self.cmd.take_action(parsed_args)
kwargs = {}
- self.images_mock.update.assert_called_with(
- self.image.id,
- parsed_args.properties,
+ self.client.update_image.assert_called_with(
+ self.image,
+ properties={'prop2': 'fake'},
**kwargs)
self.assertIsNone(result)
@@ -1579,12 +1562,12 @@ class TestImageUnset(TestImage):
result = self.cmd.take_action(parsed_args)
kwargs = {}
- self.images_mock.update.assert_called_with(
- self.image.id,
- parsed_args.properties,
+ self.client.update_image.assert_called_with(
+ self.image,
+ properties={'prop2': 'fake'},
**kwargs)
- self.image_tags_mock.delete.assert_called_with(
+ self.client.remove_tag.assert_called_with(
self.image.id, 'test'
)
self.assertIsNone(result)
@@ -1597,18 +1580,13 @@ class TestImageSave(TestImage):
def setUp(self):
super(TestImageSave, self).setUp()
- # Generate a request id
- self.resp = mock.MagicMock()
- self.resp.headers['x-openstack-request-id'] = 'req_id'
+ self.client.find_image.return_value = self.image
+ self.client.download_image.return_value = self.image
# Get the command object to test
self.cmd = image.SaveImage(self.app, None)
def test_save_data(self):
- req_id_proxy = glanceclient_utils.RequestIdProxy(
- ['some_data', self.resp]
- )
- self.images_mock.data.return_value = req_id_proxy
arglist = ['--file', '/path/to/file', self.image.id]
@@ -1618,23 +1596,58 @@ class TestImageSave(TestImage):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- with mock.patch('glanceclient.common.utils.save_image') as mocked_save:
- self.cmd.take_action(parsed_args)
- mocked_save.assert_called_once_with(req_id_proxy, '/path/to/file')
+ self.cmd.take_action(parsed_args)
- def test_save_no_data(self):
- req_id_proxy = glanceclient_utils.RequestIdProxy(
- [None, self.resp]
- )
- self.images_mock.data.return_value = req_id_proxy
+ self.client.download_image.assert_called_once_with(
+ self.image.id,
+ output='/path/to/file')
- arglist = ['--file', '/path/to/file', self.image.id]
- verifylist = [
- ('file', '/path/to/file'),
- ('image', self.image.id)
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+class TestImageGetData(TestImage):
+
+ def setUp(self):
+ super(TestImageGetData, self).setUp()
+ self.args = mock.Mock()
+
+ def test_get_data_file_file(self):
+ (fd, fname) = tempfile.mkstemp(prefix='osc_test_image')
+ self.args.file = fname
+
+ (test_fd, test_name) = image.get_data_file(self.args)
+
+ self.assertEqual(fname, test_name)
+ test_fd.close()
+
+ os.unlink(fname)
+
+ def test_get_data_file_2(self):
+
+ self.args.file = None
+
+ f = io.BytesIO(b"some initial binary data: \x00\x01")
+
+ with mock.patch('sys.stdin') as stdin:
+ stdin.return_value = f
+ stdin.isatty.return_value = False
+ stdin.buffer = f
+
+ (test_fd, test_name) = image.get_data_file(self.args)
+
+ # Ensure data written to temp file is correct
+ self.assertEqual(f, test_fd)
+ self.assertIsNone(test_name)
+
+ def test_get_data_file_3(self):
+
+ self.args.file = None
+
+ f = io.BytesIO(b"some initial binary data: \x00\x01")
+
+ with mock.patch('sys.stdin') as stdin:
+ # There is stdin, but interactive
+ stdin.return_value = f
+
+ (test_fd, test_fname) = image.get_data_file(self.args)
- # Raise SystemExit if no data was provided.
- self.assertRaises(SystemExit, self.cmd.take_action, parsed_args)
+ self.assertIsNone(test_fd)
+ self.assertIsNone(test_fname)
diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py
index 0c98a129..5788b473 100644
--- a/openstackclient/tests/unit/integ/cli/test_shell.py
+++ b/openstackclient/tests/unit/integ/cli/test_shell.py
@@ -20,16 +20,6 @@ from openstackclient import shell
from openstackclient.tests.unit.integ import base as test_base
from openstackclient.tests.unit import test_shell
-# NOTE(dtroyer): Attempt the import to detect if the SDK installed is new
-# enough to contain the os_client_config code. If so, use
-# that path for mocks.
-CONFIG_MOCK_BASE = "openstack.config.loader"
-try:
- from openstack.config import defaults # noqa
-except ImportError:
- # Fall back to os-client-config
- CONFIG_MOCK_BASE = "os_client_config.config"
-
class TestIntegShellCliNoAuth(test_base.TestInteg):
@@ -455,8 +445,8 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
temp_dir = self.useFixture(fixtures.TempDir())
return temp_dir.join(filename)
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_vendor_file")
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_config_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_vendor_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_config_file")
def test_shell_args_precedence_1(self, config_mock, vendor_mock):
"""Precedence run 1
@@ -473,7 +463,6 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
return ('file.yaml', copy.deepcopy(test_shell.PUBLIC_1))
vendor_mock.side_effect = vendor_mock_return
- print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE)
_shell = shell.OpenStackShell()
_shell.run(
"--os-password qaz extension list".split(),
@@ -527,8 +516,8 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
# +env, +cli, +occ
# see test_shell_args_precedence_2()
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_vendor_file")
- @mock.patch(CONFIG_MOCK_BASE + ".OpenStackConfig._load_config_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_vendor_file")
+ @mock.patch("openstack.config.loader.OpenStackConfig._load_config_file")
def test_shell_args_precedence_2(self, config_mock, vendor_mock):
"""Precedence run 2
@@ -545,7 +534,6 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg):
return ('file.yaml', copy.deepcopy(test_shell.PUBLIC_1))
vendor_mock.side_effect = vendor_mock_return
- print("CONFIG_MOCK_BASE=%s" % CONFIG_MOCK_BASE)
_shell = shell.OpenStackShell()
_shell.run(
"--os-username zarquon --os-password qaz "
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index a553f501..cef0a11c 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -1227,6 +1227,7 @@ class FakeSecurityGroup(object):
'id': 'security-group-id-' + uuid.uuid4().hex,
'name': 'security-group-name-' + uuid.uuid4().hex,
'description': 'security-group-description-' + uuid.uuid4().hex,
+ 'stateful': True,
'project_id': 'project-id-' + uuid.uuid4().hex,
'security_group_rules': [],
'tags': []
@@ -1832,7 +1833,7 @@ class FakeFloatingIPPortForwarding(object):
"""
attrs = attrs or {}
floatingip_id = (
- attrs.get('floatingip_id') or'floating-ip-id-' + uuid.uuid4().hex
+ attrs.get('floatingip_id') or 'floating-ip-id-' + uuid.uuid4().hex
)
# Set default attributes.
port_forwarding_attrs = {
@@ -1843,6 +1844,7 @@ class FakeFloatingIPPortForwarding(object):
'internal_port': randint(1, 65535),
'external_port': randint(1, 65535),
'protocol': 'tcp',
+ 'description': 'some description',
}
# Overwrite default attributes.
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 a98051e7..dbcd5c97 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
@@ -776,6 +776,32 @@ class TestSetFloatingIP(TestFloatingIPNetwork):
self.network.update_ip.assert_called_once_with(
self.floating_ip, **attrs)
+ def test_description_option(self):
+ arglist = [
+ self.floating_ip.id,
+ '--port', self.floating_ip.port_id,
+ '--description', self.floating_ip.description,
+ ]
+ verifylist = [
+ ('floating_ip', self.floating_ip.id),
+ ('port', self.floating_ip.port_id),
+ ('description', self.floating_ip.description),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'port_id': self.floating_ip.port_id,
+ 'description': self.floating_ip.description,
+ }
+ self.network.find_ip.assert_called_once_with(
+ self.floating_ip.id,
+ ignore_missing=False,
+ )
+ self.network.update_ip.assert_called_once_with(
+ self.floating_ip, **attrs)
+
def test_qos_policy_option(self):
qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
self.network.find_qos_policy = mock.Mock(return_value=qos_policy)
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 ea6cdd26..1028c18a 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
@@ -62,6 +62,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
self.app, self.namespace)
self.columns = (
+ 'description',
'external_port',
'floatingip_id',
'id',
@@ -73,6 +74,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
)
self.data = (
+ self.new_port_forwarding.description,
self.new_port_forwarding.external_port,
self.new_port_forwarding.floatingip_id,
self.new_port_forwarding.id,
@@ -102,6 +104,8 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
self.new_port_forwarding.floatingip_id,
'--internal-ip-address',
self.new_port_forwarding.internal_ip_address,
+ '--description',
+ self.new_port_forwarding.description,
]
verifylist = [
('port', self.new_port_forwarding.internal_port_id),
@@ -111,6 +115,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
('floating_ip', self.new_port_forwarding.floatingip_id),
('internal_ip_address', self.new_port_forwarding.
internal_ip_address),
+ ('description', self.new_port_forwarding.description),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
@@ -126,6 +131,7 @@ class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'internal_port_id': self.new_port_forwarding.
internal_port_id,
'protocol': self.new_port_forwarding.protocol,
+ 'description': self.new_port_forwarding.description,
})
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -251,7 +257,8 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'Internal IP Address',
'Internal Port',
'External Port',
- 'Protocol'
+ 'Protocol',
+ 'Description',
)
def setUp(self):
@@ -273,6 +280,7 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding):
port_forwarding.internal_port,
port_forwarding.external_port,
port_forwarding.protocol,
+ port_forwarding.description,
))
self.network.floating_ip_port_forwardings = mock.Mock(
return_value=self.port_forwardings
@@ -393,6 +401,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'--internal-protocol-port', '100',
'--external-protocol-port', '200',
'--protocol', 'tcp',
+ '--description', 'some description',
self._port_forwarding.floatingip_id,
self._port_forwarding.id,
]
@@ -402,6 +411,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
('internal_protocol_port', 100),
('external_protocol_port', 200),
('protocol', 'tcp'),
+ ('description', 'some description'),
('floating_ip', self._port_forwarding.floatingip_id),
('port_forwarding_id', self._port_forwarding.id),
]
@@ -415,6 +425,7 @@ class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding):
'internal_port': 100,
'external_port': 200,
'protocol': 'tcp',
+ 'description': 'some description',
}
self.network.update_floating_ip_port_forwarding.assert_called_with(
self._port_forwarding.floatingip_id,
@@ -428,6 +439,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
# The port forwarding to show.
columns = (
+ 'description',
'external_port',
'floatingip_id',
'id',
@@ -450,6 +462,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding):
)
)
self.data = (
+ self._port_forwarding.description,
self._port_forwarding.external_port,
self._port_forwarding.floatingip_id,
self._port_forwarding.id,
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 45d6008b..5f8eed67 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -278,24 +278,6 @@ class TestCreateNetworkIdentityV3(TestNetwork):
def test_create_with_no_tag(self):
self._test_create_with_tag(add_tags=False)
- def test_create_default_internal(self):
- arglist = [
- self._network.name,
- "--default",
- ]
- verifylist = [
- ('name', self._network.name),
- ('enable', True),
- ('share', None),
- ('project', None),
- ('external', False),
- ('default', True),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
-
class TestCreateNetworkIdentityV2(TestNetwork):
@@ -1043,21 +1025,6 @@ class TestSetNetwork(TestNetwork):
def test_set_with_no_tag(self):
self._test_set_tags(with_tags=False)
- def test_set_default_internal(self):
- arglist = [
- self._network.name,
- '--internal',
- '--default',
- ]
- verifylist = [
- ('internal', True),
- ('default', True),
- ]
-
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
-
class TestShowNetwork(TestNetwork):
diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py
index 078188ce..d7c71ea7 100644
--- a/openstackclient/tests/unit/network/v2/test_network_rbac.py
+++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py
@@ -14,6 +14,7 @@
from unittest import mock
from unittest.mock import call
+import ddt
from osc_lib import exceptions
from openstackclient.network.v2 import network_rbac
@@ -33,11 +34,14 @@ class TestNetworkRBAC(network_fakes.TestNetworkV2):
self.projects_mock = self.app.client_manager.identity.projects
+@ddt.ddt
class TestCreateNetworkRBAC(TestNetworkRBAC):
network_object = network_fakes.FakeNetwork.create_one_network()
qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group()
+ as_object = network_fakes.FakeAddressScope.create_one_address_scope()
+ snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool()
project = identity_fakes_v3.FakeProject.create_one_project()
rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac(
attrs={'tenant_id': project.id,
@@ -77,6 +81,10 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
return_value=self.qos_object)
self.network.find_security_group = mock.Mock(
return_value=self.sg_object)
+ self.network.find_address_scope = mock.Mock(
+ return_value=self.as_object)
+ self.network.find_subnet_pool = mock.Mock(
+ return_value=self.snp_object)
self.projects_mock.get.return_value = self.project
def test_network_rbac_create_no_type(self):
@@ -224,57 +232,29 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
- def test_network_rbac_create_qos_object(self):
- self.rbac_policy.object_type = 'qos_policy'
- self.rbac_policy.object_id = self.qos_object.id
- arglist = [
- '--type', 'qos_policy',
- '--action', self.rbac_policy.action,
- '--target-project', self.rbac_policy.target_tenant,
- self.qos_object.name,
- ]
- verifylist = [
- ('type', 'qos_policy'),
- ('action', self.rbac_policy.action),
- ('target_project', self.rbac_policy.target_tenant),
- ('rbac_object', self.qos_object.name),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # DisplayCommandBase.take_action() returns two tuples
- columns, data = self.cmd.take_action(parsed_args)
-
- self.network.create_rbac_policy.assert_called_with(**{
- 'object_id': self.qos_object.id,
- 'object_type': 'qos_policy',
- 'action': self.rbac_policy.action,
- 'target_tenant': self.rbac_policy.target_tenant,
- })
- self.data = [
- self.rbac_policy.action,
- self.rbac_policy.id,
- self.qos_object.id,
- 'qos_policy',
- self.rbac_policy.tenant_id,
- self.rbac_policy.target_tenant,
- ]
- self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, list(data))
+ @ddt.data(
+ ('qos_policy', "qos_object"),
+ ('security_group', "sg_object"),
+ ('subnetpool', "snp_object"),
+ ('address_scope', "as_object")
+ )
+ @ddt.unpack
+ def test_network_rbac_create_object(self, obj_type, obj_fake_attr):
+ obj_fake = getattr(self, obj_fake_attr)
- def test_network_rbac_create_security_group_object(self):
- self.rbac_policy.object_type = 'security_group'
- self.rbac_policy.object_id = self.sg_object.id
+ self.rbac_policy.object_type = obj_type
+ self.rbac_policy.object_id = obj_fake.id
arglist = [
- '--type', 'security_group',
+ '--type', obj_type,
'--action', self.rbac_policy.action,
'--target-project', self.rbac_policy.target_tenant,
- self.sg_object.name,
+ obj_fake.name,
]
verifylist = [
- ('type', 'security_group'),
+ ('type', obj_type),
('action', self.rbac_policy.action),
('target_project', self.rbac_policy.target_tenant),
- ('rbac_object', self.sg_object.name),
+ ('rbac_object', obj_fake.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -282,16 +262,16 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
columns, data = self.cmd.take_action(parsed_args)
self.network.create_rbac_policy.assert_called_with(**{
- 'object_id': self.sg_object.id,
- 'object_type': 'security_group',
+ 'object_id': obj_fake.id,
+ 'object_type': obj_type,
'action': self.rbac_policy.action,
'target_tenant': self.rbac_policy.target_tenant,
})
self.data = [
self.rbac_policy.action,
self.rbac_policy.id,
- self.sg_object.id,
- 'security_group',
+ obj_fake.id,
+ obj_type,
self.rbac_policy.tenant_id,
self.rbac_policy.target_tenant,
]
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 89a0c223..b60f1710 100644
--- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py
+++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py
@@ -24,6 +24,20 @@ from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils
+class TestAuxiliaryFunctions(tests_utils.TestCase):
+
+ def test__get_ranges(self):
+ input_reference = [
+ ([1, 2, 3, 4, 5, 6, 7], ['1-7']),
+ ([1, 2, 5, 4, 3, 6, 7], ['1-7']),
+ ([1, 2, 4, 3, 7, 6], ['1-4', '6-7']),
+ ([1, 2, 4, 3, '13', 12, '7', '6'], ['1-4', '6-7', '12-13'])
+ ]
+ for input, reference in input_reference:
+ self.assertEqual(reference,
+ list(network_segment_range._get_ranges(input)))
+
+
class TestNetworkSegmentRange(network_fakes.TestNetworkV2):
def setUp(self):
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index b1a18da6..87aea61f 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -1054,6 +1054,22 @@ class TestListPort(TestPort):
self.assertEqual(self.columns_long, columns)
self.assertListItemEqual(self.data_long, list(data))
+ def test_port_list_host(self):
+ arglist = [
+ '--host', 'foobar',
+ ]
+ verifylist = [
+ ('host', 'foobar'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'binding:host_id': 'foobar'}
+
+ self.network.ports.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertListItemEqual(self.data, list(data))
+
def test_port_list_project(self):
project = identity_fakes.FakeProject.create_one_project()
self.projects_mock.get.return_value = project
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 38861b0a..09b4957c 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -776,6 +776,146 @@ class TestRemoveSubnetFromRouter(TestRouter):
self.assertIsNone(result)
+class TestAddExtraRoutesToRouter(TestRouter):
+
+ _router = network_fakes.FakeRouter.create_one_router()
+
+ def setUp(self):
+ super(TestAddExtraRoutesToRouter, self).setUp()
+ self.network.add_extra_routes_to_router = mock.Mock(
+ return_value=self._router)
+ self.cmd = router.AddExtraRoutesToRouter(self.app, self.namespace)
+ self.network.find_router = mock.Mock(return_value=self._router)
+
+ def test_add_no_extra_route(self):
+ arglist = [
+ self._router.id,
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.add_extra_routes_to_router.assert_called_with(
+ self._router, body={'router': {'routes': []}})
+ self.assertEqual(2, len(result))
+
+ def test_add_one_extra_route(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [{'destination': 'dst1', 'gateway': 'gw1'}]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.add_extra_routes_to_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+ def test_add_multiple_extra_routes(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ '--route', 'destination=dst2,gateway=gw2',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [
+ {'destination': 'dst1', 'gateway': 'gw1'},
+ {'destination': 'dst2', 'gateway': 'gw2'},
+ ]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.add_extra_routes_to_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ {'destination': 'dst2', 'nexthop': 'gw2'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+
+class TestRemoveExtraRoutesFromRouter(TestRouter):
+
+ _router = network_fakes.FakeRouter.create_one_router()
+
+ def setUp(self):
+ super(TestRemoveExtraRoutesFromRouter, self).setUp()
+ self.network.remove_extra_routes_from_router = mock.Mock(
+ return_value=self._router)
+ self.cmd = router.RemoveExtraRoutesFromRouter(self.app, self.namespace)
+ self.network.find_router = mock.Mock(return_value=self._router)
+
+ def test_remove_no_extra_route(self):
+ arglist = [
+ self._router.id,
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.remove_extra_routes_from_router.assert_called_with(
+ self._router, body={'router': {'routes': []}})
+ self.assertEqual(2, len(result))
+
+ def test_remove_one_extra_route(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [{'destination': 'dst1', 'gateway': 'gw1'}]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.remove_extra_routes_from_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+ def test_remove_multiple_extra_routes(self):
+ arglist = [
+ self._router.id,
+ '--route', 'destination=dst1,gateway=gw1',
+ '--route', 'destination=dst2,gateway=gw2',
+ ]
+ verifylist = [
+ ('router', self._router.id),
+ ('routes', [
+ {'destination': 'dst1', 'gateway': 'gw1'},
+ {'destination': 'dst2', 'gateway': 'gw2'},
+ ]),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.network.remove_extra_routes_from_router.assert_called_with(
+ self._router, body={'router': {'routes': [
+ {'destination': 'dst1', 'nexthop': 'gw1'},
+ {'destination': 'dst2', 'nexthop': 'gw2'},
+ ]}})
+ self.assertEqual(2, len(result))
+
+
class TestSetRouter(TestRouter):
# The router to set.
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 14d57514..7c1d7fb6 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py
@@ -49,6 +49,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'name',
'project_id',
'rules',
+ 'stateful',
'tags',
)
@@ -58,6 +59,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.name,
_security_group.project_id,
security_group.NetworkSecurityGroupRulesColumn([]),
+ _security_group.stateful,
_security_group.tags,
)
@@ -101,6 +103,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'--description', self._security_group.description,
'--project', self.project.name,
'--project-domain', self.domain.name,
+ '--stateful',
self._security_group.name,
]
verifylist = [
@@ -108,6 +111,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
('name', self._security_group.name),
('project', self.project.name),
('project_domain', self.domain.name),
+ ('stateful', self._security_group.stateful),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -115,6 +119,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
self.network.create_security_group.assert_called_once_with(**{
'description': self._security_group.description,
+ 'stateful': self._security_group.stateful,
'name': self._security_group.name,
'tenant_id': self.project.id,
})
@@ -285,7 +290,8 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.security_groups.assert_called_once_with()
+ self.network.security_groups.assert_called_once_with(
+ fields=security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE)
self.assertEqual(self.columns, columns)
self.assertListItemEqual(self.data, list(data))
@@ -300,7 +306,8 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
- self.network.security_groups.assert_called_once_with()
+ self.network.security_groups.assert_called_once_with(
+ fields=security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE)
self.assertEqual(self.columns, columns)
self.assertListItemEqual(self.data, list(data))
@@ -316,7 +323,9 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {
+ 'tenant_id': project.id, 'project_id': project.id,
+ 'fields': security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE}
self.network.security_groups.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -336,7 +345,9 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- filters = {'tenant_id': project.id, 'project_id': project.id}
+ filters = {
+ 'tenant_id': project.id, 'project_id': project.id,
+ 'fields': security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE}
self.network.security_groups.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
@@ -362,7 +373,8 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
**{'tags': 'red,blue',
'any_tags': 'red,green',
'not_tags': 'orange,yellow',
- 'not_any_tags': 'black,white'}
+ 'not_any_tags': 'black,white',
+ 'fields': security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -414,11 +426,13 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
arglist = [
'--name', new_name,
'--description', new_description,
+ '--stateful',
self._security_group.name,
]
verifylist = [
('description', new_description),
('group', self._security_group.name),
+ ('stateful', self._security_group.stateful),
('name', new_name),
]
@@ -428,6 +442,7 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
attrs = {
'description': new_description,
'name': new_name,
+ 'stateful': True,
}
self.network.update_security_group.assert_called_once_with(
self._security_group,
@@ -482,6 +497,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
'name',
'project_id',
'rules',
+ 'stateful',
'tags',
)
@@ -492,6 +508,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork):
_security_group.project_id,
security_group.NetworkSecurityGroupRulesColumn(
[_security_group_rule._info]),
+ _security_group.stateful,
_security_group.tags,
)
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index e71e1dd6..47d0c6b4 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -460,6 +460,44 @@ class TestCreateSubnet(TestSubnet):
self.assertEqual(self.columns, columns)
self.assertItemEqual(self.data, data)
+ def _test_create_with_dns(self, publish_dns=True):
+ arglist = [
+ "--subnet-range", self._subnet.cidr,
+ "--network", self._subnet.network_id,
+ self._subnet.name,
+ ]
+ if publish_dns:
+ arglist += ['--dns-publish-fixed-ip']
+ else:
+ arglist += ['--no-dns-publish-fixed-ip']
+ verifylist = [
+ ('name', self._subnet.name),
+ ('subnet_range', self._subnet.cidr),
+ ('network', self._subnet.network_id),
+ ('ip_version', self._subnet.ip_version),
+ ('gateway', 'auto'),
+ ]
+ verifylist.append(('dns_publish_fixed_ip', publish_dns))
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_subnet.assert_called_once_with(
+ cidr=self._subnet.cidr,
+ ip_version=self._subnet.ip_version,
+ name=self._subnet.name,
+ network_id=self._subnet.network_id,
+ dns_publish_fixed_ip=publish_dns,
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertItemEqual(self.data, data)
+
+ def test_create_with_dns(self):
+ self._test_create_with_dns(publish_dns=True)
+
+ def test_create_with_no_dns(self):
+ self._test_create_with_dns(publish_dns=False)
+
def _test_create_with_tag(self, add_tags=True):
arglist = [
"--subnet-range", self._subnet.cidr,
diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py
index 5d41b3a1..4e204ad1 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume.py
@@ -41,8 +41,8 @@ class TestVolume(volume_fakes.TestVolume):
self.users_mock = self.app.client_manager.identity.users
self.users_mock.reset_mock()
- self.images_mock = self.app.client_manager.image.images
- self.images_mock.reset_mock()
+ self.find_image_mock = self.app.client_manager.image.find_image
+ self.find_image_mock.reset_mock()
self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
self.snapshots_mock.reset_mock()
@@ -222,7 +222,7 @@ class TestVolumeCreate(TestVolume):
def test_volume_create_image_id(self):
image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = image
+ self.find_image_mock.return_value = image
arglist = [
'--image', image.id,
@@ -260,7 +260,7 @@ class TestVolumeCreate(TestVolume):
def test_volume_create_image_name(self):
image = image_fakes.FakeImage.create_one_image()
- self.images_mock.get.return_value = image
+ self.find_image_mock.return_value = image
arglist = [
'--image', image.name,
diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py
index fdd1794b..1fbfaaee 100644
--- a/openstackclient/volume/client.py
+++ b/openstackclient/volume/client.py
@@ -67,11 +67,15 @@ def make_client(instance):
# Remember interface only if it is set
kwargs = utils.build_kwargs_dict('endpoint_type', instance.interface)
+ endpoint_override = instance.sdk_connection.config.get_endpoint(
+ 'block-storage')
+
client = volume_client(
session=instance.session,
extensions=extensions,
http_log_debug=http_log_debug,
region_name=instance.region_name,
+ endpoint_override=endpoint_override,
**kwargs
)
diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py
index 966db48f..2d1f0359 100644
--- a/openstackclient/volume/v1/volume_snapshot.py
+++ b/openstackclient/volume/v1/volume_snapshot.py
@@ -174,10 +174,10 @@ class ListVolumeSnapshot(command.Lister):
'--status',
metavar='<status>',
choices=['available', 'error', 'creating', 'deleting',
- 'error-deleting'],
+ 'error_deleting'],
help=_("Filters results by a status. "
"('available', 'error', 'creating', 'deleting'"
- " or 'error-deleting')")
+ " or 'error_deleting')")
)
parser.add_argument(
'--volume',
diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py
index 3df66e69..7d5ba82f 100644
--- a/openstackclient/volume/v2/consistency_group_snapshot.py
+++ b/openstackclient/volume/v2/consistency_group_snapshot.py
@@ -128,7 +128,7 @@ class ListConsistencyGroupSnapshot(command.Lister):
'--status',
metavar="<status>",
choices=['available', 'error', 'creating', 'deleting',
- 'error-deleting'],
+ 'error_deleting'],
help=_('Filters results by a status ("available", "error", '
'"creating", "deleting" or "error_deleting")')
)
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index 4dde1340..1e0cb183 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -193,9 +193,8 @@ class CreateVolume(command.ShowOne):
image = None
if parsed_args.image:
- image = utils.find_resource(
- image_client.images,
- parsed_args.image).id
+ image = image_client.find_image(parsed_args.image,
+ ignore_missing=False).id
size = parsed_args.size
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
index edacf683..656f59d4 100644
--- a/openstackclient/volume/v2/volume_snapshot.py
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -229,10 +229,10 @@ class ListVolumeSnapshot(command.Lister):
'--status',
metavar='<status>',
choices=['available', 'error', 'creating', 'deleting',
- 'error-deleting'],
+ 'error_deleting'],
help=_("Filters results by a status. "
"('available', 'error', 'creating', 'deleting'"
- " or 'error-deleting')")
+ " or 'error_deleting')")
)
parser.add_argument(
'--volume',
@@ -344,7 +344,7 @@ class SetVolumeSnapshot(command.Command):
'--state',
metavar='<state>',
choices=['available', 'error', 'creating', 'deleting',
- 'error-deleting'],
+ 'error_deleting'],
help=_('New snapshot state. ("available", "error", "creating", '
'"deleting", or "error_deleting") (admin only) '
'(This option simply changes the state of the snapshot '