summaryrefslogtreecommitdiff
path: root/novaclient/shell.py
diff options
context:
space:
mode:
Diffstat (limited to 'novaclient/shell.py')
-rw-r--r--novaclient/shell.py838
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: