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