Merge "Switch image to use SDK"

This commit is contained in:
Zuul 2020-03-25 15:19:20 +00:00 committed by Gerrit Code Review
commit 21c883b3d3
16 changed files with 659 additions and 557 deletions

View File

@ -38,7 +38,7 @@ jmespath==0.9.0
jsonpatch==1.16 jsonpatch==1.16
jsonpointer==1.13 jsonpointer==1.13
jsonschema==2.6.0 jsonschema==2.6.0
keystoneauth1==3.14.0 keystoneauth1==3.16.0
kombu==4.0.0 kombu==4.0.0
linecache2==1.0.0 linecache2==1.0.0
MarkupSafe==1.1.0 MarkupSafe==1.1.0
@ -50,9 +50,9 @@ msgpack-python==0.4.0
munch==2.1.0 munch==2.1.0
netaddr==0.7.18 netaddr==0.7.18
netifaces==0.10.4 netifaces==0.10.4
openstacksdk==0.17.0 openstacksdk==0.36.0
os-client-config==1.28.0 os-client-config==1.28.0
os-service-types==1.2.0 os-service-types==1.7.0
os-testr==1.0.0 os-testr==1.0.0
osc-lib==2.0.0 osc-lib==2.0.0
osc-placement==1.7.0 osc-placement==1.7.0

View File

@ -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)

View File

@ -143,7 +143,7 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
if image_info: if image_info:
image_id = image_info.get('id', '') image_id = image_info.get('id', '')
try: 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) info['image'] = "%s (%s)" % (image.name, image_id)
except Exception: except Exception:
info['image'] = image_id info['image'] = image_id
@ -735,10 +735,8 @@ class CreateServer(command.ShowOne):
# Lookup parsed_args.image # Lookup parsed_args.image
image = None image = None
if parsed_args.image: if parsed_args.image:
image = utils.find_resource( image = image_client.find_image(
image_client.images, parsed_args.image, ignore_missing=False)
parsed_args.image,
)
if not image and parsed_args.image_property: if not image and parsed_args.image_property:
def emit_duplicated_warning(img, image_property): def emit_duplicated_warning(img, image_property):
@ -749,7 +747,7 @@ class CreateServer(command.ShowOne):
'chosen_one': img_uuid_list[0]}) 'chosen_one': img_uuid_list[0]})
def _match_image(image_api, wanted_properties): def _match_image(image_api, wanted_properties):
image_list = image_api.image_list() image_list = image_api.images()
images_matched = [] images_matched = []
for img in image_list: for img in image_list:
img_dict = {} img_dict = {}
@ -768,7 +766,7 @@ class CreateServer(command.ShowOne):
return [] return []
return images_matched 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: if len(images) > 1:
emit_duplicated_warning(images, emit_duplicated_warning(images,
parsed_args.image_property) parsed_args.image_property)
@ -890,8 +888,8 @@ class CreateServer(command.ShowOne):
# one specified by --image, then the compute service will # one specified by --image, then the compute service will
# create a volume from the image and attach it to the # create a volume from the image and attach it to the
# server as a non-root volume. # server as a non-root volume.
image_id = utils.find_resource( image_id = image_client.find_image(dev_map[0],
image_client.images, dev_map[0]).id ignore_missing=False).id
mapping['uuid'] = image_id mapping['uuid'] = image_id
# 3. append size and delete_on_termination if exist # 3. append size and delete_on_termination if exist
if len(dev_map) > 2 and dev_map[2]: 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 name is given, map it to ID.
image_id = None image_id = None
if parsed_args.image: if parsed_args.image:
image_id = utils.find_resource(image_client.images, image_id = image_client.find_image(parsed_args.image,
parsed_args.image).id ignore_missing=False).id
search_opts = { search_opts = {
'reservation_id': parsed_args.reservation_id, 'reservation_id': parsed_args.reservation_id,
@ -1476,12 +1474,12 @@ class ListServer(command.Lister):
(s.image.get('id') for s in data (s.image.get('id') for s in data
if s.image))): if s.image))):
try: try:
images[i_id] = image_client.images.get(i_id) images[i_id] = image_client.get_image(i_id)
except Exception: except Exception:
pass pass
else: else:
try: try:
images_list = image_client.images.list() images_list = image_client.images()
for i in images_list: for i in images_list:
images[i.id] = i images[i.id] = i
except Exception: except Exception:
@ -1925,7 +1923,7 @@ class RebuildServer(command.ShowOne):
# If parsed_args.image is not set, default to the currently used one. # If parsed_args.image is not set, default to the currently used one.
image_id = parsed_args.image or server.to_dict().get( image_id = parsed_args.image or server.to_dict().get(
'image', {}).get('id') 'image', {}).get('id')
image = utils.find_resource(image_client.images, image_id) image = image_client.get_image(image_id)
kwargs = {} kwargs = {}
if parsed_args.property: if parsed_args.property:
@ -2195,10 +2193,7 @@ class RescueServer(command.Command):
image = None image = None
if parsed_args.image: if parsed_args.image:
image = utils.find_resource( image = image_client.find_image(parsed_args.image)
image_client.images,
parsed_args.image,
)
utils.find_resource( utils.find_resource(
compute_client.servers, compute_client.servers,

View File

@ -100,14 +100,11 @@ class CreateServerBackup(command.ShowOne):
) )
image_client = self.app.client_manager.image image_client = self.app.client_manager.image
image = utils.find_resource( image = image_client.find_image(backup_name, ignore_missing=False)
image_client.images,
backup_name,
)
if parsed_args.wait: if parsed_args.wait:
if utils.wait_for_status( if utils.wait_for_status(
image_client.images.get, image_client.get_image,
image.id, image.id,
callback=_show_progress, callback=_show_progress,
): ):

View File

@ -79,14 +79,11 @@ class CreateServerImage(command.ShowOne):
) )
image_client = self.app.client_manager.image image_client = self.app.client_manager.image
image = utils.find_resource( image = image_client.find_image(image_id)
image_client.images,
image_id,
)
if parsed_args.wait: if parsed_args.wait:
if utils.wait_for_status( if utils.wait_for_status(
image_client.images.get, image_client.get_image,
image_id, image_id,
callback=_show_progress, callback=_show_progress,
): ):

View File

@ -27,7 +27,7 @@ API_VERSION_OPTION = 'os_image_api_version'
API_NAME = "image" API_NAME = "image"
API_VERSIONS = { API_VERSIONS = {
"1": "glanceclient.v1.client.Client", "1": "glanceclient.v1.client.Client",
"2": "glanceclient.v2.client.Client", "2": "openstack.connection.Connection",
} }
IMAGE_API_TYPE = 'image' IMAGE_API_TYPE = 'image'
@ -38,44 +38,52 @@ IMAGE_API_VERSIONS = {
def make_client(instance): 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( if instance._api_version[API_NAME] != '1':
API_NAME, LOG.debug(
region_name=instance.region_name, 'Image client initialized using OpenStack SDK: %s',
interface=instance.interface, instance.sdk_connection.image,
) )
return instance.sdk_connection.image
else:
"""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)
client = image_client( endpoint = instance.get_endpoint_for_service_type(
endpoint, API_NAME,
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, region_name=instance.region_name,
interface=instance.interface, interface=instance.interface,
) )
)
return client 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
def build_option_parser(parser): def build_option_parser(parser):

View File

@ -18,8 +18,9 @@
import argparse import argparse
from base64 import b64encode from base64 import b64encode
import logging import logging
import os
import sys
from glanceclient.common import utils as gc_utils
from openstack.image import image_signer from openstack.image import image_signer
from osc_lib.api import utils as api_utils from osc_lib.api import utils as api_utils
from osc_lib.cli import format_columns from osc_lib.cli import format_columns
@ -27,11 +28,16 @@ from osc_lib.cli import parseractions
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
import six
from openstackclient.common import sdk_utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common from openstackclient.identity import common
if os.name == "nt":
import msvcrt
else:
msvcrt = None
CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"]
DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_CONTAINER_FORMAT = 'bare'
@ -44,7 +50,7 @@ MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"]
LOG = logging.getLogger(__name__) 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.""" """Format an image to make it more consistent with OSC operations."""
info = {} info = {}
@ -56,15 +62,25 @@ def _format_image(image):
'min_disk', 'protected', 'id', 'file', 'checksum', 'min_disk', 'protected', 'id', 'file', 'checksum',
'owner', 'virtual_size', 'min_ram', 'schema'] '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 # 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: if key in fields_to_show:
info[key] = image.get(key) info[key] = image.get(key)
elif key == 'tags': elif key == 'tags':
continue # handle this later 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) properties[key] = image.get(key)
if human_readable:
info['size'] = utils.format_size(image['size'])
# format the tags if they are there # format the tags if they are there
info['tags'] = format_columns.ListColumn(image.get('tags')) info['tags'] = format_columns.ListColumn(image.get('tags'))
@ -75,6 +91,51 @@ def _format_image(image):
return info 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): class AddProjectToImage(command.ShowOne):
_description = _("Associate project with image") _description = _("Associate project with image")
@ -101,16 +162,18 @@ class AddProjectToImage(command.ShowOne):
parsed_args.project, parsed_args.project,
parsed_args.project_domain).id parsed_args.project_domain).id
image_id = utils.find_resource( image = image_client.find_image(parsed_args.image,
image_client.images, ignore_missing=False)
parsed_args.image).id
image_member = image_client.image_members.create( obj = image_client.add_member(
image_id, image=image.id,
project_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): class CreateImage(command.ShowOne):
@ -302,9 +365,9 @@ class CreateImage(command.ShowOne):
# to do nothing when no options are present as opposed to always # to do nothing when no options are present as opposed to always
# setting a default. # setting a default.
if parsed_args.protected: if parsed_args.protected:
kwargs['protected'] = True kwargs['is_protected'] = True
if parsed_args.unprotected: if parsed_args.unprotected:
kwargs['protected'] = False kwargs['is_protected'] = False
if parsed_args.public: if parsed_args.public:
kwargs['visibility'] = 'public' kwargs['visibility'] = 'public'
if parsed_args.private: if parsed_args.private:
@ -314,24 +377,30 @@ class CreateImage(command.ShowOne):
if parsed_args.shared: if parsed_args.shared:
kwargs['visibility'] = 'shared' kwargs['visibility'] = 'shared'
if parsed_args.project: if parsed_args.project:
kwargs['owner'] = common.find_project( kwargs['owner_id'] = common.find_project(
identity_client, identity_client,
parsed_args.project, parsed_args.project,
parsed_args.project_domain, parsed_args.project_domain,
).id ).id
# open the file first to ensure any failures are handled before the # open the file first to ensure any failures are handled before the
# image is created # image is created. Get the file name (if it is file, and not stdin)
fp = gc_utils.get_data_file(parsed_args) # for easier further handling.
(fp, fname) = get_data_file(parsed_args)
info = {} info = {}
if fp is not None and parsed_args.volume: if fp is not None and parsed_args.volume:
raise exceptions.CommandError(_("Uploading data and using " raise exceptions.CommandError(_("Uploading data and using "
"container are not allowed at " "container are not allowed at "
"the same time")) "the same time"))
if fp is None and parsed_args.file: if fp is None and parsed_args.file:
LOG.warning(_("Failed to get an image file.")) LOG.warning(_("Failed to get an image file."))
return {}, {} 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 # sign an image using a given local private key file
if parsed_args.sign_key_path or parsed_args.sign_cert_id: if parsed_args.sign_key_path or parsed_args.sign_cert_id:
@ -361,8 +430,8 @@ class CreateImage(command.ShowOne):
sign_key_path, sign_key_path,
password=pw) password=pw)
except Exception: except Exception:
msg = (_("Error during sign operation: private key could " msg = (_("Error during sign operation: private key "
"not be loaded.")) "could not be loaded."))
raise exceptions.CommandError(msg) raise exceptions.CommandError(msg)
signature = signer.generate_signature(fp) signature = signer.generate_signature(fp)
@ -371,7 +440,8 @@ class CreateImage(command.ShowOne):
kwargs['img_signature_certificate_uuid'] = sign_cert_id kwargs['img_signature_certificate_uuid'] = sign_cert_id
kwargs['img_signature_hash_method'] = signer.hash_method kwargs['img_signature_hash_method'] = signer.hash_method
if signer.padding_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 a volume is specified.
if parsed_args.volume: if parsed_args.volume:
@ -393,26 +463,7 @@ class CreateImage(command.ShowOne):
except TypeError: except TypeError:
info['volume_type'] = None info['volume_type'] = None
else: else:
image = image_client.images.create(**kwargs) image = image_client.create_image(**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)
if not info: if not info:
info = _format_image(image) info = _format_image(image)
@ -439,11 +490,9 @@ class DeleteImage(command.Command):
image_client = self.app.client_manager.image image_client = self.app.client_manager.image
for image in parsed_args.images: for image in parsed_args.images:
try: try:
image_obj = utils.find_resource( image_obj = image_client.find_image(image,
image_client.images, ignore_missing=False)
image, image_client.delete_image(image_obj.id)
)
image_client.images.delete(image_obj.id)
except Exception as e: except Exception as e:
del_result += 1 del_result += 1
LOG.error(_("Failed to delete image with name or " LOG.error(_("Failed to delete image with name or "
@ -569,18 +618,17 @@ class ListImage(command.Lister):
kwargs = {} kwargs = {}
if parsed_args.public: if parsed_args.public:
kwargs['public'] = True kwargs['visibility'] = 'public'
if parsed_args.private: if parsed_args.private:
kwargs['private'] = True kwargs['visibility'] = 'private'
if parsed_args.community: if parsed_args.community:
kwargs['community'] = True kwargs['visibility'] = 'community'
if parsed_args.shared: if parsed_args.shared:
kwargs['shared'] = True kwargs['visibility'] = 'shared'
if parsed_args.limit: if parsed_args.limit:
kwargs['limit'] = parsed_args.limit kwargs['limit'] = parsed_args.limit
if parsed_args.marker: if parsed_args.marker:
kwargs['marker'] = utils.find_resource(image_client.images, kwargs['marker'] = image_client.find_image(parsed_args.marker).id
parsed_args.marker).id
if parsed_args.name: if parsed_args.name:
kwargs['name'] = parsed_args.name kwargs['name'] = parsed_args.name
if parsed_args.status: if parsed_args.status:
@ -599,8 +647,8 @@ class ListImage(command.Lister):
'Checksum', 'Checksum',
'Status', 'Status',
'visibility', 'visibility',
'protected', 'is_protected',
'owner', 'owner_id',
'tags', 'tags',
) )
column_headers = ( column_headers = (
@ -621,24 +669,10 @@ class ListImage(command.Lister):
column_headers = columns column_headers = columns
# List of image data received # List of image data received
data = []
limit = None
if 'limit' in kwargs: if 'limit' in kwargs:
limit = kwargs['limit'] # Disable automatic pagination in SDK
if 'marker' in kwargs: kwargs['paginated'] = False
data = image_client.api.image_list(**kwargs) data = list(image_client.images(**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
if parsed_args.property: if parsed_args.property:
for attr, value in parsed_args.property.items(): for attr, value in parsed_args.property.items():
@ -653,12 +687,10 @@ class ListImage(command.Lister):
return ( return (
column_headers, column_headers,
(utils.get_dict_properties( (utils.get_item_properties(
s, s,
columns, columns,
formatters={ formatters=_formatters,
'tags': format_columns.ListColumn,
},
) for s in data) ) for s in data)
) )
@ -684,11 +716,9 @@ class ListImageProjects(command.Lister):
"Status" "Status"
) )
image_id = utils.find_resource( image_id = image_client.find_image(parsed_args.image).id
image_client.images,
parsed_args.image).id
data = image_client.image_members.list(image_id) data = image_client.members(image=image_id)
return (columns, return (columns,
(utils.get_item_properties( (utils.get_item_properties(
@ -722,11 +752,12 @@ class RemoveProjectImage(command.Command):
parsed_args.project, parsed_args.project,
parsed_args.project_domain).id parsed_args.project_domain).id
image_id = utils.find_resource( image = image_client.find_image(parsed_args.image,
image_client.images, ignore_missing=False)
parsed_args.image).id
image_client.image_members.delete(image_id, project_id) image_client.remove_member(
member=project_id,
image=image.id)
class SaveImage(command.Command): class SaveImage(command.Command):
@ -748,19 +779,9 @@ class SaveImage(command.Command):
def take_action(self, parsed_args): def take_action(self, parsed_args):
image_client = self.app.client_manager.image image_client = self.app.client_manager.image
image = utils.find_resource( image = image_client.find_image(parsed_args.image)
image_client.images,
parsed_args.image,
)
data = image_client.images.data(image.id)
if data.wrapped is None: image_client.download_image(image.id, output=parsed_args.file)
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)
class SetImage(command.Command): class SetImage(command.Command):
@ -979,9 +1000,9 @@ class SetImage(command.Command):
# to do nothing when no options are present as opposed to always # to do nothing when no options are present as opposed to always
# setting a default. # setting a default.
if parsed_args.protected: if parsed_args.protected:
kwargs['protected'] = True kwargs['is_protected'] = True
if parsed_args.unprotected: if parsed_args.unprotected:
kwargs['protected'] = False kwargs['is_protected'] = False
if parsed_args.public: if parsed_args.public:
kwargs['visibility'] = 'public' kwargs['visibility'] = 'public'
if parsed_args.private: if parsed_args.private:
@ -997,17 +1018,20 @@ class SetImage(command.Command):
parsed_args.project, parsed_args.project,
parsed_args.project_domain, parsed_args.project_domain,
).id ).id
kwargs['owner'] = project_id kwargs['owner_id'] = project_id
image = utils.find_resource( image = image_client.find_image(parsed_args.image,
image_client.images, parsed_args.image) ignore_missing=False)
# image = utils.find_resource(
# image_client.images, parsed_args.image)
activation_status = None activation_status = None
if parsed_args.deactivate: if parsed_args.deactivate:
image_client.images.deactivate(image.id) image_client.deactivate_image(image.id)
activation_status = "deactivated" activation_status = "deactivated"
if parsed_args.activate: if parsed_args.activate:
image_client.images.reactivate(image.id) image_client.reactivate_image(image.id)
activation_status = "activated" activation_status = "activated"
membership_group_args = ('accept', 'reject', 'pending') membership_group_args = ('accept', 'reject', 'pending')
@ -1022,15 +1046,15 @@ class SetImage(command.Command):
# most one item in the membership_status list. # most one item in the membership_status list.
if membership_status[0] != 'pending': if membership_status[0] != 'pending':
membership_status[0] += 'ed' # Glance expects the past form membership_status[0] += 'ed' # Glance expects the past form
image_client.image_members.update( image_client.update_member(
image.id, project_id, membership_status[0]) image=image.id, member=project_id, status=membership_status[0])
if parsed_args.tags: if parsed_args.tags:
# Tags should be extended, but duplicates removed # Tags should be extended, but duplicates removed
kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags))) kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
try: try:
image = image_client.images.update(image.id, **kwargs) image = image_client.update_image(image.id, **kwargs)
except Exception: except Exception:
if activation_status is not None: if activation_status is not None:
LOG.info(_("Image %(id)s was %(status)s."), LOG.info(_("Image %(id)s was %(status)s."),
@ -1058,14 +1082,11 @@ class ShowImage(command.ShowOne):
def take_action(self, parsed_args): def take_action(self, parsed_args):
image_client = self.app.client_manager.image 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())) return zip(*sorted(info.items()))
@ -1101,10 +1122,8 @@ class UnsetImage(command.Command):
def take_action(self, parsed_args): def take_action(self, parsed_args):
image_client = self.app.client_manager.image image_client = self.app.client_manager.image
image = utils.find_resource( image = image_client.find_image(parsed_args.image,
image_client.images, ignore_missing=False)
parsed_args.image,
)
kwargs = {} kwargs = {}
tagret = 0 tagret = 0
@ -1112,7 +1131,7 @@ class UnsetImage(command.Command):
if parsed_args.tags: if parsed_args.tags:
for k in parsed_args.tags: for k in parsed_args.tags:
try: try:
image_client.image_tags.delete(image.id, k) image_client.remove_tag(image.id, k)
except Exception: except Exception:
LOG.error(_("tag unset failed, '%s' is a " LOG.error(_("tag unset failed, '%s' is a "
"nonexistent tag "), k) "nonexistent tag "), k)
@ -1120,13 +1139,26 @@ class UnsetImage(command.Command):
if parsed_args.properties: if parsed_args.properties:
for k in 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 " LOG.error(_("property unset failed, '%s' is a "
"nonexistent property "), k) "nonexistent property "), k)
propret += 1 propret += 1
image_client.images.update(
image.id, # We must give to update a current image for the reference on what
parsed_args.properties, # has changed
image_client.update_image(
image,
**kwargs) **kwargs)
tagtotal = len(parsed_args.tags) tagtotal = len(parsed_args.tags)

View File

@ -55,6 +55,12 @@ class TestServer(compute_fakes.TestComputev2):
self.images_mock = self.app.client_manager.image.images self.images_mock = self.app.client_manager.image.images
self.images_mock.reset_mock() 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 # Get a shortcut to the volume client VolumeManager Mock
self.volumes_mock = self.app.client_manager.volume.volumes self.volumes_mock = self.app.client_manager.volume.volumes
self.volumes_mock.reset_mock() self.volumes_mock.reset_mock()
@ -770,7 +776,8 @@ class TestServerCreate(TestServer):
self.servers_mock.create.return_value = self.new_server self.servers_mock.create.return_value = self.new_server
self.image = image_fakes.FakeImage.create_one_image() 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.flavor = compute_fakes.FakeFlavor.create_one_flavor()
self.flavors_mock.get.return_value = self.flavor self.flavors_mock.get.return_value = self.flavor
@ -1916,19 +1923,13 @@ class TestServerCreate(TestServer):
('config_drive', False), ('config_drive', False),
('server_name', self.new_server.name), ('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() # create a image_info as the side_effect of the fake image_list()
image_info = { image_info = {
'id': _image.id,
'name': _image.name,
'owner': _image.owner,
'hypervisor_type': 'qemu', 'hypervisor_type': 'qemu',
} }
self.api_mock = mock.Mock()
self.api_mock.image_list.side_effect = [ _image = image_fakes.FakeImage.create_one_image(image_info)
[image_info], [], self.images_mock.return_value = [_image]
]
self.app.client_manager.image.api = self.api_mock
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -1953,7 +1954,7 @@ class TestServerCreate(TestServer):
# ServerManager.create(name, image, flavor, **kwargs) # ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with( self.servers_mock.create.assert_called_with(
self.new_server.name, self.new_server.name,
image_info, _image,
self.flavor, self.flavor,
**kwargs **kwargs
) )
@ -1977,20 +1978,13 @@ class TestServerCreate(TestServer):
('config_drive', False), ('config_drive', False),
('server_name', self.new_server.name), ('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() # create a image_info as the side_effect of the fake image_list()
image_info = { image_info = {
'id': _image.id,
'name': _image.name,
'owner': _image.owner,
'hypervisor_type': 'qemu', 'hypervisor_type': 'qemu',
'hw_disk_bus': 'ide', 'hw_disk_bus': 'ide',
} }
self.api_mock = mock.Mock() _image = image_fakes.FakeImage.create_one_image(image_info)
self.api_mock.image_list.side_effect = [ self.images_mock.return_value = [_image]
[image_info], [],
]
self.app.client_manager.image.api = self.api_mock
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -2015,7 +2009,7 @@ class TestServerCreate(TestServer):
# ServerManager.create(name, image, flavor, **kwargs) # ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with( self.servers_mock.create.assert_called_with(
self.new_server.name, self.new_server.name,
image_info, _image,
self.flavor, self.flavor,
**kwargs **kwargs
) )
@ -2039,20 +2033,14 @@ class TestServerCreate(TestServer):
('config_drive', False), ('config_drive', False),
('server_name', self.new_server.name), ('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() # create a image_info as the side_effect of the fake image_list()
image_info = { image_info = {
'id': _image.id,
'name': _image.name,
'owner': _image.owner,
'hypervisor_type': 'qemu', 'hypervisor_type': 'qemu',
'hw_disk_bus': 'ide', 'hw_disk_bus': 'ide',
} }
self.api_mock = mock.Mock()
self.api_mock.image_list.side_effect = [ _image = image_fakes.FakeImage.create_one_image(image_info)
[image_info], [], self.images_mock.return_value = [_image]
]
self.app.client_manager.image.api = self.api_mock
parsed_args = self.check_parser(self.cmd, arglist, verifylist) 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.servers_mock.list.return_value = self.servers
self.image = image_fakes.FakeImage.create_one_image() 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.flavor = compute_fakes.FakeFlavor.create_one_flavor()
self.flavors_mock.get.return_value = self.flavor self.flavors_mock.get.return_value = self.flavor
@ -2599,7 +2590,7 @@ class TestServerList(TestServer):
self.data_no_name_lookup = [] self.data_no_name_lookup = []
Image = collections.namedtuple('Image', 'id name') 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(id=s.image['id'], name=self.image.name)
# Image will be an empty string if boot-from-volume # Image will be an empty string if boot-from-volume
for s in self.servers if s.image for s in self.servers if s.image
@ -2662,11 +2653,11 @@ class TestServerList(TestServer):
columns, data = self.cmd.take_action(parsed_args) columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs) 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() self.flavors_mock.list.assert_called()
# we did not pass image or flavor, so gets on those must be absent # 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.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(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data)) self.assertEqual(tuple(self.data), tuple(data))
@ -2753,7 +2744,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertFalse(self.images_mock.list.call_count) self.assertFalse(self.images_mock.list.call_count)
self.assertFalse(self.flavors_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.flavors_mock.get.assert_called()
self.assertEqual(self.columns, columns) self.assertEqual(self.columns, columns)
@ -2771,7 +2762,8 @@ class TestServerList(TestServer):
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args) 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.search_opts['image'] = self.image.id
self.servers_mock.list.assert_called_with(**self.kwargs) self.servers_mock.list.assert_called_with(**self.kwargs)
@ -3558,7 +3550,7 @@ class TestServerRebuild(TestServer):
# Return value for utils.find_resource for image # Return value for utils.find_resource for image
self.image = image_fakes.FakeImage.create_one_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. # Fake the rebuilt new server.
attrs = { attrs = {
@ -3598,7 +3590,7 @@ class TestServerRebuild(TestServer):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rebuild.assert_called_with(self.image, None)
def test_rebuild_with_current_image_and_password(self): def test_rebuild_with_current_image_and_password(self):
@ -3617,7 +3609,7 @@ class TestServerRebuild(TestServer):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rebuild.assert_called_with(self.image, password)
def test_rebuild_with_description_api_older(self): def test_rebuild_with_description_api_older(self):
@ -3665,7 +3657,7 @@ class TestServerRebuild(TestServer):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id) 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, self.server.rebuild.assert_called_with(self.image, None,
description=description) description=description)
@ -3694,7 +3686,7 @@ class TestServerRebuild(TestServer):
) )
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rebuild.assert_called_with(self.image, None)
@mock.patch.object(common_utils, 'wait_for_status', return_value=False) @mock.patch.object(common_utils, 'wait_for_status', return_value=False)
@ -3718,7 +3710,7 @@ class TestServerRebuild(TestServer):
) )
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rebuild.assert_called_with(self.image, None)
def test_rebuild_with_property(self): def test_rebuild_with_property(self):
@ -3738,7 +3730,7 @@ class TestServerRebuild(TestServer):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id) 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.server.rebuild.assert_called_with(
self.image, None, meta=expected_property) self.image, None, meta=expected_property)
@ -3767,7 +3759,7 @@ class TestServerRebuild(TestServer):
key_name=self.server.key_name, key_name=self.server.key_name,
) )
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rebuild.assert_called_with(*args, **kwargs)
def test_rebuild_with_keypair_name_older_version(self): def test_rebuild_with_keypair_name_older_version(self):
@ -3814,7 +3806,7 @@ class TestServerRebuild(TestServer):
key_name=None, key_name=None,
) )
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rebuild.assert_called_with(*args, **kwargs)
def test_rebuild_with_key_name_and_unset(self): def test_rebuild_with_key_name_and_unset(self):
@ -3872,7 +3864,7 @@ class TestServerRescue(TestServer):
# Return value for utils.find_resource for image # Return value for utils.find_resource for image
self.image = image_fakes.FakeImage.create_one_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() new_server = compute_fakes.FakeServer.create_one_server()
attrs = { attrs = {
@ -3913,7 +3905,7 @@ class TestServerRescue(TestServer):
def test_rescue_with_new_image(self): def test_rescue_with_new_image(self):
new_image = image_fakes.FakeImage.create_one_image() 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 = [ arglist = [
'--image', new_image.id, '--image', new_image.id,
self.server.id, self.server.id,
@ -3928,7 +3920,7 @@ class TestServerRescue(TestServer):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(self.server.id) 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) self.server.rescue.assert_called_with(image=new_image, password=None)
def test_rescue_with_current_image_and_password(self): def test_rescue_with_current_image_and_password(self):
@ -4679,7 +4671,7 @@ class TestServerShow(TestServer):
# This is the return value for utils.find_resource() # This is the return value for utils.find_resource()
self.servers_mock.get.return_value = self.server 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 self.flavors_mock.get.return_value = self.flavor
# Get the command object to test # Get the command object to test
@ -5140,7 +5132,8 @@ class TestServerGeneral(TestServer):
'links': u'http://xxx.yyy.com', 'links': u'http://xxx.yyy.com',
} }
_server = compute_fakes.FakeServer.create_one_server(attrs=server_info) _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. # Prepare result data.
info = { info = {

View File

@ -32,8 +32,8 @@ class TestServerBackup(compute_fakes.TestComputev2):
self.servers_mock.reset_mock() self.servers_mock.reset_mock()
# Get a shortcut to the image client ImageManager Mock # Get a shortcut to the image client ImageManager Mock
self.images_mock = self.app.client_manager.image.images self.images_mock = self.app.client_manager.image
self.images_mock.reset_mock() self.images_mock.find_image.reset_mock()
# Set object attributes to be tested. Could be overwritten in subclass. # Set object attributes to be tested. Could be overwritten in subclass.
self.attrs = {} self.attrs = {}
@ -60,15 +60,18 @@ class TestServerBackupCreate(TestServerBackup):
# Just return whatever Image is testing with these days # Just return whatever Image is testing with these days
def image_columns(self, image): 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 return columnlist
def image_data(self, image): def image_data(self, image):
datalist = ( datalist = (
image['id'], image['id'],
image['name'], image['name'],
image['owner'], image['owner_id'],
image['protected'], image['is_protected'],
'active', 'active',
format_columns.ListColumn(image.get('tags')), format_columns.ListColumn(image.get('tags')),
image['visibility'], image['visibility'],
@ -102,7 +105,8 @@ class TestServerBackupCreate(TestServerBackup):
count=count, 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 return images
def test_server_backup_defaults(self): def test_server_backup_defaults(self):
@ -174,16 +178,18 @@ class TestServerBackupCreate(TestServerBackup):
@mock.patch.object(common_utils, 'wait_for_status', return_value=False) @mock.patch.object(common_utils, 'wait_for_status', return_value=False)
def test_server_backup_wait_fail(self, mock_wait_for_status): def test_server_backup_wait_fail(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1) servers = self.setup_servers_mock(count=1)
images = image_fakes.FakeImage.create_images( images = self.setup_images_mock(count=1, servers=servers)
attrs={ # images = image_fakes.FakeImage.create_images(
'name': servers[0].name, # attrs={
'status': 'active', # 'name': servers[0].name,
}, # 'status': 'active',
count=5, # },
) # count=1,
# )
self.images_mock.get = mock.Mock( #
side_effect=images, # self.images_mock.find_image.return_value = images[0]
self.images_mock.get_image = mock.Mock(
side_effect=images[0],
) )
arglist = [ arglist = [
@ -215,7 +221,7 @@ class TestServerBackupCreate(TestServerBackup):
) )
mock_wait_for_status.assert_called_once_with( mock_wait_for_status.assert_called_once_with(
self.images_mock.get, self.images_mock.get_image,
images[0].id, images[0].id,
callback=mock.ANY callback=mock.ANY
) )
@ -223,16 +229,10 @@ class TestServerBackupCreate(TestServerBackup):
@mock.patch.object(common_utils, 'wait_for_status', return_value=True) @mock.patch.object(common_utils, 'wait_for_status', return_value=True)
def test_server_backup_wait_ok(self, mock_wait_for_status): def test_server_backup_wait_ok(self, mock_wait_for_status):
servers = self.setup_servers_mock(count=1) servers = self.setup_servers_mock(count=1)
images = image_fakes.FakeImage.create_images( images = self.setup_images_mock(count=1, servers=servers)
attrs={
'name': servers[0].name,
'status': 'active',
},
count=5,
)
self.images_mock.get = mock.Mock( self.images_mock.get_image = mock.Mock(
side_effect=images, side_effect=images[0],
) )
arglist = [ arglist = [
@ -263,7 +263,7 @@ class TestServerBackupCreate(TestServerBackup):
) )
mock_wait_for_status.assert_called_once_with( mock_wait_for_status.assert_called_once_with(
self.images_mock.get, self.images_mock.get_image,
images[0].id, images[0].id,
callback=mock.ANY callback=mock.ANY
) )

View File

@ -31,8 +31,8 @@ class TestServerImage(compute_fakes.TestComputev2):
self.servers_mock.reset_mock() self.servers_mock.reset_mock()
# Get a shortcut to the image client ImageManager Mock # Get a shortcut to the image client ImageManager Mock
self.images_mock = self.app.client_manager.image.images self.images_mock = self.app.client_manager.image
self.images_mock.reset_mock() self.images_mock.find_image.reset_mock()
# Set object attributes to be tested. Could be overwritten in subclass. # Set object attributes to be tested. Could be overwritten in subclass.
self.attrs = {} self.attrs = {}
@ -58,15 +58,18 @@ class TestServerImage(compute_fakes.TestComputev2):
class TestServerImageCreate(TestServerImage): class TestServerImageCreate(TestServerImage):
def image_columns(self, image): 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 return columnlist
def image_data(self, image): def image_data(self, image):
datalist = ( datalist = (
image['id'], image['id'],
image['name'], image['name'],
image['owner'], image['owner_id'],
image['protected'], image['is_protected'],
'active', 'active',
format_columns.ListColumn(image.get('tags')), format_columns.ListColumn(image.get('tags')),
image['visibility'], image['visibility'],
@ -100,7 +103,7 @@ class TestServerImageCreate(TestServerImage):
count=count, 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( self.servers_mock.create_image = mock.Mock(
return_value=images[0].id, return_value=images[0].id,
) )
@ -188,7 +191,7 @@ class TestServerImageCreate(TestServerImage):
) )
mock_wait_for_status.assert_called_once_with( mock_wait_for_status.assert_called_once_with(
self.images_mock.get, self.images_mock.get_image,
images[0].id, images[0].id,
callback=mock.ANY callback=mock.ANY
) )
@ -220,7 +223,7 @@ class TestServerImageCreate(TestServerImage):
) )
mock_wait_for_status.assert_called_once_with( mock_wait_for_status.assert_called_once_with(
self.images_mock.get, self.images_mock.get_image,
images[0].id, images[0].id,
callback=mock.ANY callback=mock.ANY
) )

View File

@ -18,9 +18,9 @@ import random
from unittest import mock from unittest import mock
import uuid 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 from osc_lib.cli import format_columns
import warlock
from openstackclient.tests.unit import fakes from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_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_members.resource_class = fakes.FakeResource(None, {})
self.image_tags = mock.Mock() self.image_tags = mock.Mock()
self.image_tags.resource_class = fakes.FakeResource(None, {}) 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.auth_token = kwargs['token']
self.management_url = kwargs['endpoint'] self.management_url = kwargs['endpoint']
self.version = 2.0 self.version = 2.0
@ -197,8 +203,8 @@ class FakeImage(object):
image_info = { image_info = {
'id': str(uuid.uuid4()), 'id': str(uuid.uuid4()),
'name': 'image-name' + uuid.uuid4().hex, 'name': 'image-name' + uuid.uuid4().hex,
'owner': 'image-owner' + uuid.uuid4().hex, 'owner_id': 'image-owner' + uuid.uuid4().hex,
'protected': bool(random.choice([0, 1])), 'is_protected': bool(random.choice([0, 1])),
'visibility': random.choice(['public', 'private']), 'visibility': random.choice(['public', 'private']),
'tags': [uuid.uuid4().hex for r in range(2)], '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 # Overwrite default attributes if there are some attributes set
image_info.update(attrs) image_info.update(attrs)
# Set up the schema return image.Image(**image_info)
model = warlock.model_factory(
IMAGE_schema,
schemas.SchemaBasedModel,
)
return model(**image_info)
@staticmethod @staticmethod
def create_images(attrs=None, count=2): def create_images(attrs=None, count=2):
@ -307,6 +307,8 @@ class FakeImage(object):
# Overwrite default attributes if there are some attributes set # Overwrite default attributes if there are some attributes set
image_member_info.update(attrs) image_member_info.update(attrs)
return member.Member(**image_member_info)
image_member = fakes.FakeModel( image_member = fakes.FakeModel(
copy.deepcopy(image_member_info)) copy.deepcopy(image_member_info))

File diff suppressed because it is too large Load Diff

View File

@ -41,8 +41,8 @@ class TestVolume(volume_fakes.TestVolume):
self.users_mock = self.app.client_manager.identity.users self.users_mock = self.app.client_manager.identity.users
self.users_mock.reset_mock() self.users_mock.reset_mock()
self.images_mock = self.app.client_manager.image.images self.find_image_mock = self.app.client_manager.image.find_image
self.images_mock.reset_mock() self.find_image_mock.reset_mock()
self.snapshots_mock = self.app.client_manager.volume.volume_snapshots self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
self.snapshots_mock.reset_mock() self.snapshots_mock.reset_mock()
@ -222,7 +222,7 @@ class TestVolumeCreate(TestVolume):
def test_volume_create_image_id(self): def test_volume_create_image_id(self):
image = image_fakes.FakeImage.create_one_image() image = image_fakes.FakeImage.create_one_image()
self.images_mock.get.return_value = image self.find_image_mock.return_value = image
arglist = [ arglist = [
'--image', image.id, '--image', image.id,
@ -260,7 +260,7 @@ class TestVolumeCreate(TestVolume):
def test_volume_create_image_name(self): def test_volume_create_image_name(self):
image = image_fakes.FakeImage.create_one_image() image = image_fakes.FakeImage.create_one_image()
self.images_mock.get.return_value = image self.find_image_mock.return_value = image
arglist = [ arglist = [
'--image', image.name, '--image', image.name,

View File

@ -193,9 +193,8 @@ class CreateVolume(command.ShowOne):
image = None image = None
if parsed_args.image: if parsed_args.image:
image = utils.find_resource( image = image_client.find_image(parsed_args.image,
image_client.images, ignore_missing=False).id
parsed_args.image).id
size = parsed_args.size size = parsed_args.size

View File

@ -0,0 +1,4 @@
---
features:
- |
Image service for v2 is switched from using glanceclient to OpenStackSDK.

View File

@ -6,8 +6,7 @@ six>=1.10.0 # MIT
Babel!=2.4.0,>=2.3.4 # BSD Babel!=2.4.0,>=2.3.4 # BSD
cliff!=2.9.0,>=2.8.0 # Apache-2.0 cliff!=2.9.0,>=2.8.0 # Apache-2.0
keystoneauth1>=3.14.0 # Apache-2.0 openstacksdk>=0.36.0 # Apache-2.0
openstacksdk>=0.17.0 # Apache-2.0
osc-lib>=2.0.0 # Apache-2.0 osc-lib>=2.0.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0
oslo.utils>=3.33.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0