Revision f3e94e06
b/kamaki/cli/__init__.py | ||
---|---|---|
43 | 43 |
|
44 | 44 |
from inspect import getargspec |
45 | 45 |
from argparse import ArgumentParser, ArgumentError |
46 |
from base64 import b64encode |
|
47 |
from os.path import abspath, basename, exists |
|
46 |
from os.path import basename |
|
48 | 47 |
from sys import exit, stdout, stderr, argv |
49 | 48 |
|
50 | 49 |
try: |
... | ... | |
268 | 267 |
if group is None: |
269 | 268 |
parser.print_help() |
270 | 269 |
shallow_load() |
271 |
print_commands(full_depth=_verbose)
|
|
270 |
print_commands(full_depth=_debug)
|
|
272 | 271 |
exit(0) |
273 | 272 |
|
274 | 273 |
cmd = load_command(group, unparsed) |
... | ... | |
294 | 293 |
allow_subclass_signatures = True |
295 | 294 |
load_command(group, cmd.path.split('_')[1:], reload_package=True) |
296 | 295 |
|
297 |
print_commands(cmd.path, full_depth=_verbose)
|
|
296 |
print_commands(cmd.path, full_depth=_debug)
|
|
298 | 297 |
exit(0) |
299 | 298 |
|
300 | 299 |
setup_logging(silent=_arguments['silent'].value, debug=_debug, verbose=_verbose, |
b/kamaki/cli/argument.py | ||
---|---|---|
200 | 200 |
lines.append(line) |
201 | 201 |
return lines |
202 | 202 |
|
203 |
class KeyValueArgument(ValueArgument): |
|
204 |
def __init__(self, help='', parsed_name=None, default={}): |
|
205 |
super(KeyValueArgument,self).__init__(help, parsed_name, default) |
|
206 |
|
|
207 |
@property |
|
208 |
def value(self): |
|
209 |
return super(KeyValueArgument, self).value |
|
210 |
@value.setter |
|
211 |
def value(self, keyvalue_pairs): |
|
212 |
if keyvalue_pairs == self.default: |
|
213 |
return |
|
214 |
if isinstance(keyvalue_pairs, str): |
|
215 |
keyvalue_pairs = keyvalue_pairs.trim().split(' ') |
|
216 |
self._value = self.default |
|
217 |
for pair in keyvalue_pairs: |
|
218 |
key,sep,val = pair.partition('=') |
|
219 |
if not sep: |
|
220 |
raise CLISyntaxError(details='Missing "="" ( "key1=val1 key2=val2 ..."') |
|
221 |
self._value[key.trim()] = val.trim() |
|
222 |
|
|
203 | 223 |
_arguments = dict(config = _config_arg, help = Argument(0, 'Show help message', ('-h', '--help')), |
204 | 224 |
debug = FlagArgument('Include debug output', ('-d', '--debug')), |
205 | 225 |
include = FlagArgument('Include protocol headers in the output', ('-i', '--include')), |
206 | 226 |
silent = FlagArgument('Do not output anything', ('-s', '--silent')), |
207 | 227 |
verbose = FlagArgument('More info at response', ('-v', '--verbose')), |
208 | 228 |
version = VersionArgument('Print current version', ('-V', '--version')), |
209 |
options = CmdLineConfigArgument(_config_arg, 'Override a config vale', ('-o', '--options')), |
|
229 |
options = CmdLineConfigArgument(_config_arg, 'Override a config value', ('-o', '--options')),
|
|
210 | 230 |
history = HistoryArgument('Show user (prefixed) history', '--history') |
211 | 231 |
) |
212 | 232 |
|
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) |
b/kamaki/cli/commands/image_cli.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A.command |
33 | 33 |
|
34 |
from kamaki.cli import command#, set_api_description |
|
34 |
API_DESCRIPTION = {'image':'Compute/Cyclades or Glance API image commands'} |
|
35 |
|
|
36 |
from kamaki.cli import command |
|
35 | 37 |
from kamaki.cli.errors import raiseCLIError |
36 | 38 |
from kamaki.cli.utils import print_dict, print_items |
37 |
#set_api_description('image', "Compute/Cyclades or Glance API image commands") |
|
38 |
API_DESCRIPTION = {'image':'Compute/Cyclades or Glance API image commands'} |
|
39 | 39 |
from kamaki.clients.image import ImageClient, ClientError |
40 |
|
|
40 |
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument, IntArgument |
|
41 |
from .cyclades_cli import _init_cyclades |
|
41 | 42 |
|
42 | 43 |
class _init_image(object): |
44 |
def __init__(self, arguments={}): |
|
45 |
self.arguments=arguments |
|
46 |
try: |
|
47 |
self.config = self.get_argument('config') |
|
48 |
except KeyError: |
|
49 |
pass |
|
50 |
|
|
51 |
def get_argument(self, arg_name): |
|
52 |
return self.arguments[arg_name].value |
|
53 |
|
|
43 | 54 |
def main(self): |
44 | 55 |
try: |
45 | 56 |
token = self.config.get('image', 'token') or self.config.get('global', 'token') |
... | ... | |
52 | 63 |
class image_public(_init_image): |
53 | 64 |
"""List public images""" |
54 | 65 |
|
55 |
def update_parser(self, parser): |
|
56 |
parser.add_argument('-l', dest='detail', action='store_true', |
|
57 |
default=False, help='show detailed output') |
|
58 |
parser.add_argument('--container-format', dest='container_format', |
|
59 |
metavar='FORMAT', help='filter by container format') |
|
60 |
parser.add_argument('--disk-format', dest='disk_format', |
|
61 |
metavar='FORMAT', help='filter by disk format') |
|
62 |
parser.add_argument('--name', dest='name', metavar='NAME', |
|
63 |
help='filter by name') |
|
64 |
parser.add_argument('--size-min', dest='size_min', metavar='BYTES', |
|
65 |
help='filter by minimum size') |
|
66 |
parser.add_argument('--size-max', dest='size_max', metavar='BYTES', |
|
67 |
help='filter by maximum size') |
|
68 |
parser.add_argument('--status', dest='status', metavar='STATUS', |
|
69 |
help='filter by status') |
|
70 |
parser.add_argument('--order', dest='order', metavar='FIELD', |
|
71 |
help='order by FIELD (use a - prefix to reverse order)') |
|
66 |
def __init__(self, arguments={}): |
|
67 |
super(image_public, self).__init__(arguments) |
|
68 |
self.arguments['detail'] = FlagArgument('show detailed output', '-l') |
|
69 |
self.arguments['container_format'] = ValueArgument('filter by container format', |
|
70 |
'--container-format') |
|
71 |
self.arguments['disk_format'] = ValueArgument('filter by disk format', '--disk-format') |
|
72 |
self.arguments['name'] = ValueArgument('filter by name', '--name') |
|
73 |
self.arguments['size_min'] = IntArgument('filter by minimum size', '--size-min') |
|
74 |
self.arguments['size_max'] = IntArgument('filter by maximum size', '--size-max') |
|
75 |
self.arguments['status'] = ValueArgument('filter by status', '--status') |
|
76 |
self.arguments['order'] = ValueArgument('order by FIELD (use a - prefix to reverse order)', |
|
77 |
'--order', default='') |
|
72 | 78 |
|
73 | 79 |
def main(self): |
74 |
super(self.__class__, self).main()
|
|
80 |
super(self.__class__, self).main()
|
|
75 | 81 |
filters = {} |
76 |
for filter in ('container_format', 'disk_format', 'name', 'size_min', |
|
77 |
'size_max', 'status'): |
|
78 |
val = getattr(self.args, filter, None) |
|
82 |
for arg in ('container_format', 'disk_format', 'name', 'size_min', 'size_max', 'status'): |
|
83 |
val = self.get_argument(arg) |
|
79 | 84 |
if val is not None: |
80 |
filters[filter] = val
|
|
85 |
filters[arg] = val
|
|
81 | 86 |
|
82 |
order = self.args.order or ''
|
|
87 |
order = self.get_argument('order')
|
|
83 | 88 |
try: |
84 |
images = self.client.list_public(self.args.detail,
|
|
85 |
filters=filters, order=order)
|
|
89 |
images = self.client.list_public(self.get_argument('detail'), filters=filters,
|
|
90 |
order=order) |
|
86 | 91 |
except ClientError as err: |
87 | 92 |
raiseCLIError(err) |
88 | 93 |
print_items(images, title=('name',)) |
... | ... | |
92 | 97 |
"""Get image metadata""" |
93 | 98 |
|
94 | 99 |
def main(self, image_id): |
95 |
super(self.__class__, self).main()
|
|
100 |
super(self.__class__, self).main()
|
|
96 | 101 |
try: |
97 | 102 |
image = self.client.get_meta(image_id) |
98 | 103 |
except ClientError as err: |
... | ... | |
103 | 108 |
class image_register(_init_image): |
104 | 109 |
"""Register an image""" |
105 | 110 |
|
106 |
def update_parser(self, parser): |
|
107 |
parser.add_argument('--checksum', dest='checksum', metavar='CHECKSUM', |
|
108 |
help='set image checksum') |
|
109 |
parser.add_argument('--container-format', dest='container_format', |
|
110 |
metavar='FORMAT', help='set container format') |
|
111 |
parser.add_argument('--disk-format', dest='disk_format', |
|
112 |
metavar='FORMAT', help='set disk format') |
|
113 |
parser.add_argument('--id', dest='id', |
|
114 |
metavar='ID', help='set image ID') |
|
115 |
parser.add_argument('--owner', dest='owner', |
|
116 |
metavar='USER', help='set image owner (admin only)') |
|
117 |
parser.add_argument('--property', dest='properties', action='append', |
|
118 |
metavar='KEY=VAL', |
|
119 |
help='add a property (can be used multiple times)') |
|
120 |
parser.add_argument('--public', dest='is_public', action='store_true', |
|
121 |
help='mark image as public') |
|
122 |
parser.add_argument('--size', dest='size', metavar='SIZE', |
|
123 |
help='set image size') |
|
111 |
def __init__(self, arguments={}): |
|
112 |
super(image_register, self).__init__(arguments) |
|
113 |
self.arguments['checksum'] = ValueArgument('set image checksum', '--checksum') |
|
114 |
self.arguments['container_format'] = ValueArgument('set container format', |
|
115 |
'--container-format') |
|
116 |
self.arguments['disk_format'] = ValueArgument('set disk format', '--disk-format') |
|
117 |
self.arguments['id'] = ValueArgument('set image ID', '--id') |
|
118 |
self.arguments['owner'] = ValueArgument('set image owner (admin only)', '--owner') |
|
119 |
self.arguments['properties'] = KeyValueArgument(parsed_name='--properties', |
|
120 |
help = 'add properties in the form "key1=val1 key2=val2 ..."') |
|
121 |
self.arguments['is_public'] = FlagArgument('mark image as public', '--public') |
|
122 |
self.arguments['size'] = IntArgument('set image size', '--size') |
|
124 | 123 |
|
125 | 124 |
def main(self, name, location): |
126 |
super(self.__class__, self).main()
|
|
125 |
super(self.__class__, self).main()
|
|
127 | 126 |
if not location.startswith('pithos://'): |
128 |
account = self.config.get('storage', 'account').split()[0] |
|
127 |
account = self.config.get('store', 'account') \ |
|
128 |
or self.config.get('global', 'account') |
|
129 | 129 |
if account[-1] == '/': |
130 | 130 |
account = account[:-1] |
131 |
container = self.config.get('storage', 'container') |
|
131 |
container = self.config.get('store', 'container') \ |
|
132 |
or self.config.get('global', 'container') |
|
132 | 133 |
location = 'pithos://%s/%s'%(account, location) \ |
133 | 134 |
if container is None or len(container) == 0 \ |
134 | 135 |
else 'pithos://%s/%s/%s' % (account, container, location) |
135 | 136 |
|
136 | 137 |
params = {} |
137 |
for key in ('checksum', 'container_format', 'disk_format', 'id', |
|
138 |
'owner', 'size'): |
|
139 |
val = getattr(self.args, key) |
|
138 |
for key in ('checksum','container_format','disk_format','id','owner','size','is_public'): |
|
139 |
val = self.get_argument(key) |
|
140 | 140 |
if val is not None: |
141 | 141 |
params[key] = val |
142 | 142 |
|
143 |
if self.args.is_public: |
|
144 |
params['is_public'] = 'true' |
|
145 |
|
|
146 |
properties = {} |
|
147 |
for property in self.args.properties or []: |
|
148 |
key, sep, val = property.partition('=') |
|
149 |
if not sep: |
|
150 |
raise CLIError(message="Invalid property '%s'" % property, importance=1) |
|
151 |
properties[key.strip()] = val.strip() |
|
152 |
|
|
153 | 143 |
try: |
154 |
self.client.register(name, location, params, properties)
|
|
144 |
self.client.register(name, location, params, self.get_argument('properties'))
|
|
155 | 145 |
except ClientError as err: |
156 | 146 |
raiseCLIError(err) |
157 | 147 |
|
... | ... | |
160 | 150 |
"""Get image members""" |
161 | 151 |
|
162 | 152 |
def main(self, image_id): |
163 |
super(self.__class__, self).main()
|
|
153 |
super(self.__class__, self).main()
|
|
164 | 154 |
try: |
165 | 155 |
members = self.client.list_members(image_id) |
166 | 156 |
except ClientError as err: |
... | ... | |
173 | 163 |
"""List shared images""" |
174 | 164 |
|
175 | 165 |
def main(self, member): |
176 |
super(self.__class__, self).main()
|
|
166 |
super(self.__class__, self).main()
|
|
177 | 167 |
try: |
178 | 168 |
images = self.client.list_shared(member) |
179 | 169 |
except ClientError as err: |
... | ... | |
186 | 176 |
"""Add a member to an image""" |
187 | 177 |
|
188 | 178 |
def main(self, image_id, member): |
189 |
super(self.__class__, self).main()
|
|
179 |
super(self.__class__, self).main()
|
|
190 | 180 |
try: |
191 | 181 |
self.client.add_member(image_id, member) |
192 | 182 |
except ClientError as err: |
... | ... | |
213 | 203 |
self.client.set_members(image_id, member) |
214 | 204 |
except ClientError as err: |
215 | 205 |
raiseCLIError(err) |
206 |
|
|
207 |
|
|
208 |
@command() |
|
209 |
class image_list(_init_cyclades): |
|
210 |
"""List images""" |
|
211 |
|
|
212 |
def __init__(self, arguments={}): |
|
213 |
super(image_list, self).__init__(arguments) |
|
214 |
self.arguments['detail'] = FlagArgument('show detailed output', '-l') |
|
215 |
|
|
216 |
def _print(self, images): |
|
217 |
for img in images: |
|
218 |
iname = img.pop('name') |
|
219 |
iid = img.pop('id') |
|
220 |
print('%s (%s)'%(bold(iname), bold(unicode(iid)))) |
|
221 |
if self.get_argument('detail'): |
|
222 |
image_info._print(img) |
|
223 |
|
|
224 |
def main(self): |
|
225 |
super(self.__class__, self).main() |
|
226 |
try: |
|
227 |
images = self.client.list_images(self.get_argument('detail')) |
|
228 |
except ClientError as err: |
|
229 |
raiseCLIError(err) |
|
230 |
self._print(images) |
|
231 |
|
|
232 |
@command() |
|
233 |
class image_info(_init_cyclades): |
|
234 |
"""Get image details""" |
|
235 |
|
|
236 |
@classmethod |
|
237 |
def _print(self, image): |
|
238 |
if image.has_key('metadata'): |
|
239 |
image['metadata'] = image['metadata']['values'] |
|
240 |
print_dict(image, ident=14) |
|
241 |
|
|
242 |
def main(self, image_id): |
|
243 |
super(self.__class__, self).main() |
|
244 |
try: |
|
245 |
image = self.client.get_image_details(image_id) |
|
246 |
except ClientError as err: |
|
247 |
raiseCLIError(err) |
|
248 |
self._print(image) |
|
249 |
|
|
250 |
@command() |
|
251 |
class image_delete(_init_cyclades): |
|
252 |
"""Delete image""" |
|
253 |
|
|
254 |
def main(self, image_id): |
|
255 |
super(self.__class__, self).main() |
|
256 |
try: |
|
257 |
self.client.delete_image(image_id) |
|
258 |
except ClientError as err: |
|
259 |
raiseCLIError(err) |
|
260 |
|
|
261 |
@command() |
|
262 |
class image_properties(_init_cyclades): |
|
263 |
"""Get image properties""" |
|
264 |
|
|
265 |
def main(self, image_id, key=None): |
|
266 |
super(self.__class__, self).main() |
|
267 |
try: |
|
268 |
reply = self.client.get_image_metadata(image_id, key) |
|
269 |
except ClientError as err: |
|
270 |
raiseCLIError(err) |
|
271 |
print_dict(reply) |
|
272 |
|
|
273 |
@command() |
|
274 |
class image_addproperty(_init_cyclades): |
|
275 |
"""Add an image property""" |
|
276 |
|
|
277 |
def main(self, image_id, key, val): |
|
278 |
super(self.__class__, self).main() |
|
279 |
try: |
|
280 |
reply = self.client.create_image_metadata(image_id, key, val) |
|
281 |
except ClientError as err: |
|
282 |
raiseCLIError(err) |
|
283 |
print_dict(reply) |
|
284 |
|
|
285 |
@command() |
|
286 |
class image_setproperty(_init_cyclades): |
|
287 |
"""Update an image property""" |
|
288 |
|
|
289 |
def main(self, image_id, key, val): |
|
290 |
super(self.__class__, self).main() |
|
291 |
metadata = {key: val} |
|
292 |
try: |
|
293 |
reply = self.client.update_image_metadata(image_id, **metadata) |
|
294 |
except ClientError as err: |
|
295 |
raiseCLIError(err) |
|
296 |
print_dict(reply) |
|
297 |
|
|
298 |
@command() |
|
299 |
class image_delproperty(_init_cyclades): |
|
300 |
"""Delete an image property""" |
|
301 |
|
|
302 |
def main(self, image_id, key): |
|
303 |
super(self.__class__, self).main() |
|
304 |
try: |
|
305 |
self.client.delete_image_metadata(image_id, key) |
|
306 |
except ClientError as err: |
|
307 |
raiseCLIError(err) |
Also available in: Unified diff