diff --git a/.mailmap b/.mailmap index d13219ab..c6f6c9a8 100644 --- a/.mailmap +++ b/.mailmap @@ -33,3 +33,4 @@ + diff --git a/Authors b/Authors index 608fec52..b359fec2 100644 --- a/Authors +++ b/Authors @@ -3,11 +3,14 @@ Anne Gentle Anthony Young Antony Messerli Armando Migliaccio +Bilal Akhtar Chiradeep Vittal Chmouel Boudjnah Chris Behrens +Christian Berendt Cory Wright David Pravec +Dan Prince Dean Troyer Devin Carlen Ed Leafe @@ -21,7 +24,9 @@ Jay Pipes Jesse Andrews Joe Heck Joel Moore +John Dewey Jonathan Bryce +Jordan Rinke Josh Durgin Josh Kearney Joshua McKenty @@ -38,6 +43,7 @@ MORITA Kazutaka Muneyuki Noguchi Nachi Ueno Paul Voccio +Ricardo Carrillo Cruz Rick Clark Rick Harris Rob Kost @@ -49,6 +55,7 @@ Soren Hansen Thierry Carrez Todd Willey Trey Morris +Tushar Patil Vishvananda Ishaya Youcef Laribi Zhixue Wu diff --git a/bin/nova-api b/bin/nova-api index 44bbfaf8..11176a02 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -56,7 +56,7 @@ def run_app(paste_config_file): if config is None: LOG.debug(_("No paste configuration for app: %s"), api) continue - LOG.debug(_("App Config: %s\n%r"), api, config) + LOG.debug(_("App Config: %(api)s\n%(config)r") % locals()) wsgi.paste_config_to_flags(config, { "verbose": FLAGS.verbose, "%s_host" % api: config.get('host', '0.0.0.0'), diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index 1a994d95..d38ba254 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -120,9 +120,9 @@ def main(): mac = argv[2] ip = argv[3] hostname = argv[4] - LOG.debug(_("Called %s for mac %s with ip %s and " - "hostname %s on interface %s"), - action, mac, ip, hostname, interface) + msg = _("Called %(action)s for mac %(mac)s with ip %(ip)s and" + " hostname %(hostname)s on interface %(interface)s") % locals() + LOG.debug(msg) globals()[action + '_lease'](mac, ip, hostname, interface) else: print init_leases(interface) diff --git a/bin/nova-manage b/bin/nova-manage index cf693140..68036504 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -267,6 +267,14 @@ class RoleCommands(object): self.manager.remove_role(user, role, project) +def _db_error(caught_exception): + print caught_exception + print _("The above error may show that the database has not " + "been created.\nPlease create a database using " + "nova-manage sync db before running this command.") + exit(1) + + class UserCommands(object): """Class for managing users.""" @@ -282,13 +290,19 @@ class UserCommands(object): def admin(self, name, access=None, secret=None): """creates a new admin and prints exports arguments: name [access] [secret]""" - user = self.manager.create_user(name, access, secret, True) + try: + user = self.manager.create_user(name, access, secret, True) + except exception.DBError, e: + _db_error(e) self._print_export(user) def create(self, name, access=None, secret=None): """creates a new user and prints exports arguments: name [access] [secret]""" - user = self.manager.create_user(name, access, secret, False) + try: + user = self.manager.create_user(name, access, secret, False) + except exception.DBError, e: + _db_error(e) self._print_export(user) def delete(self, name): @@ -409,9 +423,14 @@ class ProjectCommands(object): with open(filename, 'w') as f: f.write(zip_file) except db.api.NoMoreNetworks: - print ('No more networks available. If this is a new ' - 'installation, you need\nto call something like this:\n\n' - ' nova-manage network create 10.0.0.0/8 10 64\n\n') + print _('No more networks available. If this is a new ' + 'installation, you need\nto call something like this:\n\n' + ' nova-manage network create 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.") class FloatingIpCommands(object): diff --git a/bin/nova-spoolsentry b/bin/nova-spoolsentry index ab20268a..c5348285 100644 --- a/bin/nova-spoolsentry +++ b/bin/nova-spoolsentry @@ -74,10 +74,8 @@ class SpoolSentry(object): return rv def send_data(self, data): - data = { - 'data': base64.b64encode(pickle.dumps(data).encode('zlib')), - 'key': self.key - } + data = {'data': base64.b64encode(pickle.dumps(data).encode('zlib')), + 'key': self.key} req = urllib2.Request(self.sentry_url) res = urllib2.urlopen(req, urllib.urlencode(data)) if res.getcode() != 200: diff --git a/krm_mapping.json.sample b/krm_mapping.json.sample deleted file mode 100644 index 1ecfba63..00000000 --- a/krm_mapping.json.sample +++ /dev/null @@ -1,3 +0,0 @@ -{ - "machine" : ["kernel", "ramdisk"] -} diff --git a/nova/adminclient.py b/nova/adminclient.py index b2609c8c..c614b274 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -21,6 +21,7 @@ Nova User API client library. import base64 import boto +import boto.exception import httplib from boto.ec2.regioninfo import RegionInfo @@ -190,6 +191,45 @@ class HostInfo(object): setattr(self, name, value) +class InstanceType(object): + """ + Information about a Nova instance type, as parsed through SAX. + + **Fields include** + + * name + * vcpus + * disk_gb + * memory_mb + * flavor_id + + """ + + def __init__(self, connection=None): + self.connection = connection + self.name = None + self.vcpus = None + self.disk_gb = None + self.memory_mb = None + self.flavor_id = None + + def __repr__(self): + return 'InstanceType:%s' % self.name + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + if name == "memoryMb": + self.memory_mb = str(value) + elif name == "flavorId": + self.flavor_id = str(value) + elif name == "diskGb": + self.disk_gb = str(value) + else: + setattr(self, name, str(value)) + + class NovaAdminClient(object): def __init__( @@ -249,10 +289,14 @@ class NovaAdminClient(object): def get_user(self, name): """Grab a single user by name.""" - user = self.apiconn.get_object('DescribeUser', {'Name': name}, - UserInfo) - if user.username != None: - return user + try: + return self.apiconn.get_object('DescribeUser', + {'Name': name}, + UserInfo) + except boto.exception.BotoServerError, e: + if e.status == 400 and e.error_code == 'NotFound': + return None + raise def has_user(self, username): """Determine if user exists.""" @@ -337,6 +381,13 @@ class NovaAdminClient(object): 'MemberUsers': member_users} return self.apiconn.get_object('RegisterProject', params, ProjectInfo) + def modify_project(self, projectname, manager_user=None, description=None): + """Modifies an existing project.""" + params = {'Name': projectname, + 'ManagerUser': manager_user, + 'Description': description} + return self.apiconn.get_status('ModifyProject', params) + def delete_project(self, projectname): """Permanently deletes the specified project.""" return self.apiconn.get_object('DeregisterProject', @@ -373,3 +424,8 @@ class NovaAdminClient(object): def get_hosts(self): return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)]) + + def get_instance_types(self): + """Grabs the list of all users.""" + return self.apiconn.get_list('DescribeInstanceTypes', {}, + [('item', InstanceType)]) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index a6915ce0..e652f1ca 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -473,8 +473,8 @@ class LdapDriver(object): raise exception.NotFound(_("The group at dn %s doesn't exist") % group_dn) if self.__is_in_group(uid, group_dn): - raise exception.Duplicate(_("User %s is already a member of " - "the group %s") % (uid, group_dn)) + raise exception.Duplicate(_("User %(uid)s is already a member of " + "the group %(group_dn)s") % locals()) attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))] self.conn.modify_s(group_dn, attr) @@ -585,10 +585,11 @@ class LdapDriver(object): else: return None - @staticmethod - def __dn_to_uid(dn): + def __dn_to_uid(self, dn): """Convert user dn to uid""" - return dn.split(',')[0].split('=')[1] + query = '(objectclass=novaUser)' + user = self.__find_object(dn, query) + return user[FLAGS.ldap_user_id_attribute][0] class FakeLdapDriver(LdapDriver): diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 1652e24e..450ab803 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -272,16 +272,22 @@ class AuthManager(object): project = self.get_project(project_id) if project == None: - LOG.audit(_("failed authorization: no project named %s (user=%s)"), - project_id, user.name) + pjid = project_id + uname = user.name + LOG.audit(_("failed authorization: no project named %(pjid)s" + " (user=%(uname)s)") % locals()) raise exception.NotFound(_('No project called %s could be found') % project_id) if not self.is_admin(user) and not self.is_project_member(user, project): - LOG.audit(_("Failed authorization: user %s not admin and not " - "member of project %s"), user.name, project.name) - raise exception.NotFound(_('User %s is not a member of project %s') - % (user.id, project.id)) + uname = user.name + uid = user.id + pjname = project.name + pjid = project.id + LOG.audit(_("Failed authorization: user %(uname)s not admin" + " and not member of project %(pjname)s") % locals()) + raise exception.NotFound(_('User %(uid)s is not a member of' + ' project %(pjid)s') % locals()) if check_type == 's3': sign = signer.Signer(user.secret.encode()) expected_signature = sign.s3_authorization(headers, verb, path) @@ -408,14 +414,16 @@ class AuthManager(object): raise exception.NotFound(_("The %s role can not be found") % role) if project is not None and role in FLAGS.global_roles: raise exception.NotFound(_("The %s role is global only") % role) + uid = User.safe_id(user) + pid = Project.safe_id(project) if project: - LOG.audit(_("Adding role %s to user %s in project %s"), role, - User.safe_id(user), Project.safe_id(project)) + LOG.audit(_("Adding role %(role)s to user %(uid)s" + " in project %(pid)s") % locals()) else: - LOG.audit(_("Adding sitewide role %s to user %s"), role, - User.safe_id(user)) + LOG.audit(_("Adding sitewide role %(role)s to user %(uid)s") + % locals()) with self.driver() as drv: - drv.add_role(User.safe_id(user), role, Project.safe_id(project)) + drv.add_role(uid, role, pid) def remove_role(self, user, role, project=None): """Removes role for user @@ -434,14 +442,16 @@ class AuthManager(object): @type project: Project or project_id @param project: Project in which to remove local role. """ + uid = User.safe_id(user) + pid = Project.safe_id(project) if project: - LOG.audit(_("Removing role %s from user %s on project %s"), - role, User.safe_id(user), Project.safe_id(project)) + LOG.audit(_("Removing role %(role)s from user %(uid)s" + " on project %(pid)s") % locals()) else: - LOG.audit(_("Removing sitewide role %s from user %s"), role, - User.safe_id(user)) + LOG.audit(_("Removing sitewide role %(role)s" + " from user %(uid)s") % locals()) with self.driver() as drv: - drv.remove_role(User.safe_id(user), role, Project.safe_id(project)) + drv.remove_role(uid, role, pid) @staticmethod def get_roles(project_roles=True): @@ -502,8 +512,8 @@ class AuthManager(object): description, member_users) if project_dict: - LOG.audit(_("Created project %s with manager %s"), name, - manager_user) + LOG.audit(_("Created project %(name)s with" + " manager %(manager_user)s") % locals()) project = Project(**project_dict) return project @@ -530,11 +540,12 @@ class AuthManager(object): def add_to_project(self, user, project): """Add user to project""" - LOG.audit(_("Adding user %s to project %s"), User.safe_id(user), - Project.safe_id(project)) + uid = User.safe_id(user) + pid = Project.safe_id(project) + LOG.audit(_("Adding user %(uid)s to project %(pid)s") % locals()) with self.driver() as drv: return drv.add_to_project(User.safe_id(user), - Project.safe_id(project)) + Project.safe_id(project)) def is_project_manager(self, user, project): """Checks if user is project manager""" @@ -550,11 +561,11 @@ class AuthManager(object): def remove_from_project(self, user, project): """Removes a user from a project""" - LOG.audit(_("Remove user %s from project %s"), User.safe_id(user), - Project.safe_id(project)) + uid = User.safe_id(user) + pid = Project.safe_id(project) + LOG.audit(_("Remove user %(uid)s from project %(pid)s") % locals()) with self.driver() as drv: - return drv.remove_from_project(User.safe_id(user), - Project.safe_id(project)) + return drv.remove_from_project(uid, pid) @staticmethod def get_project_vpn_data(project): @@ -634,7 +645,10 @@ class AuthManager(object): user_dict = drv.create_user(name, access, secret, admin) if user_dict: rv = User(**user_dict) - LOG.audit(_("Created user %s (admin: %r)"), rv.name, rv.admin) + rvname = rv.name + rvadmin = rv.admin + LOG.audit(_("Created user %(rvname)s" + " (admin: %(rvadmin)r)") % locals()) return rv def delete_user(self, user): @@ -656,7 +670,8 @@ class AuthManager(object): if secret_key: LOG.audit(_("Secret Key change for user %s"), uid) if admin is not None: - LOG.audit(_("Admin status set to %r for user %s"), admin, uid) + LOG.audit(_("Admin status set to %(admin)r" + " for user %(uid)s") % locals()) with self.driver() as drv: drv.modify_user(uid, access_key, secret_key, admin) diff --git a/nova/fakerabbit.py b/nova/fakerabbit.py index 7c2d7177..dd82a936 100644 --- a/nova/fakerabbit.py +++ b/nova/fakerabbit.py @@ -45,8 +45,9 @@ class Exchange(object): self._routes = {} def publish(self, message, routing_key=None): - LOG.debug(_('(%s) publish (key: %s) %s'), - self.name, routing_key, message) + nm = self.name + LOG.debug(_('(%(nm)s) publish (key: %(routing_key)s)' + ' %(message)s') % locals()) routing_key = routing_key.split('.')[0] if routing_key in self._routes: for f in self._routes[routing_key]: @@ -92,8 +93,8 @@ class Backend(base.BaseBackend): def queue_bind(self, queue, exchange, routing_key, **kwargs): global EXCHANGES global QUEUES - LOG.debug(_('Binding %s to %s with key %s'), - queue, exchange, routing_key) + LOG.debug(_('Binding %(queue)s to %(exchange)s with' + ' key %(routing_key)s') % locals()) EXCHANGES[exchange].bind(QUEUES[queue].push, routing_key) def declare_consumer(self, queue, callback, *args, **kwargs): @@ -117,7 +118,7 @@ class Backend(base.BaseBackend): content_type=content_type, content_encoding=content_encoding) message.result = True - LOG.debug(_('Getting from %s: %s'), queue, message) + LOG.debug(_('Getting from %(queue)s: %(message)s') % locals()) return message def prepare_message(self, message_data, delivery_mode, diff --git a/nova/flags.py b/nova/flags.py index 81e2e36f..3ba3fe6f 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -208,7 +208,7 @@ def _get_my_ip(): (addr, port) = csock.getsockname() csock.close() return addr - except socket.gaierror as ex: + except socket.error as ex: return "127.0.0.1" @@ -218,7 +218,7 @@ def _get_my_ip(): DEFINE_string('my_ip', _get_my_ip(), 'host ip address') DEFINE_list('region_list', [], - 'list of region=url pairs separated by commas') + 'list of region=fqdn pairs separated by commas') DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake') DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID') DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key') @@ -286,8 +286,8 @@ DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'), DEFINE_string('sql_connection', 'sqlite:///$state_path/nova.sqlite', 'connection string for sql database') -DEFINE_string('sql_idle_timeout', - '3600', +DEFINE_integer('sql_idle_timeout', + 3600, 'timeout for idle sql database connections') DEFINE_integer('sql_max_retries', 12, 'sql connection attempts') DEFINE_integer('sql_retry_interval', 10, 'sql connection retry interval') diff --git a/nova/log.py b/nova/log.py index e1c9f46f..b541488b 100644 --- a/nova/log.py +++ b/nova/log.py @@ -31,6 +31,7 @@ import cStringIO import json import logging import logging.handlers +import sys import traceback from nova import flags @@ -191,6 +192,12 @@ class NovaLogger(logging.Logger): kwargs.pop('exc_info') self.error(message, **kwargs) + +def handle_exception(type, value, tb): + logging.root.critical(str(value), exc_info=(type, value, tb)) + + +sys.excepthook = handle_exception logging.setLoggerClass(NovaLogger) diff --git a/nova/rpc.py b/nova/rpc.py index bbfa7113..2b1f7298 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -46,7 +46,7 @@ LOG = logging.getLogger('nova.rpc') class Connection(carrot_connection.BrokerConnection): """Connection instance object""" @classmethod - def instance(cls, new=False): + def instance(cls, new=True): """Returns the instance""" if new or not hasattr(cls, '_instance'): params = dict(hostname=FLAGS.rabbit_host, @@ -89,15 +89,16 @@ class Consumer(messaging.Consumer): self.failed_connection = False break except: # Catching all because carrot sucks - LOG.exception(_("AMQP server on %s:%d is unreachable." - " Trying again in %d seconds.") % ( - FLAGS.rabbit_host, - FLAGS.rabbit_port, - FLAGS.rabbit_retry_interval)) + fl_host = FLAGS.rabbit_host + fl_port = FLAGS.rabbit_port + fl_intv = FLAGS.rabbit_retry_interval + LOG.exception(_("AMQP server on %(fl_host)s:%(fl_port)d is" + " unreachable. Trying again in %(fl_intv)d seconds.") + % locals()) self.failed_connection = True if self.failed_connection: LOG.exception(_("Unable to connect to AMQP server " - "after %d tries. Shutting down."), + "after %d tries. Shutting down."), FLAGS.rabbit_max_retries) sys.exit(1) @@ -152,7 +153,7 @@ class TopicConsumer(Consumer): class AdapterConsumer(TopicConsumer): """Calls methods on a proxy object based on method and args""" def __init__(self, connection=None, topic="broadcast", proxy=None): - LOG.debug(_('Initing the Adapter Consumer for %s') % (topic)) + LOG.debug(_('Initing the Adapter Consumer for %s') % topic) self.proxy = proxy super(AdapterConsumer, self).__init__(connection=connection, topic=topic) @@ -167,7 +168,7 @@ class AdapterConsumer(TopicConsumer): Example: {'method': 'echo', 'args': {'value': 42}} """ - LOG.debug(_('received %s') % (message_data)) + LOG.debug(_('received %s') % message_data) msg_id = message_data.pop('_msg_id', None) ctxt = _unpack_context(message_data) @@ -180,7 +181,7 @@ class AdapterConsumer(TopicConsumer): # messages stay in the queue indefinitely, so for now # we just log the message and send an error string # back to the caller - LOG.warn(_('no method for message: %s') % (message_data)) + LOG.warn(_('no method for message: %s') % message_data) msg_reply(msg_id, _('No method for message: %s') % message_data) return @@ -245,7 +246,7 @@ def msg_reply(msg_id, reply=None, failure=None): LOG.error(_("Returning exception %s to caller"), message) LOG.error(tb) failure = (failure[0].__name__, str(failure[1]), tb) - conn = Connection.instance(True) + conn = Connection.instance() publisher = DirectPublisher(connection=conn, msg_id=msg_id) try: publisher.send({'result': reply, 'failure': failure}) @@ -318,7 +319,7 @@ def call(context, topic, msg): self.result = data['result'] wait_msg = WaitMessage() - conn = Connection.instance(True) + conn = Connection.instance() consumer = DirectConsumer(connection=conn, msg_id=msg_id) consumer.register_callback(wait_msg) diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index baf4966d..0191ceb3 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -43,7 +43,9 @@ class SimpleScheduler(chance.ChanceScheduler): def schedule_run_instance(self, context, instance_id, *_args, **_kwargs): """Picks a host that is up and has the fewest running instances.""" instance_ref = db.instance_get(context, instance_id) - if instance_ref['availability_zone'] and context.is_admin: + if (instance_ref['availability_zone'] + and ':' in instance_ref['availability_zone'] + and context.is_admin): zone, _x, host = instance_ref['availability_zone'].partition(':') service = db.service_get_by_args(context.elevated(), host, 'nova-compute') @@ -75,7 +77,9 @@ class SimpleScheduler(chance.ChanceScheduler): def schedule_create_volume(self, context, volume_id, *_args, **_kwargs): """Picks a host that is up and has the fewest volumes.""" volume_ref = db.volume_get(context, volume_id) - if (':' in volume_ref['availability_zone']) and context.is_admin: + if (volume_ref['availability_zone'] + and ':' in volume_ref['availability_zone'] + and context.is_admin): zone, _x, host = volume_ref['availability_zone'].partition(':') service = db.service_get_by_args(context.elevated(), host, 'nova-volume') diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 66a16b0c..2569e262 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -36,6 +36,7 @@ from nova.auth import manager class FakeHttplibSocket(object): """a fake socket implementation for httplib.HTTPResponse, trivial""" def __init__(self, response_string): + self.response_string = response_string self._buffer = StringIO.StringIO(response_string) def makefile(self, _mode, _other): @@ -66,13 +67,16 @@ class FakeHttplibConnection(object): # For some reason, the response doesn't have "HTTP/1.0 " prepended; I # guess that's a function the web server usually provides. resp = "HTTP/1.0 %s" % resp - sock = FakeHttplibSocket(resp) - self.http_response = httplib.HTTPResponse(sock) + self.sock = FakeHttplibSocket(resp) + self.http_response = httplib.HTTPResponse(self.sock) self.http_response.begin() def getresponse(self): return self.http_response + def getresponsebody(self): + return self.sock.response_string + def close(self): """Required for compatibility with boto/tornado""" pass @@ -104,7 +108,7 @@ class ApiEc2TestCase(test.TestCase): self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(), 'nova.api.ec2.cloud.CloudController')) - def expect_http(self, host=None, is_secure=False): + def expect_http(self, host=None, is_secure=False, api_version=None): """Returns a new EC2 connection""" self.ec2 = boto.connect_ec2( aws_access_key_id='fake', @@ -113,13 +117,31 @@ class ApiEc2TestCase(test.TestCase): region=regioninfo.RegionInfo(None, 'test', self.host), port=8773, path='/services/Cloud') + if api_version: + self.ec2.APIVersion = api_version self.mox.StubOutWithMock(self.ec2, 'new_http_connection') - http = FakeHttplibConnection( + self.http = FakeHttplibConnection( self.app, '%s:8773' % (self.host), False) # pylint: disable-msg=E1103 - self.ec2.new_http_connection(host, is_secure).AndReturn(http) - return http + self.ec2.new_http_connection(host, is_secure).AndReturn(self.http) + return self.http + + def test_xmlns_version_matches_request_version(self): + self.expect_http(api_version='2010-10-30') + self.mox.ReplayAll() + + user = self.manager.create_user('fake', 'fake', 'fake') + project = self.manager.create_project('fake', 'fake', 'fake') + + # Any request should be fine + self.ec2.get_all_instances() + self.assertTrue(self.ec2.APIVersion in self.http.getresponsebody(), + 'The version in the xmlns of the response does ' + 'not match the API version given in the request.') + + self.manager.delete_project(project) + self.manager.delete_user(user) def test_describe_instances(self): """Test that, after creating a user and a project, the describe diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 771b1fcc..445cc6e8 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -87,6 +87,16 @@ class CloudTestCase(test.TestCase): # NOTE(vish): create depends on pool, so just call helper directly return cloud._gen_key(self.context, self.context.user.id, name) + def test_describe_regions(self): + """Makes sure describe regions runs without raising an exception""" + result = self.cloud.describe_regions(self.context) + self.assertEqual(len(result['regionInfo']), 1) + regions = FLAGS.region_list + FLAGS.region_list = ["one=test_host1", "two=test_host2"] + result = self.cloud.describe_regions(self.context) + self.assertEqual(len(result['regionInfo']), 2) + FLAGS.region_list = regions + def test_describe_addresses(self): """Makes sure describe addresses runs without raising an exception""" address = "10.10.10.10" diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 09f6ee94..2aa0690e 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -49,7 +49,7 @@ class ComputeTestCase(test.TestCase): self.manager = manager.AuthManager() self.user = self.manager.create_user('fake', 'fake', 'fake') self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.get_admin_context() + self.context = context.RequestContext('fake', 'fake', False) def tearDown(self): self.manager.delete_user(self.user) @@ -69,6 +69,13 @@ class ComputeTestCase(test.TestCase): inst['ami_launch_index'] = 0 return db.instance_create(self.context, inst)['id'] + def _create_group(self): + values = {'name': 'testgroup', + 'description': 'testgroup', + 'user_id': self.user.id, + 'project_id': self.project.id} + return db.security_group_create(self.context, values) + def test_create_instance_defaults_display_name(self): """Verify that an instance cannot be created without a display_name.""" cases = [dict(), dict(display_name=None)] @@ -82,23 +89,55 @@ class ComputeTestCase(test.TestCase): def test_create_instance_associates_security_groups(self): """Make sure create associates security groups""" - values = {'name': 'default', - 'description': 'default', - 'user_id': self.user.id, - 'project_id': self.project.id} - group = db.security_group_create(self.context, values) + group = self._create_group() ref = self.compute_api.create( self.context, instance_type=FLAGS.default_instance_type, image_id=None, - security_group=['default']) + security_group=['testgroup']) try: self.assertEqual(len(db.security_group_get_by_instance( - self.context, ref[0]['id'])), 1) + self.context, ref[0]['id'])), 1) + group = db.security_group_get(self.context, group['id']) + self.assert_(len(group.instances) == 1) finally: db.security_group_destroy(self.context, group['id']) db.instance_destroy(self.context, ref[0]['id']) + def test_destroy_instance_disassociates_security_groups(self): + """Make sure destroying disassociates security groups""" + group = self._create_group() + + ref = self.compute_api.create( + self.context, + instance_type=FLAGS.default_instance_type, + image_id=None, + security_group=['testgroup']) + try: + db.instance_destroy(self.context, ref[0]['id']) + group = db.security_group_get(self.context, group['id']) + self.assert_(len(group.instances) == 0) + finally: + db.security_group_destroy(self.context, group['id']) + + def test_destroy_security_group_disassociates_instances(self): + """Make sure destroying security groups disassociates instances""" + group = self._create_group() + + ref = self.compute_api.create( + self.context, + instance_type=FLAGS.default_instance_type, + image_id=None, + security_group=['testgroup']) + + try: + db.security_group_destroy(self.context, group['id']) + group = db.security_group_get(context.get_admin_context( + read_deleted=True), group['id']) + self.assert_(len(group.instances) == 0) + finally: + db.instance_destroy(self.context, ref[0]['id']) + def test_run_terminate(self): """Make sure it is possible to run and terminate instance""" instance_id = self._create_instance() diff --git a/nova/tests/test_localization.py b/nova/tests/test_localization.py new file mode 100644 index 00000000..6992773f --- /dev/null +++ b/nova/tests/test_localization.py @@ -0,0 +1,100 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import glob +import logging +import os +import re +import sys +import unittest + +import nova + + +class LocalizationTestCase(unittest.TestCase): + def test_multiple_positional_format_placeholders(self): + pat = re.compile("\W_\(") + single_pat = re.compile("\W%\W") + root_path = os.path.dirname(nova.__file__) + problems = {} + for root, dirs, files in os.walk(root_path): + for fname in files: + if not fname.endswith(".py"): + continue + pth = os.path.join(root, fname) + txt = fulltext = file(pth).read() + txt_lines = fulltext.splitlines() + if not pat.search(txt): + continue + problems[pth] = [] + pos = txt.find("_(") + while pos > -1: + # Make sure that this isn't part of a dunder; + # e.g., __init__(... + # or something like 'self.assert_(...' + test_txt = txt[pos - 1: pos + 10] + if not (pat.search(test_txt)): + txt = txt[pos + 2:] + pos = txt.find("_(") + continue + pos += 2 + txt = txt[pos:] + innerChars = [] + # Count pairs of open/close parens until _() closing + # paren is found. + parenCount = 1 + pos = 0 + while parenCount > 0: + char = txt[pos] + if char == "(": + parenCount += 1 + elif char == ")": + parenCount -= 1 + innerChars.append(char) + pos += 1 + inner_all = "".join(innerChars) + # Filter out '%%' and '%(' + inner = inner_all.replace("%%", "").replace("%(", "") + # Filter out the single '%' operators + inner = single_pat.sub("", inner) + # Within the remaining content, count % + fmtCount = inner.count("%") + if fmtCount > 1: + inner_first = inner_all.splitlines()[0] + lns = ["%s" % (p + 1) + for p, t in enumerate(txt_lines) + if inner_first in t] + lnums = ", ".join(lns) + # Using ugly string concatenation to avoid having + # this test fail itself. + inner_all = "_" + "(" + "%s" % inner_all + problems[pth].append("Line: %s Text: %s" % + (lnums, inner_all)) + # Look for more + pos = txt.find("_(") + if not problems[pth]: + del problems[pth] + if problems: + out = ["Problem(s) found in localized string formatting", + "(see http://www.gnu.org/software/hello/manual/" + "gettext/Python.html for more information)", + "", + " ------------ Files to fix ------------"] + for pth in problems: + out.append(" %s:" % pth) + for val in set(problems[pth]): + out.append(" %s" % val) + raise AssertionError("\n".join(out)) diff --git a/nova/tests/test_rpc.py b/nova/tests/test_rpc.py index 85593ab4..4820e04f 100644 --- a/nova/tests/test_rpc.py +++ b/nova/tests/test_rpc.py @@ -86,7 +86,8 @@ class RpcTestCase(test.TestCase): @staticmethod def echo(context, queue, value): """Calls echo in the passed queue""" - LOG.debug(_("Nested received %s, %s"), queue, value) + LOG.debug(_("Nested received %(queue)s, %(value)s") + % locals()) ret = rpc.call(context, queue, {"method": "echo", diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index f6800e3d..6e5a0114 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -221,7 +221,12 @@ class IptablesFirewallTestCase(test.TestCase): self.project = self.manager.create_project('fake', 'fake', 'fake') self.context = context.RequestContext('fake', 'fake') self.network = utils.import_object(FLAGS.network_manager) - self.fw = libvirt_conn.IptablesFirewallDriver() + + class FakeLibvirtConnection(object): + pass + self.fake_libvirt_connection = FakeLibvirtConnection() + self.fw = libvirt_conn.IptablesFirewallDriver( + get_connection=lambda: self.fake_libvirt_connection) def tearDown(self): self.manager.delete_project(self.project) @@ -256,7 +261,7 @@ class IptablesFirewallTestCase(test.TestCase): ':FORWARD ACCEPT [0:0]', ':OUTPUT ACCEPT [349256:75777230]', 'COMMIT', - '# Completed on Tue Jan 18 23:47:56 2011' + '# Completed on Tue Jan 18 23:47:56 2011', ] def test_static_filters(self): @@ -474,6 +479,19 @@ class NWFilterTestCase(test.TestCase): 'project_id': 'fake'}) inst_id = instance_ref['id'] + ip = '10.11.12.13' + + network_ref = db.project_get_network(self.context, + 'fake') + + fixed_ip = {'address': ip, + 'network_id': network_ref['id']} + + admin_ctxt = context.get_admin_context() + db.fixed_ip_create(admin_ctxt, fixed_ip) + db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, + 'instance_id': instance_ref['id']}) + def _ensure_all_called(): instance_filter = 'nova-instance-%s' % instance_ref['name'] secgroup_filter = 'nova-secgroup-%s' % self.security_group['id'] diff --git a/run_tests.py b/run_tests.py index 5c8436ae..24786e8a 100644 --- a/run_tests.py +++ b/run_tests.py @@ -26,6 +26,8 @@ from nose import config from nose import result from nose import core +from nova import log as logging + class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): @@ -58,6 +60,7 @@ class NovaTestRunner(core.TextTestRunner): if __name__ == '__main__': + logging.basicConfig() c = config.Config(stream=sys.stdout, env=os.environ, verbosity=3,