Complete switch from glanceclient to SDK for image service
In https://review.opendev.org/#/c/650374/ a work has been started to switch image service support from glanceclient with all it's dependencies to the SDK version. With this change version 1 (anyway deprecated since ages) is also being switched to SDK. Change-Id: Ic391500af02a73d81d64a9e9113cca85c9e24390
This commit is contained in:
parent
60e7c51df4
commit
768a64aac5
@ -26,64 +26,18 @@ DEFAULT_API_VERSION = '2'
|
|||||||
API_VERSION_OPTION = 'os_image_api_version'
|
API_VERSION_OPTION = 'os_image_api_version'
|
||||||
API_NAME = "image"
|
API_NAME = "image"
|
||||||
API_VERSIONS = {
|
API_VERSIONS = {
|
||||||
"1": "glanceclient.v1.client.Client",
|
"1": "openstack.connection.Connection",
|
||||||
"2": "openstack.connection.Connection",
|
"2": "openstack.connection.Connection",
|
||||||
}
|
}
|
||||||
|
|
||||||
IMAGE_API_TYPE = 'image'
|
|
||||||
IMAGE_API_VERSIONS = {
|
|
||||||
'1': 'openstackclient.api.image_v1.APIv1',
|
|
||||||
'2': 'openstackclient.api.image_v2.APIv2',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def make_client(instance):
|
def make_client(instance):
|
||||||
|
|
||||||
if instance._api_version[API_NAME] != '1':
|
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
'Image client initialized using OpenStack SDK: %s',
|
'Image client initialized using OpenStack SDK: %s',
|
||||||
instance.sdk_connection.image,
|
instance.sdk_connection.image,
|
||||||
)
|
)
|
||||||
return 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)
|
|
||||||
|
|
||||||
endpoint = instance.get_endpoint_for_service_type(
|
|
||||||
API_NAME,
|
|
||||||
region_name=instance.region_name,
|
|
||||||
interface=instance.interface,
|
|
||||||
)
|
|
||||||
|
|
||||||
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):
|
||||||
|
@ -22,13 +22,13 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from cliff import columns as cliff_columns
|
from cliff import columns as cliff_columns
|
||||||
from glanceclient.common import utils as gc_utils
|
|
||||||
from osc_lib.api import utils as api_utils
|
from osc_lib.api import utils as api_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
from osc_lib.command import command
|
||||||
from osc_lib import utils
|
from osc_lib import utils
|
||||||
|
|
||||||
|
from openstackclient.common import sdk_utils
|
||||||
from openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
@ -47,6 +47,36 @@ DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx",
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_columns(item):
|
||||||
|
# Trick sdk_utils to return URI attribute
|
||||||
|
column_map = {
|
||||||
|
'is_protected': 'protected',
|
||||||
|
'owner_id': 'owner'
|
||||||
|
}
|
||||||
|
hidden_columns = ['location', 'checksum',
|
||||||
|
'copy_from', 'created_at', 'status', 'updated_at']
|
||||||
|
return sdk_utils.get_osc_show_columns_for_sdk_resource(
|
||||||
|
item.to_dict(), column_map, hidden_columns)
|
||||||
|
|
||||||
|
|
||||||
|
_formatters = {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HumanReadableSizeColumn(cliff_columns.FormattableColumn):
|
||||||
|
def human_readable(self):
|
||||||
|
"""Return a formatted visibility string
|
||||||
|
|
||||||
|
:rtype:
|
||||||
|
A string formatted to public/private
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._value:
|
||||||
|
return utils.format_size(self._value)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
class VisibilityColumn(cliff_columns.FormattableColumn):
|
class VisibilityColumn(cliff_columns.FormattableColumn):
|
||||||
def human_readable(self):
|
def human_readable(self):
|
||||||
"""Return a formatted visibility string
|
"""Return a formatted visibility string
|
||||||
@ -210,7 +240,7 @@ class CreateImage(command.ShowOne):
|
|||||||
# Special case project option back to API attribute name 'owner'
|
# Special case project option back to API attribute name 'owner'
|
||||||
val = getattr(parsed_args, 'project', None)
|
val = getattr(parsed_args, 'project', None)
|
||||||
if val:
|
if val:
|
||||||
kwargs['owner'] = val
|
kwargs['owner_id'] = val
|
||||||
|
|
||||||
# Handle exclusive booleans with care
|
# Handle exclusive booleans with care
|
||||||
# Avoid including attributes in kwargs if an option is not
|
# Avoid including attributes in kwargs if an option is not
|
||||||
@ -219,9 +249,9 @@ class CreateImage(command.ShowOne):
|
|||||||
# to do nothing when no options are present as opposed to always
|
# 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['is_public'] = True
|
kwargs['is_public'] = True
|
||||||
if parsed_args.private:
|
if parsed_args.private:
|
||||||
@ -250,23 +280,31 @@ class CreateImage(command.ShowOne):
|
|||||||
kwargs["data"] = io.open(parsed_args.file, "rb")
|
kwargs["data"] = io.open(parsed_args.file, "rb")
|
||||||
else:
|
else:
|
||||||
# Read file from stdin
|
# Read file from stdin
|
||||||
if sys.stdin.isatty() is not True:
|
if not sys.stdin.isatty():
|
||||||
if msvcrt:
|
if msvcrt:
|
||||||
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
||||||
# Send an open file handle to glanceclient so it will
|
if hasattr(sys.stdin, 'buffer'):
|
||||||
# do a chunked transfer
|
kwargs['data'] = sys.stdin.buffer
|
||||||
|
else:
|
||||||
kwargs["data"] = sys.stdin
|
kwargs["data"] = sys.stdin
|
||||||
|
|
||||||
if not parsed_args.volume:
|
if not parsed_args.volume:
|
||||||
# Wrap the call to catch exceptions in order to close files
|
# Wrap the call to catch exceptions in order to close files
|
||||||
try:
|
try:
|
||||||
image = image_client.images.create(**kwargs)
|
image = image_client.create_image(**kwargs)
|
||||||
finally:
|
finally:
|
||||||
# Clean up open files - make sure data isn't a string
|
# Clean up open files - make sure data isn't a string
|
||||||
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
|
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
|
||||||
kwargs['data'] != sys.stdin):
|
kwargs['data'] != sys.stdin):
|
||||||
kwargs['data'].close()
|
kwargs['data'].close()
|
||||||
|
|
||||||
|
if image:
|
||||||
|
display_columns, columns = _get_columns(image)
|
||||||
|
_formatters['properties'] = format_columns.DictColumn
|
||||||
|
data = utils.get_item_properties(image, columns,
|
||||||
|
formatters=_formatters)
|
||||||
|
return (display_columns, data)
|
||||||
|
elif info:
|
||||||
info.update(image._info)
|
info.update(image._info)
|
||||||
info['properties'] = format_columns.DictColumn(
|
info['properties'] = format_columns.DictColumn(
|
||||||
info.get('properties', {}))
|
info.get('properties', {}))
|
||||||
@ -289,11 +327,8 @@ class DeleteImage(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
|
||||||
for image in parsed_args.images:
|
for image in parsed_args.images:
|
||||||
image_obj = utils.find_resource(
|
image_obj = image_client.find_image(image)
|
||||||
image_client.images,
|
image_client.delete_image(image_obj.id)
|
||||||
image,
|
|
||||||
)
|
|
||||||
image_client.images.delete(image_obj.id)
|
|
||||||
|
|
||||||
|
|
||||||
class ListImage(command.Lister):
|
class ListImage(command.Lister):
|
||||||
@ -359,15 +394,9 @@ class ListImage(command.Lister):
|
|||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if parsed_args.public:
|
if parsed_args.public:
|
||||||
kwargs['public'] = True
|
kwargs['is_public'] = True
|
||||||
if parsed_args.private:
|
if parsed_args.private:
|
||||||
kwargs['private'] = True
|
kwargs['is_private'] = True
|
||||||
# Note: We specifically need to do that below to get the 'status'
|
|
||||||
# column.
|
|
||||||
#
|
|
||||||
# Always set kwargs['detailed'] to True, and then filter the columns
|
|
||||||
# according to whether the --long option is specified or not.
|
|
||||||
kwargs['detailed'] = True
|
|
||||||
|
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = (
|
columns = (
|
||||||
@ -379,8 +408,8 @@ class ListImage(command.Lister):
|
|||||||
'Checksum',
|
'Checksum',
|
||||||
'Status',
|
'Status',
|
||||||
'is_public',
|
'is_public',
|
||||||
'protected',
|
'is_protected',
|
||||||
'owner',
|
'owner_id',
|
||||||
'properties',
|
'properties',
|
||||||
)
|
)
|
||||||
column_headers = (
|
column_headers = (
|
||||||
@ -401,16 +430,7 @@ class ListImage(command.Lister):
|
|||||||
column_headers = columns
|
column_headers = columns
|
||||||
|
|
||||||
# List of image data received
|
# List of image data received
|
||||||
data = []
|
data = list(image_client.images(**kwargs))
|
||||||
# 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 parsed_args.property:
|
if parsed_args.property:
|
||||||
# NOTE(dtroyer): coerce to a list to subscript it in py3
|
# NOTE(dtroyer): coerce to a list to subscript it in py3
|
||||||
@ -426,7 +446,7 @@ class ListImage(command.Lister):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
column_headers,
|
column_headers,
|
||||||
(utils.get_dict_properties(
|
(utils.get_item_properties(
|
||||||
s,
|
s,
|
||||||
columns,
|
columns,
|
||||||
formatters={
|
formatters={
|
||||||
@ -456,13 +476,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)
|
|
||||||
|
|
||||||
gc_utils.save_image(data, parsed_args.file)
|
image_client.download_image(image.id, output=parsed_args.file)
|
||||||
|
|
||||||
|
|
||||||
class SetImage(command.Command):
|
class SetImage(command.Command):
|
||||||
@ -621,22 +637,17 @@ 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['is_public'] = True
|
kwargs['is_public'] = True
|
||||||
if parsed_args.private:
|
if parsed_args.private:
|
||||||
kwargs['is_public'] = False
|
kwargs['is_public'] = False
|
||||||
if parsed_args.force:
|
|
||||||
kwargs['force'] = True
|
|
||||||
|
|
||||||
# Wrap the call to catch exceptions in order to close files
|
# Wrap the call to catch exceptions in order to close files
|
||||||
try:
|
try:
|
||||||
image = utils.find_resource(
|
image = image_client.find_image(parsed_args.image)
|
||||||
image_client.images,
|
|
||||||
parsed_args.image,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not parsed_args.location and not parsed_args.copy_from:
|
if not parsed_args.location and not parsed_args.copy_from:
|
||||||
if parsed_args.volume:
|
if parsed_args.volume:
|
||||||
@ -666,8 +677,9 @@ class SetImage(command.Command):
|
|||||||
if parsed_args.stdin:
|
if parsed_args.stdin:
|
||||||
if msvcrt:
|
if msvcrt:
|
||||||
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
||||||
# Send an open file handle to glanceclient so it
|
if hasattr(sys.stdin, 'buffer'):
|
||||||
# will do a chunked transfer
|
kwargs['data'] = sys.stdin.buffer
|
||||||
|
else:
|
||||||
kwargs["data"] = sys.stdin
|
kwargs["data"] = sys.stdin
|
||||||
else:
|
else:
|
||||||
LOG.warning(_('Use --stdin to enable read image '
|
LOG.warning(_('Use --stdin to enable read image '
|
||||||
@ -677,7 +689,7 @@ class SetImage(command.Command):
|
|||||||
image.properties.update(kwargs['properties'])
|
image.properties.update(kwargs['properties'])
|
||||||
kwargs['properties'] = image.properties
|
kwargs['properties'] = image.properties
|
||||||
|
|
||||||
image = image_client.images.update(image.id, **kwargs)
|
image = image_client.update_image(image.id, **kwargs)
|
||||||
finally:
|
finally:
|
||||||
# Clean up open files - make sure data isn't a string
|
# Clean up open files - make sure data isn't a string
|
||||||
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
|
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
|
||||||
@ -705,16 +717,12 @@ 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 = image_client.find_image(parsed_args.image)
|
||||||
image_client.images,
|
|
||||||
parsed_args.image,
|
|
||||||
)
|
|
||||||
|
|
||||||
info = {}
|
|
||||||
info.update(image._info)
|
|
||||||
if parsed_args.human_readable:
|
if parsed_args.human_readable:
|
||||||
if 'size' in info:
|
_formatters['size'] = HumanReadableSizeColumn
|
||||||
info['size'] = utils.format_size(info['size'])
|
display_columns, columns = _get_columns(image)
|
||||||
info['properties'] = format_columns.DictColumn(
|
_formatters['properties'] = format_columns.DictColumn
|
||||||
info.get('properties', {}))
|
data = utils.get_item_properties(image, columns,
|
||||||
return zip(*sorted(info.items()))
|
formatters=_formatters)
|
||||||
|
return (display_columns, data)
|
||||||
|
@ -13,10 +13,11 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
import copy
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from openstack.image.v1 import image
|
||||||
|
|
||||||
from openstackclient.tests.unit import fakes
|
from openstackclient.tests.unit import fakes
|
||||||
from openstackclient.tests.unit import utils
|
from openstackclient.tests.unit import utils
|
||||||
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
|
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
|
||||||
@ -111,13 +112,10 @@ class FakeImage(object):
|
|||||||
'Alpha': 'a',
|
'Alpha': 'a',
|
||||||
'Beta': 'b',
|
'Beta': 'b',
|
||||||
'Gamma': 'g'},
|
'Gamma': 'g'},
|
||||||
|
'status': 'status' + uuid.uuid4().hex
|
||||||
}
|
}
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
image = fakes.FakeResource(
|
return image.Image(**image_info)
|
||||||
info=copy.deepcopy(image_info),
|
|
||||||
loaded=True)
|
|
||||||
|
|
||||||
return image
|
|
||||||
|
@ -17,7 +17,6 @@ import copy
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib import exceptions
|
|
||||||
|
|
||||||
from openstackclient.image.v1 import image
|
from openstackclient.image.v1 import image
|
||||||
from openstackclient.tests.unit import fakes
|
from openstackclient.tests.unit import fakes
|
||||||
@ -29,9 +28,8 @@ class TestImage(image_fakes.TestImagev1):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImage, self).setUp()
|
super(TestImage, self).setUp()
|
||||||
|
|
||||||
# Get a shortcut to the ServerManager Mock
|
self.app.client_manager.image = mock.Mock()
|
||||||
self.images_mock = self.app.client_manager.image.images
|
self.client = self.app.client_manager.image
|
||||||
self.images_mock.reset_mock()
|
|
||||||
|
|
||||||
|
|
||||||
class TestImageCreate(TestImage):
|
class TestImageCreate(TestImage):
|
||||||
@ -48,6 +46,7 @@ class TestImageCreate(TestImage):
|
|||||||
'owner',
|
'owner',
|
||||||
'properties',
|
'properties',
|
||||||
'protected',
|
'protected',
|
||||||
|
'size'
|
||||||
)
|
)
|
||||||
data = (
|
data = (
|
||||||
new_image.container_format,
|
new_image.container_format,
|
||||||
@ -57,28 +56,24 @@ class TestImageCreate(TestImage):
|
|||||||
new_image.min_disk,
|
new_image.min_disk,
|
||||||
new_image.min_ram,
|
new_image.min_ram,
|
||||||
new_image.name,
|
new_image.name,
|
||||||
new_image.owner,
|
new_image.owner_id,
|
||||||
format_columns.DictColumn(new_image.properties),
|
format_columns.DictColumn(new_image.properties),
|
||||||
new_image.protected,
|
new_image.is_protected,
|
||||||
|
new_image.size
|
||||||
)
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageCreate, self).setUp()
|
super(TestImageCreate, self).setUp()
|
||||||
|
|
||||||
self.images_mock.create.return_value = self.new_image
|
self.client.create_image = mock.Mock(return_value=self.new_image)
|
||||||
# This is the return value for utils.find_resource()
|
self.client.find_image = mock.Mock(return_value=self.new_image)
|
||||||
self.images_mock.get.return_value = self.new_image
|
self.client.update_image = mock.Mock(return_image=self.new_image)
|
||||||
self.images_mock.update.return_value = self.new_image
|
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = image.CreateImage(self.app, None)
|
self.cmd = image.CreateImage(self.app, None)
|
||||||
|
|
||||||
def test_image_reserve_no_options(self):
|
@mock.patch('sys.stdin', side_effect=[None])
|
||||||
mock_exception = {
|
def test_image_reserve_no_options(self, raw_input):
|
||||||
'find.side_effect': exceptions.CommandError('x'),
|
|
||||||
'get.side_effect': exceptions.CommandError('x'),
|
|
||||||
}
|
|
||||||
self.images_mock.configure_mock(**mock_exception)
|
|
||||||
arglist = [
|
arglist = [
|
||||||
self.new_image.name,
|
self.new_image.name,
|
||||||
]
|
]
|
||||||
@ -95,25 +90,20 @@ class TestImageCreate(TestImage):
|
|||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
# ImageManager.create(name=, **)
|
# ImageManager.create(name=, **)
|
||||||
self.images_mock.create.assert_called_with(
|
self.client.create_image.assert_called_with(
|
||||||
name=self.new_image.name,
|
name=self.new_image.name,
|
||||||
container_format=image.DEFAULT_CONTAINER_FORMAT,
|
container_format=image.DEFAULT_CONTAINER_FORMAT,
|
||||||
disk_format=image.DEFAULT_DISK_FORMAT,
|
disk_format=image.DEFAULT_DISK_FORMAT
|
||||||
data=mock.ANY,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify update() was not called, if it was show the args
|
# Verify update() was not called, if it was show the args
|
||||||
self.assertEqual(self.images_mock.update.call_args_list, [])
|
self.assertEqual(self.client.update_image.call_args_list, [])
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(self.data, data)
|
||||||
|
|
||||||
def test_image_reserve_options(self):
|
@mock.patch('sys.stdin', side_effect=[None])
|
||||||
mock_exception = {
|
def test_image_reserve_options(self, raw_input):
|
||||||
'find.side_effect': exceptions.CommandError('x'),
|
|
||||||
'get.side_effect': exceptions.CommandError('x'),
|
|
||||||
}
|
|
||||||
self.images_mock.configure_mock(**mock_exception)
|
|
||||||
arglist = [
|
arglist = [
|
||||||
'--container-format', 'ovf',
|
'--container-format', 'ovf',
|
||||||
'--disk-format', 'ami',
|
'--disk-format', 'ami',
|
||||||
@ -144,20 +134,19 @@ class TestImageCreate(TestImage):
|
|||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
# ImageManager.create(name=, **)
|
# ImageManager.create(name=, **)
|
||||||
self.images_mock.create.assert_called_with(
|
self.client.create_image.assert_called_with(
|
||||||
name=self.new_image.name,
|
name=self.new_image.name,
|
||||||
container_format='ovf',
|
container_format='ovf',
|
||||||
disk_format='ami',
|
disk_format='ami',
|
||||||
min_disk=10,
|
min_disk=10,
|
||||||
min_ram=4,
|
min_ram=4,
|
||||||
protected=True,
|
is_protected=True,
|
||||||
is_public=False,
|
is_public=False,
|
||||||
owner='q',
|
owner_id='q',
|
||||||
data=mock.ANY,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify update() was not called, if it was show the args
|
# Verify update() was not called, if it was show the args
|
||||||
self.assertEqual(self.images_mock.update.call_args_list, [])
|
self.assertEqual(self.client.update_image.call_args_list, [])
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(self.data, data)
|
||||||
@ -167,11 +156,6 @@ class TestImageCreate(TestImage):
|
|||||||
mock_file = mock.Mock(name='File')
|
mock_file = mock.Mock(name='File')
|
||||||
mock_open.return_value = mock_file
|
mock_open.return_value = mock_file
|
||||||
mock_open.read.return_value = self.data
|
mock_open.read.return_value = self.data
|
||||||
mock_exception = {
|
|
||||||
'find.side_effect': exceptions.CommandError('x'),
|
|
||||||
'get.side_effect': exceptions.CommandError('x'),
|
|
||||||
}
|
|
||||||
self.images_mock.configure_mock(**mock_exception)
|
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
'--file', 'filer',
|
'--file', 'filer',
|
||||||
@ -203,15 +187,12 @@ class TestImageCreate(TestImage):
|
|||||||
# Ensure the input file is closed
|
# Ensure the input file is closed
|
||||||
mock_file.close.assert_called_with()
|
mock_file.close.assert_called_with()
|
||||||
|
|
||||||
# ImageManager.get(name) not to be called since update action exists
|
|
||||||
self.images_mock.get.assert_not_called()
|
|
||||||
|
|
||||||
# ImageManager.create(name=, **)
|
# ImageManager.create(name=, **)
|
||||||
self.images_mock.create.assert_called_with(
|
self.client.create_image.assert_called_with(
|
||||||
name=self.new_image.name,
|
name=self.new_image.name,
|
||||||
container_format=image.DEFAULT_CONTAINER_FORMAT,
|
container_format=image.DEFAULT_CONTAINER_FORMAT,
|
||||||
disk_format=image.DEFAULT_DISK_FORMAT,
|
disk_format=image.DEFAULT_DISK_FORMAT,
|
||||||
protected=False,
|
is_protected=False,
|
||||||
is_public=True,
|
is_public=True,
|
||||||
properties={
|
properties={
|
||||||
'Alpha': '1',
|
'Alpha': '1',
|
||||||
@ -221,7 +202,7 @@ class TestImageCreate(TestImage):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Verify update() was not called, if it was show the args
|
# Verify update() was not called, if it was show the args
|
||||||
self.assertEqual(self.images_mock.update.call_args_list, [])
|
self.assertEqual(self.client.update_image.call_args_list, [])
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(self.data, data)
|
||||||
@ -235,8 +216,8 @@ class TestImageDelete(TestImage):
|
|||||||
super(TestImageDelete, self).setUp()
|
super(TestImageDelete, self).setUp()
|
||||||
|
|
||||||
# This is the return value for utils.find_resource()
|
# This is the return value for utils.find_resource()
|
||||||
self.images_mock.get.return_value = self._image
|
self.client.find_image = mock.Mock(return_value=self._image)
|
||||||
self.images_mock.delete.return_value = None
|
self.client.delete_image = mock.Mock(return_value=None)
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = image.DeleteImage(self.app, None)
|
self.cmd = image.DeleteImage(self.app, None)
|
||||||
@ -252,7 +233,7 @@ class TestImageDelete(TestImage):
|
|||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.images_mock.delete.assert_called_with(self._image.id)
|
self.client.delete_image.assert_called_with(self._image.id)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
|
||||||
@ -269,7 +250,7 @@ class TestImageList(TestImage):
|
|||||||
(
|
(
|
||||||
_image.id,
|
_image.id,
|
||||||
_image.name,
|
_image.name,
|
||||||
'',
|
_image.status
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -277,13 +258,13 @@ class TestImageList(TestImage):
|
|||||||
info = {
|
info = {
|
||||||
'id': _image.id,
|
'id': _image.id,
|
||||||
'name': _image.name,
|
'name': _image.name,
|
||||||
'owner': _image.owner,
|
'owner': _image.owner_id,
|
||||||
'container_format': _image.container_format,
|
'container_format': _image.container_format,
|
||||||
'disk_format': _image.disk_format,
|
'disk_format': _image.disk_format,
|
||||||
'min_disk': _image.min_disk,
|
'min_disk': _image.min_disk,
|
||||||
'min_ram': _image.min_ram,
|
'min_ram': _image.min_ram,
|
||||||
'is_public': _image.is_public,
|
'is_public': _image.is_public,
|
||||||
'protected': _image.protected,
|
'protected': _image.is_protected,
|
||||||
'properties': _image.properties,
|
'properties': _image.properties,
|
||||||
}
|
}
|
||||||
image_info = copy.deepcopy(info)
|
image_info = copy.deepcopy(info)
|
||||||
@ -291,11 +272,10 @@ class TestImageList(TestImage):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageList, self).setUp()
|
super(TestImageList, self).setUp()
|
||||||
|
|
||||||
self.api_mock = mock.Mock()
|
self.client.images = mock.Mock()
|
||||||
self.api_mock.image_list.side_effect = [
|
self.client.images.side_effect = [
|
||||||
[self.image_info], [],
|
[self._image], [],
|
||||||
]
|
]
|
||||||
self.app.client_manager.image.api = self.api_mock
|
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = image.ListImage(self.app, None)
|
self.cmd = image.ListImage(self.app, None)
|
||||||
@ -313,10 +293,7 @@ class TestImageList(TestImage):
|
|||||||
# returns a tuple containing the column names and an iterable
|
# returns a tuple containing the column names and an iterable
|
||||||
# containing the data to be listed.
|
# containing the data to be listed.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.api_mock.image_list.assert_called_with(
|
self.client.images.assert_called_with()
|
||||||
detailed=True,
|
|
||||||
marker=self._image.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.datalist, tuple(data))
|
self.assertEqual(self.datalist, tuple(data))
|
||||||
@ -336,10 +313,8 @@ class TestImageList(TestImage):
|
|||||||
# returns a tuple containing the column names and an iterable
|
# returns a tuple containing the column names and an iterable
|
||||||
# containing the data to be listed.
|
# containing the data to be listed.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.api_mock.image_list.assert_called_with(
|
self.client.images.assert_called_with(
|
||||||
detailed=True,
|
is_public=True,
|
||||||
public=True,
|
|
||||||
marker=self._image.id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -360,10 +335,8 @@ class TestImageList(TestImage):
|
|||||||
# returns a tuple containing the column names and an iterable
|
# returns a tuple containing the column names and an iterable
|
||||||
# containing the data to be listed.
|
# containing the data to be listed.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.api_mock.image_list.assert_called_with(
|
self.client.images.assert_called_with(
|
||||||
detailed=True,
|
is_private=True,
|
||||||
private=True,
|
|
||||||
marker=self._image.id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
@ -382,10 +355,7 @@ class TestImageList(TestImage):
|
|||||||
# returns a tuple containing the column names and an iterable
|
# returns a tuple containing the column names and an iterable
|
||||||
# containing the data to be listed.
|
# containing the data to be listed.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.api_mock.image_list.assert_called_with(
|
self.client.images.assert_called_with()
|
||||||
detailed=True,
|
|
||||||
marker=self._image.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
collist = (
|
collist = (
|
||||||
'ID',
|
'ID',
|
||||||
@ -405,14 +375,14 @@ class TestImageList(TestImage):
|
|||||||
datalist = ((
|
datalist = ((
|
||||||
self._image.id,
|
self._image.id,
|
||||||
self._image.name,
|
self._image.name,
|
||||||
'',
|
self._image.disk_format,
|
||||||
'',
|
self._image.container_format,
|
||||||
'',
|
self._image.size,
|
||||||
'',
|
self._image.checksum,
|
||||||
'',
|
self._image.status,
|
||||||
image.VisibilityColumn(True),
|
image.VisibilityColumn(self._image.is_public),
|
||||||
False,
|
self._image.is_protected,
|
||||||
self._image.owner,
|
self._image.owner_id,
|
||||||
format_columns.DictColumn(
|
format_columns.DictColumn(
|
||||||
{'Alpha': 'a', 'Beta': 'b', 'Gamma': 'g'}),
|
{'Alpha': 'a', 'Beta': 'b', 'Gamma': 'g'}),
|
||||||
), )
|
), )
|
||||||
@ -436,12 +406,9 @@ class TestImageList(TestImage):
|
|||||||
# returns a tuple containing the column names and an iterable
|
# returns a tuple containing the column names and an iterable
|
||||||
# containing the data to be listed.
|
# containing the data to be listed.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.api_mock.image_list.assert_called_with(
|
self.client.images.assert_called_with()
|
||||||
detailed=True,
|
|
||||||
marker=self._image.id,
|
|
||||||
)
|
|
||||||
sf_mock.assert_called_with(
|
sf_mock.assert_called_with(
|
||||||
[self.image_info],
|
[self._image],
|
||||||
attr='a',
|
attr='a',
|
||||||
value='1',
|
value='1',
|
||||||
property_field='properties',
|
property_field='properties',
|
||||||
@ -453,7 +420,7 @@ class TestImageList(TestImage):
|
|||||||
@mock.patch('osc_lib.utils.sort_items')
|
@mock.patch('osc_lib.utils.sort_items')
|
||||||
def test_image_list_sort_option(self, si_mock):
|
def test_image_list_sort_option(self, si_mock):
|
||||||
si_mock.side_effect = [
|
si_mock.side_effect = [
|
||||||
[self.image_info], [],
|
[self._image], [],
|
||||||
]
|
]
|
||||||
|
|
||||||
arglist = ['--sort', 'name:asc']
|
arglist = ['--sort', 'name:asc']
|
||||||
@ -464,12 +431,9 @@ class TestImageList(TestImage):
|
|||||||
# returns a tuple containing the column names and an iterable
|
# returns a tuple containing the column names and an iterable
|
||||||
# containing the data to be listed.
|
# containing the data to be listed.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.api_mock.image_list.assert_called_with(
|
self.client.images.assert_called_with()
|
||||||
detailed=True,
|
|
||||||
marker=self._image.id,
|
|
||||||
)
|
|
||||||
si_mock.assert_called_with(
|
si_mock.assert_called_with(
|
||||||
[self.image_info],
|
[self._image],
|
||||||
'name:asc'
|
'name:asc'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -485,8 +449,8 @@ class TestImageSet(TestImage):
|
|||||||
super(TestImageSet, self).setUp()
|
super(TestImageSet, self).setUp()
|
||||||
|
|
||||||
# This is the return value for utils.find_resource()
|
# This is the return value for utils.find_resource()
|
||||||
self.images_mock.get.return_value = self._image
|
self.client.find_image = mock.Mock(return_value=self._image)
|
||||||
self.images_mock.update.return_value = self._image
|
self.client.update_image = mock.Mock(return_value=self._image)
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = image.SetImage(self.app, None)
|
self.cmd = image.SetImage(self.app, None)
|
||||||
@ -502,8 +466,7 @@ class TestImageSet(TestImage):
|
|||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.images_mock.update.assert_called_with(self._image.id,
|
self.client.update_image.assert_called_with(self._image.id, **{})
|
||||||
**{})
|
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_image_set_options(self):
|
def test_image_set_options(self):
|
||||||
@ -541,7 +504,7 @@ class TestImageSet(TestImage):
|
|||||||
'size': 35165824
|
'size': 35165824
|
||||||
}
|
}
|
||||||
# ImageManager.update(image, **kwargs)
|
# ImageManager.update(image, **kwargs)
|
||||||
self.images_mock.update.assert_called_with(
|
self.client.update_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -565,11 +528,11 @@ class TestImageSet(TestImage):
|
|||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'protected': True,
|
'is_protected': True,
|
||||||
'is_public': False,
|
'is_public': False,
|
||||||
}
|
}
|
||||||
# ImageManager.update(image, **kwargs)
|
# ImageManager.update(image, **kwargs)
|
||||||
self.images_mock.update.assert_called_with(
|
self.client.update_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -593,11 +556,11 @@ class TestImageSet(TestImage):
|
|||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'protected': False,
|
'is_protected': False,
|
||||||
'is_public': True,
|
'is_public': True,
|
||||||
}
|
}
|
||||||
# ImageManager.update(image, **kwargs)
|
# ImageManager.update(image, **kwargs)
|
||||||
self.images_mock.update.assert_called_with(
|
self.client.update_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -625,7 +588,7 @@ class TestImageSet(TestImage):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
# ImageManager.update(image, **kwargs)
|
# ImageManager.update(image, **kwargs)
|
||||||
self.images_mock.update.assert_called_with(
|
self.client.update_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -683,7 +646,7 @@ class TestImageSet(TestImage):
|
|||||||
'',
|
'',
|
||||||
)
|
)
|
||||||
# ImageManager.update(image_id, remove_props=, **)
|
# ImageManager.update(image_id, remove_props=, **)
|
||||||
self.images_mock.update.assert_called_with(
|
self.client.update_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
name='updated_image',
|
name='updated_image',
|
||||||
volume='volly',
|
volume='volly',
|
||||||
@ -710,7 +673,7 @@ class TestImageSet(TestImage):
|
|||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
}
|
}
|
||||||
# ImageManager.update(image, **kwargs)
|
# ImageManager.update(image, **kwargs)
|
||||||
self.images_mock.update.assert_called_with(
|
self.client.update_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -742,16 +705,16 @@ class TestImageShow(TestImage):
|
|||||||
_image.min_disk,
|
_image.min_disk,
|
||||||
_image.min_ram,
|
_image.min_ram,
|
||||||
_image.name,
|
_image.name,
|
||||||
_image.owner,
|
_image.owner_id,
|
||||||
format_columns.DictColumn(_image.properties),
|
format_columns.DictColumn(_image.properties),
|
||||||
_image.protected,
|
_image.is_protected,
|
||||||
_image.size,
|
_image.size,
|
||||||
)
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestImageShow, self).setUp()
|
super(TestImageShow, self).setUp()
|
||||||
|
|
||||||
self.images_mock.get.return_value = self._image
|
self.client.find_image = mock.Mock(return_value=self._image)
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = image.ShowImage(self.app, None)
|
self.cmd = image.ShowImage(self.app, None)
|
||||||
@ -769,7 +732,7 @@ class TestImageShow(TestImage):
|
|||||||
# returns a two-part tuple with a tuple of column names and a tuple of
|
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||||
# data to be shown.
|
# data to be shown.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.images_mock.get.assert_called_with(
|
self.client.find_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -791,9 +754,9 @@ class TestImageShow(TestImage):
|
|||||||
# returns a two-part tuple with a tuple of column names and a tuple of
|
# returns a two-part tuple with a tuple of column names and a tuple of
|
||||||
# data to be shown.
|
# data to be shown.
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.images_mock.get.assert_called_with(
|
self.client.find_image.assert_called_with(
|
||||||
self._image.id,
|
self._image.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
size_index = columns.index('size')
|
size_index = columns.index('size')
|
||||||
self.assertEqual(data[size_index], '2K')
|
self.assertEqual(data[size_index].human_readable(), '2K')
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Complete switch from glanceclient to the SDK for image service.
|
@ -10,7 +10,6 @@ openstacksdk>=0.36.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
|
||||||
python-glanceclient>=2.8.0 # Apache-2.0
|
|
||||||
python-keystoneclient>=3.22.0 # Apache-2.0
|
python-keystoneclient>=3.22.0 # Apache-2.0
|
||||||
python-novaclient>=15.1.0 # Apache-2.0
|
python-novaclient>=15.1.0 # Apache-2.0
|
||||||
python-cinderclient>=3.3.0 # Apache-2.0
|
python-cinderclient>=3.3.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user