1 # Copyright 2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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
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
45 image_cmds = CommandTree('image',
46 'Compute/Cyclades or Glance API image commands')
47 _commands = [image_cmds]
50 class _init_image(_command_init):
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:
65 class image_public(_init_image):
66 """List public images"""
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='')
87 super(self.__class__, self).main()
89 for arg in ('container_format',
95 val = self.get_argument(arg)
99 order = self.get_argument('order')
100 detail = self.get_argument('detail')
102 images = self.client.list_public(detail, filters, order)
103 except ClientError as err:
105 print_items(images, title=('name',))
109 class image_meta(_init_image):
110 """Get image metadata"""
112 def main(self, image_id):
113 super(self.__class__, self).main()
115 image = self.client.get_meta(image_id)
116 except ClientError as err:
122 class image_test(_init_image):
125 def __init__(self, arguments={}):
126 super(self.__class__, self).__init__(arguments)
127 self.arguments['props'] = KeyValueArgument('prop', '--prop')
130 print self.get_argument('props')
135 class image_register(_init_image):
136 """(Re)Register an image
137 call with --update or without an image name to update image properties
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')
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)
172 location = 'pithos://%s/%s/%s' % (account, container, location)
175 for key in ('checksum',
181 val = self.get_argument(key)
185 update = self.get_argument('update')
186 properties = self.get_argument('properties')
188 if name and not update:
189 self.client.register(name, location, params, properties)
191 self.client.reregister(location, name, params, properties)
192 except ClientError as err:
197 class image_members(_init_image):
198 """Get image members"""
200 def main(self, image_id):
201 super(self.__class__, self).main()
203 members = self.client.list_members(image_id)
204 except ClientError as err:
206 for member in members:
207 print(member['member_id'])
211 class image_shared(_init_image):
212 """List shared images"""
214 def main(self, member):
215 super(self.__class__, self).main()
217 images = self.client.list_shared(member)
218 except ClientError as err:
221 print(image['image_id'])
225 class image_addmember(_init_image):
226 """Add a member to an image"""
228 def main(self, image_id, member):
229 super(self.__class__, self).main()
231 self.client.add_member(image_id, member)
232 except ClientError as err:
237 class image_delmember(_init_image):
238 """Remove a member from an image"""
240 def main(self, image_id, member):
241 super(self.__class__, self).main()
243 self.client.remove_member(image_id, member)
244 except ClientError as err:
249 class image_setmembers(_init_image):
250 """Set the members of an image"""
252 def main(self, image_id, *member):
253 super(self.__class__, self).main()
255 self.client.set_members(image_id, member)
256 except ClientError as err:
261 class image_list(_init_cyclades):
264 def __init__(self, arguments={}):
265 super(image_list, self).__init__(arguments)
266 self.arguments['detail'] = FlagArgument('show detailed output', '-l')
268 def _print(self, images):
270 iname = img.pop('name')
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)
280 super(self.__class__, self).main()
282 images = self.client.list_images(self.get_argument('detail'))
283 except ClientError as err:
289 class image_info(_init_cyclades):
290 """Get image details"""
293 def _print(self, image):
294 if 'metadata' in image:
295 image['metadata'] = image['metadata']['values']
298 def main(self, image_id):
299 super(self.__class__, self).main()
301 image = self.client.get_image_details(image_id)
302 except ClientError as err:
308 class image_delete(_init_cyclades):
311 def main(self, image_id):
312 super(self.__class__, self).main()
314 self.client.delete_image(image_id)
315 except ClientError as err:
320 class image_properties(_init_cyclades):
321 """Get image properties"""
323 def main(self, image_id, key=None):
324 super(self.__class__, self).main()
326 reply = self.client.get_image_metadata(image_id, key)
327 except ClientError as err:
333 class image_addproperty(_init_cyclades):
334 """Add an image property"""
336 def main(self, image_id, key, val):
337 super(self.__class__, self).main()
339 reply = self.client.create_image_metadata(image_id, key, val)
340 except ClientError as err:
346 class image_setproperty(_init_cyclades):
347 """Update an image property"""
349 def main(self, image_id, key, val):
350 super(self.__class__, self).main()
351 metadata = {key: val}
353 reply = self.client.update_image_metadata(image_id, **metadata)
354 except ClientError as err:
360 class image_delproperty(_init_cyclades):
361 """Delete an image property"""
363 def main(self, image_id, key):
364 super(self.__class__, self).main()
366 self.client.delete_image_metadata(image_id, key)
367 except ClientError as err: