Revision 0b052394

b/kamaki/cli/argument/__init__.py
556 556
            self._arguments.update(new_arguments)
557 557
            self.update_parser()
558 558

  
559
    def _parse_required_arguments(self, required, parsed_args):
560
        if not required:
561
            return True
562
        if isinstance(required, tuple):
563
            for item in required:
564
                if not self._parse_required_arguments(item, parsed_args):
565
                    return False
566
            return True
567
        if isinstance(required, list):
568
            for item in required:
569
                if self._parse_required_arguments(item, parsed_args):
570
                    return True
571
            return False
572
        return required in parsed_args
573

  
559 574
    def parse(self, new_args=None):
560 575
        """Parse user input"""
561 576
        try:
562 577
            pkargs = (new_args,) if new_args else ()
563 578
            self._parsed, unparsed = self.parser.parse_known_args(*pkargs)
564
            pdict = vars(self._parsed)
565
            diff = set(self.required or []).difference(
566
                [k for k in pdict if pdict[k] not in (None, )])
567
            if diff:
579
            parsed_args = [
580
                k for k, v in vars(self._parsed).items() if v not in (None, )]
581
            if not self._parse_required_arguments(self.required, parsed_args):
568 582
                self.print_help()
569
                miss = ['/'.join(self.arguments[k].parsed_name) for k in diff]
570
                raise CLISyntaxError(
571
                    'Missing required arguments (%s)' % ', '.join(miss))
583
                raise CLISyntaxError('Missing required arguments')
584

  
572 585
        except SystemExit:
573 586
            raiseCLIError(CLISyntaxError('Argument Syntax Error'))
574 587
        for name, arg in self.arguments.items():
b/kamaki/cli/commands/cyclades.py
42 42
from kamaki.cli.errors import (
43 43
    raiseCLIError, CLISyntaxError, CLIBaseUrlError, CLIInvalidArgument)
44 44
from kamaki.clients.cyclades import CycladesClient, ClientError
45
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
46
from kamaki.cli.argument import ProgressBarArgument, DateArgument, IntArgument
45
from kamaki.cli.argument import (
46
    FlagArgument, ValueArgument, KeyValueArgument, RepeatableArgument,
47
    ProgressBarArgument, DateArgument, IntArgument)
47 48
from kamaki.cli.commands import _command_init, errors, addLogSettings
48 49
from kamaki.cli.commands import (
49 50
    _optional_output_cmd, _optional_json, _name_filter, _id_filter)
......
446 447
            image_id=self['image_id'])
447 448

  
448 449

  
450
class FirewallProfileArgument(ValueArgument):
451

  
452
    profiles = ('DISABLED', 'ENABLED', 'PROTECTED')
453

  
454
    @property
455
    def value(self):
456
        return getattr(self, '_value', None)
457

  
458
    @value.setter
459
    def value(self, new_profile):
460
        if new_profile:
461
            new_profile = new_profile.upper()
462
            if new_profile in self.profiles:
463
                self._value = new_profile
464
            else:
465
                raise CLIInvalidArgument(
466
                    'Invalid firewall profile %s' % new_profile,
467
                    details=['Valid values: %s' % ', '.join(self.profiles)])
468

  
469

  
449 470
@command(server_cmds)
450 471
class server_modify(_init_cyclades, _optional_output_cmd):
451 472
    """Modify attributes of a virtual server"""
......
453 474
    arguments = dict(
454 475
        server_name=ValueArgument('The new name', '--name'),
455 476
        flavor_id=IntArgument('Set a different flavor', '--flavor-id'),
477
        firewall_profile=FirewallProfileArgument(
478
            'Valid values: %s' % (', '.join(FirewallProfileArgument.profiles)),
479
            '--firewall'),
480
        metadata_to_set=KeyValueArgument(
481
            'Set metadata in key=value form (can be repeated)',
482
            '--set-metadata'),
483
        metadata_to_delete=RepeatableArgument(
484
            'Delete metadata by key (can be repeated)', '--del-metadata')
456 485
    )
457
    required = ['server_name', 'flavor_id']
486
    required = [
487
        'server_name', 'flavor_id', 'firewall_profile', 'metadata_to_set',
488
        'metadata_to_del']
458 489

  
459 490
    @errors.generic.all
460 491
    @errors.cyclades.connection
......
464 495
            self.client.update_server_name((server_id), self['server_name'])
465 496
        if self['flavor_id']:
466 497
            self.client.resize_server(server_id, self['flavor_id'])
498
        if self['firewall_profile']:
499
            self.client.set_firewall_profile(
500
                server_id=server_id, profile=self['firewall_profile'])
501
        if self['metadata_to_set']:
502
            self.client.update_server_metadata(
503
                server_id, **self['metadata_to_set'])
504
        for key in self['metadata_to_delete']:
505
            errors.cyclades.metadata(
506
                self.client.delete_server_metadata)(server_id, key=key)
467 507
        if self['with_output']:
468 508
            self._optional_output(self.client.get_server_details(server_id))
469 509

  
......
638 678

  
639 679

  
640 680
@command(server_cmds)
641
class server_firewall(_init_cyclades):
642
    """Manage virtual server firewall profiles for public networks"""
643

  
644

  
645
@command(server_cmds)
646
class server_firewall_set(
647
        _init_cyclades, _optional_output_cmd, _firewall_wait):
648
    """Set the firewall profile on virtual server public network
649
    Values for profile:
650
    - DISABLED: Shutdown firewall
651
    - ENABLED: Firewall in normal mode
652
    - PROTECTED: Firewall in secure mode
653
    """
654

  
655
    arguments = dict(
656
        wait=FlagArgument('Wait server firewall to build', ('-w', '--wait')),
657
        timeout=IntArgument(
658
            'Set wait timeout in seconds (default: 60)', '--timeout',
659
            default=60)
660
    )
661

  
662
    @errors.generic.all
663
    @errors.cyclades.connection
664
    @errors.cyclades.server_id
665
    @errors.cyclades.firewall
666
    def _run(self, server_id, profile):
667
        if self['timeout'] and not self['wait']:
668
            raise CLIInvalidArgument('Invalid use of --timeout', details=[
669
                'Timeout is used only along with -w/--wait'])
670
        old_profile = self.client.get_firewall_profile(server_id)
671
        if old_profile.lower() == profile.lower():
672
            self.error('Firewall of server %s: allready in status %s' % (
673
                server_id, old_profile))
674
        else:
675
            self._optional_output(self.client.set_firewall_profile(
676
                server_id=int(server_id), profile=('%s' % profile).upper()))
677
            if self['wait']:
678
                self._wait(server_id, old_profile, timeout=self['timeout'])
679

  
680
    def main(self, server_id, profile):
681
        super(self.__class__, self)._run()
682
        self._run(server_id=server_id, profile=profile)
683

  
684

  
685
@command(server_cmds)
686
class server_firewall_get(_init_cyclades):
687
    """Get the firewall profile for a virtual servers' public network"""
688

  
689
    @errors.generic.all
690
    @errors.cyclades.connection
691
    @errors.cyclades.server_id
692
    def _run(self, server_id):
693
        self.writeln(self.client.get_firewall_profile(server_id))
694

  
695
    def main(self, server_id):
696
        super(self.__class__, self)._run()
697
        self._run(server_id=server_id)
698

  
699

  
700
@command(server_cmds)
701 681
class server_addr(_init_cyclades, _optional_json):
702 682
    """List the addresses of all network interfaces on a virtual server"""
703 683

  
......
718 698

  
719 699

  
720 700
@command(server_cmds)
721
class server_metadata(_init_cyclades):
722
    """Manage Server metadata (key:value pairs of server attributes)"""
723

  
724

  
725
@command(server_cmds)
726 701
class server_metadata_list(_init_cyclades, _optional_json):
727 702
    """Get server metadata"""
728 703

  
b/kamaki/cli/commands/errors.py
399 399
                    'metadata' in ('%s' % ce).lower()
400 400
                ):
401 401
                        raiseCLIError(
402
                            ce, 'No v. server metadata with key %s' % key)
402
                            ce, 'No virtual server metadata with key %s' % key)
403 403
                raise
404 404
        return _raise
405 405

  

Also available in: Unified diff