diff --git a/.mailmap b/.mailmap index 7e031fc7..3f0238ee 100644 --- a/.mailmap +++ b/.mailmap @@ -29,6 +29,7 @@ + @@ -36,6 +37,7 @@ + @@ -44,5 +46,4 @@ - diff --git a/Authors b/Authors index 8b54240c..546c9091 100644 --- a/Authors +++ b/Authors @@ -80,7 +80,7 @@ Trey Morris Tushar Patil Vasiliy Shlykov Vishvananda Ishaya -William Wolf +William Wolf Yoshiaki Tamura Youcef Laribi Yuriy Taraday diff --git a/bin/nova-manage b/bin/nova-manage index a36ec86d..c95b216c 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -397,11 +397,10 @@ class ProjectCommands(object): arguments: project_id [key] [value]""" ctxt = context.get_admin_context() if key: - quo = {'project_id': project_id, key: value} try: - db.quota_update(ctxt, project_id, quo) + db.quota_update(ctxt, project_id, key, value) except exception.NotFound: - db.quota_create(ctxt, quo) + db.quota_create(ctxt, project_id, key, value) project_quota = quota.get_quota(ctxt, project_id) for key, value in project_quota.iteritems(): print '%s: %s' % (key, value) diff --git a/nova/scheduler/host_filter.py b/nova/scheduler/host_filter.py index a47e41da..8519b8b5 100644 --- a/nova/scheduler/host_filter.py +++ b/nova/scheduler/host_filter.py @@ -305,3 +305,8 @@ class HostFilterScheduler(zone_aware_scheduler.ZoneAwareScheduler): instance_type = request_spec['instance_type'] query = driver.instance_type_to_filter(instance_type) return driver.filter_hosts(self.zone_manager, query) + + def weigh_hosts(self, num, request_spec, hosts): + """Derived classes must override this method and return + a lists of hosts in [{weight, hostname}] format.""" + return [] diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index f9c5f65f..2fc5f1f8 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -53,6 +53,9 @@ class ZoneAwareScheduler(driver.Scheduler): # Create build plan and provision ... build_plan = self.select(context, request_spec) + if not build_plan: + raise driver.NoValidHost(_('No hosts were available')) + for item in build_plan: self.provision_instance(context, topic, item) @@ -67,15 +70,14 @@ class ZoneAwareScheduler(driver.Scheduler): anything about the children.""" return self._schedule(context, "compute", request_spec, *args, **kwargs) + # TODO(sandy): We're only focused on compute instances right now, + # so we don't implement the default "schedule()" method required + # of Schedulers. def schedule(self, context, topic, request_spec, *args, **kwargs): """The schedule() contract requires we return the one best-suited host for this request. """ - res = self._schedule(context, topic, request_spec, *args, **kwargs) - # TODO(sirp): should this be a host object rather than a weight-dict? - if not res: - raise driver.NoValidHost(_('No hosts were available')) - return res[0] + raise driver.NoValidHost(_('No hosts were available')) def _schedule(self, context, topic, request_spec, *args, **kwargs): """Returns a list of hosts that meet the required specs, diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index f271c03f..c8559615 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -279,6 +279,26 @@ class CloudTestCase(test.TestCase): user_group=['all']) self.assertEqual(True, result['is_public']) + def test_deregister_image(self): + deregister_image = self.cloud.deregister_image + + def fake_delete(self, context, id): + return None + + self.stubs.Set(local.LocalImageService, 'delete', fake_delete) + # valid image + result = deregister_image(self.context, 'ami-00000001') + self.assertEqual(result['imageId'], 'ami-00000001') + # invalid image + self.stubs.UnsetAll() + + def fake_detail_empty(self, context): + return [] + + self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty) + self.assertRaises(exception.ImageNotFound, deregister_image, + self.context, 'ami-bad001') + def test_console_output(self): instance_type = FLAGS.default_instance_type max_count = 1 @@ -338,41 +358,6 @@ class CloudTestCase(test.TestCase): self._create_key('test') self.cloud.delete_key_pair(self.context, 'test') - def test_run_instances(self): - if FLAGS.connection_type == 'fake': - LOG.debug(_("Can't test instances without a real virtual env.")) - return - image_id = FLAGS.default_image - instance_type = FLAGS.default_instance_type - max_count = 1 - kwargs = {'image_id': image_id, - 'instance_type': instance_type, - 'max_count': max_count} - rv = self.cloud.run_instances(self.context, **kwargs) - # TODO: check for proper response - instance_id = rv['reservationSet'][0].keys()[0] - instance = rv['reservationSet'][0][instance_id][0] - LOG.debug(_("Need to watch instance %s until it's running..."), - instance['instance_id']) - while True: - greenthread.sleep(1) - info = self.cloud._get_instance(instance['instance_id']) - LOG.debug(info['state']) - if info['state'] == power_state.RUNNING: - break - self.assert_(rv) - - if FLAGS.connection_type != 'fake': - time.sleep(45) # Should use boto for polling here - for reservations in rv['reservationSet']: - # for res_id in reservations.keys(): - # LOG.debug(reservations[res_id]) - # for instance in reservations[res_id]: - for instance in reservations[reservations.keys()[0]]: - instance_id = instance['instance_id'] - LOG.debug(_("Terminating instance %s"), instance_id) - rv = self.compute.terminate_instance(instance_id) - def test_terminate_instances(self): inst1 = db.instance_create(self.context, {'reservation_id': 'a', 'image_id': 1, diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 1311ba36..d743f94f 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -642,7 +642,7 @@ class LibvirtConnTestCase(test.TestCase): try: conn.spawn(instance, network_info) except Exception, e: - count = (0 <= e.message.find('Unexpected method call')) + count = (0 <= str(e.message).find('Unexpected method call')) self.assertTrue(count) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 6072f545..be1e3569 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -16,6 +16,7 @@ """Test suite for XenAPI.""" +import eventlet import functools import json import os @@ -198,6 +199,28 @@ class XenAPIVMTestCase(test.TestCase): self.context = context.RequestContext('fake', 'fake', False) self.conn = xenapi_conn.get_connection(False) + def test_parallel_builds(self): + stubs.stubout_loopingcall_delay(self.stubs) + + def _do_build(id, proj, user, *args): + values = { + 'id': id, + 'project_id': proj, + 'user_id': user, + 'image_id': 1, + 'kernel_id': 2, + 'ramdisk_id': 3, + 'instance_type_id': '3', # m1.large + 'mac_address': 'aa:bb:cc:dd:ee:ff', + 'os_type': 'linux'} + instance = db.instance_create(self.context, values) + self.conn.spawn(instance) + + gt1 = eventlet.spawn(_do_build, 1, self.project.id, self.user.id) + gt2 = eventlet.spawn(_do_build, 2, self.project.id, self.user.id) + gt1.wait() + gt2.wait() + def test_list_instances_0(self): instances = self.conn.list_instances() self.assertEquals(instances, []) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 205f6c90..4833ccb0 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -16,6 +16,7 @@ """Stubouts, mocks and fixtures for the test suite""" +import eventlet from nova.virt import xenapi_conn from nova.virt.xenapi import fake from nova.virt.xenapi import volume_utils @@ -28,29 +29,6 @@ def stubout_instance_snapshot(stubs): @classmethod def fake_fetch_image(cls, session, instance_id, image, user, project, type): - # Stubout wait_for_task - def fake_wait_for_task(self, task, id): - class FakeEvent: - - def send(self, value): - self.rv = value - - def wait(self): - return self.rv - - done = FakeEvent() - self._poll_task(id, task, done) - rv = done.wait() - return rv - - def fake_loop(self): - pass - - stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task', - fake_wait_for_task) - - stubs.Set(xenapi_conn.XenAPISession, '_stop_loop', fake_loop) - from nova.virt.xenapi.fake import create_vdi name_label = "instance-%s" % instance_id #TODO: create fake SR record @@ -63,11 +41,6 @@ def stubout_instance_snapshot(stubs): stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image) - def fake_parse_xmlrpc_value(val): - return val - - stubs.Set(xenapi_conn, '_parse_xmlrpc_value', fake_parse_xmlrpc_value) - def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): from nova.virt.xenapi.fake import create_vdi @@ -144,6 +117,16 @@ def stubout_loopingcall_start(stubs): stubs.Set(utils.LoopingCall, 'start', fake_start) +def stubout_loopingcall_delay(stubs): + def fake_start(self, interval, now=True): + self._running = True + eventlet.sleep(1) + self.f(*self.args, **self.kw) + # This would fail before parallel xenapi calls were fixed + assert self._running == False + stubs.Set(utils.LoopingCall, 'start', fake_start) + + class FakeSessionForVMTests(fake.SessionBase): """ Stubs out a XenAPISession for VM tests """ def __init__(self, uri):