diff --git a/.bzrignore b/.bzrignore index 14d8028f..91277d10 100644 --- a/.bzrignore +++ b/.bzrignore @@ -13,3 +13,7 @@ nova/vcsversion.py clean.sqlite run_tests.log tests.sqlite +nova/tests/instance-* +tags +.coverage +covhtml diff --git a/.mailmap b/.mailmap index ff304c89..76e7bc66 100644 --- a/.mailmap +++ b/.mailmap @@ -14,6 +14,7 @@ + diff --git a/Authors b/Authors index f3129c26..0e593146 100644 --- a/Authors +++ b/Authors @@ -1,4 +1,5 @@ Adam Gandelman +Adam Johnson Alex Meade Alexander Sakhnov Andrey Brindeyev @@ -8,6 +9,7 @@ Anne Gentle Anthony Young Antony Messerli Armando Migliaccio +Arvind Somya Bilal Akhtar Brian Lamar Brian Schott @@ -19,6 +21,7 @@ Christian Berendt Chuck Short Cory Wright Dan Prince +Dan Wendlandt Dave Walker David Pravec Dean Troyer @@ -64,6 +67,7 @@ Lvov Maxim Mark Washenberger Masanori Itoh Matt Dietz +Matthew Hooker Michael Gundlach Mike Scherbakov Mohammed Naser @@ -83,6 +87,7 @@ Rick Harris Rob Kost Ryan Lane Ryan Lucio +Ryu Ishimoto Salvatore Orlando Sandy Walsh Sateesh Chodapuneedi @@ -101,3 +106,4 @@ Yoshiaki Tamura Youcef Laribi Yuriy Taraday Zhixue Wu +Zed Shaw diff --git a/bin/nova-manage b/bin/nova-manage index 7477c213..75d74903 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -61,6 +61,7 @@ import os import sys import time +from optparse import OptionParser # If ../nova/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... @@ -103,6 +104,14 @@ flags.DEFINE_flag(flags.HelpshortFlag()) flags.DEFINE_flag(flags.HelpXMLFlag()) +# Decorators for actions +def args(*args, **kwargs): + def _decorator(func): + func.__dict__.setdefault('options', []).insert(0, (args, kwargs)) + return func + return _decorator + + def param2id(object_id): """Helper function to convert various id types to internal id. args: [object_id], e.g. 'vol-0000000a' or 'volume-0000000a' or '10' @@ -120,10 +129,11 @@ class VpnCommands(object): self.manager = manager.AuthManager() self.pipe = pipelib.CloudPipe() + @args('--project', dest="project", metavar='', + help='Project name') def list(self, project=None): - """Print a listing of the VPN data for one or all projects. + """Print a listing of the VPN data for one or all projects.""" - args: [project=all]""" print "%-12s\t" % 'project', print "%-20s\t" % 'ip:port', print "%-20s\t" % 'private_ip', @@ -165,17 +175,23 @@ class VpnCommands(object): self.pipe.launch_vpn_instance(p.id) time.sleep(10) + @args('--project', dest="project_id", metavar='', + help='Project name') def run(self, project_id): """Start the VPN for a given project.""" self.pipe.launch_vpn_instance(project_id) + @args('--project', dest="project_id", metavar='', + help='Project name') + @args('--ip', dest="ip", metavar='', help='IP Address') + @args('--port', dest="port", metavar='', help='Port') def change(self, project_id, ip, port): """Change the ip and port for a vpn. this will update all networks associated with a project not sure if that's the desired behavior or not, patches accepted - args: project, ip, port""" + """ # TODO(tr3buchet): perhaps this shouldn't update all networks # associated with a project in the future project = self.manager.get_project(project_id) @@ -210,10 +226,10 @@ class ShellCommands(object): Falls back to Python shell if unavailable""" self.run('python') + @args('--shell', dest="shell", metavar='', + help='Python shell') def run(self, shell=None): - """Runs a Python interactive interpreter. - - args: [shell=bpython]""" + """Runs a Python interactive interpreter.""" if not shell: shell = 'bpython' @@ -247,6 +263,7 @@ class ShellCommands(object): readline.parse_and_bind("tab:complete") code.interact() + @args('--path', dest='path', metavar='', help='Script path') def script(self, path): """Runs the script from the specifed path with flags set properly. arguments: path""" @@ -259,10 +276,13 @@ class RoleCommands(object): def __init__(self): self.manager = manager.AuthManager() + @args('--user', dest="user", metavar='', help='User name') + @args('--role', dest="role", metavar='', help='User role') + @args('--project', dest="project", metavar='', + help='Project name') def add(self, user, role, project=None): """adds role to user - if project is specified, adds project specific role - arguments: user, role [project]""" + if project is specified, adds project specific role""" if project: projobj = self.manager.get_project(project) if not projobj.has_member(user): @@ -270,17 +290,23 @@ class RoleCommands(object): return self.manager.add_role(user, role, project) + @args('--user', dest="user", metavar='', help='User name') + @args('--role', dest="role", metavar='', help='User role') + @args('--project', dest="project", metavar='', + help='Project name') def has(self, user, role, project=None): """checks to see if user has role if project is specified, returns True if user has - the global role and the project role - arguments: user, role [project]""" + the global role and the project role""" print self.manager.has_role(user, role, project) + @args('--user', dest="user", metavar='', help='User name') + @args('--role', dest="role", metavar='', help='User role') + @args('--project', dest="project", metavar='', + help='Project name') def remove(self, user, role, project=None): """removes role from user - if project is specified, removes project specific role - arguments: user, role [project]""" + if project is specified, removes project specific role""" self.manager.remove_role(user, role, project) @@ -304,32 +330,37 @@ class UserCommands(object): def __init__(self): self.manager = manager.AuthManager() + @args('--name', dest="name", metavar='', help='Admin name') + @args('--access', dest="access", metavar='', help='Access') + @args('--secret', dest="secret", metavar='', help='Secret') def admin(self, name, access=None, secret=None): - """creates a new admin and prints exports - arguments: name [access] [secret]""" + """creates a new admin and prints exports""" try: user = self.manager.create_user(name, access, secret, True) except exception.DBError, e: _db_error(e) self._print_export(user) + @args('--name', dest="name", metavar='', help='User name') + @args('--access', dest="access", metavar='', help='Access') + @args('--secret', dest="secret", metavar='', help='Secret') def create(self, name, access=None, secret=None): - """creates a new user and prints exports - arguments: name [access] [secret]""" + """creates a new user and prints exports""" try: user = self.manager.create_user(name, access, secret, False) except exception.DBError, e: _db_error(e) self._print_export(user) + @args('--name', dest="name", metavar='', help='User name') def delete(self, name): """deletes an existing user arguments: name""" self.manager.delete_user(name) + @args('--name', dest="name", metavar='', help='User name') def exports(self, name): - """prints access and secrets for user in export format - arguments: name""" + """prints access and secrets for user in export format""" user = self.manager.get_user(name) if user: self._print_export(user) @@ -337,11 +368,17 @@ class UserCommands(object): print "User %s doesn't exist" % name def list(self): - """lists all users - arguments: """ + """lists all users""" for user in self.manager.get_users(): print user.name + @args('--name', dest="name", metavar='', help='User name') + @args('--access', dest="access_key", metavar='', + help='Access key') + @args('--secret', dest="secret_key", metavar='', + help='Secret key') + @args('--is_admin', dest='is_admin', metavar="<'T'|'F'>", + help='Is admin?') def modify(self, name, access_key, secret_key, is_admin): """update a users keys & admin flag arguments: accesskey secretkey admin @@ -355,9 +392,11 @@ class UserCommands(object): is_admin = False self.manager.modify_user(name, access_key, secret_key, is_admin) + @args('--name', dest="user_id", metavar='', help='User name') + @args('--project', dest="project_id", metavar='', + help='Project name') def revoke(self, user_id, project_id=None): - """revoke certs for a user - arguments: user_id [project_id]""" + """revoke certs for a user""" if project_id: crypto.revoke_certs_by_user_and_project(user_id, project_id) else: @@ -370,45 +409,62 @@ class ProjectCommands(object): def __init__(self): self.manager = manager.AuthManager() + @args('--project', dest="project_id", metavar='', + help='Project name') + @args('--user', dest="user_id", metavar='', help='User name') def add(self, project_id, user_id): - """Adds user to project - arguments: project_id user_id""" + """Adds user to project""" try: self.manager.add_to_project(user_id, project_id) except exception.UserNotFound as ex: print ex raise + @args('--project', dest="name", metavar='', + help='Project name') + @args('--user', dest="project_manager", metavar='', + help='Project manager') + @args('--desc', dest="description", metavar='', + help='Description') def create(self, name, project_manager, description=None): - """Creates a new project - arguments: name project_manager [description]""" + """Creates a new project""" try: self.manager.create_project(name, project_manager, description) except exception.UserNotFound as ex: print ex raise + @args('--project', dest="name", metavar='', + help='Project name') + @args('--user', dest="project_manager", metavar='', + help='Project manager') + @args('--desc', dest="description", metavar='', + help='Description') def modify(self, name, project_manager, description=None): - """Modifies a project - arguments: name project_manager [description]""" + """Modifies a project""" try: self.manager.modify_project(name, project_manager, description) except exception.UserNotFound as ex: print ex raise + @args('--project', dest="name", metavar='', + help='Project name') def delete(self, name): - """Deletes an existing project - arguments: name""" + """Deletes an existing project""" try: self.manager.delete_project(name) except exception.ProjectNotFound as ex: print ex raise + @args('--project', dest="project_id", metavar='', + help='Project name') + @args('--user', dest="user_id", metavar='', help='User name') + @args('--file', dest="filename", metavar='', + help='File name(Default: novarc)') def environment(self, project_id, user_id, filename='novarc'): - """Exports environment variables to an sourcable file - arguments: project_id user_id [filename='novarc]""" + """Exports environment variables to an sourcable file""" try: rc = self.manager.get_environment_rc(user_id, project_id) except (exception.UserNotFound, exception.ProjectNotFound) as ex: @@ -420,15 +476,18 @@ class ProjectCommands(object): with open(filename, 'w') as f: f.write(rc) + @args('--user', dest="username", metavar='', help='User name') def list(self, username=None): - """Lists all projects - arguments: [username]""" + """Lists all projects""" for project in self.manager.get_projects(username): print project.name + @args('--project', dest="project_id", metavar='', + help='Project name') + @args('--key', dest="key", metavar='', help='Key') + @args('--value', dest="value", metavar='', help='Value') def quota(self, project_id, key=None, value=None): - """Set or display quotas for project - arguments: project_id [key] [value]""" + """Set or display quotas for project""" ctxt = context.get_admin_context() if key: if value.lower() == 'unlimited': @@ -443,18 +502,21 @@ class ProjectCommands(object): value = 'unlimited' print '%s: %s' % (key, value) + @args('--project', dest="project_id", metavar='', + help='Project name') + @args('--user', dest="user_id", metavar='', help='User name') def remove(self, project_id, user_id): - """Removes user from project - arguments: project_id user_id""" + """Removes user from project""" try: self.manager.remove_from_project(user_id, project_id) except (exception.UserNotFound, exception.ProjectNotFound) as ex: print ex raise + @args('--project', dest="project_id", metavar='', + help='Project name') def scrub(self, project_id): - """Deletes data associated with project - arguments: project_id""" + """Deletes data associated with project""" admin_context = context.get_admin_context() networks = db.project_get_networks(admin_context, project_id) for network in networks: @@ -463,9 +525,13 @@ class ProjectCommands(object): for group in groups: db.security_group_destroy(admin_context, group['id']) + @args('--project', dest="project_id", metavar='', + help='Project name') + @args('--user', dest="user_id", metavar='', help='User name') + @args('--file', dest="filename", metavar='', + help='File name(Default: nova.zip)') def zipfile(self, project_id, user_id, filename='nova.zip'): - """Exports credentials for project to a zip file - arguments: project_id user_id [filename='nova.zip]""" + """Exports credentials for project to a zip file""" try: zip_file = self.manager.get_credentials(user_id, project_id) if filename == "-": @@ -482,9 +548,9 @@ class ProjectCommands(object): ' nova-manage network create pvt 10.0.0.0/8 10 64\n\n') except exception.ProcessExecutionError, e: print e - print _("The above error may show that the certificate db has not " - "been created.\nPlease create a database by running a " - "nova-api server on this host.") + print _("The above error may show that the certificate db has " + "not been created.\nPlease create a database by running " + "a nova-api server on this host.") AccountCommands = ProjectCommands @@ -492,8 +558,9 @@ AccountCommands = ProjectCommands class FixedIpCommands(object): """Class for managing fixed ip.""" + @args('--host', dest="host", metavar='', help='Host') def list(self, host=None): - """Lists all fixed ips (optionally by host) arguments: [host]""" + """Lists all fixed ips (optionally by host)""" ctxt = context.get_admin_context() try: @@ -528,23 +595,23 @@ class FixedIpCommands(object): class FloatingIpCommands(object): """Class for managing floating ip.""" + @args('--ip_range', dest="range", metavar='', help='IP range') def create(self, range): - """Creates floating ips for zone by range - arguments: ip_range""" + """Creates floating ips for zone by range""" for address in netaddr.IPNetwork(range): db.floating_ip_create(context.get_admin_context(), {'address': str(address)}) + @args('--ip_range', dest="ip_range", metavar='', help='IP range') def delete(self, ip_range): - """Deletes floating ips by range - arguments: range""" + """Deletes floating ips by range""" for address in netaddr.IPNetwork(ip_range): db.floating_ip_destroy(context.get_admin_context(), str(address)) + @args('--host', dest="host", metavar='', help='Host') def list(self, host=None): """Lists all floating ips (optionally by host) - arguments: [host] Note: if host is given, only active floating IPs are returned""" ctxt = context.get_admin_context() if host is None: @@ -563,30 +630,76 @@ class FloatingIpCommands(object): class NetworkCommands(object): """Class for managing networks.""" - def create(self, label=None, fixed_range=None, num_networks=None, + @args('--label', dest="label", metavar='