Revision 00b1248e
b/kamaki/cli/commands/cyclades.py | ||
---|---|---|
479 | 479 |
'--firewall'), |
480 | 480 |
metadata_to_set=KeyValueArgument( |
481 | 481 |
'Set metadata in key=value form (can be repeated)', |
482 |
'--set-metadata'),
|
|
482 |
'--metadata-set'),
|
|
483 | 483 |
metadata_to_delete=RepeatableArgument( |
484 |
'Delete metadata by key (can be repeated)', '--del-metadata')
|
|
484 |
'Delete metadata by key (can be repeated)', '--metadata-del')
|
|
485 | 485 |
) |
486 | 486 |
required = [ |
487 | 487 |
'server_name', 'flavor_id', 'firewall_profile', 'metadata_to_set', |
488 |
'metadata_to_del'] |
|
488 |
'metadata_to_delete']
|
|
489 | 489 |
|
490 | 490 |
@errors.generic.all |
491 | 491 |
@errors.cyclades.connection |
... | ... | |
698 | 698 |
|
699 | 699 |
|
700 | 700 |
@command(server_cmds) |
701 |
class server_metadata_list(_init_cyclades, _optional_json): |
|
702 |
"""Get server metadata""" |
|
703 |
|
|
704 |
@errors.generic.all |
|
705 |
@errors.cyclades.connection |
|
706 |
@errors.cyclades.server_id |
|
707 |
@errors.cyclades.metadata |
|
708 |
def _run(self, server_id, key=''): |
|
709 |
self._print( |
|
710 |
self.client.get_server_metadata(int(server_id), key), |
|
711 |
self.print_dict) |
|
712 |
|
|
713 |
def main(self, server_id, key=''): |
|
714 |
super(self.__class__, self)._run() |
|
715 |
self._run(server_id=server_id, key=key) |
|
716 |
|
|
717 |
|
|
718 |
@command(server_cmds) |
|
719 |
class server_metadata_set(_init_cyclades, _optional_json): |
|
720 |
"""Set / update virtual server metadata |
|
721 |
Metadata should be given in key/value pairs in key=value format |
|
722 |
For example: /server metadata set <server id> key1=value1 key2=value2 |
|
723 |
Old, unreferenced metadata will remain intact |
|
724 |
""" |
|
725 |
|
|
726 |
@errors.generic.all |
|
727 |
@errors.cyclades.connection |
|
728 |
@errors.cyclades.server_id |
|
729 |
def _run(self, server_id, keyvals): |
|
730 |
assert keyvals, 'Please, add some metadata ( key=value)' |
|
731 |
metadata = dict() |
|
732 |
for keyval in keyvals: |
|
733 |
k, sep, v = keyval.partition('=') |
|
734 |
if sep and k: |
|
735 |
metadata[k] = v |
|
736 |
else: |
|
737 |
raiseCLIError( |
|
738 |
'Invalid piece of metadata %s' % keyval, |
|
739 |
importance=2, details=[ |
|
740 |
'Correct metadata format: key=val', |
|
741 |
'For example:', |
|
742 |
'/server metadata set <server id>' |
|
743 |
'key1=value1 key2=value2']) |
|
744 |
self._print( |
|
745 |
self.client.update_server_metadata(int(server_id), **metadata), |
|
746 |
self.print_dict) |
|
747 |
|
|
748 |
def main(self, server_id, *key_equals_val): |
|
749 |
super(self.__class__, self)._run() |
|
750 |
self._run(server_id=server_id, keyvals=key_equals_val) |
|
751 |
|
|
752 |
|
|
753 |
@command(server_cmds) |
|
754 |
class server_metadata_delete(_init_cyclades, _optional_output_cmd): |
|
755 |
"""Delete virtual server metadata""" |
|
756 |
|
|
757 |
@errors.generic.all |
|
758 |
@errors.cyclades.connection |
|
759 |
@errors.cyclades.server_id |
|
760 |
@errors.cyclades.metadata |
|
761 |
def _run(self, server_id, key): |
|
762 |
self._optional_output( |
|
763 |
self.client.delete_server_metadata(int(server_id), key)) |
|
764 |
|
|
765 |
def main(self, server_id, key): |
|
766 |
super(self.__class__, self)._run() |
|
767 |
self._run(server_id=server_id, key=key) |
|
768 |
|
|
769 |
|
|
770 |
@command(server_cmds) |
|
771 | 701 |
class server_stats(_init_cyclades, _optional_json): |
772 | 702 |
"""Get virtual server statistics""" |
773 | 703 |
|
b/kamaki/cli/commands/image.py | ||
---|---|---|
284 | 284 |
|
285 | 285 |
|
286 | 286 |
@command(image_cmds) |
287 |
class image_meta(_init_image): |
|
288 |
"""Manage image metadata and custom properties""" |
|
289 |
|
|
290 |
|
|
291 |
@command(image_cmds) |
|
292 | 287 |
class image_info(_init_image, _optional_json): |
293 |
"""Get image metadata |
|
294 |
Image metadata include: |
|
295 |
- image file information (location, size, etc.) |
|
296 |
- image information (id, name, etc.) |
|
297 |
- image os properties (os, fs, etc.) |
|
298 |
""" |
|
288 |
"""Get image metadata""" |
|
299 | 289 |
|
300 | 290 |
@errors.generic.all |
301 | 291 |
@errors.plankton.connection |
... | ... | |
312 | 302 |
|
313 | 303 |
|
314 | 304 |
@command(image_cmds) |
315 |
class image_meta_set(_init_image, _optional_output_cmd):
|
|
305 |
class image_modify(_init_image, _optional_json):
|
|
316 | 306 |
"""Add / update metadata and properties for an image |
317 | 307 |
The original image preserves the values that are not affected |
318 | 308 |
""" |
319 | 309 |
|
320 | 310 |
arguments = dict( |
321 |
name=ValueArgument('Set a new name', ('--name')),
|
|
322 |
disk_format=ValueArgument('Set a new disk format', ('--disk-format')),
|
|
311 |
image_name=ValueArgument('Change name', '--name'),
|
|
312 |
disk_format=ValueArgument('Change disk format', '--disk-format'),
|
|
323 | 313 |
container_format=ValueArgument( |
324 |
'Set a new container format', ('--container-format')),
|
|
325 |
status=ValueArgument('Set a new status', ('--status')),
|
|
326 |
publish=FlagArgument('publish the image', ('--publish')),
|
|
327 |
unpublish=FlagArgument('unpublish the image', ('--unpublish')),
|
|
328 |
properties=KeyValueArgument(
|
|
314 |
'Change container format', '--container-format'),
|
|
315 |
status=ValueArgument('Change status', '--status'),
|
|
316 |
publish=FlagArgument('Publish the image', '--publish'),
|
|
317 |
unpublish=FlagArgument('Unpublish the image', '--unpublish'),
|
|
318 |
property_to_set=KeyValueArgument(
|
|
329 | 319 |
'set property in key=value form (can be repeated)', |
330 |
('-p', '--property')) |
|
320 |
('-p', '--property-set')), |
|
321 |
property_to_del=RepeatableArgument( |
|
322 |
'Delete property by key (can be repeated)', '--property-del') |
|
331 | 323 |
) |
332 |
|
|
333 |
def _check_empty(self): |
|
334 |
for term in ( |
|
335 |
'name', 'disk_format', 'container_format', 'status', 'publish', |
|
336 |
'unpublish', 'properties'): |
|
337 |
if self[term]: |
|
338 |
if self['publish'] and self['unpublish']: |
|
339 |
raiseCLIError( |
|
340 |
'--publish and --unpublish are mutually exclusive') |
|
341 |
return |
|
342 |
raiseCLIError( |
|
343 |
'Nothing to update, please use arguments (-h for a list)') |
|
324 |
required = [ |
|
325 |
'image_name', 'disk_format', 'container_format', 'status', 'publish', |
|
326 |
'unpublish', 'property_to_set'] |
|
344 | 327 |
|
345 | 328 |
@errors.generic.all |
346 | 329 |
@errors.plankton.connection |
347 | 330 |
@errors.plankton.id |
348 | 331 |
def _run(self, image_id): |
349 |
self._check_empty() |
|
350 | 332 |
meta = self.client.get_meta(image_id) |
351 |
for k, v in self['properties'].items():
|
|
333 |
for k, v in self['property_to_set'].items():
|
|
352 | 334 |
meta['properties'][k.upper()] = v |
335 |
for k in self['property_to_del']: |
|
336 |
meta['properties'][k.upper()] = None |
|
353 | 337 |
self._optional_output(self.client.update_image( |
354 | 338 |
image_id, |
355 |
name=self['name'], |
|
339 |
name=self['image_name'],
|
|
356 | 340 |
disk_format=self['disk_format'], |
357 | 341 |
container_format=self['container_format'], |
358 | 342 |
status=self['status'], |
... | ... | |
365 | 349 |
|
366 | 350 |
|
367 | 351 |
@command(image_cmds) |
368 |
class image_meta_delete(_init_image, _optional_output_cmd): |
|
369 |
"""Remove/empty image metadata and/or custom properties""" |
|
370 |
|
|
371 |
arguments = dict( |
|
372 |
disk_format=FlagArgument('Empty disk format', ('--disk-format')), |
|
373 |
container_format=FlagArgument( |
|
374 |
'Empty container format', ('--container-format')), |
|
375 |
status=FlagArgument('Empty status', ('--status')), |
|
376 |
properties=RepeatableArgument( |
|
377 |
'Property keys to remove', ('-p', '--property')) |
|
378 |
) |
|
379 |
|
|
380 |
def _check_empty(self): |
|
381 |
for t in ('disk_format', 'container_format', 'status', 'properties'): |
|
382 |
if self[t]: |
|
383 |
return |
|
384 |
raiseCLIError( |
|
385 |
'Nothing to update, please use arguments (-h for a list)') |
|
386 |
|
|
387 |
@errors.generic.all |
|
388 |
@errors.plankton.connection |
|
389 |
@errors.plankton.id |
|
390 |
def _run(self, image_id): |
|
391 |
self._check_empty() |
|
392 |
meta = self.client.get_meta(image_id) |
|
393 |
for k in self['properties']: |
|
394 |
meta['properties'].pop(k.upper(), None) |
|
395 |
self._optional_output(self.client.update_image( |
|
396 |
image_id, |
|
397 |
disk_format='' if self['disk_format'] else None, |
|
398 |
container_format='' if self['container_format'] else None, |
|
399 |
status='' if self['status'] else None, |
|
400 |
**meta['properties'])) |
|
401 |
|
|
402 |
def main(self, image_id): |
|
403 |
super(self.__class__, self)._run() |
|
404 |
self._run(image_id=image_id) |
|
405 |
|
|
406 |
|
|
407 |
@command(image_cmds) |
|
408 | 352 |
class image_register(_init_image, _optional_json): |
409 | 353 |
"""(Re)Register an image file to an Image service |
410 | 354 |
The image file must be stored at a pithos repository |
Also available in: Unified diff