From 8faabb3bbaa199cce8a52d6e6ed40b15e4a3a000 Mon Sep 17 00:00:00 2001 From: NiallBunting Date: Tue, 22 Sep 2015 09:59:30 +0000 Subject: [PATCH] Glance `image set` Resolve Fracturing Currently `image set` uses the new api, where other parts of osc the old api is used. This deprecates the v2 api in favour of the v1 to maintain the same commands across osc. However the functionality now remains there as people could now be using this functionality. This also adds the --unprotected argument, as in the previous version if --protected was not supplied it would just make the argument --unprotected without the users explicit consent. The patch also fixes the documentation for image set as it was outdated. Change-Id: I990d20332c80165102badef7ac94ddbeb7824950 Closes-Bug: 1498092 --- doc/source/command-objects/image.rst | 60 +++++++++++++++++- openstackclient/image/v2/image.py | 65 ++++++++++++++++++-- openstackclient/tests/image/v2/test_image.py | 3 +- 3 files changed, 119 insertions(+), 9 deletions(-) diff --git a/doc/source/command-objects/image.rst b/doc/source/command-objects/image.rst index 257414242..b62f6e0b0 100644 --- a/doc/source/command-objects/image.rst +++ b/doc/source/command-objects/image.rst @@ -193,7 +193,7 @@ Save an image locally image set --------- -*Only supported for Image v1* +*Image v1, v2* Set image properties @@ -252,6 +252,8 @@ Set image properties Size of image data (in bytes) + *Image version 1 only.* + .. option:: --protected Prevent image from being deleted @@ -272,38 +274,94 @@ Set image properties Upload image to this store + *Image version 1 only.* + .. option:: --location Download image from an existing URL + *Image version 1 only.* + .. option:: --copy-from Copy image from the data store (similar to --location) + *Image version 1 only.* + .. option:: --file Upload image from local file + *Image version 1 only.* + .. option:: --volume Update image with a volume + *Image version 1 only.* + .. option:: --force Force image update if volume is in use (only meaningful with --volume) + *Image version 1 only.* + .. option:: --checksum Image hash used for verification + *Image version 1 only.* + .. option:: --stdin Allow to read image data from standard input + *Image version 1 only.* + .. option:: --property Set a property on this image (repeat for multiple values) + *Image version 1 only.* + +.. option:: --architecture + + Operating system Architecture + + .. versionadded:: 2 + +.. option:: --ramdisk-id + + ID of image stored in Glance that should be used as + the ramdisk when booting an AMI-style image + + .. versionadded:: 2 + +.. option:: --os-distro + + Common name of operating system distribution + + .. versionadded:: 2 + +.. option:: --os-version + + Operating system version as specified by the distributor + + .. versionadded:: 2 + +.. option:: --kernel-id + + ID of image in Glance that should be used as the + kernel when booting an AMI-style image + + .. versionadded:: 2 + +.. option:: --instance-uuid + + ID of instance used to create this image + + .. versionadded:: 2 + .. describe:: Image to modify (name or ID) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 4c019db64..a8a0c1090 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -25,6 +25,7 @@ from cliff import show from glanceclient.common import utils as gc_utils from openstackclient.api import utils as api_utils +from openstackclient.common import exceptions from openstackclient.common import parseractions from openstackclient.common import utils from openstackclient.identity import common @@ -336,9 +337,22 @@ class SetImage(show.ShowOne): """Set image properties""" log = logging.getLogger(__name__ + ".SetImage") + deadopts = ('size', 'store', 'location', 'copy-from', 'checksum') def get_parser(self, prog_name): parser = super(SetImage, self).get_parser(prog_name) + # TODO(bunting): There are additional arguments that v1 supported + # --size - does not exist in v2 + # --store - does not exist in v2 + # --location - maybe location add? + # --copy-from - does not exist in v2 + # --file - should be able to upload file + # --volume - needs adding + # --force - needs adding + # --checksum - maybe could be done client side + # --stdin - could be implemented + # --property - needs adding + # --tags - needs adding parser.add_argument( "image", metavar="", @@ -354,12 +368,28 @@ class SetImage(show.ShowOne): metavar="", help="Operating system Architecture" ) - parser.add_argument( + protected_group = parser.add_mutually_exclusive_group() + protected_group.add_argument( "--protected", - dest="protected", action="store_true", help="Prevent image from being deleted" ) + protected_group.add_argument( + "--unprotected", + action="store_true", + help="Allow image to be deleted (default)" + ) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--public", + action="store_true", + help="Image is accessible to the public", + ) + public_group.add_argument( + "--private", + action="store_true", + help="Image is inaccessible to the public (default)", + ) parser.add_argument( "--instance-uuid", metavar="", @@ -372,12 +402,11 @@ class SetImage(show.ShowOne): help="Minimum disk size needed to boot image, in gigabytes" ) visibility_choices = ["public", "private"] - parser.add_argument( + public_group.add_argument( "--visibility", metavar="", choices=visibility_choices, - help="Scope of image accessibility. Valid values: %s" - % visibility_choices + help=argparse.SUPPRESS ) help_msg = ("ID of image in Glance that should be used as the kernel" " when booting an AMI-style image") @@ -432,12 +461,25 @@ class SetImage(show.ShowOne): choices=container_choices, help=help_msg ) + for deadopt in self.deadopts: + parser.add_argument( + "--%s" % deadopt, + metavar="<%s>" % deadopt, + dest=deadopt.replace('-', '_'), + help=argparse.SUPPRESS + ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) image_client = self.app.client_manager.image + for deadopt in self.deadopts: + if getattr(parsed_args, deadopt.replace('-', '_'), None): + raise exceptions.CommandError( + "ERROR: --%s was given, which is an Image v1 option" + " that is no longer supported in Image v2" % deadopt) + kwargs = {} copy_attrs = ('architecture', 'container_format', 'disk_format', 'file', 'kernel_id', 'locations', 'name', @@ -451,10 +493,21 @@ class SetImage(show.ShowOne): # Only include a value in kwargs for attributes that are # actually present on the command line kwargs[attr] = val + + # Handle exclusive booleans with care + # Avoid including attributes in kwargs if an option is not + # present on the command line. These exclusive booleans are not + # a single value for the pair of options because the default must be + # to do nothing when no options are present as opposed to always + # setting a default. if parsed_args.protected: kwargs['protected'] = True - else: + if parsed_args.unprotected: kwargs['protected'] = False + if parsed_args.public: + kwargs['visibility'] = 'public' + if parsed_args.private: + kwargs['visibility'] = 'private' if not kwargs: self.log.warning("No arguments specified") diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py index bfb947651..0c4aad276 100644 --- a/openstackclient/tests/image/v2/test_image.py +++ b/openstackclient/tests/image/v2/test_image.py @@ -527,8 +527,7 @@ class TestImageSet(TestImage): 'name': 'new-name', 'owner': 'new-owner', 'min_disk': 2, - 'min_ram': 4, - 'protected': False + 'min_ram': 4 } # ImageManager.update(image, **kwargs) self.images_mock.update.assert_called_with(