Skip to content

Commit d80deab

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Move update code from image create command"
2 parents 60d1417 + bd58977 commit d80deab

4 files changed

Lines changed: 223 additions & 105 deletions

File tree

doc/source/backwards-incompatible.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@ List of Backwards Incompatible Changes
4747
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1450872
4848
* Commit: https://review.openstack.org/#/c/179446/
4949

50+
4. Command `openstack image create` does not update already existing image
51+
52+
Previously, the image create command updated already existing image if it had
53+
same name. It disabled possibility to create multiple images with same name
54+
and lead to potentially unwanted update of existing images by image create
55+
command.
56+
Now, update code was moved from create action to set action.
57+
58+
* In favor of: Create multiple images with same name (as glance does).
59+
* As of: 1.5.0
60+
* Removed in: NA
61+
* Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1461817
62+
* Commit: https://review.openstack.org/#/c/194654/
63+
5064
For Developers
5165
==============
5266

doc/source/command-objects/image.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ Set image properties
210210
[--size <size>]
211211
[--protected | --unprotected]
212212
[--public | --private]
213+
[--store <store>]
214+
[--location <image-url>]
215+
[--copy-from <image-url>]
216+
[--file <file>]
217+
[--volume <volume>]
218+
[--force]
219+
[--checksum <checksum>]
220+
[--stdin]
213221
[--property <key=value> [...] ]
214222
<image>
215223
@@ -260,6 +268,38 @@ Set image properties
260268
261269
Image is inaccessible to the public (default)
262270
271+
.. option:: --store <store>
272+
273+
Upload image to this store
274+
275+
.. option:: --location <image-url>
276+
277+
Download image from an existing URL
278+
279+
.. option:: --copy-from <image-url>
280+
281+
Copy image from the data store (similar to --location)
282+
283+
.. option:: --file <file>
284+
285+
Upload image from local file
286+
287+
.. option:: --volume <volume>
288+
289+
Update image with a volume
290+
291+
.. option:: --force
292+
293+
Force image update if volume is in use (only meaningful with --volume)
294+
295+
.. option:: --checksum <checksum>
296+
297+
Image hash used for verification
298+
299+
.. option:: --stdin
300+
301+
Allow to read image data from standard input
302+
263303
.. option:: --property <key=value>
264304
265305
Set a property on this image (repeat for multiple values)

openstackclient/image/v1/image.py

Lines changed: 104 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333

3434
from glanceclient.common import utils as gc_utils
3535
from openstackclient.api import utils as api_utils
36-
from openstackclient.common import exceptions
3736
from openstackclient.common import parseractions
3837
from openstackclient.common import utils
3938

@@ -244,29 +243,7 @@ def take_action(self, parsed_args):
244243

245244
# Wrap the call to catch exceptions in order to close files
246245
try:
247-
try:
248-
image = utils.find_resource(
249-
image_client.images,
250-
parsed_args.name,
251-
)
252-
253-
# Preserve previous properties if any are being set now
254-
if image.properties:
255-
if parsed_args.properties:
256-
image.properties.update(kwargs['properties'])
257-
kwargs['properties'] = image.properties
258-
259-
except exceptions.CommandError:
260-
if not parsed_args.volume:
261-
# This is normal for a create or reserve (create w/o
262-
# an image), but skip for create from volume
263-
image = image_client.images.create(**kwargs)
264-
else:
265-
# Update an existing reservation
266-
267-
# If an image is specified via --file, --location or
268-
# --copy-from let the API handle it
269-
image = image_client.images.update(image.id, **kwargs)
246+
image = image_client.images.create(**kwargs)
270247
finally:
271248
# Clean up open files - make sure data isn't a string
272249
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
@@ -561,6 +538,51 @@ def get_parser(self, prog_name):
561538
help="Set a property on this image "
562539
"(repeat option to set multiple properties)",
563540
)
541+
parser.add_argument(
542+
"--store",
543+
metavar="<store>",
544+
help="Upload image to this store",
545+
)
546+
parser.add_argument(
547+
"--location",
548+
metavar="<image-url>",
549+
help="Download image from an existing URL",
550+
)
551+
parser.add_argument(
552+
"--copy-from",
553+
metavar="<image-url>",
554+
help="Copy image from the data store (similar to --location)",
555+
)
556+
parser.add_argument(
557+
"--file",
558+
metavar="<file>",
559+
help="Upload image from local file",
560+
)
561+
parser.add_argument(
562+
"--volume",
563+
metavar="<volume>",
564+
help="Create image from a volume",
565+
)
566+
parser.add_argument(
567+
"--force",
568+
dest='force',
569+
action='store_true',
570+
default=False,
571+
help="Force image change if volume is in use "
572+
"(only meaningful with --volume)",
573+
)
574+
parser.add_argument(
575+
"--stdin",
576+
dest='stdin',
577+
action='store_true',
578+
default=False,
579+
help="Read image data from standard input",
580+
)
581+
parser.add_argument(
582+
"--checksum",
583+
metavar="<checksum>",
584+
help="Image hash used for verification",
585+
)
564586
return parser
565587

566588
def take_action(self, parsed_args):
@@ -569,7 +591,8 @@ def take_action(self, parsed_args):
569591

570592
kwargs = {}
571593
copy_attrs = ('name', 'owner', 'min_disk', 'min_ram', 'properties',
572-
'container_format', 'disk_format', 'size')
594+
'container_format', 'disk_format', 'size', 'store',
595+
'location', 'copy_from', 'volume', 'force', 'checksum')
573596
for attr in copy_attrs:
574597
if attr in parsed_args:
575598
val = getattr(parsed_args, attr, None)
@@ -592,20 +615,63 @@ def take_action(self, parsed_args):
592615
if parsed_args.private:
593616
kwargs['is_public'] = False
594617

595-
if not kwargs:
596-
self.log.warning('no arguments specified')
597-
return {}, {}
598-
599-
image = utils.find_resource(
600-
image_client.images,
601-
parsed_args.image,
602-
)
603-
604-
if image.properties and parsed_args.properties:
605-
image.properties.update(kwargs['properties'])
606-
kwargs['properties'] = image.properties
618+
# Wrap the call to catch exceptions in order to close files
619+
try:
620+
image = utils.find_resource(
621+
image_client.images,
622+
parsed_args.image,
623+
)
607624

608-
image = image_client.images.update(image.id, **kwargs)
625+
if not parsed_args.location and not parsed_args.copy_from:
626+
if parsed_args.volume:
627+
volume_client = self.app.client_manager.volume
628+
source_volume = utils.find_resource(
629+
volume_client.volumes,
630+
parsed_args.volume,
631+
)
632+
response, body = volume_client.volumes.upload_to_image(
633+
source_volume.id,
634+
parsed_args.force,
635+
parsed_args.image,
636+
(parsed_args.container_format
637+
if parsed_args.container_format
638+
else image.container_format),
639+
(parsed_args.disk_format
640+
if parsed_args.disk_format
641+
else image.disk_format),
642+
)
643+
info = body['os-volume_upload_image']
644+
elif parsed_args.file:
645+
# Send an open file handle to glanceclient so it will
646+
# do a chunked transfer
647+
kwargs["data"] = io.open(parsed_args.file, "rb")
648+
else:
649+
# Read file from stdin
650+
if sys.stdin.isatty() is not True:
651+
if parsed_args.stdin:
652+
if msvcrt:
653+
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
654+
# Send an open file handle to glanceclient so it
655+
# will do a chunked transfer
656+
kwargs["data"] = sys.stdin
657+
else:
658+
self.log.warning('Use --stdin to enable read image'
659+
' data from standard input')
660+
661+
if image.properties and parsed_args.properties:
662+
image.properties.update(kwargs['properties'])
663+
kwargs['properties'] = image.properties
664+
665+
if not kwargs:
666+
self.log.warning('no arguments specified')
667+
return {}, {}
668+
669+
image = image_client.images.update(image.id, **kwargs)
670+
finally:
671+
# Clean up open files - make sure data isn't a string
672+
if ('data' in kwargs and hasattr(kwargs['data'], 'close') and
673+
kwargs['data'] != sys.stdin):
674+
kwargs['data'].close()
609675

610676
info = {}
611677
info.update(image._info)

0 commit comments

Comments
 (0)