From 6da61e03f03f9a65cbc9203ea9fdd47c64990184 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 9 Apr 2013 13:59:12 -0500 Subject: [PATCH] Adds image `create` and `delete` functionality. We use the V1 API for `create` since it does not yet exist in the V2 API in glanceclient. For blueprint glance-client. Change-Id: Ifa819c14f6a013f4530d16247a671e5a1c740a28 --- openstackclient/image/client.py | 2 +- openstackclient/image/v1/__init__.py | 14 +++ openstackclient/image/v1/image.py | 156 +++++++++++++++++++++++++++ openstackclient/image/v2/image.py | 23 +++- setup.py | 4 + tools/pip-requires | 2 +- 6 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 openstackclient/image/v1/__init__.py create mode 100644 openstackclient/image/v1/image.py diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index 8a63da9c7..371605692 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -22,7 +22,7 @@ LOG = logging.getLogger(__name__) API_NAME = "image" API_VERSIONS = { - "1.0": "glanceclient.v2.client.Client", + "1": "glanceclient.v1.client.Client", "2": "glanceclient.v2.client.Client" } diff --git a/openstackclient/image/v1/__init__.py b/openstackclient/image/v1/__init__.py new file mode 100644 index 000000000..ebf59b327 --- /dev/null +++ b/openstackclient/image/v1/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2013 OpenStack, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py new file mode 100644 index 000000000..fa566bd91 --- /dev/null +++ b/openstackclient/image/v1/image.py @@ -0,0 +1,156 @@ +# Copyright 2013 OpenStack, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Image V1 Action Implementations""" + +import logging +import os +import sys + +if os.name == "nt": + import msvcrt +else: + msvcrt = None + +from cliff import show + + +class CreateImage(show.ShowOne): + """Create image command""" + + api = "image" + log = logging.getLogger(__name__ + ".CreateImage") + + def get_parser(self, prog_name): + parser = super(CreateImage, self).get_parser(prog_name) + parser.add_argument( + "name", + metavar="", + help="Name of image.") + parser.add_argument( + "--disk_format", + default="raw", + metavar="", + help="Disk format of image.") + parser.add_argument( + "--id", + metavar="", + help="ID of image to reserve.") + parser.add_argument( + "--store", + metavar="", + help="Store to upload image to.") + parser.add_argument( + "--container-format", + default="bare", + metavar="", + help="Container format of image.") + parser.add_argument( + "--owner", + metavar="", + help="Owner of the image.") + parser.add_argument( + "--size", + metavar="", + help="Size of image in bytes. Only used with --location and" + " --copy-from.") + parser.add_argument( + "--min-disk", + metavar="", + help="Minimum size of disk needed to boot image in gigabytes.") + parser.add_argument( + "--min-ram", + metavar="", + help="Minimum amount of ram needed to boot image in megabytes.") + parser.add_argument( + "--location", + metavar="", + help="URL where the data for this image already resides.") + parser.add_argument( + "--file", + metavar="", + help="Local file that contains disk image.") + parser.add_argument( + "--checksum", + metavar="", + help="Hash of image data used for verification.") + parser.add_argument( + "--copy-from", + metavar="", + help="Similar to --location, but this indicates that the image" + " should immediately be copied from the data store.") + parser.add_argument( + "--metadata", + metavar="", + default=[], + action="append", + help="Arbitrary metadata to associate with image.") + protected_group = parser.add_mutually_exclusive_group() + protected_group.add_argument( + "--protected", + dest="protected", + action="store_true", + help="Prevent image from being deleted (default: False).") + protected_group.add_argument( + "--unprotected", + dest="protected", + action="store_false", + default=False, + help="Allow images to be deleted (default: True).") + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--public", + dest="is_public", + action="store_true", + default=True, + help="Image is accessible to the public (default).") + public_group.add_argument( + "--private", + dest="is_public", + action="store_false", + help="Image is inaccessible to the public.") + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + # NOTE(jk0): Since create() takes kwargs, it's easiest to just make a + # copy of parsed_args and remove what we don't need. + args = vars(parsed_args) + args = dict(filter(lambda x: x[1] is not None, args.items())) + args.pop("columns") + args.pop("formatter") + args.pop("prefix") + args.pop("variables") + + args["properties"] = {} + for _metadata in args.pop("metadata"): + key, value = _metadata.split("=", 1) + args["properties"][key] = value + + if "location" not in args and "copy_from" not in args: + if "file" in args: + args["data"] = open(args.pop("file"), "rb") + else: + args["data"] = None + if sys.stdin.isatty() is not True: + if msvcrt: + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + args["data"] = sys.stdin + + image_client = self.app.client_manager.image + data = image_client.images.create(**args)._info.copy() + + return zip(*sorted(data.iteritems())) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index ce53df209..3b9b349d3 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -13,7 +13,7 @@ # under the License. # -"""Image Action Implementations""" +"""Image V2 Action Implementations""" import logging @@ -25,6 +25,27 @@ from glanceclient.common import utils as gc_utils from openstackclient.common import utils +class DeleteImage(command.Command): + """Delete image command""" + + api = "image" + log = logging.getLogger(__name__ + ".DeleteImage") + + def get_parser(self, prog_name): + parser = super(DeleteImage, self).get_parser(prog_name) + parser.add_argument( + "id", + metavar="", + help="ID of image to delete.") + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + image_client = self.app.client_manager.image + image_client.images.delete(parsed_args.id) + + class ListImage(lister.Lister): """List image command""" diff --git a/setup.py b/setup.py index 9666cf95f..473f30042 100644 --- a/setup.py +++ b/setup.py @@ -173,7 +173,11 @@ setuptools.setup( 'set_user=openstackclient.identity.v3.user:SetUser', 'show_user=openstackclient.identity.v3.user:ShowUser', ], + 'openstack.image.v1': [ + 'create_image=openstackclient.image.v1.image:CreateImage', + ], 'openstack.image.v2': [ + 'delete_image=openstackclient.image.v2.image:DeleteImage', 'list_image=openstackclient.image.v2.image:ListImage', 'save_image=openstackclient.image.v2.image:SaveImage', 'show_image=openstackclient.image.v2.image:ShowImage', diff --git a/tools/pip-requires b/tools/pip-requires index 720413ed7..9b71d402b 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -1,7 +1,7 @@ cliff keyring pycrypto -python-glanceclient>=0.5.1 +python-glanceclient>=0.9.0,<2 python-keystoneclient>=0.2,<1.0 python-novaclient>=2 python-cinderclient>=1