307 lines
12 KiB
Python
Raw Normal View History

# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# 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.
from glanceclient.common import progressbar
from glanceclient.common import utils
from glanceclient import exc
import json
import os
from os.path import expanduser
IMAGE_SCHEMA = None
def get_image_schema():
global IMAGE_SCHEMA
if IMAGE_SCHEMA is None:
schema_path = expanduser("~/.glanceclient/image_schema.json")
if os.path.exists(schema_path) and os.path.isfile(schema_path):
with open(schema_path, "r") as f:
schema_raw = f.read()
IMAGE_SCHEMA = json.loads(schema_raw)
return IMAGE_SCHEMA
@utils.schema_args(get_image_schema)
@utils.arg('--property', metavar="<key=value>", action='append',
default=[], help=('Arbitrary property to associate with image.'
' May be used multiple times.'))
def do_image_create(gc, args):
"""Create a new image."""
schema = gc.schemas.get("image")
_args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()]
fields = dict(filter(lambda x: x[1] is not None and
(x[0] == 'property' or
schema.is_core_property(x[0])),
_args))
raw_properties = fields.pop('property', [])
for datum in raw_properties:
key, value = datum.split('=', 1)
fields[key] = value
image = gc.images.create(**fields)
Add support for location parameters in v2 commands Currently glanceclient's v2 commands don't support modification operations on an image's location attribute - the argparse specification for the location attribute of the image-update command causes the image id argument to be included in list of locations and so the command parsing fails (because it causes the image id to appear to be missing). Furthermore even if the 'locations' argument were to be accepted by argparse (e.g. by changing the argument specs and using --id to specify the image id) the command would still fail because the arguments are passed directly to the schema which expects the value of the 'locations' argument to be a valid dictionary (there is nobody to convert the argument string to a python dictionary that the schema expects). This commit adds the following location related commands to glanceclient: --location-add: Add a new location to the list of image locations. --location-delete: Remove an existing location from the list of image locations. --location-update: Update the metadata of existing location. The glanceclient.v2.images.Controller class has been agumented with three new methods to support the commands listed above: - add_location - delete_locations - update_location The server has not been modified, i.e. all location related API requests are passed to the server via HTTP PATCH requests and handled by the server's image update function. The v2 'image' and 'shell' related tests have also been supplemented. Note that in order to use these options the server must be first configured to expose location related info to the clients (i.e. 'show_multiple_locations' must be set to 'True"). I also added a mailmap entry for myself. DocImpact Closes-bug: #1271452 Co-Author: David Koo (koofoss) <david.koo@huawei.com> Change-Id: Id1f320af05d9344645836359758e4aa227aafc69
2014-02-11 11:06:02 +08:00
utils.print_image(image)
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to update.')
Add support for location parameters in v2 commands Currently glanceclient's v2 commands don't support modification operations on an image's location attribute - the argparse specification for the location attribute of the image-update command causes the image id argument to be included in list of locations and so the command parsing fails (because it causes the image id to appear to be missing). Furthermore even if the 'locations' argument were to be accepted by argparse (e.g. by changing the argument specs and using --id to specify the image id) the command would still fail because the arguments are passed directly to the schema which expects the value of the 'locations' argument to be a valid dictionary (there is nobody to convert the argument string to a python dictionary that the schema expects). This commit adds the following location related commands to glanceclient: --location-add: Add a new location to the list of image locations. --location-delete: Remove an existing location from the list of image locations. --location-update: Update the metadata of existing location. The glanceclient.v2.images.Controller class has been agumented with three new methods to support the commands listed above: - add_location - delete_locations - update_location The server has not been modified, i.e. all location related API requests are passed to the server via HTTP PATCH requests and handled by the server's image update function. The v2 'image' and 'shell' related tests have also been supplemented. Note that in order to use these options the server must be first configured to expose location related info to the clients (i.e. 'show_multiple_locations' must be set to 'True"). I also added a mailmap entry for myself. DocImpact Closes-bug: #1271452 Co-Author: David Koo (koofoss) <david.koo@huawei.com> Change-Id: Id1f320af05d9344645836359758e4aa227aafc69
2014-02-11 11:06:02 +08:00
@utils.schema_args(get_image_schema, omit=['id', 'locations'])
@utils.arg('--property', metavar="<key=value>", action='append',
default=[], help=('Arbitrary property to associate with image.'
' May be used multiple times.'))
@utils.arg('--remove-property', metavar="key", action='append', default=[],
help="Name of arbitrary property to remove from the image.")
def do_image_update(gc, args):
"""Update an existing image."""
schema = gc.schemas.get("image")
_args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()]
fields = dict(filter(lambda x: x[1] is not None and
(x[0] in ['property', 'remove_property'] or
schema.is_core_property(x[0])),
_args))
raw_properties = fields.pop('property', [])
for datum in raw_properties:
key, value = datum.split('=', 1)
fields[key] = value
remove_properties = fields.pop('remove_property', None)
image_id = fields.pop('id')
image = gc.images.update(image_id, remove_properties, **fields)
Add support for location parameters in v2 commands Currently glanceclient's v2 commands don't support modification operations on an image's location attribute - the argparse specification for the location attribute of the image-update command causes the image id argument to be included in list of locations and so the command parsing fails (because it causes the image id to appear to be missing). Furthermore even if the 'locations' argument were to be accepted by argparse (e.g. by changing the argument specs and using --id to specify the image id) the command would still fail because the arguments are passed directly to the schema which expects the value of the 'locations' argument to be a valid dictionary (there is nobody to convert the argument string to a python dictionary that the schema expects). This commit adds the following location related commands to glanceclient: --location-add: Add a new location to the list of image locations. --location-delete: Remove an existing location from the list of image locations. --location-update: Update the metadata of existing location. The glanceclient.v2.images.Controller class has been agumented with three new methods to support the commands listed above: - add_location - delete_locations - update_location The server has not been modified, i.e. all location related API requests are passed to the server via HTTP PATCH requests and handled by the server's image update function. The v2 'image' and 'shell' related tests have also been supplemented. Note that in order to use these options the server must be first configured to expose location related info to the clients (i.e. 'show_multiple_locations' must be set to 'True"). I also added a mailmap entry for myself. DocImpact Closes-bug: #1271452 Co-Author: David Koo (koofoss) <david.koo@huawei.com> Change-Id: Id1f320af05d9344645836359758e4aa227aafc69
2014-02-11 11:06:02 +08:00
utils.print_image(image)
@utils.arg('--page-size', metavar='<SIZE>', default=None, type=int,
help='Number of images to request in each paginated request.')
@utils.arg('--visibility', metavar='<VISIBILITY>',
help='The visibility of the images to display.')
@utils.arg('--member-status', metavar='<MEMBER_STATUS>',
help='The status of images to display.')
@utils.arg('--owner', metavar='<OWNER>',
help='Display images owned by <OWNER>.')
@utils.arg('--checksum', metavar='<CHECKSUM>',
help='Displays images that match the checksum.')
@utils.arg('--tag', metavar='<TAG>', action='append',
help="Filter images by a user-defined tag.")
def do_image_list(gc, args):
"""List images you can access."""
filter_keys = ['visibility', 'member_status', 'owner', 'checksum', 'tag']
filter_items = [(key, getattr(args, key)) for key in filter_keys]
filters = dict([item for item in filter_items if item[1] is not None])
kwargs = {'filters': filters}
if args.page_size is not None:
kwargs['page_size'] = args.page_size
images = gc.images.list(**kwargs)
columns = ['ID', 'Name']
utils.print_list(images, columns)
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to describe.')
@utils.arg('--max-column-width', metavar='<integer>', default=80,
help='The max column width of the printed table.')
def do_image_show(gc, args):
"""Describe a specific image."""
image = gc.images.get(args.id)
ignore = ['self', 'access', 'file', 'schema']
Add support for location parameters in v2 commands Currently glanceclient's v2 commands don't support modification operations on an image's location attribute - the argparse specification for the location attribute of the image-update command causes the image id argument to be included in list of locations and so the command parsing fails (because it causes the image id to appear to be missing). Furthermore even if the 'locations' argument were to be accepted by argparse (e.g. by changing the argument specs and using --id to specify the image id) the command would still fail because the arguments are passed directly to the schema which expects the value of the 'locations' argument to be a valid dictionary (there is nobody to convert the argument string to a python dictionary that the schema expects). This commit adds the following location related commands to glanceclient: --location-add: Add a new location to the list of image locations. --location-delete: Remove an existing location from the list of image locations. --location-update: Update the metadata of existing location. The glanceclient.v2.images.Controller class has been agumented with three new methods to support the commands listed above: - add_location - delete_locations - update_location The server has not been modified, i.e. all location related API requests are passed to the server via HTTP PATCH requests and handled by the server's image update function. The v2 'image' and 'shell' related tests have also been supplemented. Note that in order to use these options the server must be first configured to expose location related info to the clients (i.e. 'show_multiple_locations' must be set to 'True"). I also added a mailmap entry for myself. DocImpact Closes-bug: #1271452 Co-Author: David Koo (koofoss) <david.koo@huawei.com> Change-Id: Id1f320af05d9344645836359758e4aa227aafc69
2014-02-11 11:06:02 +08:00
utils.print_image(image, int(args.max_column_width))
@utils.arg('--image-id', metavar='<IMAGE_ID>', required=True,
help='Image to display members of.')
def do_member_list(gc, args):
"""Describe sharing permissions by image."""
members = gc.image_members.list(args.image_id)
columns = ['Image ID', 'Member ID', 'Status']
utils.print_list(members, columns)
@utils.arg('image_id', metavar='<IMAGE_ID>',
help='Image from which to remove member.')
@utils.arg('member_id', metavar='<MEMBER_ID>',
help='Tenant to remove as member.')
def do_member_delete(gc, args):
"""Delete image member."""
if not (args.image_id and args.member_id):
utils.exit('Unable to delete member. Specify image_id and member_id')
else:
gc.image_members.delete(args.image_id, args.member_id)
@utils.arg('image_id', metavar='<IMAGE_ID>',
help='Image from which to update member.')
@utils.arg('member_id', metavar='<MEMBER_ID>',
help='Tenant to update.')
@utils.arg('member_status', metavar='<MEMBER_STATUS>',
help='Updated status of member.')
def do_member_update(gc, args):
"""Update the status of a member for a given image."""
if not (args.image_id and args.member_id and args.member_status):
utils.exit('Unable to update member. Specify image_id, member_id and'
' member_status')
else:
member = gc.image_members.update(args.image_id, args.member_id,
args.member_status)
member = [member]
columns = ['Image ID', 'Member ID', 'Status']
utils.print_list(member, columns)
@utils.arg('image_id', metavar='<IMAGE_ID>',
help='Image with which to create member.')
@utils.arg('member_id', metavar='<MEMBER_ID>',
help='Tenant to add as member.')
def do_member_create(gc, args):
"""Create member for a given image."""
if not (args.image_id and args.member_id):
utils.exit('Unable to create member. Specify image_id and member_id')
else:
member = gc.image_members.create(args.image_id, args.member_id)
member = [member]
columns = ['Image ID', 'Member ID', 'Status']
utils.print_list(member, columns)
@utils.arg('model', metavar='<MODEL>', help='Name of model to describe.')
def do_explain(gc, args):
"""Describe a specific model."""
try:
schema = gc.schemas.get(args.model)
except exc.HTTPNotFound:
utils.exit('Unable to find requested model \'%s\'' % args.model)
else:
formatters = {'Attribute': lambda m: m.name}
columns = ['Attribute', 'Description']
utils.print_list(schema.properties, columns, formatters)
@utils.arg('--file', metavar='<FILE>',
help='Local file to save downloaded image data to. '
'If this is not specified the image data will be '
'written to stdout.')
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to download.')
@utils.arg('--progress', action='store_true', default=False,
help='Show download progress bar.')
def do_image_download(gc, args):
"""Download a specific image."""
body = gc.images.data(args.id)
if args.progress:
body = progressbar.VerboseIteratorWrapper(body, len(body))
utils.save_image(body, args.file)
@utils.arg('--file', metavar='<FILE>',
help=('Local file that contains disk image to be uploaded.'
' Alternatively, images can be passed'
' to the client via stdin.'))
@utils.arg('--size', metavar='<IMAGE_SIZE>', type=int,
help='Size in bytes of image to be uploaded. Default is to get '
'size from provided data object but this is supported in case '
'where size cannot be inferred.',
default=None)
@utils.arg('--progress', action='store_true', default=False,
help='Show upload progress bar.')
@utils.arg('id', metavar='<IMAGE_ID>',
help='ID of image to upload data to.')
def do_image_upload(gc, args):
"""Upload data for a specific image."""
image_data = utils.get_data_file(args)
if args.progress:
filesize = utils.get_file_size(image_data)
image_data = progressbar.VerboseFileWrapper(image_data, filesize)
gc.images.upload(args.id, image_data, args.size)
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to delete.')
def do_image_delete(gc, args):
"""Delete specified image."""
gc.images.delete(args.id)
@utils.arg('image_id', metavar='<IMAGE_ID>',
help='Image to be updated with the given tag.')
@utils.arg('tag_value', metavar='<TAG_VALUE>',
help='Value of the tag.')
def do_image_tag_update(gc, args):
"""Update an image with the given tag."""
if not (args.image_id and args.tag_value):
utils.exit('Unable to update tag. Specify image_id and tag_value')
else:
gc.image_tags.update(args.image_id, args.tag_value)
image = gc.images.get(args.image_id)
image = [image]
columns = ['ID', 'Tags']
utils.print_list(image, columns)
@utils.arg('image_id', metavar='<IMAGE_ID>',
help='ID of the image from which to delete tag.')
@utils.arg('tag_value', metavar='<TAG_VALUE>',
help='Value of the tag.')
def do_image_tag_delete(gc, args):
"""Delete the tag associated with the given image."""
if not (args.image_id and args.tag_value):
utils.exit('Unable to delete tag. Specify image_id and tag_value')
else:
gc.image_tags.delete(args.image_id, args.tag_value)
Add support for location parameters in v2 commands Currently glanceclient's v2 commands don't support modification operations on an image's location attribute - the argparse specification for the location attribute of the image-update command causes the image id argument to be included in list of locations and so the command parsing fails (because it causes the image id to appear to be missing). Furthermore even if the 'locations' argument were to be accepted by argparse (e.g. by changing the argument specs and using --id to specify the image id) the command would still fail because the arguments are passed directly to the schema which expects the value of the 'locations' argument to be a valid dictionary (there is nobody to convert the argument string to a python dictionary that the schema expects). This commit adds the following location related commands to glanceclient: --location-add: Add a new location to the list of image locations. --location-delete: Remove an existing location from the list of image locations. --location-update: Update the metadata of existing location. The glanceclient.v2.images.Controller class has been agumented with three new methods to support the commands listed above: - add_location - delete_locations - update_location The server has not been modified, i.e. all location related API requests are passed to the server via HTTP PATCH requests and handled by the server's image update function. The v2 'image' and 'shell' related tests have also been supplemented. Note that in order to use these options the server must be first configured to expose location related info to the clients (i.e. 'show_multiple_locations' must be set to 'True"). I also added a mailmap entry for myself. DocImpact Closes-bug: #1271452 Co-Author: David Koo (koofoss) <david.koo@huawei.com> Change-Id: Id1f320af05d9344645836359758e4aa227aafc69
2014-02-11 11:06:02 +08:00
@utils.arg('--url', metavar='<URL>', required=True,
help='URL of location to add.')
@utils.arg('--metadata', metavar='<STRING>', default='{}',
help=('Metadata associated with the location. '
'Must be a valid JSON object (default: %(default)s)'))
@utils.arg('id', metavar='<ID>',
help='ID of image to which the location is to be added.')
def do_location_add(gc, args):
"""Add a location (and related metadata) to an image."""
try:
metadata = json.loads(args.metadata)
except ValueError:
utils.exit('Metadata is not a valid JSON object.')
else:
image = gc.images.add_location(args.id, args.url, metadata)
utils.print_dict(image)
@utils.arg('--url', metavar='<URL>', action='append', required=True,
help='URL of location to remove. May be used multiple times.')
@utils.arg('id', metavar='<ID>',
help='ID of image whose locations are to be removed.')
def do_location_delete(gc, args):
"""Remove locations (and related metadata) from an image."""
image = gc.images.delete_locations(args.id, set(args.url))
@utils.arg('--url', metavar='<URL>', required=True,
help='URL of location to update.')
@utils.arg('--metadata', metavar='<STRING>', default='{}',
help=('Metadata associated with the location. '
'Must be a valid JSON object (default: %(default)s)'))
@utils.arg('id', metavar='<ID>',
help='ID of image whose location is to be updated.')
def do_location_update(gc, args):
"""Update metadata of an image's location."""
try:
metadata = json.loads(args.metadata)
except ValueError:
utils.exit('Metadata is not a valid JSON object.')
else:
image = gc.images.update_location(args.id, args.url, metadata)
utils.print_dict(image)