Revision 264a13f7 kamaki/cli/commands/cyclades.py

b/kamaki/cli/commands/cyclades.py
371 371
                                '%s' % ve])
372 372

  
373 373

  
374
class NetworkIpArgument(RepeatableArgument):
375

  
376
    @property
377
    def value(self):
378
        return getattr(self, '_value', [])
379

  
380
    @value.setter
381
    def value(self, new_value):
382
        for v in (new_value or []):
383
            net_and_ip = v.split(',')
384
            if len(net_and_ip) < 2:
385
                raise CLIInvalidArgument(
386
                    'Value "%s" is missing parts' % v,
387
                    details=['Correct format: %s NETWORK_ID,IP' % (
388
                        self.parsed_name[0])])
389
            self._value = getattr(self, '_value', list())
390
            self._value.append(
391
                dict(network=net_and_ip[0], fixed_ip=net_and_ip[1]))
392

  
393

  
374 394
@command(server_cmds)
375 395
class server_create(_init_cyclades, _optional_json, _server_wait):
376 396
    """Create a server (aka Virtual Machine)"""
......
386 406
            'Create a cluster of servers of this size. In this case, the name'
387 407
            'parameter is the prefix of each server in the cluster (e.g.,'
388 408
            'srv1, srv2, etc.',
389
            '--cluster-size')
409
            '--cluster-size'),
410
        network_id=RepeatableArgument(
411
            'Connect server to network (can be repeated)', '--network'),
412
        network_id_and_ip=NetworkIpArgument(
413
            'Connect server to network w. floating ip ( NETWORK_ID,IP )'
414
            '(can be repeated)',
415
            '--network-with-ip'),
390 416
    )
391 417
    required = ('server_name', 'flavor_id', 'image_id')
392 418

  
393 419
    @errors.cyclades.cluster_size
394 420
    def _create_cluster(self, prefix, flavor_id, image_id, size):
421
        networks = [dict(network=netid) for netid in (
422
            self['network_id'] or [])] + (self['network_id_and_ip'] or [])
395 423
        servers = [dict(
396 424
            name='%s%s' % (prefix, i if size > 1 else ''),
397 425
            flavor_id=flavor_id,
398 426
            image_id=image_id,
399
            personality=self['personality']) for i in range(1, 1 + size)]
427
            personality=self['personality'],
428
            networks=networks) for i in range(1, 1 + size)]
400 429
        if size == 1:
401 430
            return [self.client.create_server(**servers[0])]
402 431
        try:
......
836 865

  
837 866

  
838 867
@command(network_cmds)
839
class network_info(_init_cyclades, _optional_json):
840
    """Detailed information on a network
841
    To get a list of available networks and network ids, try /network list
842
    """
843

  
844
    @errors.generic.all
845
    @errors.cyclades.connection
846
    @errors.cyclades.network_id
847
    def _run(self, network_id):
848
        network = self.client.get_network_details(int(network_id))
849
        _add_name(self, network)
850
        self._print(network, self.print_dict, exclude=('id'))
851

  
852
    def main(self, network_id):
853
        super(self.__class__, self)._run()
854
        self._run(network_id=network_id)
855

  
856

  
857
@command(network_cmds)
858
class network_list(_init_cyclades, _optional_json, _name_filter, _id_filter):
859
    """List networks"""
860

  
861
    PERMANENTS = ('id', 'name')
862

  
863
    arguments = dict(
864
        detail=FlagArgument('show detailed output', ('-l', '--details')),
865
        limit=IntArgument('limit # of listed networks', ('-n', '--number')),
866
        more=FlagArgument(
867
            'output results in pages (-n to set items per page, default 10)',
868
            '--more'),
869
        enum=FlagArgument('Enumerate results', '--enumerate'),
870
        status=ValueArgument('filter by status', ('--status')),
871
        public=FlagArgument('only public networks', ('--public')),
872
        private=FlagArgument('only private networks', ('--private')),
873
        dhcp=FlagArgument('show networks with dhcp', ('--with-dhcp')),
874
        no_dhcp=FlagArgument('show networks without dhcp', ('--without-dhcp')),
875
        user_id=ValueArgument('filter by user id', ('--user-id')),
876
        user_name=ValueArgument('filter by user name', ('--user-name')),
877
        gateway=ValueArgument('filter by gateway (IPv4)', ('--gateway')),
878
        gateway6=ValueArgument('filter by gateway (IPv6)', ('--gateway6')),
879
        cidr=ValueArgument('filter by cidr (IPv4)', ('--cidr')),
880
        cidr6=ValueArgument('filter by cidr (IPv6)', ('--cidr6')),
881
        type=ValueArgument('filter by type', ('--type')),
882
    )
883

  
884
    def _apply_common_filters(self, networks):
885
        common_filter = dict()
886
        if self['public']:
887
            if self['private']:
888
                return []
889
            common_filter['public'] = self['public']
890
        elif self['private']:
891
            common_filter['public'] = False
892
        if self['dhcp']:
893
            if self['no_dhcp']:
894
                return []
895
            common_filter['dhcp'] = True
896
        elif self['no_dhcp']:
897
            common_filter['dhcp'] = False
898
        if self['user_id'] or self['user_name']:
899
            uuid = self['user_id'] or self._username2uuid(self['user_name'])
900
            common_filter['user_id'] = uuid
901
        for term in ('status', 'gateway', 'gateway6', 'cidr', 'cidr6', 'type'):
902
            if self[term]:
903
                common_filter[term] = self[term]
904
        return filter_dicts_by_dict(networks, common_filter)
905

  
906
    def _add_name(self, networks, key='user_id'):
907
        uuids = self._uuids2usernames(
908
            list(set([net[key] for net in networks])))
909
        for net in networks:
910
            v = net.get(key, None)
911
            if v:
912
                net[key] += ' (%s)' % uuids[v]
913
        return networks
914

  
915
    @errors.generic.all
916
    @errors.cyclades.connection
917
    def _run(self):
918
        withcommons = False
919
        for term in (
920
                'status', 'public', 'private', 'user_id', 'user_name', 'type',
921
                'gateway', 'gateway6', 'cidr', 'cidr6', 'dhcp', 'no_dhcp'):
922
            if self[term]:
923
                withcommons = True
924
                break
925
        detail = self['detail'] or withcommons
926
        networks = self.client.list_networks(detail)
927
        networks = self._filter_by_name(networks)
928
        networks = self._filter_by_id(networks)
929
        if withcommons:
930
            networks = self._apply_common_filters(networks)
931
        if not (self['detail'] or (
932
                self['json_output'] or self['output_format'])):
933
            remove_from_items(networks, 'links')
934
        if detail and not self['detail']:
935
            for net in networks:
936
                for key in set(net).difference(self.PERMANENTS):
937
                    net.pop(key)
938
        if self['detail'] and not (
939
                self['json_output'] or self['output_format']):
940
            self._add_name(networks)
941
            self._add_name(networks, 'tenant_id')
942
        kwargs = dict(with_enumeration=self['enum'])
943
        if self['more']:
944
            kwargs['out'] = StringIO()
945
            kwargs['title'] = ()
946
        if self['limit']:
947
            networks = networks[:self['limit']]
948
        self._print(networks, **kwargs)
949
        if self['more']:
950
            pager(kwargs['out'].getvalue())
951

  
952
    def main(self):
953
        super(self.__class__, self)._run()
954
        self._run()
955

  
956

  
957
@command(network_cmds)
958
class network_create(_init_cyclades, _optional_json, _network_wait):
959
    """Create an (unconnected) network"""
960

  
961
    arguments = dict(
962
        cidr=ValueArgument('explicitly set cidr', '--with-cidr'),
963
        gateway=ValueArgument('explicitly set gateway', '--with-gateway'),
964
        dhcp=FlagArgument('Use dhcp (default: off)', '--with-dhcp'),
965
        type=ValueArgument(
966
            'Valid network types are '
967
            'CUSTOM, IP_LESS_ROUTED, MAC_FILTERED (default), PHYSICAL_VLAN',
968
            '--with-type',
969
            default='MAC_FILTERED'),
970
        wait=FlagArgument('Wait network to build', ('-w', '--wait'))
971
    )
972

  
973
    @errors.generic.all
974
    @errors.cyclades.connection
975
    @errors.cyclades.network_max
976
    def _run(self, name):
977
        r = self.client.create_network(
978
            name,
979
            cidr=self['cidr'],
980
            gateway=self['gateway'],
981
            dhcp=self['dhcp'],
982
            type=self['type'])
983
        _add_name(self, r)
984
        self._print(r, self.print_dict)
985
        if self['wait'] and r['status'] in ('PENDING', ):
986
            self._wait(r['id'], 'PENDING')
987

  
988
    def main(self, name):
989
        super(self.__class__, self)._run()
990
        self._run(name)
991

  
992

  
993
@command(network_cmds)
994
class network_rename(_init_cyclades, _optional_output_cmd):
995
    """Set the name of a network"""
996

  
997
    @errors.generic.all
998
    @errors.cyclades.connection
999
    @errors.cyclades.network_id
1000
    def _run(self, network_id, new_name):
1001
        self._optional_output(
1002
                self.client.update_network_name(int(network_id), new_name))
1003

  
1004
    def main(self, network_id, new_name):
1005
        super(self.__class__, self)._run()
1006
        self._run(network_id=network_id, new_name=new_name)
1007

  
1008

  
1009
@command(network_cmds)
1010
class network_delete(_init_cyclades, _optional_output_cmd, _network_wait):
1011
    """Delete a network"""
1012

  
1013
    arguments = dict(
1014
        wait=FlagArgument('Wait network to build', ('-w', '--wait'))
1015
    )
1016

  
1017
    @errors.generic.all
1018
    @errors.cyclades.connection
1019
    @errors.cyclades.network_in_use
1020
    @errors.cyclades.network_id
1021
    def _run(self, network_id):
1022
        status = 'DELETED'
1023
        if self['wait']:
1024
            r = self.client.get_network_details(network_id)
1025
            status = r['status']
1026
            if status in ('DELETED', ):
1027
                return
1028

  
1029
        r = self.client.delete_network(int(network_id))
1030
        self._optional_output(r)
1031

  
1032
        if self['wait']:
1033
            self._wait(network_id, status)
1034

  
1035
    def main(self, network_id):
1036
        super(self.__class__, self)._run()
1037
        self._run(network_id=network_id)
1038

  
1039

  
1040
@command(network_cmds)
1041
class network_connect(_init_cyclades, _optional_output_cmd):
1042
    """Connect a server to a network"""
1043

  
1044
    @errors.generic.all
1045
    @errors.cyclades.connection
1046
    @errors.cyclades.server_id
1047
    @errors.cyclades.network_id
1048
    def _run(self, server_id, network_id):
1049
        self._optional_output(
1050
                self.client.connect_server(int(server_id), int(network_id)))
1051

  
1052
    def main(self, server_id, network_id):
1053
        super(self.__class__, self)._run()
1054
        self._run(server_id=server_id, network_id=network_id)
1055

  
1056

  
1057
@command(network_cmds)
1058
class network_disconnect(_init_cyclades):
1059
    """Disconnect a nic that connects a server to a network
1060
    Nic ids are listed as "attachments" in detailed network information
1061
    To get detailed network information: /network info <network id>
1062
    """
1063

  
1064
    @errors.cyclades.nic_format
1065
    def _server_id_from_nic(self, nic_id):
1066
        return nic_id.split('-')[1]
1067

  
1068
    @errors.generic.all
1069
    @errors.cyclades.connection
1070
    @errors.cyclades.server_id
1071
    @errors.cyclades.nic_id
1072
    def _run(self, nic_id, server_id):
1073
        num_of_disconnected = self.client.disconnect_server(server_id, nic_id)
1074
        if not num_of_disconnected:
1075
            raise ClientError(
1076
                'Network Interface %s not found on server %s' % (
1077
                    nic_id, server_id),
1078
                status=404)
1079
        print('Disconnected %s connections' % num_of_disconnected)
1080

  
1081
    def main(self, nic_id):
1082
        super(self.__class__, self)._run()
1083
        server_id = self._server_id_from_nic(nic_id=nic_id)
1084
        self._run(nic_id=nic_id, server_id=server_id)
1085

  
1086

  
1087
@command(network_cmds)
1088 868
class network_wait(_init_cyclades, _network_wait):
1089 869
    """Wait for server to finish [PENDING, ACTIVE, DELETED]"""
1090 870

  
......
1194 974

  
1195 975
@command(ip_cmds)
1196 976
class ip_attach(_init_cyclades, _optional_output_cmd):
1197
    """Attach a floating IP to a server
1198
    """
1199

  
1200
    @errors.generic.all
1201
    @errors.cyclades.connection
1202
    @errors.cyclades.server_id
1203
    def _run(self, server_id, ip):
1204
        self._optional_output(self.client.attach_floating_ip(server_id, ip))
977
    """DEPRECATED, use /port create"""
1205 978

  
1206
    def main(self, server_id, IP):
1207
        super(self.__class__, self)._run()
1208
        self._run(server_id=server_id, ip=IP)
979
    def main(self):
980
        raise CLISyntaxError('DEPRECATED, replaced by kamaki port create')
1209 981

  
1210 982

  
1211 983
@command(ip_cmds)
1212 984
class ip_detach(_init_cyclades, _optional_output_cmd):
1213
    """Detach a floating IP from a server
1214
    """
1215

  
1216
    @errors.generic.all
1217
    @errors.cyclades.connection
1218
    @errors.cyclades.server_id
1219
    def _run(self, server_id, ip):
1220
        self._optional_output(self.client.detach_floating_ip(server_id, ip))
985
    """DEPRECATED, use /port delete"""
1221 986

  
1222
    def main(self, server_id, IP):
1223
        super(self.__class__, self)._run()
1224
        self._run(server_id=server_id, ip=IP)
987
    def main(self):
988
        raise CLISyntaxError('DEPRECATED, replaced by kamaki port delete')

Also available in: Unified diff