diff options
Diffstat (limited to 'novaclient/shell.py')
| -rw-r--r-- | novaclient/shell.py | 838 |
1 files changed, 73 insertions, 765 deletions
diff --git a/novaclient/shell.py b/novaclient/shell.py index f9f5252e..9642645d 100644 --- a/novaclient/shell.py +++ b/novaclient/shell.py @@ -20,56 +20,25 @@ Command-line interface to the OpenStack Nova API. """ import argparse -import novaclient -import getpass import httplib2 import os import prettytable import sys -import textwrap -import uuid -# Choices for flags. -DAY_CHOICES = [getattr(novaclient, i).lower() - for i in dir(novaclient) - if i.startswith('BACKUP_WEEKLY_')] -HOUR_CHOICES = [getattr(novaclient, i).lower() - for i in dir(novaclient) - if i.startswith('BACKUP_DAILY_')] - - -def pretty_choice_list(l): - return ', '.join("'%s'" % i for i in l) - -# Sentinal for boot --key -AUTO_KEY = object() - - -# Decorator for args -def arg(*args, **kwargs): - def _decorator(func): - # Because of the sematics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs)) - return func - return _decorator - - -class CommandError(Exception): - pass +from novaclient import exceptions +from novaclient import utils +from novaclient.v1_0 import shell as shell_v1_0 +from novaclient.v1_1 import shell as shell_v1_1 def env(e): return os.environ.get(e, '') -class OpenStackShell(object): - - # Hook for the test suite to inject a fake server. - _api_class = novaclient.OpenStack +class OpenStackComputeShell(object): - def __init__(self): - self.parser = argparse.ArgumentParser( + def get_base_parser(self): + parser = argparse.ArgumentParser( prog='nova', description=__doc__.strip(), epilog='See "nova help COMMAND" '\ @@ -79,44 +48,62 @@ class OpenStackShell(object): ) # Global arguments - self.parser.add_argument('-h', '--help', + parser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS, ) - self.parser.add_argument('--debug', + parser.add_argument('--debug', default=False, action='store_true', help=argparse.SUPPRESS) - self.parser.add_argument('--username', + parser.add_argument('--username', default=env('NOVA_USERNAME'), help='Defaults to env[NOVA_USERNAME].') - self.parser.add_argument('--apikey', + parser.add_argument('--apikey', default=env('NOVA_API_KEY'), help='Defaults to env[NOVA_API_KEY].') - self.parser.add_argument('--projectid', + parser.add_argument('--projectid', default=env('NOVA_PROJECT_ID'), help='Defaults to env[NOVA_PROJECT_ID].') - auth_url = env('NOVA_URL') - if auth_url == '': - auth_url = 'https://auth.api.rackspacecloud.com/v1.0' - self.parser.add_argument('--url', - default=auth_url, + parser.add_argument('--url', + default=env('NOVA_URL'), help='Defaults to env[NOVA_URL].') - # Subcommands - subparsers = self.parser.add_subparsers(metavar='<subcommand>') + parser.add_argument('--version', + default=env('NOVA_VERSION'), + help='Accepts 1.0 or 1.1, defaults to env[NOVA_VERSION].') + + return parser + + def get_subcommand_parser(self, version): + parser = self.get_base_parser() + self.subcommands = {} + subparsers = parser.add_subparsers(metavar='<subcommand>') + + try: + actions_module = { + '1.0': shell_v1_0, + '1.1': shell_v1_1, + }[version] + except KeyError: + actions_module = shell_v1_0 + + self._find_actions(subparsers, actions_module) + self._find_actions(subparsers, self) + + return parser - # Everything that's do_* is a subcommand. - for attr in (a for a in dir(self) if a.startswith('do_')): + def _find_actions(self, subparsers, actions_module): + for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hypen-separated instead of underscores. command = attr[3:].replace('_', '-') - callback = getattr(self, attr) + callback = getattr(actions_module, attr) desc = callback.__doc__ or '' help = desc.strip().split('\n')[0] arguments = getattr(callback, 'arguments', []) @@ -137,18 +124,26 @@ class OpenStackShell(object): subparser.set_defaults(func=callback) def main(self, argv): - # Parse args and call whatever callback was selected - args = self.parser.parse_args(argv) + # Parse args once to find version + parser = self.get_base_parser() + (options, args) = parser.parse_known_args(argv) - # Short-circuit and deal with help right away. - if args.func == self.do_help: - self.do_help(args) - return 0 + # build available subcommands based on version + subcommand_parser = self.get_subcommand_parser(options.version) + self.parser = subcommand_parser + + # Parse args again and call whatever callback was selected + args = subcommand_parser.parse_args(argv) # Deal with global arguments if args.debug: httplib2.debuglevel = 1 + # Short-circuit and deal with help right away. + if args.func == self.do_help: + self.do_help(args) + return 0 + user, apikey, projectid, url = args.username, args.apikey, \ args.projectid, args.url @@ -156,21 +151,31 @@ class OpenStackShell(object): # for username or apikey but for compatibility it is not. if not user: - raise CommandError("You must provide a username, either via " + raise exceptions.CommandError("You must provide a username, either via " "--username or via env[NOVA_USERNAME]") if not apikey: - raise CommandError("You must provide an API key, either via " + raise exceptions.CommandError("You must provide an API key, either via " "--apikey or via env[NOVA_API_KEY]") - self.cs = self._api_class(user, apikey, projectid, url) + self.cs = self.get_api_class(options.version)(user, apikey, projectid, url) + try: self.cs.authenticate() - except novaclient.Unauthorized: - raise CommandError("Invalid OpenStack Nova credentials.") + except exceptions.Unauthorized: + raise exceptions.CommandError("Invalid OpenStack Nova credentials.") - args.func(args) + args.func(self.cs, args) - @arg('command', metavar='<subcommand>', nargs='?', + def get_api_class(self, version): + try: + return { + "1.0": shell_v1_0.CLIENT_CLASS, + "1.1": shell_v1_1.CLIENT_CLASS, + }[version] + except KeyError: + return shell_v1_0.CLIENT_CLASS + + @utils.arg('command', metavar='<subcommand>', nargs='?', help='Display help for <subcommand>') def do_help(self, args): """ @@ -180,682 +185,11 @@ class OpenStackShell(object): if args.command in self.subcommands: self.subcommands[args.command].print_help() else: - raise CommandError("'%s' is not a valid subcommand." % + raise exceptions.CommandError("'%s' is not a valid subcommand." % args.command) else: self.parser.print_help() - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('--enable', dest='enabled', default=None, action='store_true', - help='Enable backups.') - @arg('--disable', dest='enabled', action='store_false', - help='Disable backups.') - @arg('--weekly', metavar='<day>', choices=DAY_CHOICES, - help='Schedule a weekly backup for <day> (one of: %s).' % - pretty_choice_list(DAY_CHOICES)) - @arg('--daily', metavar='<time-window>', choices=HOUR_CHOICES, - help='Schedule a daily backup during <time-window> (one of: %s).' % - pretty_choice_list(HOUR_CHOICES)) - def do_backup_schedule(self, args): - """ - Show or edit the backup schedule for a server. - - With no flags, the backup schedule will be shown. If flags are given, - the backup schedule will be modified accordingly. - """ - server = self._find_server(args.server) - - # If we have some flags, update the backup - backup = {} - if args.daily: - backup['daily'] = getattr(novaclient, 'BACKUP_DAILY_%s' % - args.daily.upper()) - if args.weekly: - backup['weekly'] = getattr(novaclient, 'BACKUP_WEEKLY_%s' % - args.weekly.upper()) - if args.enabled is not None: - backup['enabled'] = args.enabled - if backup: - server.backup_schedule.update(**backup) - else: - print_dict(server.backup_schedule._info) - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_backup_schedule_delete(self, args): - """ - Delete the backup schedule for a server. - """ - server = self._find_server(args.server) - server.backup_schedule.delete() - - def _boot(self, args, reservation_id=None, min_count=None, max_count=None): - """Boot a new server.""" - if min_count is None: - min_count = 1 - if max_count is None: - max_count = min_count - if min_count > max_count: - raise CommandError("min_instances should be <= max_instances") - if not min_count or not max_count: - raise CommandError("min_instances nor max_instances should be 0") - - flavor = args.flavor or self.cs.flavors.find(ram=256) - image = args.image or self.cs.images.find(name="Ubuntu 10.04 LTS "\ - "(lucid)") - - # Map --ipgroup <name> to an ID. - # XXX do this for flavor/image? - if args.ipgroup: - ipgroup = self._find_ipgroup(args.ipgroup) - else: - ipgroup = None - - metadata = dict(v.split('=') for v in args.meta) - - files = {} - for f in args.files: - dst, src = f.split('=', 1) - try: - files[dst] = open(src) - except IOError, e: - raise CommandError("Can't open '%s': %s" % (src, e)) - - if args.key is AUTO_KEY: - possible_keys = [os.path.join(os.path.expanduser('~'), '.ssh', k) - for k in ('id_dsa.pub', 'id_rsa.pub')] - for k in possible_keys: - if os.path.exists(k): - keyfile = k - break - else: - raise CommandError("Couldn't find a key file: tried " - "~/.ssh/id_dsa.pub or ~/.ssh/id_rsa.pub") - elif args.key: - keyfile = args.key - else: - keyfile = None - - if keyfile: - try: - files['/root/.ssh/authorized_keys2'] = open(keyfile) - except IOError, e: - raise CommandError("Can't open '%s': %s" % (keyfile, e)) - - return (args.name, image, flavor, ipgroup, metadata, files, - reservation_id, min_count, max_count) - - @arg('--flavor', - default=None, - metavar='<flavor>', - help="Flavor ID (see 'novaclient flavors'). "\ - "Defaults to 256MB RAM instance.") - @arg('--image', - default=None, - metavar='<image>', - help="Image ID (see 'novaclient images'). "\ - "Defaults to Ubuntu 10.04 LTS.") - @arg('--ipgroup', - default=None, - metavar='<group>', - help="IP group name or ID (see 'novaclient ipgroup-list').") - @arg('--meta', - metavar="<key=value>", - action='append', - default=[], - help="Record arbitrary key/value metadata. "\ - "May be give multiple times.") - @arg('--file', - metavar="<dst-path=src-path>", - action='append', - dest='files', - default=[], - help="Store arbitrary files from <src-path> locally to <dst-path> "\ - "on the new server. You may store up to 5 files.") - @arg('--key', - metavar='<path>', - nargs='?', - const=AUTO_KEY, - help="Key the server with an SSH keypair. "\ - "Looks in ~/.ssh for a key, "\ - "or takes an explicit <path> to one.") - @arg('name', metavar='<name>', help='Name for the new server') - def do_boot(self, args): - """Boot a new server.""" - name, image, flavor, ipgroup, metadata, files, reservation_id, \ - min_count, max_count = self._boot(args) - - server = self.cs.servers.create(args.name, image, flavor, - ipgroup=ipgroup, - meta=metadata, - files=files, - min_count=min_count, - max_count=max_count) - print_dict(server._info) - - @arg('--flavor', - default=None, - metavar='<flavor>', - help="Flavor ID (see 'novaclient flavors'). "\ - "Defaults to 256MB RAM instance.") - @arg('--image', - default=None, - metavar='<image>', - help="Image ID (see 'novaclient images'). "\ - "Defaults to Ubuntu 10.04 LTS.") - @arg('--ipgroup', - default=None, - metavar='<group>', - help="IP group name or ID (see 'novaclient ipgroup-list').") - @arg('--meta', - metavar="<key=value>", - action='append', - default=[], - help="Record arbitrary key/value metadata. "\ - "May be give multiple times.") - @arg('--file', - metavar="<dst-path=src-path>", - action='append', - dest='files', - default=[], - help="Store arbitrary files from <src-path> locally to <dst-path> "\ - "on the new server. You may store up to 5 files.") - @arg('--key', - metavar='<path>', - nargs='?', - const=AUTO_KEY, - help="Key the server with an SSH keypair. "\ - "Looks in ~/.ssh for a key, "\ - "or takes an explicit <path> to one.") - @arg('account', metavar='<account>', help='Account to build this'\ - ' server for') - @arg('name', metavar='<name>', help='Name for the new server') - def do_boot_for_account(self, args): - """Boot a new server in an account.""" - name, image, flavor, ipgroup, metadata, files, reservation_id, \ - min_count, max_count = self._boot(args) - - server = self.cs.accounts.create_instance_for(args.account, args.name, - image, flavor, - ipgroup=ipgroup, - meta=metadata, - files=files) - print_dict(server._info) - - @arg('--flavor', - default=None, - metavar='<flavor>', - help="Flavor ID (see 'novaclient flavors'). "\ - "Defaults to 256MB RAM instance.") - @arg('--image', - default=None, - metavar='<image>', - help="Image ID (see 'novaclient images'). "\ - "Defaults to Ubuntu 10.04 LTS.") - @arg('--ipgroup', - default=None, - metavar='<group>', - help="IP group name or ID (see 'novaclient ipgroup-list').") - @arg('--meta', - metavar="<key=value>", - action='append', - default=[], - help="Record arbitrary key/value metadata. "\ - "May be give multiple times.") - @arg('--file', - metavar="<dst-path=src-path>", - action='append', - dest='files', - default=[], - help="Store arbitrary files from <src-path> locally to <dst-path> "\ - "on the new server. You may store up to 5 files.") - @arg('--key', - metavar='<path>', - nargs='?', - const=AUTO_KEY, - help="Key the server with an SSH keypair. "\ - "Looks in ~/.ssh for a key, "\ - "or takes an explicit <path> to one.") - @arg('--reservation_id', - default=None, - metavar='<reservation_id>', - help="Reservation ID (a UUID). "\ - "If unspecified will be generated by the server.") - @arg('--min_instances', - default=None, - type=int, - metavar='<number>', - help="The minimum number of instances to build. "\ - "Defaults to 1.") - @arg('--max_instances', - default=None, - type=int, - metavar='<number>', - help="The maximum number of instances to build. "\ - "Defaults to 'min_instances' setting.") - @arg('name', metavar='<name>', help='Name for the new server') - def do_zone_boot(self, args): - """Boot a new server, potentially across Zones.""" - reservation_id = args.reservation_id - min_count = args.min_instances - max_count = args.max_instances - name, image, flavor, ipgroup, metadata, \ - files, reservation_id, min_count, max_count = \ - self._boot(args, - reservation_id=reservation_id, - min_count=min_count, - max_count=max_count) - - reservation_id = self.cs.zones.boot(args.name, image, flavor, - ipgroup=ipgroup, - meta=metadata, - files=files, - reservation_id=reservation_id, - min_count=min_count, - max_count=max_count) - print "Reservation ID=", reservation_id - - def _translate_flavor_keys(self, collection): - convert = [('ram', 'memory_mb'), ('disk', 'local_gb')] - for item in collection: - keys = item.__dict__.keys() - for from_key, to_key in convert: - if from_key in keys and to_key not in keys: - setattr(item, to_key, item._info[from_key]) - - def do_flavor_list(self, args): - """Print a list of available 'flavors' (sizes of servers).""" - flavors = self.cs.flavors.list() - self._translate_flavor_keys(flavors) - print_list(flavors, [ - 'ID', - 'Name', - 'Memory_MB', - 'Swap', - 'Local_GB', - 'VCPUs', - 'RXTX_Quota', - 'RXTX_Cap']) - - def do_image_list(self, args): - """Print a list of available images to boot from.""" - print_list(self.cs.images.list(), ['ID', 'Name', 'Status']) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('name', metavar='<name>', help='Name of snapshot.') - def do_image_create(self, args): - """Create a new image by taking a snapshot of a running server.""" - server = self._find_server(args.server) - image = self.cs.images.create(server, args.name) - print_dict(image._info) - - @arg('image', metavar='<image>', help='Name or ID of image.') - def do_image_delete(self, args): - """ - Delete an image. - - It should go without saying, but you can only delete images you - created. - """ - image = self._find_image(args.image) - image.delete() - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('group', metavar='<group>', help='Name or ID of group.') - @arg('address', metavar='<address>', help='IP address to share.') - def do_ip_share(self, args): - """Share an IP address from the given IP group onto a server.""" - server = self._find_server(args.server) - group = self._find_ipgroup(args.group) - server.share_ip(group, args.address) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('address', metavar='<address>', - help='Shared IP address to remove from the server.') - def do_ip_unshare(self, args): - """Stop sharing an given address with a server.""" - server = self._find_server(args.server) - server.unshare_ip(args.address) - - def do_ipgroup_list(self, args): - """Show IP groups.""" - def pretty_server_list(ipgroup): - return ", ".join(self.cs.servers.get(id).name - for id in ipgroup.servers) - - print_list(self.cs.ipgroups.list(), - fields=['ID', 'Name', 'Server List'], - formatters={'Server List': pretty_server_list}) - - @arg('group', metavar='<group>', help='Name or ID of group.') - def do_ipgroup_show(self, args): - """Show details about a particular IP group.""" - group = self._find_ipgroup(args.group) - print_dict(group._info) - - @arg('name', metavar='<name>', help='What to name this new group.') - @arg('server', metavar='<server>', nargs='?', - help='Server (name or ID) to make a member of this new group.') - def do_ipgroup_create(self, args): - """Create a new IP group.""" - if args.server: - server = self._find_server(args.server) - else: - server = None - group = self.cs.ipgroups.create(args.name, server) - print_dict(group._info) - - @arg('group', metavar='<group>', help='Name or ID of group.') - def do_ipgroup_delete(self, args): - """Delete an IP group.""" - self._find_ipgroup(args.group).delete() - - @arg('--fixed_ip', - dest='fixed_ip', - metavar='<fixed_ip>', - default=None, - help='Only match against fixed IP.') - @arg('--reservation_id', - dest='reservation_id', - metavar='<reservation_id>', - default=None, - help='Only return instances that match reservation_id.') - @arg('--recurse_zones', - dest='recurse_zones', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=0, - help='Recurse through all zones if set.') - @arg('--ip', - dest='ip', - metavar='<ip_regexp>', - default=None, - help='Search with regular expression match by IP address') - @arg('--ip6', - dest='ip6', - metavar='<ip6_regexp>', - default=None, - help='Search with regular expression match by IPv6 address') - @arg('--server_name', - dest='server_name', - metavar='<name_regexp>', - default=None, - help='Search with regular expression match by server name') - @arg('--name', - dest='display_name', - metavar='<name_regexp>', - default=None, - help='Search with regular expression match by display name') - @arg('--instance_name', - dest='name', - metavar='<name_regexp>', - default=None, - help='Search with regular expression match by instance name') - def do_list(self, args): - """List active servers.""" - recurse_zones = args.recurse_zones - search_opts = { - 'reservation_id': args.reservation_id, - 'fixed_ip': args.fixed_ip, - 'recurse_zones': recurse_zones, - 'ip': args.ip, - 'ip6': args.ip6, - 'name': args.name, - 'server_name': args.server_name, - 'display_name': args.display_name} - if recurse_zones: - to_print = ['UUID', 'Name', 'Status', 'Public IP', 'Private IP'] - else: - to_print = ['ID', 'Name', 'Status', 'Public IP', 'Private IP'] - print_list(self.cs.servers.list(search_opts=search_opts), - to_print) - - @arg('--hard', - dest='reboot_type', - action='store_const', - const=novaclient.REBOOT_HARD, - default=novaclient.REBOOT_SOFT, - help='Perform a hard reboot (instead of a soft one).') - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_reboot(self, args): - """Reboot a server.""" - self._find_server(args.server).reboot(args.reboot_type) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('image', metavar='<image>', help="Name or ID of new image.") - def do_rebuild(self, args): - """Shutdown, re-image, and re-boot a server.""" - server = self._find_server(args.server) - image = self._find_image(args.image) - server.rebuild(image) - - @arg('server', metavar='<server>', help='Name (old name) or ID of server.') - @arg('name', metavar='<name>', help='New name for the server.') - def do_rename(self, args): - """Rename a server.""" - self._find_server(args.server).update(name=args.name) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('flavor', metavar='<flavor>', help="Name or ID of new flavor.") - def do_resize(self, args): - """Resize a server.""" - server = self._find_server(args.server) - flavor = self._find_flavor(args.flavor) - server.resize(flavor) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('name', metavar='<name>', help='Name of snapshot.') - @arg('backup_type', metavar='<daily|weekly>', help='type of backup') - @arg('rotation', type=int, metavar='<rotation>', - help="Number of backups to retain. Used for backup image_type.") - def do_backup(self, args): - """Resize a server.""" - server = self._find_server(args.server) - server.backup(args.name, args.backup_type, args.rotation) - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_migrate(self, args): - """Migrate a server.""" - self._find_server(args.server).migrate() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_pause(self, args): - """Pause a server.""" - self._find_server(args.server).pause() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_unpause(self, args): - """Unpause a server.""" - self._find_server(args.server).unpause() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_suspend(self, args): - """Suspend a server.""" - self._find_server(args.server).suspend() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_resume(self, args): - """Resume a server.""" - self._find_server(args.server).resume() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_rescue(self, args): - """Rescue a server.""" - self._find_server(args.server).rescue() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_unrescue(self, args): - """Unrescue a server.""" - self._find_server(args.server).unrescue() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_diagnostics(self, args): - """Retrieve server diagnostics.""" - print_dict(self.cs.servers.diagnostics(args.server)[1]) - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_actions(self, args): - """Retrieve server actions.""" - print_list( - self.cs.servers.actions(args.server), - ["Created_At", "Action", "Error"]) - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_resize_confirm(self, args): - """Confirm a previous resize.""" - self._find_server(args.server).confirm_resize() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_resize_revert(self, args): - """Revert a previous resize (and return to the previous VM).""" - self._find_server(args.server).revert_resize() - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_root_password(self, args): - """ - Change the root password for a server. - """ - server = self._find_server(args.server) - p1 = getpass.getpass('New password: ') - p2 = getpass.getpass('Again: ') - if p1 != p2: - raise CommandError("Passwords do not match.") - server.update(password=p1) - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_show(self, args): - """Show details about the given server.""" - s = self._find_server(args.server) - - info = s._info.copy() - addresses = info.pop('addresses') - for addrtype in addresses: - info['%s ip' % addrtype] = ', '.join(addresses[addrtype]) - - flavorId = info.get('flavorId', None) - if flavorId: - info['flavor'] = self._find_flavor(info.pop('flavorId')).name - imageId = info.get('imageId', None) - if imageId: - info['image'] = self._find_image(info.pop('imageId')).name - - print_dict(info) - - @arg('server', metavar='<server>', help='Name or ID of server.') - def do_delete(self, args): - """Immediately shut down and delete a server.""" - self._find_server(args.server).delete() - - # --zone_username is required since --username is already used. - @arg('zone', metavar='<zone_id>', help='ID of the zone', default=None) - @arg('--api_url', dest='api_url', default=None, help='New URL.') - @arg('--zone_username', dest='zone_username', default=None, - help='New zone username.') - @arg('--password', dest='password', default=None, help='New password.') - @arg('--weight_offset', dest='weight_offset', default=None, - help='Child Zone weight offset.') - @arg('--weight_scale', dest='weight_scale', default=None, - help='Child Zone weight scale.') - def do_zone(self, args): - """Show or edit a child zone. No zone arg for this zone.""" - zone = self.cs.zones.get(args.zone) - - # If we have some flags, update the zone - zone_delta = {} - if args.api_url: - zone_delta['api_url'] = args.api_url - if args.zone_username: - zone_delta['username'] = args.zone_username - if args.password: - zone_delta['password'] = args.password - if args.weight_offset: - zone_delta['weight_offset'] = args.weight_offset - if args.weight_scale: - zone_delta['weight_scale'] = args.weight_scale - if zone_delta: - zone.update(**zone_delta) - else: - print_dict(zone._info) - - def do_zone_info(self, args): - """Get this zones name and capabilities.""" - zone = self.cs.zones.info() - print_dict(zone._info) - - @arg('api_url', metavar='<api_url>', help="URL for the Zone's API") - @arg('zone_username', metavar='<zone_username>', - help='Authentication username.') - @arg('password', metavar='<password>', help='Authentication password.') - @arg('weight_offset', metavar='<weight_offset>', - help='Child Zone weight offset (typically 0.0).') - @arg('weight_scale', metavar='<weight_scale>', - help='Child Zone weight scale (typically 1.0).') - def do_zone_add(self, args): - """Add a new child zone.""" - zone = self.cs.zones.create(args.api_url, args.zone_username, - args.password, args.weight_offset, - args.weight_scale) - print_dict(zone._info) - - @arg('zone', metavar='<zone>', help='Name or ID of the zone') - def do_zone_delete(self, args): - """Delete a zone.""" - self.cs.zones.delete(args.zone) - - def do_zone_list(self, args): - """List the children of a zone.""" - print_list(self.cs.zones.list(), ['ID', 'Name', 'Is Active', \ - 'API URL', 'Weight Offset', 'Weight Scale']) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('network_id', metavar='<network_id>', help='Network ID.') - def do_add_fixed_ip(self, args): - """Add new IP address to network.""" - server = self._find_server(args.server) - server.add_fixed_ip(args.network_id) - - @arg('server', metavar='<server>', help='Name or ID of server.') - @arg('address', metavar='<address>', help='IP Address.') - def do_remove_fixed_ip(self, args): - """Remove an IP address from a server.""" - server = self._find_server(args.server) - server.remove_fixed_ip(args.address) - - def _find_server(self, server): - """Get a server by name or ID.""" - return self._find_resource(self.cs.servers, server) - - def _find_ipgroup(self, group): - """Get an IP group by name or ID.""" - return self._find_resource(self.cs.ipgroups, group) - - def _find_image(self, image): - """Get an image by name or ID.""" - return self._find_resource(self.cs.images, image) - - def _find_flavor(self, flavor): - """Get a flavor by name, ID, or RAM size.""" - try: - return self._find_resource(self.cs.flavors, flavor) - except novaclient.NotFound: - return self.cs.flavors.find(ram=flavor) - - def _find_resource(self, manager, name_or_id): - """Helper for the _find_* methods.""" - try: - if isinstance(name_or_id, int) or name_or_id.isdigit(): - return manager.get(int(name_or_id)) - - try: - uuid.UUID(name_or_id) - return manager.get(name_or_id) - except ValueError: - return manager.find(name=name_or_id) - except novaclient.NotFound: - raise CommandError("No %s with a name or ID of '%s' exists." % - (manager.resource_class.__name__.lower(), name_or_id)) - # I'm picky about my shell help. class OpenStackHelpFormatter(argparse.HelpFormatter): @@ -865,35 +199,9 @@ class OpenStackHelpFormatter(argparse.HelpFormatter): super(OpenStackHelpFormatter, self).start_section(heading) -# Helpers -def print_list(objs, fields, formatters={}): - pt = prettytable.PrettyTable([f for f in fields], caching=False) - pt.aligns = ['l' for f in fields] - - for o in objs: - row = [] - for field in fields: - if field in formatters: - row.append(formatters[field](o)) - else: - field_name = field.lower().replace(' ', '_') - data = getattr(o, field_name, '') - row.append(data) - pt.add_row(row) - - pt.printt(sortby=fields[0]) - - -def print_dict(d): - pt = prettytable.PrettyTable(['Property', 'Value'], caching=False) - pt.aligns = ['l', 'l'] - [pt.add_row(list(r)) for r in d.iteritems()] - pt.printt(sortby='Property') - - def main(): try: - OpenStackShell().main(sys.argv[1:]) + OpenStackComputeShell().main(sys.argv[1:]) except Exception, e: if httplib2.debuglevel == 1: |
