26
from glanceclient import exc
20
27
from glanceclient.common import utils
21
28
import glanceclient.v1.images
23
30
#NOTE(bcwaldon): import deprecated cli functions
24
31
from glanceclient.v1.legacy_shell import *
33
CONTAINER_FORMATS = 'Acceptable formats: ami, ari, aki, bare, and ovf.'
34
DISK_FORMATS = ('Acceptable formats: ami, ari, aki, vhd, vmdk, raw, '
35
'qcow2, vdi, and iso.')
27
38
@utils.arg('--name', metavar='<NAME>',
28
39
help='Filter images to those that have this name.')
29
40
@utils.arg('--status', metavar='<STATUS>',
30
41
help='Filter images to those that have this status.')
31
42
@utils.arg('--container-format', metavar='<CONTAINER_FORMAT>',
32
help='Filter images to those that have this container format.')
43
help='Filter images to those that have this container format. '
33
45
@utils.arg('--disk-format', metavar='<DISK_FORMAT>',
34
help='Filter images to those that have this disk format.')
46
help='Filter images to those that have this disk format. '
35
48
@utils.arg('--size-min', metavar='<SIZE>',
36
49
help='Filter images to those with a size greater than this.')
37
50
@utils.arg('--size-max', metavar='<SIZE>',
41
54
action='append', dest='properties', default=[])
42
55
@utils.arg('--page-size', metavar='<SIZE>', default=None, type=int,
43
56
help='Number of images to request in each paginated request.')
57
@utils.arg('--human-readable', action='store_true', default=False,
58
help='Print image size in a human-friendly format.')
44
59
def do_image_list(gc, args):
45
60
"""List images you can access."""
46
61
filter_keys = ['name', 'status', 'container_format', 'disk_format',
58
73
images = gc.images.list(**kwargs)
59
74
columns = ['ID', 'Name', 'Disk Format', 'Container Format',
77
if args.human_readable:
78
def convert_size(image):
79
image.size = utils.make_size_human_readable(image.size)
82
images = (convert_size(image) for image in images)
61
84
utils.print_list(images, columns)
64
def _image_show(image):
87
def _image_show(image, human_readable=False):
65
88
# Flatten image properties dict for display
66
89
info = copy.deepcopy(image._info)
91
info['size'] = utils.make_size_human_readable(info['size'])
67
92
for (k, v) in info.pop('properties').iteritems():
68
93
info['Property \'%s\'' % k] = v
70
95
utils.print_dict(info)
98
def _set_data_field(fields, args):
99
if 'location' not in fields and 'copy_from' not in fields:
101
fields['data'] = open(args.file, 'rb')
103
# We distinguish between cases where image data is pipelined:
104
# (1) glance ... < /tmp/file or cat /tmp/file | glance ...
105
# and cases where no image data is provided:
107
if (sys.stdin.isatty() is not True):
108
# Our input is from stdin, and we are part of
109
# a pipeline, so data may be present. (We are of
112
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
113
fields['data'] = sys.stdin
115
# We are of type (2) above, no image data supplied
116
fields['data'] = None
73
119
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to describe.')
120
@utils.arg('--human-readable', action='store_true', default=False,
121
help='Print image size in a human-friendly format.')
74
122
def do_image_show(gc, args):
75
123
"""Describe a specific image."""
76
124
image = gc.images.get(args.id)
125
_image_show(image, args.human_readable)
80
128
@utils.arg('--file', metavar='<FILE>',
92
140
help='ID of image to reserve.')
93
141
@utils.arg('--name', metavar='<NAME>',
94
142
help='Name of image.')
95
@utils.arg('--disk-format', metavar='<CONTAINER_FORMAT>',
96
help='Disk format of image.')
97
@utils.arg('--container-format', metavar='<DISK_FORMAT>',
98
help='Container format of image.')
143
@utils.arg('--disk-format', metavar='<DISK_FORMAT>',
144
help='Disk format of image. ' + DISK_FORMATS)
145
@utils.arg('--container-format', metavar='<CONTAINER_FORMAT>',
146
help='Container format of image. ' + CONTAINER_FORMATS)
99
147
@utils.arg('--owner', metavar='<TENANT_ID>',
100
148
help='Tenant who should own image.')
101
149
@utils.arg('--size', metavar='<SIZE>',
115
163
' creation. Alternatively, images can be passed to the client'
117
165
@utils.arg('--checksum', metavar='<CHECKSUM>',
118
help='Hash of image data used Glance can use for verification.')
166
help=('Hash of image data used Glance can use for verification.'
167
' Provide a md5 checksum here.'))
119
168
@utils.arg('--copy-from', metavar='<IMAGE_URL>',
120
169
help=('Similar to \'--location\' in usage, but this indicates that'
121
170
' the Glance server should immediately copy the data and'
124
173
# to use --is-public
125
174
@utils.arg('--public', action='store_true', default=False,
126
175
help=argparse.SUPPRESS)
127
@utils.arg('--is-public', type=utils.string_to_bool,
176
@utils.arg('--is-public', type=utils.string_to_bool, metavar='[True|False]',
128
177
help='Make image accessible to the public.')
129
@utils.arg('--is-protected', type=utils.string_to_bool,
178
@utils.arg('--is-protected', type=utils.string_to_bool, metavar='[True|False]',
130
179
help='Prevent image from being deleted.')
131
180
@utils.arg('--property', metavar="<key=value>", action='append', default=[],
132
181
help=("Arbitrary property to associate with image. "
133
182
"May be used multiple times."))
183
@utils.arg('--human-readable', action='store_true', default=False,
184
help='Print image size in a human-friendly format.')
134
185
def do_image_create(gc, args):
135
186
"""Create a new image."""
136
187
# Filter out None values
151
202
CREATE_PARAMS = glanceclient.v1.images.CREATE_PARAMS
152
203
fields = dict(filter(lambda x: x[0] in CREATE_PARAMS, fields.items()))
154
if 'location' not in fields and 'copy_from' not in fields:
156
fields['data'] = open(args.file, 'r')
158
fields['data'] = sys.stdin
205
_set_data_field(fields, args)
160
207
image = gc.images.create(**fields)
208
_image_show(image, args.human_readable)
164
211
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to modify.')
165
212
@utils.arg('--name', metavar='<NAME>',
166
213
help='Name of image.')
167
214
@utils.arg('--disk-format', metavar='<CONTAINER_FORMAT>',
168
help='Disk format of image.')
215
help='Disk format of image. ' + CONTAINER_FORMATS)
169
216
@utils.arg('--container-format', metavar='<DISK_FORMAT>',
170
help='Container format of image.')
217
help='Container format of image. ' + DISK_FORMATS)
171
218
@utils.arg('--owner', metavar='<TENANT_ID>',
172
219
help='Tenant who should own image.')
173
220
@utils.arg('--size', metavar='<SIZE>',
191
238
help=('Similar to \'--location\' in usage, but this indicates that'
192
239
' the Glance server should immediately copy the data and'
193
240
' store it in its configured image store.'))
194
@utils.arg('--is-public', type=utils.string_to_bool,
241
@utils.arg('--is-public', type=utils.string_to_bool, metavar='[True|False]',
195
242
help='Make image accessible to the public.')
196
@utils.arg('--is-protected', type=utils.string_to_bool,
243
@utils.arg('--is-protected', type=utils.string_to_bool, metavar='[True|False]',
197
244
help='Prevent image from being deleted.')
198
245
@utils.arg('--property', metavar="<key=value>", action='append', default=[],
199
246
help=("Arbitrary property to associate with image. "
202
249
help=("If this flag is present, delete all image properties "
203
250
"not explicitly set in the update request. Otherwise, "
204
251
"those properties not referenced are preserved."))
252
@utils.arg('--human-readable', action='store_true', default=False,
253
help='Print image size in a human-friendly format.')
205
254
def do_image_update(gc, args):
206
255
"""Update a specific image."""
207
256
# Filter out None values
222
271
UPDATE_PARAMS = glanceclient.v1.images.UPDATE_PARAMS
223
272
fields = dict(filter(lambda x: x[0] in UPDATE_PARAMS, fields.items()))
225
if 'location' not in fields and 'copy_from' not in fields:
227
fields['data'] = open(args.file, 'r')
229
fields['data'] = sys.stdin
274
_set_data_field(fields, args)
231
276
image = gc.images.update(image_id, purge_props=args.purge_props, **fields)
235
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to delete.')
277
_image_show(image, args.human_readable)
280
@utils.arg('id', metavar='<IMAGE_ID>', nargs='+',
281
help='ID of image(s) to delete.')
236
282
def do_image_delete(gc, args):
237
"""Delete a specific image."""
238
gc.images.delete(args.id)
283
"""Delete specified image(s)."""
284
for image in args.id:
287
print 'Requesting image delete for %s ...' % image,
289
gc.images.delete(image)
294
except exc.HTTPException, e:
297
print '%s: Unable to delete image %s' % (e, image)
241
300
@utils.arg('--image-id', metavar='<IMAGE_ID>',
274
333
@utils.arg('image_id', metavar='<IMAGE_ID>',
275
help='Image to add member to.')
334
help='Image from which to remove member')
276
335
@utils.arg('tenant_id', metavar='<TENANT_ID>',
277
help='Tenant to add as member')
336
help='Tenant to remove as member')
278
337
def do_member_delete(gc, args):
279
338
"""Remove a shared image from a tenant."""
280
if not options.dry_run:
281
340
gc.image_members.delete(args.image_id, args.tenant_id)
283
342
print "Dry run. We would have done the following:"
284
print ('Remove "%(member_id)s" from the member list of image '
285
'"%(image_id)s"' % locals())
343
print ('Remove "%s" from the member list of image '
344
'"%s"' % (args.tenant_id, args.image_id))