Revision f3e94e06 kamaki/cli/commands/cyclades_cli.py
b/kamaki/cli/commands/cyclades_cli.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from kamaki.cli import command#, set_api_description |
|
35 |
from kamaki.cli.utils import print_dict, print_items, print_list, format_size, bold |
|
36 |
from kamaki.cli.errors import CLIError, raiseCLIError |
|
37 |
#set_api_description('server', "Compute/Cyclades API server commands") |
|
38 |
#set_api_description('flavor', "'Compute/Cyclades API flavor commands'") |
|
39 |
#set_api_description('image', "Compute/Cyclades or Glance API image commands") |
|
40 |
#set_api_description('network', "Compute/Cyclades API network commands") |
|
41 | 34 |
API_DESCRIPTION = {'server':'Compute/Cyclades API server commands', |
42 | 35 |
'flavor':'Compute/Cyclades API flavor commands', |
43 | 36 |
'image':'Compute/Cyclades or Glance API image commands', |
44 | 37 |
'network': 'Compute/Cyclades API network commands'} |
38 |
|
|
39 |
from kamaki.cli import command |
|
40 |
from kamaki.cli.utils import print_dict, print_items, print_list, format_size, bold |
|
41 |
from kamaki.cli.errors import CLIError, raiseCLIError |
|
45 | 42 |
from kamaki.clients.cyclades import CycladesClient, ClientError |
43 |
from kamaki.cli.argument import FlagArgument, ValueArgument |
|
44 |
from base64 import b64encode |
|
45 |
from os.path import abspath, exists |
|
46 | 46 |
|
47 | 47 |
class _init_cyclades(object): |
48 |
def main(self): |
|
49 |
token = self.config.get('compute', 'token') or self.config.get('global', 'token') |
|
50 |
base_url = self.config.get('compute', 'url') or self.config.get('global', 'url') |
|
48 |
def __init__(self, arguments={}): |
|
49 |
self.arguments=arguments |
|
50 |
try: |
|
51 |
self.config = self.get_argument('config') |
|
52 |
except KeyError: |
|
53 |
pass |
|
54 |
|
|
55 |
def get_argument(self, argterm): |
|
56 |
return self.arguments[argterm].value |
|
57 |
|
|
58 |
def main(self, service='compute'): |
|
59 |
token = self.config.get(service, 'token') or self.config.get('global', 'token') |
|
60 |
base_url = self.config.get(service, 'url') or self.config.get('global', 'url') |
|
51 | 61 |
self.client = CycladesClient(base_url=base_url, token=token) |
52 | 62 |
|
63 |
class _init_server(_init_cyclades): |
|
64 |
def main(self): |
|
65 |
super(_init_server, self).main('server') |
|
66 |
|
|
53 | 67 |
@command() |
54 | 68 |
class server_list(_init_cyclades): |
55 | 69 |
"""List servers""" |
56 | 70 |
|
71 |
def __init__(self, arguments={}): |
|
72 |
super(server_list, self).__init__(arguments) |
|
73 |
self.arguments['detail'] = FlagArgument('show detailed output', '-l') |
|
74 |
|
|
57 | 75 |
def _print(self, servers): |
58 | 76 |
for server in servers: |
59 | 77 |
sname = server.pop('name') |
60 | 78 |
sid = server.pop('id') |
61 | 79 |
print('%s (%s)'%(bold(sname), bold(unicode(sid)))) |
62 |
if getattr(self.args, 'detail'):
|
|
80 |
if self.get_argument('detail'):
|
|
63 | 81 |
server_info._print(server) |
64 | 82 |
print('- - -') |
65 | 83 |
|
66 |
def update_parser(self, parser): |
|
67 |
parser.add_argument('-l', dest='detail', action='store_true', |
|
68 |
default=False, help='show detailed output') |
|
69 |
|
|
70 | 84 |
def main(self): |
71 | 85 |
super(self.__class__, self).main() |
72 | 86 |
try: |
73 |
servers = self.client.list_servers(self.args.detail)
|
|
87 |
servers = self.client.list_servers(self.get_argument('detail'))
|
|
74 | 88 |
self._print(servers) |
75 | 89 |
#print_items(servers) |
76 | 90 |
except ClientError as err: |
... | ... | |
107 | 121 |
importance=1) |
108 | 122 |
self._print(server) |
109 | 123 |
|
124 |
class PersonalityArgument(ValueArgument): |
|
125 |
@property |
|
126 |
def value(self): |
|
127 |
return [self._value] if hasattr(self, '_value') else [] |
|
128 |
@value.setter |
|
129 |
def value(self, newvalue): |
|
130 |
if newvalue == self.default: |
|
131 |
return self.value |
|
132 |
termlist = newvalue.split() |
|
133 |
if len(termlist) > 4: |
|
134 |
raise CLISyntaxError(details='Wrong number of personality terms ("PATH [OWNER [GROUP [MODE]]]"') |
|
135 |
path = termlist[0] |
|
136 |
self._value = dict(path=path) |
|
137 |
if not exists(path): |
|
138 |
raise CLIError(message="File %s does not exist" % path, importance=1) |
|
139 |
with open(path) as f: |
|
140 |
self._value['contents'] = b64encode(f.read()) |
|
141 |
try: |
|
142 |
self._value['owner'] = termlist[1] |
|
143 |
self._value['group'] = termlist[2] |
|
144 |
self._value['mode'] = termlist[3] |
|
145 |
except IndexError: |
|
146 |
pass |
|
147 |
|
|
110 | 148 |
@command() |
111 | 149 |
class server_create(_init_cyclades): |
112 | 150 |
"""Create a server""" |
113 | 151 |
|
152 |
def __init__(self, arguments={}): |
|
153 |
super(server_create, self).__init__(arguments) |
|
154 |
self.arguments['personality'] = PersonalityArgument(parsed_name='--personality', |
|
155 |
help='add a personality file ( "PATH [OWNER [GROUP [MODE]]]" )') |
|
156 |
|
|
114 | 157 |
def update_parser(self, parser): |
115 | 158 |
parser.add_argument('--personality', dest='personalities', |
116 | 159 |
action='append', default=[], |
... | ... | |
119 | 162 |
|
120 | 163 |
def main(self, name, flavor_id, image_id): |
121 | 164 |
super(self.__class__, self).main() |
122 |
personalities = [] |
|
123 |
for personality in self.args.personalities: |
|
124 |
p = personality.split(',') |
|
125 |
p.extend([None] * (5 - len(p))) # Fill missing fields with None |
|
126 |
|
|
127 |
path = p[0] |
|
128 |
|
|
129 |
if not path: |
|
130 |
raise CLIError(message='Invalid personality argument %s'%p, importance=1) |
|
131 |
if not exists(path): |
|
132 |
raise CLIError(message="File %s does not exist" % path, importance=1) |
|
133 |
|
|
134 |
with open(path) as f: |
|
135 |
contents = b64encode(f.read()) |
|
136 |
|
|
137 |
d = {'path': p[1] or abspath(path), 'contents': contents} |
|
138 |
if p[2]: |
|
139 |
d['owner'] = p[2] |
|
140 |
if p[3]: |
|
141 |
d['group'] = p[3] |
|
142 |
if p[4]: |
|
143 |
d['mode'] = int(p[4]) |
|
144 |
personalities.append(d) |
|
145 |
|
|
146 | 165 |
try: |
147 | 166 |
reply = self.client.create_server(name, int(flavor_id), image_id, |
148 |
personalities)
|
|
167 |
self.get_argument('personality'))
|
|
149 | 168 |
except ClientError as err: |
150 | 169 |
raiseCLIError(err) |
151 | 170 |
print_dict(reply) |
... | ... | |
180 | 199 |
class server_reboot(_init_cyclades): |
181 | 200 |
"""Reboot a server""" |
182 | 201 |
|
183 |
def update_parser(self, parser):
|
|
184 |
parser.add_argument('-f', dest='hard', action='store_true',
|
|
185 |
default=False, help='perform a hard reboot')
|
|
202 |
def __init__(self, arguments={}):
|
|
203 |
super(server_reboot, self).__init__(arguments)
|
|
204 |
self.arguments['hard'] = FlagArgument('perform a hard reboot', '-f')
|
|
186 | 205 |
|
187 | 206 |
def main(self, server_id): |
188 | 207 |
super(self.__class__, self).main() |
189 | 208 |
try: |
190 |
self.client.reboot_server(int(server_id), self.args.hard)
|
|
209 |
self.client.reboot_server(int(server_id), self.get_argument('hard'))
|
|
191 | 210 |
except ClientError as err: |
192 | 211 |
raiseCLIError(err) |
193 | 212 |
except ValueError: |
... | ... | |
245 | 264 |
raiseCLIError(err) |
246 | 265 |
except ValueError: |
247 | 266 |
raise CLIError(message='Server id must be positive integer', importance=1) |
267 |
|
|
248 | 268 |
@command() |
249 | 269 |
class server_addr(_init_cyclades): |
250 | 270 |
"""List a server's nic address""" |
... | ... | |
333 | 353 |
class flavor_list(_init_cyclades): |
334 | 354 |
"""List flavors""" |
335 | 355 |
|
336 |
def update_parser(self, parser):
|
|
337 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
338 |
default=False, help='show detailed output')
|
|
356 |
def __init__(self, arguments={}):
|
|
357 |
super(flavor_list, self).__init__(arguments)
|
|
358 |
self.arguments['detail'] = FlagArgument('show detailed output', '-l')
|
|
339 | 359 |
|
340 | 360 |
def main(self): |
341 | 361 |
super(self.__class__, self).main() |
342 | 362 |
try: |
343 |
flavors = self.client.list_flavors(self.args.detail)
|
|
363 |
flavors = self.client.list_flavors(self.get_argument('detail'))
|
|
344 | 364 |
except ClientError as err: |
345 | 365 |
raiseCLIError(err) |
346 | 366 |
print_items(flavors) |
... | ... | |
360 | 380 |
print_dict(flavor) |
361 | 381 |
|
362 | 382 |
@command() |
363 |
class image_list(_init_cyclades): |
|
364 |
"""List images""" |
|
365 |
|
|
366 |
def update_parser(self, parser): |
|
367 |
parser.add_argument('-l', dest='detail', action='store_true', |
|
368 |
default=False, help='show detailed output') |
|
369 |
|
|
370 |
def _print(self, images): |
|
371 |
for img in images: |
|
372 |
iname = img.pop('name') |
|
373 |
iid = img.pop('id') |
|
374 |
print('%s (%s)'%(bold(iname), bold(unicode(iid)))) |
|
375 |
if getattr(self.args, 'detail'): |
|
376 |
image_info._print(img) |
|
377 |
print('- - -') |
|
378 |
|
|
379 |
def main(self): |
|
380 |
super(self.__class__, self).main() |
|
381 |
try: |
|
382 |
images = self.client.list_images(self.args.detail) |
|
383 |
except ClientError as err: |
|
384 |
raiseCLIError(err) |
|
385 |
#print_items(images) |
|
386 |
self._print(images) |
|
387 |
|
|
388 |
@command() |
|
389 |
class image_info(_init_cyclades): |
|
390 |
"""Get image details""" |
|
391 |
|
|
392 |
@classmethod |
|
393 |
def _print(self, image): |
|
394 |
if image.has_key('metadata'): |
|
395 |
image['metadata'] = image['metadata']['values'] |
|
396 |
print_dict(image, ident=14) |
|
397 |
|
|
398 |
def main(self, image_id): |
|
399 |
super(self.__class__, self).main() |
|
400 |
try: |
|
401 |
image = self.client.get_image_details(image_id) |
|
402 |
except ClientError as err: |
|
403 |
raiseCLIError(err) |
|
404 |
self._print(image) |
|
405 |
|
|
406 |
@command() |
|
407 |
class image_delete(_init_cyclades): |
|
408 |
"""Delete image""" |
|
409 |
|
|
410 |
def main(self, image_id): |
|
411 |
super(self.__class__, self).main() |
|
412 |
try: |
|
413 |
self.client.delete_image(image_id) |
|
414 |
except ClientError as err: |
|
415 |
raiseCLIError(err) |
|
416 |
|
|
417 |
@command() |
|
418 |
class image_properties(_init_cyclades): |
|
419 |
"""Get image properties""" |
|
420 |
|
|
421 |
def main(self, image_id, key=None): |
|
422 |
super(self.__class__, self).main() |
|
423 |
try: |
|
424 |
reply = self.client.get_image_metadata(image_id, key) |
|
425 |
except ClientError as err: |
|
426 |
raiseCLIError(err) |
|
427 |
print_dict(reply) |
|
428 |
|
|
429 |
@command() |
|
430 |
class image_addproperty(_init_cyclades): |
|
431 |
"""Add an image property""" |
|
432 |
|
|
433 |
def main(self, image_id, key, val): |
|
434 |
super(self.__class__, self).main() |
|
435 |
try: |
|
436 |
reply = self.client.create_image_metadata(image_id, key, val) |
|
437 |
except ClientError as err: |
|
438 |
raiseCLIError(err) |
|
439 |
print_dict(reply) |
|
440 |
|
|
441 |
@command() |
|
442 |
class image_setproperty(_init_cyclades): |
|
443 |
"""Update an image property""" |
|
444 |
|
|
445 |
def main(self, image_id, key, val): |
|
446 |
super(self.__class__, self).main() |
|
447 |
metadata = {key: val} |
|
448 |
try: |
|
449 |
reply = self.client.update_image_metadata(image_id, **metadata) |
|
450 |
except ClientError as err: |
|
451 |
raiseCLIError(err) |
|
452 |
print_dict(reply) |
|
453 |
|
|
454 |
@command() |
|
455 |
class image_delproperty(_init_cyclades): |
|
456 |
"""Delete an image property""" |
|
457 |
|
|
458 |
def main(self, image_id, key): |
|
459 |
super(self.__class__, self).main() |
|
460 |
try: |
|
461 |
self.client.delete_image_metadata(image_id, key) |
|
462 |
except ClientError as err: |
|
463 |
raiseCLIError(err) |
|
464 |
|
|
465 |
@command() |
|
466 | 383 |
class network_list(_init_cyclades): |
467 | 384 |
"""List networks""" |
468 | 385 |
|
469 |
def update_parser(self, parser):
|
|
470 |
parser.add_argument('-l', dest='detail', action='store_true',
|
|
471 |
default=False, help='show detailed output')
|
|
386 |
def __init__(self, arguments={}):
|
|
387 |
super(network_list, self).__init__(arguments)
|
|
388 |
self.arguments['detail'] = FlagArgument('show detailed output', '-l')
|
|
472 | 389 |
|
473 | 390 |
def print_networks(self, nets): |
474 | 391 |
for net in nets: |
475 | 392 |
netname = bold(net.pop('name')) |
476 | 393 |
netid = bold(unicode(net.pop('id'))) |
477 | 394 |
print('%s (%s)'%(netname, netid)) |
478 |
if getattr(self.args, 'detail'):
|
|
395 |
if self.get_argument('detail'):
|
|
479 | 396 |
network_info.print_network(net) |
480 |
print('- - -') |
|
481 | 397 |
|
482 | 398 |
def main(self): |
483 | 399 |
super(self.__class__, self).main() |
484 | 400 |
try: |
485 |
networks = self.client.list_networks(self.args.detail)
|
|
401 |
networks = self.client.list_networks(self.get_argument('detail'))
|
|
486 | 402 |
except ClientError as err: |
487 | 403 |
raiseCLIError(err) |
488 | 404 |
self.print_networks(networks) |
... | ... | |
491 | 407 |
class network_create(_init_cyclades): |
492 | 408 |
"""Create a network""" |
493 | 409 |
|
494 |
def update_parser(self, parser): |
|
495 |
try: |
|
496 |
super(self.__class__, self).update_parser(parser) |
|
497 |
except AttributeError: |
|
498 |
pass |
|
499 |
parser.add_argument('--with-cidr', action='store', dest='cidr', default=False, |
|
500 |
help='specific cidr for new network') |
|
501 |
parser.add_argument('--with-gateway', action='store', dest='gateway', default=False, |
|
502 |
help='specific getaway for new network') |
|
503 |
parser.add_argument('--with-dhcp', action='store', dest='dhcp', default=False, |
|
504 |
help='specific dhcp for new network') |
|
505 |
parser.add_argument('--with-type', action='store', dest='type', default=False, |
|
506 |
help='specific type for new network') |
|
410 |
def __init__(self, arguments={}): |
|
411 |
super(network_create, self).__init__(arguments) |
|
412 |
self.arguments['cidr'] = ValueArgument('specific cidr for new network', '--with-cidr') |
|
413 |
self.arguments['gateway'] = ValueArgument('specific gateway for new network', |
|
414 |
'--with-gateway') |
|
415 |
self.arguments['dhcp'] = ValueArgument('specific dhcp for new network', '--with-dhcp') |
|
416 |
self.arguments['type'] = ValueArgument('specific type for new network', '--with-type') |
|
507 | 417 |
|
508 | 418 |
def main(self, name): |
509 | 419 |
super(self.__class__, self).main() |
510 | 420 |
try: |
511 |
reply = self.client.create_network(name, cidr=getattr(self.args, 'cidr'),
|
|
512 |
gateway=getattr(self.args, 'gateway'), dhcp=getattr(self.args, 'gateway'),
|
|
513 |
type=getattr(self.args, 'type'))
|
|
421 |
reply = self.client.create_network(name, cidr= self.get_argument('cidr'),
|
|
422 |
gateway=self.get_argument('gateway'), dhcp=self.get_argument('dhcp'),
|
|
423 |
type=self.get_argument('type'))
|
|
514 | 424 |
except ClientError as err: |
515 | 425 |
raiseCLIError(err) |
516 | 426 |
print_dict(reply) |
Also available in: Unified diff