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