Add --update functionality to image register ui
[kamaki] / kamaki / cli / commands / image_cli.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.command
33
34 from kamaki.cli import command
35 from kamaki.cli.command_tree import CommandTree
36 from kamaki.cli.errors import raiseCLIError
37 from kamaki.cli.utils import print_dict, print_items, bold
38 from kamaki.clients.image import ImageClient, ClientError
39 from kamaki.cli.argument import\
40     FlagArgument, ValueArgument, KeyValueArgument, IntArgument
41 from kamaki.cli.commands.cyclades_cli import _init_cyclades
42 from kamaki.cli.commands import _command_init
43
44
45 image_cmds = CommandTree('image',
46     'Compute/Cyclades or Glance API image commands')
47 _commands = [image_cmds]
48
49
50 class _init_image(_command_init):
51     def main(self):
52         try:
53             token = self.config.get('image', 'token')\
54                 or self.config.get('compute', 'token')\
55                 or self.config.get('global', 'token')
56             base_url = self.config.get('image', 'url')\
57                 or self.config.get('compute', 'url')\
58                 or self.config.get('global', 'url')
59             self.client = ImageClient(base_url=base_url, token=token)
60         except ClientError as err:
61             raiseCLIError(err)
62
63
64 @command(image_cmds)
65 class image_public(_init_image):
66     """List public images"""
67
68     def __init__(self, arguments={}):
69         super(image_public, self).__init__(arguments)
70         self.arguments['detail'] = FlagArgument('show detailed output', '-l')
71         self.arguments['container_format'] =\
72             ValueArgument('filter by container format', '--container-format')
73         self.arguments['disk_format'] =\
74             ValueArgument('filter by disk format', '--disk-format')
75         self.arguments['name'] = ValueArgument('filter by name', '--name')
76         self.arguments['size_min'] =\
77             IntArgument('filter by minimum size', '--size-min')
78         self.arguments['size_max'] =\
79             IntArgument('filter by maximum size', '--size-max')
80         self.arguments['status'] =\
81             ValueArgument('filter by status', '--status')
82         self.arguments['order'] =\
83             ValueArgument('order by FIELD (use a - prefix to reverse order)',
84             '--order', default='')
85
86     def main(self):
87         super(self.__class__, self).main()
88         filters = {}
89         for arg in ('container_format',
90             'disk_format',
91             'name',
92             'size_min',
93             'size_max',
94             'status'):
95             val = self.get_argument(arg)
96             if val is not None:
97                 filters[arg] = val
98
99         order = self.get_argument('order')
100         detail = self.get_argument('detail')
101         try:
102             images = self.client.list_public(detail, filters, order)
103         except ClientError as err:
104             raiseCLIError(err)
105         print_items(images, title=('name',))
106
107
108 @command(image_cmds)
109 class image_meta(_init_image):
110     """Get image metadata"""
111
112     def main(self, image_id):
113         super(self.__class__, self).main()
114         try:
115             image = self.client.get_meta(image_id)
116         except ClientError as err:
117             raiseCLIError(err)
118         print_dict(image)
119
120
121 @command(image_cmds)
122 class image_test(_init_image):
123     """Test stuff"""
124
125     def __init__(self, arguments={}):
126         super(self.__class__, self).__init__(arguments)
127         self.arguments['props'] = KeyValueArgument('prop', '--prop')
128
129     def main(self):
130         print self.get_argument('props')
131         print('OK...')
132
133
134 @command(image_cmds)
135 class image_register(_init_image):
136     """(Re)Register an image
137         call with --update or without an image name to update image properties
138     """
139
140     def __init__(self, arguments={}):
141         super(image_register, self).__init__(arguments)
142         self.arguments['checksum'] =\
143             ValueArgument('set image checksum', '--checksum')
144         self.arguments['container_format'] =\
145             ValueArgument('set container format', '--container-format')
146         self.arguments['disk_format'] =\
147             ValueArgument('set disk format', '--disk-format')
148         self.arguments['id'] = ValueArgument('set image ID', '--id')
149         self.arguments['owner'] =\
150             ValueArgument('set image owner (admin only)', '--owner')
151         self.arguments['properties'] =\
152             KeyValueArgument(parsed_name='--property',
153             help='add property in key=value form (can be repeated)')
154         self.arguments['is_public'] =\
155             FlagArgument('mark image as public', '--public')
156         self.arguments['size'] = IntArgument('set image size', '--size')
157         self.arguments['update'] = FlagArgument(
158             'update an existing image properties', '--update')
159
160     def main(self, location, name=None):
161         super(self.__class__, self).main()
162         if not location.startswith('pithos://'):
163             account = self.config.get('store', 'account') \
164                 or self.config.get('global', 'account')
165             if account[-1] == '/':
166                 account = account[:-1]
167             container = self.config.get('store', 'container') \
168                 or self.config.get('global', 'container')
169             if container is None or len(container) == 0:
170                 location = 'pithos://%s/%s' % (account, location)
171             else:
172                 location = 'pithos://%s/%s/%s' % (account, container, location)
173
174         params = {}
175         for key in ('checksum',
176             'container_format',
177             'disk_format', 'id',
178             'owner',
179             'size',
180             'is_public'):
181             val = self.get_argument(key)
182             if val is not None:
183                 params[key] = val
184
185         update = self.get_argument('update')
186         properties = self.get_argument('properties')
187         try:
188             if name and not update:
189                 self.client.register(name, location, params, properties)
190             else:
191                 self.client.reregister(location, name, params, properties)
192         except ClientError as err:
193             raiseCLIError(err)
194
195
196 @command(image_cmds)
197 class image_members(_init_image):
198     """Get image members"""
199
200     def main(self, image_id):
201         super(self.__class__, self).main()
202         try:
203             members = self.client.list_members(image_id)
204         except ClientError as err:
205             raiseCLIError(err)
206         for member in members:
207             print(member['member_id'])
208
209
210 @command(image_cmds)
211 class image_shared(_init_image):
212     """List shared images"""
213
214     def main(self, member):
215         super(self.__class__, self).main()
216         try:
217             images = self.client.list_shared(member)
218         except ClientError as err:
219             raiseCLIError(err)
220         for image in images:
221             print(image['image_id'])
222
223
224 @command(image_cmds)
225 class image_addmember(_init_image):
226     """Add a member to an image"""
227
228     def main(self, image_id, member):
229         super(self.__class__, self).main()
230         try:
231             self.client.add_member(image_id, member)
232         except ClientError as err:
233             raiseCLIError(err)
234
235
236 @command(image_cmds)
237 class image_delmember(_init_image):
238     """Remove a member from an image"""
239
240     def main(self, image_id, member):
241         super(self.__class__, self).main()
242         try:
243             self.client.remove_member(image_id, member)
244         except ClientError as err:
245             raiseCLIError(err)
246
247
248 @command(image_cmds)
249 class image_setmembers(_init_image):
250     """Set the members of an image"""
251
252     def main(self, image_id, *member):
253         super(self.__class__, self).main()
254         try:
255             self.client.set_members(image_id, member)
256         except ClientError as err:
257             raiseCLIError(err)
258
259
260 @command(image_cmds)
261 class image_list(_init_cyclades):
262     """List images"""
263
264     def __init__(self, arguments={}):
265         super(image_list, self).__init__(arguments)
266         self.arguments['detail'] = FlagArgument('show detailed output', '-l')
267
268     def _print(self, images):
269         for img in images:
270             iname = img.pop('name')
271             iid = img.pop('id')
272             print('%s (%s)' % (unicode(iid), bold(iname)))
273             if self.get_argument('detail'):
274                 if 'metadata' in img:
275                     img['metadata'] = img['metadata']['values']
276                 print_dict(img, ident=2)
277             print(' ')
278
279     def main(self):
280         super(self.__class__, self).main()
281         try:
282             images = self.client.list_images(self.get_argument('detail'))
283         except ClientError as err:
284             raiseCLIError(err)
285         self._print(images)
286
287
288 @command(image_cmds)
289 class image_info(_init_cyclades):
290     """Get image details"""
291
292     @classmethod
293     def _print(self, image):
294         if 'metadata' in image:
295             image['metadata'] = image['metadata']['values']
296         print_dict(image)
297
298     def main(self, image_id):
299         super(self.__class__, self).main()
300         try:
301             image = self.client.get_image_details(image_id)
302         except ClientError as err:
303             raiseCLIError(err)
304         self._print(image)
305
306
307 @command(image_cmds)
308 class image_delete(_init_cyclades):
309     """Delete image"""
310
311     def main(self, image_id):
312         super(self.__class__, self).main()
313         try:
314             self.client.delete_image(image_id)
315         except ClientError as err:
316             raiseCLIError(err)
317
318
319 @command(image_cmds)
320 class image_properties(_init_cyclades):
321     """Get image properties"""
322
323     def main(self, image_id, key=None):
324         super(self.__class__, self).main()
325         try:
326             reply = self.client.get_image_metadata(image_id, key)
327         except ClientError as err:
328             raiseCLIError(err)
329         print_dict(reply)
330
331
332 @command(image_cmds)
333 class image_addproperty(_init_cyclades):
334     """Add an image property"""
335
336     def main(self, image_id, key, val):
337         super(self.__class__, self).main()
338         try:
339             reply = self.client.create_image_metadata(image_id, key, val)
340         except ClientError as err:
341             raiseCLIError(err)
342         print_dict(reply)
343
344
345 @command(image_cmds)
346 class image_setproperty(_init_cyclades):
347     """Update an image property"""
348
349     def main(self, image_id, key, val):
350         super(self.__class__, self).main()
351         metadata = {key: val}
352         try:
353             reply = self.client.update_image_metadata(image_id, **metadata)
354         except ClientError as err:
355             raiseCLIError(err)
356         print_dict(reply)
357
358
359 @command(image_cmds)
360 class image_delproperty(_init_cyclades):
361     """Delete an image property"""
362
363     def main(self, image_id, key):
364         super(self.__class__, self).main()
365         try:
366             self.client.delete_image_metadata(image_id, key)
367         except ClientError as err:
368             raiseCLIError(err)