Skip to content

Commit 768a64a

Browse files
gtemaemonty
authored andcommitted
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
1 parent 60e7c51 commit 768a64a

6 files changed

Lines changed: 155 additions & 229 deletions

File tree

openstackclient/image/client.py

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,64 +26,18 @@
2626
API_VERSION_OPTION = 'os_image_api_version'
2727
API_NAME = "image"
2828
API_VERSIONS = {
29-
"1": "glanceclient.v1.client.Client",
29+
"1": "openstack.connection.Connection",
3030
"2": "openstack.connection.Connection",
3131
}
3232

33-
IMAGE_API_TYPE = 'image'
34-
IMAGE_API_VERSIONS = {
35-
'1': 'openstackclient.api.image_v1.APIv1',
36-
'2': 'openstackclient.api.image_v2.APIv2',
37-
}
38-
3933

4034
def make_client(instance):
4135

42-
if instance._api_version[API_NAME] != '1':
43-
LOG.debug(
44-
'Image client initialized using OpenStack SDK: %s',
45-
instance.sdk_connection.image,
46-
)
47-
return instance.sdk_connection.image
48-
else:
49-
"""Returns an image service client"""
50-
image_client = utils.get_client_class(
51-
API_NAME,
52-
instance._api_version[API_NAME],
53-
API_VERSIONS)
54-
LOG.debug('Instantiating image client: %s', image_client)
55-
56-
endpoint = instance.get_endpoint_for_service_type(
57-
API_NAME,
58-
region_name=instance.region_name,
59-
interface=instance.interface,
60-
)
61-
62-
client = image_client(
63-
endpoint,
64-
token=instance.auth.get_token(instance.session),
65-
cacert=instance.cacert,
66-
insecure=not instance.verify,
67-
)
68-
69-
# Create the low-level API
70-
71-
image_api = utils.get_client_class(
72-
API_NAME,
73-
instance._api_version[API_NAME],
74-
IMAGE_API_VERSIONS)
75-
LOG.debug('Instantiating image api: %s', image_api)
76-
77-
client.api = image_api(
78-
session=instance.session,
79-
endpoint=instance.get_endpoint_for_service_type(
80-
IMAGE_API_TYPE,
81-
region_name=instance.region_name,
82-
interface=instance.interface,
83-
)
84-
)
85-
86-
return client
36+
LOG.debug(
37+
'Image client initialized using OpenStack SDK: %s',
38+
instance.sdk_connection.image,
39+
)
40+
return instance.sdk_connection.image
8741

8842

8943
def build_option_parser(parser):

openstackclient/image/v1/image.py

Lines changed: 73 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
import sys
2323

2424
from cliff import columns as cliff_columns
25-
from glanceclient.common import utils as gc_utils
2625
from osc_lib.api import utils as api_utils
2726
from osc_lib.cli import format_columns
2827
from osc_lib.cli import parseractions
2928
from osc_lib.command import command
3029
from osc_lib import utils
3130

31+
from openstackclient.common import sdk_utils
3232
from openstackclient.i18n import _
3333

3434
if os.name == "nt":
@@ -47,6 +47,36 @@
4747
LOG = logging.getLogger(__name__)
4848

4949

50+
def _get_columns(item):
51+
# Trick sdk_utils to return URI attribute
52+
column_map = {
53+
'is_protected': 'protected',
54+
'owner_id': 'owner'
55+
}
56+
hidden_columns = ['location', 'checksum',
57+
'copy_from', 'created_at', 'status', 'updated_at']
58+
return sdk_utils.get_osc_show_columns_for_sdk_resource(
59+
item.to_dict(), column_map, hidden_columns)
60+
61+
62+
_formatters = {
63+
}
64+
65+
66+
class HumanReadableSizeColumn(cliff_columns.FormattableColumn):
67+
def human_readable(self):
68+
"""Return a formatted visibility string
69+
70+
:rtype:
71+
A string formatted to public/private
72+
"""
73+
74+
if self._value:
75+
return utils.format_size(self._value)
76+
else:
77+
return ''
78+
79+
5080
class VisibilityColumn(cliff_columns.FormattableColumn):
5181
def human_readable(self):
5282
"""Return a formatted visibility string
@@ -210,7 +240,7 @@ def take_action(self, parsed_args):
210240
# Special case project option back to API attribute name 'owner'
211241
val = getattr(parsed_args, 'project', None)
212242
if val:
213-
kwargs['owner'] = val
243+
kwargs['owner_id'] = val
214244

215245
# Handle exclusive booleans with care
216246
# Avoid including attributes in kwargs if an option is not
@@ -219,9 +249,9 @@ def take_action(self, parsed_args):
219249
# to do nothing when no options are present as opposed to always
220250
# setting a default.
221251
if parsed_args.protected:
222-
kwargs['protected'] = True
252+
kwargs['is_protected'] = True
223253
if parsed_args.unprotected:
224-
kwargs['protected'] = False
254+
kwargs['is_protected'] = False
225255
if parsed_args.public:
226256
kwargs['is_public'] = True
227257
if parsed_args.private:
@@ -250,27 +280,35 @@ def take_action(self, parsed_args):
250280
kwargs["data"] = io.open(parsed_args.file, "rb")
251281
else:
252282
# Read file from stdin
253-
if sys.stdin.isatty() is not True:
283+
if not sys.stdin.isatty():
254284
if msvcrt:
255285
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
256-
# Send an open file handle to glanceclient so it will
257-
# do a chunked transfer
258-
kwargs["data"] = sys.stdin
286+
if hasattr(sys.stdin, 'buffer'):
287+
kwargs['data'] = sys.stdin.buffer
288+
else:
289+
kwargs["data"] = sys.stdin
259290

260291
if not parsed_args.volume:
261292
# Wrap the call to catch exceptions in order to close files
262293
try:
263-
image = image_client.images.create(**kwargs)
294+
image = image_client.create_image(**kwargs)
264295
finally:
265296
# Clean up open files - make sure data isn't a string
266297
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
267298
kwargs['data'] != sys.stdin):
268299
kwargs['data'].close()
269300

301+
if image:
302+
display_columns, columns = _get_columns(image)
303+
_formatters['properties'] = format_columns.DictColumn
304+
data = utils.get_item_properties(image, columns,
305+
formatters=_formatters)
306+
return (display_columns, data)
307+
elif info:
270308
info.update(image._info)
271309
info['properties'] = format_columns.DictColumn(
272310
info.get('properties', {}))
273-
return zip(*sorted(info.items()))
311+
return zip(*sorted(info.items()))
274312

275313

276314
class DeleteImage(command.Command):
@@ -289,11 +327,8 @@ def get_parser(self, prog_name):
289327
def take_action(self, parsed_args):
290328
image_client = self.app.client_manager.image
291329
for image in parsed_args.images:
292-
image_obj = utils.find_resource(
293-
image_client.images,
294-
image,
295-
)
296-
image_client.images.delete(image_obj.id)
330+
image_obj = image_client.find_image(image)
331+
image_client.delete_image(image_obj.id)
297332

298333

299334
class ListImage(command.Lister):
@@ -359,15 +394,9 @@ def take_action(self, parsed_args):
359394

360395
kwargs = {}
361396
if parsed_args.public:
362-
kwargs['public'] = True
397+
kwargs['is_public'] = True
363398
if parsed_args.private:
364-
kwargs['private'] = True
365-
# Note: We specifically need to do that below to get the 'status'
366-
# column.
367-
#
368-
# Always set kwargs['detailed'] to True, and then filter the columns
369-
# according to whether the --long option is specified or not.
370-
kwargs['detailed'] = True
399+
kwargs['is_private'] = True
371400

372401
if parsed_args.long:
373402
columns = (
@@ -379,8 +408,8 @@ def take_action(self, parsed_args):
379408
'Checksum',
380409
'Status',
381410
'is_public',
382-
'protected',
383-
'owner',
411+
'is_protected',
412+
'owner_id',
384413
'properties',
385414
)
386415
column_headers = (
@@ -401,16 +430,7 @@ def take_action(self, parsed_args):
401430
column_headers = columns
402431

403432
# List of image data received
404-
data = []
405-
# No pages received yet, so start the page marker at None.
406-
marker = None
407-
while True:
408-
page = image_client.api.image_list(marker=marker, **kwargs)
409-
if not page:
410-
break
411-
data.extend(page)
412-
# Set the marker to the id of the last item we received
413-
marker = page[-1]['id']
433+
data = list(image_client.images(**kwargs))
414434

415435
if parsed_args.property:
416436
# NOTE(dtroyer): coerce to a list to subscript it in py3
@@ -426,7 +446,7 @@ def take_action(self, parsed_args):
426446

427447
return (
428448
column_headers,
429-
(utils.get_dict_properties(
449+
(utils.get_item_properties(
430450
s,
431451
columns,
432452
formatters={
@@ -456,13 +476,9 @@ def get_parser(self, prog_name):
456476

457477
def take_action(self, parsed_args):
458478
image_client = self.app.client_manager.image
459-
image = utils.find_resource(
460-
image_client.images,
461-
parsed_args.image,
462-
)
463-
data = image_client.images.data(image)
479+
image = image_client.find_image(parsed_args.image)
464480

465-
gc_utils.save_image(data, parsed_args.file)
481+
image_client.download_image(image.id, output=parsed_args.file)
466482

467483

468484
class SetImage(command.Command):
@@ -621,22 +637,17 @@ def take_action(self, parsed_args):
621637
# to do nothing when no options are present as opposed to always
622638
# setting a default.
623639
if parsed_args.protected:
624-
kwargs['protected'] = True
640+
kwargs['is_protected'] = True
625641
if parsed_args.unprotected:
626-
kwargs['protected'] = False
642+
kwargs['is_protected'] = False
627643
if parsed_args.public:
628644
kwargs['is_public'] = True
629645
if parsed_args.private:
630646
kwargs['is_public'] = False
631-
if parsed_args.force:
632-
kwargs['force'] = True
633647

634648
# Wrap the call to catch exceptions in order to close files
635649
try:
636-
image = utils.find_resource(
637-
image_client.images,
638-
parsed_args.image,
639-
)
650+
image = image_client.find_image(parsed_args.image)
640651

641652
if not parsed_args.location and not parsed_args.copy_from:
642653
if parsed_args.volume:
@@ -666,9 +677,10 @@ def take_action(self, parsed_args):
666677
if parsed_args.stdin:
667678
if msvcrt:
668679
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
669-
# Send an open file handle to glanceclient so it
670-
# will do a chunked transfer
671-
kwargs["data"] = sys.stdin
680+
if hasattr(sys.stdin, 'buffer'):
681+
kwargs['data'] = sys.stdin.buffer
682+
else:
683+
kwargs["data"] = sys.stdin
672684
else:
673685
LOG.warning(_('Use --stdin to enable read image '
674686
'data from standard input'))
@@ -677,7 +689,7 @@ def take_action(self, parsed_args):
677689
image.properties.update(kwargs['properties'])
678690
kwargs['properties'] = image.properties
679691

680-
image = image_client.images.update(image.id, **kwargs)
692+
image = image_client.update_image(image.id, **kwargs)
681693
finally:
682694
# Clean up open files - make sure data isn't a string
683695
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
@@ -705,16 +717,12 @@ def get_parser(self, prog_name):
705717

706718
def take_action(self, parsed_args):
707719
image_client = self.app.client_manager.image
708-
image = utils.find_resource(
709-
image_client.images,
710-
parsed_args.image,
711-
)
720+
image = image_client.find_image(parsed_args.image)
712721

713-
info = {}
714-
info.update(image._info)
715722
if parsed_args.human_readable:
716-
if 'size' in info:
717-
info['size'] = utils.format_size(info['size'])
718-
info['properties'] = format_columns.DictColumn(
719-
info.get('properties', {}))
720-
return zip(*sorted(info.items()))
723+
_formatters['size'] = HumanReadableSizeColumn
724+
display_columns, columns = _get_columns(image)
725+
_formatters['properties'] = format_columns.DictColumn
726+
data = utils.get_item_properties(image, columns,
727+
formatters=_formatters)
728+
return (display_columns, data)

openstackclient/tests/unit/image/v1/fakes.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
# under the License.
1414
#
1515

16-
import copy
1716
from unittest import mock
1817
import uuid
1918

19+
from openstack.image.v1 import image
20+
2021
from openstackclient.tests.unit import fakes
2122
from openstackclient.tests.unit import utils
2223
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
@@ -111,13 +112,10 @@ def create_one_image(attrs=None):
111112
'Alpha': 'a',
112113
'Beta': 'b',
113114
'Gamma': 'g'},
115+
'status': 'status' + uuid.uuid4().hex
114116
}
115117

116118
# Overwrite default attributes if there are some attributes set
117119
image_info.update(attrs)
118120

119-
image = fakes.FakeResource(
120-
info=copy.deepcopy(image_info),
121-
loaded=True)
122-
123-
return image
121+
return image.Image(**image_info)

0 commit comments

Comments
 (0)