Revision 264a13f7
b/kamaki/cli/argument/__init__.py | ||
---|---|---|
459 | 459 |
if arg.arity != 0: |
460 | 460 |
ret += ' %s' % required.upper() |
461 | 461 |
ret = ('{:<%s}' % lt_pn).format(ret) |
462 |
prefix = ('\n%s' % tab2) if len(ret) < lt_pn else ' '
|
|
463 |
step, cur = (len(arg.help) / (lt_all - lt_pn)) or len(arg.help), 0
|
|
462 |
prefix = ('\n%s' % tab2) if len(ret) > lt_pn else ' '
|
|
463 |
cur = 0
|
|
464 | 464 |
while arg.help[cur:]: |
465 |
next = cur + step
|
|
465 |
next = cur + lt_all - lt_pn
|
|
466 | 466 |
ret += prefix |
467 | 467 |
ret += ('{:<%s}' % (lt_all - lt_pn)).format(arg.help[cur:next]) |
468 | 468 |
cur, finish = next, '\n%s' % tab2 |
... | ... | |
581 | 581 |
if not self._parse_required_arguments(self.required, parsed_args): |
582 | 582 |
self.print_help() |
583 | 583 |
raise CLISyntaxError('Missing required arguments') |
584 |
|
|
585 | 584 |
except SystemExit: |
586 | 585 |
raiseCLIError(CLISyntaxError('Argument Syntax Error')) |
587 | 586 |
for name, arg in self.arguments.items(): |
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') |
b/kamaki/cli/commands/network.py | ||
---|---|---|
63 | 63 |
def _run(self, service='network'): |
64 | 64 |
if getattr(self, 'cloud', None): |
65 | 65 |
base_url = self._custom_url(service) or self._custom_url( |
66 |
'compute')
|
|
66 |
'network')
|
|
67 | 67 |
if base_url: |
68 | 68 |
token = self._custom_token(service) or self._custom_token( |
69 |
'compute') or self.config.get_cloud('token')
|
|
69 |
'network') or self.config.get_cloud('token')
|
|
70 | 70 |
self.client = CycladesNetworkClient( |
71 | 71 |
base_url=base_url, token=token) |
72 | 72 |
return |
... | ... | |
74 | 74 |
self.cloud = 'default' |
75 | 75 |
if getattr(self, 'auth_base', False): |
76 | 76 |
cyclades_endpoints = self.auth_base.get_service_endpoints( |
77 |
self._custom_type('compute') or 'compute',
|
|
78 |
self._custom_version('compute') or '')
|
|
77 |
self._custom_type('network') or 'network',
|
|
78 |
self._custom_version('network') or '')
|
|
79 | 79 |
base_url = cyclades_endpoints['publicURL'] |
80 | 80 |
token = self.auth_base.token |
81 | 81 |
self.client = CycladesNetworkClient(base_url=base_url, token=token) |
... | ... | |
108 | 108 |
@errors.generic.all |
109 | 109 |
@errors.cyclades.connection |
110 | 110 |
def _run(self): |
111 |
detail = self['detail'] or self['user_id'] |
|
112 |
nets = self.client.list_networks(detail=detail) |
|
111 |
nets = self.client.list_networks() |
|
113 | 112 |
nets = self._filter_by_user_id(nets) |
114 | 113 |
nets = self._filter_by_name(nets) |
115 | 114 |
nets = self._filter_by_id(nets) |
116 |
if detail and not self['detail']:
|
|
115 |
if not self['detail']: |
|
117 | 116 |
nets = [dict( |
118 | 117 |
id=n['id'], name=n['name'], links=n['links']) for n in nets] |
119 | 118 |
kwargs = dict() |
... | ... | |
145 | 144 |
self._run(network_id=network_id) |
146 | 145 |
|
147 | 146 |
|
147 |
class NetworkTypeArgument(ValueArgument): |
|
148 |
|
|
149 |
types = ('CUSTOM', 'MAC_FILTERED', 'IP_LESS_ROUTED', 'PHYSICAL_VLAN') |
|
150 |
|
|
151 |
@property |
|
152 |
def value(self): |
|
153 |
return getattr(self, '_value', None) |
|
154 |
|
|
155 |
@value.setter |
|
156 |
def value(self, new_value): |
|
157 |
if new_value and new_value.upper() in self.types: |
|
158 |
self._value = new_value.upper() |
|
159 |
elif new_value: |
|
160 |
raise CLIInvalidArgument( |
|
161 |
'Invalid network type %s' % new_value, details=[ |
|
162 |
'Valid types: %s' % ', '.join(self.types), ]) |
|
163 |
|
|
164 |
|
|
148 | 165 |
@command(network_cmds) |
149 | 166 |
class network_create(_init_network, _optional_json): |
150 |
"""Create a new network |
|
151 |
Valid network types: CUSTOM MAC_FILTERED IP_LESS_ROUTED PHYSICAL_VLAN |
|
152 |
""" |
|
167 |
"""Create a new network""" |
|
153 | 168 |
|
154 | 169 |
arguments = dict( |
155 | 170 |
name=ValueArgument('Network name', '--name'), |
156 | 171 |
shared=FlagArgument( |
157 |
'Make network shared (special privileges required)', '--shared') |
|
172 |
'Make network shared (special privileges required)', '--shared'), |
|
173 |
network_type=NetworkTypeArgument( |
|
174 |
'Valid network types: %s' % (', '.join(NetworkTypeArgument.types)), |
|
175 |
'--type') |
|
158 | 176 |
) |
177 |
required = ('network_type', ) |
|
159 | 178 |
|
160 | 179 |
@errors.generic.all |
161 | 180 |
@errors.cyclades.connection |
... | ... | |
165 | 184 |
network_type, name=self['name'], shared=self['shared']) |
166 | 185 |
self._print(net, self.print_dict) |
167 | 186 |
|
168 |
def main(self, network_type):
|
|
187 |
def main(self): |
|
169 | 188 |
super(self.__class__, self)._run() |
170 |
self._run(network_type=network_type)
|
|
189 |
self._run(network_type=self['network_type'])
|
|
171 | 190 |
|
172 | 191 |
|
173 | 192 |
@command(network_cmds) |
... | ... | |
187 | 206 |
|
188 | 207 |
|
189 | 208 |
@command(network_cmds) |
190 |
class network_set(_init_network, _optional_json): |
|
191 |
"""Set an attribute of a network, leave the rest untouched (update) |
|
192 |
Only "--name" is supported for now |
|
193 |
""" |
|
209 |
class network_modify(_init_network, _optional_json): |
|
210 |
"""Modify network attributes""" |
|
194 | 211 |
|
195 |
arguments = dict(name=ValueArgument('New name of the network', '--name')) |
|
212 |
arguments = dict(new_name=ValueArgument('Rename the network', '--name')) |
|
213 |
required = ['new_name', ] |
|
196 | 214 |
|
197 | 215 |
@errors.generic.all |
198 | 216 |
@errors.cyclades.connection |
199 | 217 |
@errors.cyclades.network_id |
200 | 218 |
def _run(self, network_id): |
201 |
if self['name'] in (None, ): |
|
202 |
raise CLISyntaxError( |
|
203 |
'Missing network attributes to update', |
|
204 |
details=[ |
|
205 |
'At least one if the following is expected:', |
|
206 |
' --name=<new name>']) |
|
207 |
r = self.client.update_network(network_id, name=self['name']) |
|
219 |
r = self.client.update_network(network_id, name=self['new_name']) |
|
208 | 220 |
self._print(r, self.print_dict) |
209 | 221 |
|
210 | 222 |
def main(self, network_id): |
... | ... | |
225 | 237 |
'--more') |
226 | 238 |
) |
227 | 239 |
|
228 |
def _filter_by_user_id(self, nets): |
|
229 |
return filter_dicts_by_dict(nets, dict(user_id=self['user_id'])) if ( |
|
230 |
self['user_id']) else nets |
|
231 |
|
|
232 | 240 |
@errors.generic.all |
233 | 241 |
@errors.cyclades.connection |
234 | 242 |
def _run(self): |
235 | 243 |
nets = self.client.list_subnets() |
236 |
nets = self._filter_by_user_id(nets) |
|
237 | 244 |
nets = self._filter_by_name(nets) |
238 | 245 |
nets = self._filter_by_id(nets) |
239 | 246 |
if not self['detail']: |
... | ... | |
289 | 296 |
|
290 | 297 |
@command(subnet_cmds) |
291 | 298 |
class subnet_create(_init_network, _optional_json): |
292 |
"""Create a new subnet |
|
293 |
""" |
|
299 |
"""Create a new subnet""" |
|
294 | 300 |
|
295 | 301 |
arguments = dict( |
296 | 302 |
name=ValueArgument('Subnet name', '--name'), |
... | ... | |
301 | 307 |
gateway=ValueArgument('Gateway IP', '--gateway'), |
302 | 308 |
subnet_id=ValueArgument('The id for the subnet', '--id'), |
303 | 309 |
ipv6=FlagArgument('If set, IP version is set to 6, else 4', '--ipv6'), |
304 |
enable_dhcp=FlagArgument('Enable dhcp (default: off)', '--with-dhcp') |
|
310 |
enable_dhcp=FlagArgument('Enable dhcp (default: off)', '--with-dhcp'), |
|
311 |
network_id=ValueArgument('Set the network ID', '--network-id'), |
|
312 |
cidr=ValueArgument('Set the CIDR', '--cidr') |
|
305 | 313 |
) |
314 |
required = ('network_id', 'cidr') |
|
306 | 315 |
|
307 | 316 |
@errors.generic.all |
308 | 317 |
@errors.cyclades.connection |
... | ... | |
314 | 323 |
self['subnet_id'], self['ipv6'], self['enable_dhcp']) |
315 | 324 |
self._print(net, self.print_dict) |
316 | 325 |
|
317 |
def main(self, network_id, cidr):
|
|
326 |
def main(self): |
|
318 | 327 |
super(self.__class__, self)._run() |
319 |
self._run(network_id=network_id, cidr=cidr)
|
|
328 |
self._run(network_id=self['network_id'], cidr=self['cidr'])
|
|
320 | 329 |
|
321 | 330 |
|
322 | 331 |
# @command(subnet_cmds) |
... | ... | |
335 | 344 |
|
336 | 345 |
|
337 | 346 |
@command(subnet_cmds) |
338 |
class subnet_set(_init_network, _optional_json): |
|
339 |
"""Set an attribute of a subnet, leave the rest untouched (update) |
|
340 |
Only "--name" is supported for now |
|
341 |
""" |
|
347 |
class subnet_modify(_init_network, _optional_json): |
|
348 |
"""Modify the attributes of a subnet""" |
|
342 | 349 |
|
343 |
arguments = dict(name=ValueArgument('New name of the subnet', '--name')) |
|
350 |
arguments = dict( |
|
351 |
new_name=ValueArgument('New name of the subnet', '--name') |
|
352 |
) |
|
353 |
required = ['new_name'] |
|
344 | 354 |
|
345 | 355 |
@errors.generic.all |
346 | 356 |
@errors.cyclades.connection |
347 | 357 |
def _run(self, subnet_id): |
348 |
if self['name'] in (None, ): |
|
349 |
raise CLISyntaxError( |
|
350 |
'Missing subnet attributes to update', |
|
351 |
details=[ |
|
352 |
'At least one if the following is expected:', |
|
353 |
' --name=<new name>']) |
|
354 | 358 |
r = self.client.get_subnet_details(subnet_id) |
355 | 359 |
r = self.client.update_subnet( |
356 |
subnet_id, r['network_id'], name=self['name']) |
|
360 |
subnet_id, r['network_id'], name=self['new_name'])
|
|
357 | 361 |
self._print(r, self.print_dict) |
358 | 362 |
|
359 | 363 |
def main(self, subnet_id): |
... | ... | |
393 | 397 |
|
394 | 398 |
@command(port_cmds) |
395 | 399 |
class port_delete(_init_network, _optional_output_cmd): |
396 |
"""Delete a port""" |
|
400 |
"""Delete a port (== disconnect server from network)"""
|
|
397 | 401 |
|
398 | 402 |
@errors.generic.all |
399 | 403 |
@errors.cyclades.connection |
... | ... | |
407 | 411 |
|
408 | 412 |
|
409 | 413 |
@command(port_cmds) |
410 |
class port_set(_init_network, _optional_json): |
|
411 |
"""Set an attribute of a port, leave the rest untouched (update) |
|
412 |
Only "--name" is supported for now |
|
413 |
""" |
|
414 |
class port_modify(_init_network, _optional_json): |
|
415 |
"""Modify the attributes of a port""" |
|
414 | 416 |
|
415 |
arguments = dict(name=ValueArgument('New name of the port', '--name')) |
|
417 |
arguments = dict(new_name=ValueArgument('New name of the port', '--name')) |
|
418 |
required = ['new_name', ] |
|
416 | 419 |
|
417 | 420 |
@errors.generic.all |
418 | 421 |
@errors.cyclades.connection |
419 | 422 |
def _run(self, port_id): |
420 |
if self['name'] in (None, ): |
|
421 |
raise CLISyntaxError( |
|
422 |
'Missing port attributes to update', |
|
423 |
details=[ |
|
424 |
'At least one if the following is expected:', |
|
425 |
' --name=<new name>']) |
|
426 | 423 |
r = self.client.get_port_details(port_id) |
427 | 424 |
r = self.client.update_port( |
428 |
port_id, r['network_id'], name=self['name']) |
|
425 |
port_id, r['network_id'], name=self['new_name'])
|
|
429 | 426 |
self._print(r, self.print_dict) |
430 | 427 |
|
431 | 428 |
def main(self, port_id): |
... | ... | |
435 | 432 |
|
436 | 433 |
@command(port_cmds) |
437 | 434 |
class port_create(_init_network, _optional_json): |
438 |
"""Create a new port""" |
|
435 |
"""Create a new port (== connect server to network)"""
|
|
439 | 436 |
|
440 | 437 |
arguments = dict( |
441 | 438 |
name=ValueArgument('A human readable name', '--name'), |
... | ... | |
446 | 443 |
'Subnet id for fixed ips (used with --ip-address)', |
447 | 444 |
'--subnet-id'), |
448 | 445 |
ip_address=ValueArgument( |
449 |
'IP address for subnet id (used with --subnet-id', '--ip-address') |
|
446 |
'IP address for subnet id (used with --subnet-id', '--ip-address'), |
|
447 |
network_id=ValueArgument('Set the network ID', '--network-id'), |
|
448 |
device_id=ValueArgument( |
|
449 |
'The device is either a virtual server or a virtual router', |
|
450 |
'--device-id') |
|
450 | 451 |
) |
452 |
retuired = ('network_id', 'device_id') |
|
451 | 453 |
|
452 | 454 |
@errors.generic.all |
453 | 455 |
@errors.cyclades.connection |
454 | 456 |
@errors.cyclades.network_id |
455 | 457 |
def _run(self, network_id, device_id): |
456 |
if bool(self['subnet_id']) != bool(self['ip_address']): |
|
457 |
raise CLIInvalidArgument('Invalid use of arguments', details=[ |
|
458 |
'--subnet-id and --ip-address should be used together']) |
|
459 | 458 |
fixed_ips = [dict( |
460 | 459 |
subnet_id=self['subnet_id'], ip_address=self['ip_address'])] if ( |
461 | 460 |
self['subnet_id']) else None |
... | ... | |
466 | 465 |
fixed_ips=fixed_ips) |
467 | 466 |
self._print(r, self.print_dict) |
468 | 467 |
|
469 |
def main(self, network_id, device_id):
|
|
468 |
def main(self): |
|
470 | 469 |
super(self.__class__, self)._run() |
471 |
self._run(network_id=network_id, device_id=device_id) |
|
470 |
self._run(network_id=self['network_id'], device_id=self['device_id']) |
|
471 |
|
|
472 |
|
|
473 |
# Warn users for some importand changes |
|
474 |
|
|
475 |
|
|
476 |
@command(network_cmds) |
|
477 |
class network_connect(_init_network, _optional_output_cmd): |
|
478 |
"""DEPRECATED, use /port create""" |
|
479 |
|
|
480 |
def main(self): |
|
481 |
raise CLISyntaxError('DEPRECATED, replaced by kamaki port create') |
|
482 |
|
|
483 |
|
|
484 |
@command(network_cmds) |
|
485 |
class network_disconnect(_init_network): |
|
486 |
"""DEPRECATED, /use port delete""" |
|
487 |
|
|
488 |
def main(self): |
|
489 |
raise CLISyntaxError('DEPRECATED, replaced by kamaki port delete') |
b/kamaki/cli/one_command.py | ||
---|---|---|
93 | 93 |
update_parser_help(parser, cmd) |
94 | 94 |
|
95 | 95 |
if _help or not cmd.is_command: |
96 |
#parser.parser.print_help() |
|
97 | 96 |
if cmd.cmd_class: |
98 | 97 |
parser.required = getattr(cmd.cmd_class, 'required', None) |
99 | 98 |
parser.print_help() |
... | ... | |
109 | 108 |
parser.required = getattr(cls, 'required', None) |
110 | 109 |
parser.update_arguments(executable.arguments) |
111 | 110 |
for term in _best_match: |
112 |
parser.unparsed.remove(term) |
|
113 |
#exec_cmd(executable, parser.unparsed, parser.parser.print_help) |
|
111 |
parser.unparsed.remove(term) |
|
114 | 112 |
exec_cmd(executable, parser.unparsed, parser.print_help) |
b/kamaki/clients/compute/__init__.py | ||
---|---|---|
113 | 113 |
availability_zone=None, |
114 | 114 |
metadata=None, |
115 | 115 |
personality=None, |
116 |
networks=None, |
|
116 | 117 |
response_headers=dict(location=None)): |
117 | 118 |
"""Submit request to create a new server |
118 | 119 |
|
... | ... | |
140 | 141 |
if personality: |
141 | 142 |
req['server']['personality'] = personality |
142 | 143 |
|
144 |
if networks: |
|
145 |
req['server']['networks'] = networks |
|
146 |
|
|
143 | 147 |
r = self.servers_post( |
144 | 148 |
json_data=req, |
145 | 149 |
security_group=security_group, |
b/kamaki/clients/cyclades/__init__.py | ||
---|---|---|
44 | 44 |
|
45 | 45 |
def create_server( |
46 | 46 |
self, name, flavor_id, image_id, |
47 |
metadata=None, personality=None): |
|
47 |
metadata=None, personality=None, networks=None):
|
|
48 | 48 |
"""Submit request to create a new server |
49 | 49 |
|
50 | 50 |
:param name: (str) |
... | ... | |
58 | 58 |
:param personality: a list of (file path, file contents) tuples, |
59 | 59 |
describing files to be injected into virtual server upon creation |
60 | 60 |
|
61 |
:param networks: (list of dicts) Networks to connect to, list this: |
|
62 |
"networks": [ |
|
63 |
{"network": <network_uuid>}, |
|
64 |
{"network": <network_uuid>, "fixed_ip": address}, |
|
65 |
{"port": <port_id>}, ...] |
|
66 |
|
|
61 | 67 |
:returns: a dict with the new virtual server details |
62 | 68 |
|
63 | 69 |
:raises ClientError: wraps request errors |
b/kamaki/clients/cyclades/rest_api.py | ||
---|---|---|
44 | 44 |
path = path4url('servers', server_id, 'stats') |
45 | 45 |
return self.get(path, success=success, **kwargs) |
46 | 46 |
|
47 |
def networks_get( |
|
48 |
self, |
|
49 |
network_id='', |
|
50 |
command='', |
|
51 |
success=(200, 203), |
|
52 |
**kwargs): |
|
53 |
"""GET base_url/networks[/network_id][/command] request |
|
54 |
|
|
55 |
:param network_id: integer (str or int) |
|
56 |
|
|
57 |
:param command: (str) 'detail' or '' |
|
58 |
|
|
59 |
:param success: success code or list or tuple of accepted success |
|
60 |
codes. if server response code is not in this list, a ClientError |
|
61 |
raises |
|
62 |
|
|
63 |
:returns: request response |
|
64 |
""" |
|
65 |
path = path4url('networks', network_id, command) |
|
66 |
return self.get(path, success=success, **kwargs) |
|
67 |
|
|
68 |
def networks_delete( |
|
69 |
self, |
|
70 |
network_id='', |
|
71 |
command='', |
|
72 |
success=204, |
|
73 |
**kwargs): |
|
74 |
"""DEL ETE base_url/networks[/network_id][/command] request |
|
75 |
|
|
76 |
:param network_id: integer (str or int) |
|
77 |
|
|
78 |
:param command: (str) 'detail' or '' |
|
79 |
|
|
80 |
:param success: success code or list or tuple of accepted success |
|
81 |
codes. if server response code is not in this list, a ClientError |
|
82 |
raises |
|
83 |
|
|
84 |
:returns: request response |
|
85 |
""" |
|
86 |
path = path4url('networks', network_id, command) |
|
87 |
return self.delete(path, success=success, **kwargs) |
|
88 |
|
|
89 |
def networks_post( |
|
90 |
self, |
|
91 |
network_id='', |
|
92 |
command='', |
|
93 |
json_data=None, |
|
94 |
success=202, |
|
95 |
**kwargs): |
|
96 |
"""POST base_url/servers[/server_id]/[command] request |
|
97 |
|
|
98 |
:param network_id: integer (str or int) |
|
99 |
|
|
100 |
:param command: (str) 'detail' or '' |
|
101 |
|
|
102 |
:param json_data: (dict) will be send as data |
|
103 |
|
|
104 |
:param success: success code or list or tuple of accepted success |
|
105 |
codes. if server response code is not in this list, a ClientError |
|
106 |
raises |
|
107 |
|
|
108 |
:returns: request response |
|
109 |
""" |
|
110 |
data = json_data |
|
111 |
if json_data is not None: |
|
112 |
data = json.dumps(json_data) |
|
113 |
self.set_header('Content-Type', 'application/json') |
|
114 |
self.set_header('Content-Length', len(data)) |
|
115 |
|
|
116 |
path = path4url('networks', network_id, command) |
|
117 |
return self.post(path, data=data, success=success, **kwargs) |
|
118 |
|
|
119 |
def networks_put( |
|
120 |
self, |
|
121 |
network_id='', |
|
122 |
command='', |
|
123 |
json_data=None, |
|
124 |
success=204, |
|
125 |
**kwargs): |
|
126 |
"""PUT base_url/servers[/server_id]/[command] request |
|
127 |
|
|
128 |
:param network_id: integer (str or int) |
|
129 |
|
|
130 |
:param command: (str) 'detail' or '' |
|
131 |
|
|
132 |
:param json_data: (dict) will be send as data |
|
133 |
|
|
134 |
:param success: success code or list or tuple of accepted success |
|
135 |
codes. if server response code is not in this list, a ClientError |
|
136 |
raises |
|
137 |
|
|
138 |
:returns: request response |
|
139 |
""" |
|
140 |
data = json_data |
|
141 |
if json_data is not None: |
|
142 |
data = json.dumps(json_data) |
|
143 |
self.set_header('Content-Type', 'application/json') |
|
144 |
self.set_header('Content-Length', len(data)) |
|
145 |
|
|
146 |
path = path4url('networks', network_id, command) |
|
147 |
return self.put(path, data=data, success=success, **kwargs) |
|
47 |
# def networks_get( |
|
48 |
# self, |
|
49 |
# network_id='', |
|
50 |
# command='', |
|
51 |
# success=(200, 203), |
|
52 |
# **kwargs): |
|
53 |
# """GET base_url/networks[/network_id][/command] request |
|
54 |
|
|
55 |
# :param network_id: integer (str or int) |
|
56 |
|
|
57 |
# :param command: (str) 'detail' or '' |
|
58 |
|
|
59 |
# :param success: success code or list or tuple of accepted success |
|
60 |
# codes. if server response code is not in this list, a ClientError |
|
61 |
# raises |
|
62 |
|
|
63 |
# :returns: request response |
|
64 |
# """ |
|
65 |
# path = path4url('networks', network_id, command) |
|
66 |
# return self.get(path, success=success, **kwargs) |
|
67 |
|
|
68 |
# def networks_delete( |
|
69 |
# self, |
|
70 |
# network_id='', |
|
71 |
# command='', |
|
72 |
# success=204, |
|
73 |
# **kwargs): |
|
74 |
# """DEL ETE base_url/networks[/network_id][/command] request |
|
75 |
|
|
76 |
# :param network_id: integer (str or int) |
|
77 |
|
|
78 |
# :param command: (str) 'detail' or '' |
|
79 |
|
|
80 |
# :param success: success code or list or tuple of accepted success |
|
81 |
# codes. if server response code is not in this list, a ClientError |
|
82 |
# raises |
|
83 |
|
|
84 |
# :returns: request response |
|
85 |
# """ |
|
86 |
# path = path4url('networks', network_id, command) |
|
87 |
# return self.delete(path, success=success, **kwargs) |
|
88 |
|
|
89 |
# def networks_post( |
|
90 |
# self, |
|
91 |
# network_id='', |
|
92 |
# command='', |
|
93 |
# json_data=None, |
|
94 |
# success=202, |
|
95 |
# **kwargs): |
|
96 |
# """POST base_url/servers[/server_id]/[command] request |
|
97 |
|
|
98 |
# :param network_id: integer (str or int) |
|
99 |
|
|
100 |
# :param command: (str) 'detail' or '' |
|
101 |
|
|
102 |
# :param json_data: (dict) will be send as data |
|
103 |
|
|
104 |
# :param success: success code or list or tuple of accepted success |
|
105 |
# codes. if server response code is not in this list, a ClientError |
|
106 |
# raises |
|
107 |
|
|
108 |
# :returns: request response |
|
109 |
# """ |
|
110 |
# data = json_data |
|
111 |
# if json_data is not None: |
|
112 |
# data = json.dumps(json_data) |
|
113 |
# self.set_header('Content-Type', 'application/json') |
|
114 |
# self.set_header('Content-Length', len(data)) |
|
115 |
|
|
116 |
# path = path4url('networks', network_id, command) |
|
117 |
# return self.post(path, data=data, success=success, **kwargs) |
|
118 |
|
|
119 |
# def networks_put( |
|
120 |
# self, |
|
121 |
# network_id='', |
|
122 |
# command='', |
|
123 |
# json_data=None, |
|
124 |
# success=204, |
|
125 |
# **kwargs): |
|
126 |
# """PUT base_url/servers[/server_id]/[command] request |
|
127 |
|
|
128 |
# :param network_id: integer (str or int) |
|
129 |
|
|
130 |
# :param command: (str) 'detail' or '' |
|
131 |
|
|
132 |
# :param json_data: (dict) will be send as data |
|
133 |
|
|
134 |
# :param success: success code or list or tuple of accepted success |
|
135 |
# codes. if server response code is not in this list, a ClientError |
|
136 |
# raises |
|
137 |
|
|
138 |
# :returns: request response |
|
139 |
# """ |
|
140 |
# data = json_data |
|
141 |
# if json_data is not None: |
|
142 |
# data = json.dumps(json_data) |
|
143 |
# self.set_header('Content-Type', 'application/json') |
|
144 |
# self.set_header('Content-Length', len(data)) |
|
145 |
|
|
146 |
# path = path4url('networks', network_id, command) |
|
147 |
# return self.put(path, data=data, success=success, **kwargs) |
Also available in: Unified diff