Merge "Switch image to use SDK"
This commit is contained in:
commit
21c883b3d3
@ -38,7 +38,7 @@ jmespath==0.9.0
|
||||
jsonpatch==1.16
|
||||
jsonpointer==1.13
|
||||
jsonschema==2.6.0
|
||||
keystoneauth1==3.14.0
|
||||
keystoneauth1==3.16.0
|
||||
kombu==4.0.0
|
||||
linecache2==1.0.0
|
||||
MarkupSafe==1.1.0
|
||||
@ -50,9 +50,9 @@ msgpack-python==0.4.0
|
||||
munch==2.1.0
|
||||
netaddr==0.7.18
|
||||
netifaces==0.10.4
|
||||
openstacksdk==0.17.0
|
||||
openstacksdk==0.36.0
|
||||
os-client-config==1.28.0
|
||||
os-service-types==1.2.0
|
||||
os-service-types==1.7.0
|
||||
os-testr==1.0.0
|
||||
osc-lib==2.0.0
|
||||
osc-placement==1.7.0
|
||||
|
60
openstackclient/common/sdk_utils.py
Normal file
60
openstackclient/common/sdk_utils.py
Normal 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)
|
@ -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:
|
||||
@ -1925,7 +1923,7 @@ class RebuildServer(command.ShowOne):
|
||||
# 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)
|
||||
image = image_client.get_image(image_id)
|
||||
|
||||
kwargs = {}
|
||||
if parsed_args.property:
|
||||
@ -2195,10 +2193,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,
|
||||
|
@ -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,
|
||||
):
|
||||
|
@ -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,
|
||||
):
|
||||
|
@ -27,7 +27,7 @@ API_VERSION_OPTION = 'os_image_api_version'
|
||||
API_NAME = "image"
|
||||
API_VERSIONS = {
|
||||
"1": "glanceclient.v1.client.Client",
|
||||
"2": "glanceclient.v2.client.Client",
|
||||
"2": "openstack.connection.Connection",
|
||||
}
|
||||
|
||||
IMAGE_API_TYPE = 'image'
|
||||
@ -38,44 +38,52 @@ IMAGE_API_VERSIONS = {
|
||||
|
||||
|
||||
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,
|
||||
)
|
||||
if instance._api_version[API_NAME] != '1':
|
||||
LOG.debug(
|
||||
'Image client initialized using OpenStack SDK: %s',
|
||||
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,
|
||||
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,
|
||||
endpoint = instance.get_endpoint_for_service_type(
|
||||
API_NAME,
|
||||
region_name=instance.region_name,
|
||||
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):
|
||||
|
@ -18,8 +18,9 @@
|
||||
import argparse
|
||||
from base64 import b64encode
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from glanceclient.common import utils as gc_utils
|
||||
from openstack.image import image_signer
|
||||
from osc_lib.api import utils as api_utils
|
||||
from osc_lib.cli import format_columns
|
||||
@ -27,11 +28,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 +50,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 +62,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 +91,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")
|
||||
|
||||
@ -101,16 +162,18 @@ class AddProjectToImage(command.ShowOne):
|
||||
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 +365,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 +377,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 +430,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 +440,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 +463,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 +490,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 +618,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 +647,8 @@ class ListImage(command.Lister):
|
||||
'Checksum',
|
||||
'Status',
|
||||
'visibility',
|
||||
'protected',
|
||||
'owner',
|
||||
'is_protected',
|
||||
'owner_id',
|
||||
'tags',
|
||||
)
|
||||
column_headers = (
|
||||
@ -621,24 +669,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 +687,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 +716,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 +752,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 +779,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 +1000,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 +1018,20 @@ class SetImage(command.Command):
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain,
|
||||
).id
|
||||
kwargs['owner'] = project_id
|
||||
kwargs['owner_id'] = project_id
|
||||
|
||||
image = utils.find_resource(
|
||||
image_client.images, parsed_args.image)
|
||||
image = image_client.find_image(parsed_args.image,
|
||||
ignore_missing=False)
|
||||
|
||||
# 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 +1046,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 +1082,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 +1122,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 +1131,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 +1139,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)
|
||||
|
@ -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 = {
|
||||
@ -3598,7 +3590,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)
|
||||
|
||||
def test_rebuild_with_current_image_and_password(self):
|
||||
@ -3617,7 +3609,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 +3657,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 +3686,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 +3710,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 +3730,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 +3759,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 +3806,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 +3864,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 +3905,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 +3920,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 +4671,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 +5132,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 = {
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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))
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Image service for v2 is switched from using glanceclient to OpenStackSDK.
|
@ -6,8 +6,7 @@ six>=1.10.0 # MIT
|
||||
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
cliff!=2.9.0,>=2.8.0 # Apache-2.0
|
||||
keystoneauth1>=3.14.0 # Apache-2.0
|
||||
openstacksdk>=0.17.0 # Apache-2.0
|
||||
openstacksdk>=0.36.0 # Apache-2.0
|
||||
osc-lib>=2.0.0 # Apache-2.0
|
||||
oslo.i18n>=3.15.3 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user