From a727de9e27cd892263409f9adedc0900bed3d4c3 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 27 May 2011 11:10:24 +0900 Subject: [PATCH 01/47] compute: implement ec2 stop/start instances This patch implements ec2 stop/start instances with block device mapping support. --- nova/scheduler/simple.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nova/scheduler/simple.py b/nova/scheduler/simple.py index dd568d2c..ccbc79a3 100644 --- a/nova/scheduler/simple.py +++ b/nova/scheduler/simple.py @@ -40,7 +40,7 @@ flags.DEFINE_integer("max_networks", 1000, class SimpleScheduler(chance.ChanceScheduler): """Implements Naive Scheduler that tries to find least loaded host.""" - def schedule_run_instance(self, context, instance_id, *_args, **_kwargs): + def _schedule_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'] @@ -76,6 +76,12 @@ class SimpleScheduler(chance.ChanceScheduler): " for this request. Is the appropriate" " service running?")) + def schedule_run_instance(self, context, instance_id, *_args, **_kwargs): + return self._schedule_instance(context, instance_id, *_args, **_kwargs) + + def schedule_start_instance(self, context, instance_id, *_args, **_kwargs): + return self._schedule_instance(context, instance_id, *_args, **_kwargs) + 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) From 068929a24dba6399c01dcc6b1cac57ffa44bed66 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Fri, 27 May 2011 11:11:06 +0900 Subject: [PATCH 02/47] unittest: tests for boot from volume and stop/start instances --- nova/tests/test_cloud.py | 322 +++++++++++++++++++++++++++++++++++-- nova/tests/test_compute.py | 15 ++ 2 files changed, 327 insertions(+), 10 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 8c7520fe..331fadaa 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -63,6 +63,7 @@ class CloudTestCase(test.TestCase): self.compute = self.start_service('compute') self.scheduter = self.start_service('scheduler') self.network = self.start_service('network') + self.volume = self.start_service('volume') self.image_service = utils.import_object(FLAGS.image_service) self.manager = manager.AuthManager() @@ -85,6 +86,7 @@ class CloudTestCase(test.TestCase): db.network_disassociate(self.context, network_ref['id']) self.manager.delete_project(self.project) self.manager.delete_user(self.user) + self.volume.kill() self.compute.kill() self.network.kill() super(CloudTestCase, self).tearDown() @@ -364,15 +366,22 @@ class CloudTestCase(test.TestCase): self.assertRaises(exception.ImageNotFound, deregister_image, self.context, 'ami-bad001') - def test_console_output(self): - instance_type = FLAGS.default_instance_type - max_count = 1 - kwargs = {'image_id': 'ami-1', - 'instance_type': instance_type, - 'max_count': max_count} + def _run_instance(self, **kwargs): rv = self.cloud.run_instances(self.context, **kwargs) greenthread.sleep(0.3) instance_id = rv['instancesSet'][0]['instanceId'] + return instance_id + + def _run_instance_wait(self, **kwargs): + ec2_instance_id = self._run_instance(**kwargs) + self._wait_for_running(ec2_instance_id) + return ec2_instance_id + + def test_console_output(self): + instance_id = self._run_instance( + image_id='ami-1', + instance_type=FLAGS.default_instance_type, + max_count=1) output = self.cloud.get_console_output(context=self.context, instance_id=[instance_id]) self.assertEquals(b64decode(output['output']), 'FAKE CONSOLE?OUTPUT') @@ -383,10 +392,7 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) def test_ajax_console(self): - kwargs = {'image_id': 'ami-1'} - rv = self.cloud.run_instances(self.context, **kwargs) - instance_id = rv['instancesSet'][0]['instanceId'] - greenthread.sleep(0.3) + instance_id = self._run_instance(image_id='ami-1') output = self.cloud.get_ajax_console(context=self.context, instance_id=[instance_id]) self.assertEquals(output['url'], @@ -470,3 +476,299 @@ class CloudTestCase(test.TestCase): vol = db.volume_get(self.context, vol['id']) self.assertEqual(None, vol['mountpoint']) db.volume_destroy(self.context, vol['id']) + + def _restart_compute_service(self, periodic_interval=None): + """restart compute service. NOTE: fake driver forgets all instances.""" + self.compute.kill() + if periodic_interval: + self.compute = self.start_service( + 'compute', periodic_interval=periodic_interval) + else: + self.compute = self.start_service('compute') + + def _wait_for_state(self, ctxt, instance_id, predicate): + """Wait for an stopping instance to be a given state""" + id = ec2utils.ec2_id_to_id(instance_id) + while True: + info = self.cloud.compute_api.get(context=ctxt, instance_id=id) + LOG.debug(info) + if predicate(info): + break + greenthread.sleep(1) + + def _wait_for_running(self, instance_id): + def is_running(info): + return info['state_description'] == 'running' + self._wait_for_state(self.context, instance_id, is_running) + + def _wait_for_stopped(self, instance_id): + def is_stopped(info): + return info['state_description'] == 'stopped' + self._wait_for_state(self.context, instance_id, is_stopped) + + def _wait_for_terminate(self, instance_id): + def is_deleted(info): + return info['deleted'] + elevated = self.context.elevated(read_deleted=True) + self._wait_for_state(elevated, instance_id, is_deleted) + + def test_stop_start_instance(self): + """Makes sure stop/start instnace works""" + # enforce periodic tasks run in short time to avoid wait for 60s. + self._restart_compute_service(periodic_interval=0.3) + + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1,} + instance_id = self._run_instance_wait(**kwargs) + + # a running instance can't be started. It is just ignored. + result = self.cloud.start_instances(self.context, [instance_id]) + greenthread.sleep(0.3) + self.assertTrue(result) + + result = self.cloud.stop_instances(self.context, [instance_id]) + greenthread.sleep(0.3) + self.assertTrue(result) + self._wait_for_stopped(instance_id) + + result = self.cloud.start_instances(self.context, [instance_id]) + greenthread.sleep(0.3) + self.assertTrue(result) + self._wait_for_running(instance_id) + + result = self.cloud.stop_instances(self.context, [instance_id]) + greenthread.sleep(0.3) + self.assertTrue(result) + self._wait_for_stopped(instance_id) + + result = self.cloud.terminate_instances(self.context, [instance_id]) + greenthread.sleep(0.3) + self.assertTrue(result) + + self._restart_compute_service() + + def _volume_create(self): + kwargs = {'status': 'available', + 'host': self.volume.host, + 'size': 1, + 'attach_status': 'detached',} + return db.volume_create(self.context, kwargs) + + def _assert_volume_attached(self, vol, instance_id, mountpoint): + self.assertEqual(vol['instance_id'], instance_id) + self.assertEqual(vol['mountpoint'], mountpoint) + self.assertEqual(vol['status'], "in-use") + self.assertEqual(vol['attach_status'], "attached") + + def _assert_volume_detached(self, vol): + self.assertEqual(vol['instance_id'], None) + self.assertEqual(vol['mountpoint'], None) + self.assertEqual(vol['status'], "available") + self.assertEqual(vol['attach_status'], "detached") + + def test_stop_start_with_volume(self): + """Make sure run instance with block device mapping works""" + + # enforce periodic tasks run in short time to avoid wait for 60s. + self._restart_compute_service(periodic_interval=0.3) + + vol1 = self._volume_create() + vol2 = self._volume_create() + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1, + 'block_device_mapping': [{'device_name': '/dev/vdb', + 'volume_id': vol1['id'], + 'delete_on_termination': False,}, + {'device_name': '/dev/vdc', + 'volume_id': vol2['id'], + 'delete_on_termination': True,}, + ]} + ec2_instance_id = self._run_instance_wait(**kwargs) + instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) + + vols = db.volume_get_all_by_instance(self.context, instance_id) + self.assertEqual(len(vols), 2) + for vol in vols: + self.assertTrue(vol['id'] == vol1['id'] or vol['id'] == vol2['id']) + + vol = db.volume_get(self.context, vol1['id']) + self._assert_volume_attached(vol, instance_id, '/dev/vdb') + + vol = db.volume_get(self.context, vol2['id']) + self._assert_volume_attached(vol, instance_id, '/dev/vdc') + + result = self.cloud.stop_instances(self.context, [ec2_instance_id]) + self.assertTrue(result) + self._wait_for_stopped(ec2_instance_id) + + vol = db.volume_get(self.context, vol1['id']) + self._assert_volume_detached(vol) + vol = db.volume_get(self.context, vol2['id']) + self._assert_volume_detached(vol) + + self.cloud.start_instances(self.context, [ec2_instance_id]) + self._wait_for_running(ec2_instance_id) + vols = db.volume_get_all_by_instance(self.context, instance_id) + self.assertEqual(len(vols), 2) + for vol in vols: + self.assertTrue(vol['id'] == vol1['id'] or vol['id'] == vol2['id']) + self.assertTrue(vol['mountpoint'] == '/dev/vdb' or + vol['mountpoint'] == '/dev/vdc') + self.assertEqual(vol['instance_id'], instance_id) + self.assertEqual(vol['status'], "in-use") + self.assertEqual(vol['attach_status'], "attached") + + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + greenthread.sleep(0.3) + + admin_ctxt = context.get_admin_context(read_deleted=False) + vol = db.volume_get(admin_ctxt, vol1['id']) + self.assertFalse(vol['deleted']) + db.volume_destroy(self.context, vol1['id']) + + greenthread.sleep(0.3) + admin_ctxt = context.get_admin_context(read_deleted=True) + vol = db.volume_get(admin_ctxt, vol2['id']) + self.assertTrue(vol['deleted']) + + self._restart_compute_service() + + def test_stop_with_attached_volume(self): + """Make sure attach info is reflected to block device mapping""" + # enforce periodic tasks run in short time to avoid wait for 60s. + self._restart_compute_service(periodic_interval=0.3) + + vol1 = self._volume_create() + vol2 = self._volume_create() + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1, + 'block_device_mapping': [{'device_name': '/dev/vdb', + 'volume_id': vol1['id'], + 'delete_on_termination': True,},]} + ec2_instance_id = self._run_instance_wait(**kwargs) + instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) + + vols = db.volume_get_all_by_instance(self.context, instance_id) + self.assertEqual(len(vols), 1) + for vol in vols: + self.assertEqual(vol['id'], vol1['id']) + self._assert_volume_attached(vol, instance_id, '/dev/vdb') + + vol = db.volume_get(self.context, vol2['id']) + self._assert_volume_detached(vol) + + self.cloud.compute_api.attach_volume(self.context, + instance_id=instance_id, + volume_id=vol2['id'], + device='/dev/vdc') + greenthread.sleep(0.3) + vol = db.volume_get(self.context, vol2['id']) + self._assert_volume_attached(vol, instance_id, '/dev/vdc') + + self.cloud.compute_api.detach_volume(self.context, + volume_id=vol1['id']) + greenthread.sleep(0.3) + vol = db.volume_get(self.context, vol1['id']) + self._assert_volume_detached(vol) + + result = self.cloud.stop_instances(self.context, [ec2_instance_id]) + self.assertTrue(result) + self._wait_for_stopped(ec2_instance_id) + + for vol_id in (vol1['id'], vol2['id']): + vol = db.volume_get(self.context, vol_id) + self._assert_volume_detached(vol) + + self.cloud.start_instances(self.context, [ec2_instance_id]) + self._wait_for_running(ec2_instance_id) + vols = db.volume_get_all_by_instance(self.context, instance_id) + self.assertEqual(len(vols), 1) + for vol in vols: + self.assertEqual(vol['id'], vol2['id']) + self._assert_volume_attached(vol, instance_id, '/dev/vdc') + + vol = db.volume_get(self.context, vol1['id']) + self._assert_volume_detached(vol) + + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + greenthread.sleep(0.3) + + for vol_id in (vol1['id'], vol2['id']): + vol = db.volume_get(self.context, vol_id) + self.assertEqual(vol['id'], vol_id) + self._assert_volume_detached(vol) + db.volume_destroy(self.context, vol_id) + + self._restart_compute_service() + + def _create_snapshot(self, ec2_volume_id): + result = self.cloud.create_snapshot(self.context, + volume_id=ec2_volume_id) + greenthread.sleep(0.3) + return result['snapshotId'] + + def test_run_with_snapshot(self): + """Makes sure run/stop/start instance with snapshot works.""" + vol = self._volume_create() + ec2_volume_id = ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x') + + ec2_snapshot1_id = self._create_snapshot(ec2_volume_id) + snapshot1_id = ec2utils.ec2_id_to_id(ec2_snapshot1_id) + ec2_snapshot2_id = self._create_snapshot(ec2_volume_id) + snapshot2_id = ec2utils.ec2_id_to_id(ec2_snapshot2_id) + + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1, + 'block_device_mapping': [{'device_name': '/dev/vdb', + 'snapshot_id': snapshot1_id, + 'delete_on_termination': False,}, + {'device_name': '/dev/vdc', + 'snapshot_id': snapshot2_id, + 'delete_on_termination': True,},],} + ec2_instance_id = self._run_instance_wait(**kwargs) + instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) + + vols = db.volume_get_all_by_instance(self.context, instance_id) + self.assertEqual(len(vols), 2) + vol1_id = None + vol2_id = None + for vol in vols: + snapshot_id = vol['snapshot_id'] + if snapshot_id == snapshot1_id: + vol1_id = vol['id'] + mountpoint = '/dev/vdb' + elif snapshot_id == snapshot2_id: + vol2_id = vol['id'] + mountpoint = '/dev/vdc' + else: + self.fail() + + self._assert_volume_attached(vol, instance_id, mountpoint) + + self.assertTrue(vol1_id) + self.assertTrue(vol2_id) + + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + greenthread.sleep(0.3) + self._wait_for_terminate(ec2_instance_id) + + greenthread.sleep(0.3) + admin_ctxt = context.get_admin_context(read_deleted=False) + vol = db.volume_get(admin_ctxt, vol1_id) + self._assert_volume_detached(vol) + self.assertFalse(vol['deleted']) + db.volume_destroy(self.context, vol1_id) + + greenthread.sleep(0.3) + admin_ctxt = context.get_admin_context(read_deleted=True) + vol = db.volume_get(admin_ctxt, vol2_id) + self.assertTrue(vol['deleted']) + + for snapshot_id in (ec2_snapshot1_id, ec2_snapshot2_id): + self.cloud.delete_snapshot(self.context, snapshot_id) + greenthread.sleep(0.3) + db.volume_destroy(self.context, vol['id']) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 9170837b..f35f9ce7 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -229,6 +229,21 @@ class ComputeTestCase(test.TestCase): self.assert_(instance_ref['launched_at'] < terminate) self.assert_(instance_ref['deleted_at'] > terminate) + def test_stop(self): + """Ensure instance can be stopped""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.compute.stop_instance(self.context, instance_id) + self.compute.terminate_instance(self.context, instance_id) + + def test_start(self): + """Ensure instance can be started""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.compute.stop_instance(self.context, instance_id) + self.compute.start_instance(self.context, instance_id) + self.compute.terminate_instance(self.context, instance_id) + def test_pause(self): """Ensure instance can be paused""" instance_id = self._create_instance() From 07e81bb615ef44b4373974ff867354013ac6564c Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 1 Jun 2011 12:39:31 -0700 Subject: [PATCH 03/47] reservation id's properly forwarded to child zones on create --- nova/scheduler/zone_aware_scheduler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index 35ffdbde..4b96f987 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -90,6 +90,7 @@ class ZoneAwareScheduler(driver.Scheduler): image_id = instance_properties['image_id'] meta = instance_properties['metadata'] flavor_id = instance_type['flavorid'] + reservation_id = instance_properties['reservation_id'] files = kwargs['injected_files'] ipgroup = None # Not supported in OS API ... yet @@ -98,7 +99,8 @@ class ZoneAwareScheduler(driver.Scheduler): child_blob = zone_info['child_blob'] zone = db.zone_get(context, child_zone) url = zone.api_url - LOG.debug(_("Forwarding instance create call to child zone %(url)s") + LOG.debug(_("Forwarding instance create call to child zone %(url)s" + ". ReservationID=%(reservation_id)s") % locals()) nova = None try: @@ -109,7 +111,7 @@ class ZoneAwareScheduler(driver.Scheduler): "to talk to zone at %(url)s.") % locals()) nova.servers.create(name, image_id, flavor_id, ipgroup, meta, files, - child_blob) + child_blob, reservation_id=reservation_id) def _provision_resource_from_blob(self, context, item, instance_id, request_spec, kwargs): From cae593e602ff4cee51805d13807e6d8a45c12461 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 1 Jun 2011 17:35:49 -0700 Subject: [PATCH 04/47] list --reservation now works across zones --- nova/scheduler/api.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index de066071..0f423655 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -118,7 +118,8 @@ def _process(func, zone): return func(nova, zone) -def call_zone_method(context, method, errors_to_ignore=None, *args, **kwargs): +def call_zone_method(context, method_name, errors_to_ignore=None, + novaclient_collection_name='zones', *args, **kwargs): """Returns a list of (zone, call_result) objects.""" if not isinstance(errors_to_ignore, (list, tuple)): # This will also handle the default None @@ -138,11 +139,12 @@ def call_zone_method(context, method, errors_to_ignore=None, *args, **kwargs): #TODO (dabo) - add logic for failure counts per zone, # with escalation after a given number of failures. continue - zone_method = getattr(nova.zones, method) + novaclient_collection = getattr(nova, novaclient_collection_name) + collection_method = getattr(novaclient_collection, method_name) def _error_trap(*args, **kwargs): try: - return zone_method(*args, **kwargs) + return collection_method(*args, **kwargs) except Exception as e: if type(e) in errors_to_ignore: return None From a81c3078bed719adcca9a1a9ed8bbdba003af057 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 1 Jun 2011 17:49:49 -0700 Subject: [PATCH 05/47] tests all passing again --- nova/scheduler/api.py | 5 +---- nova/tests/test_scheduler.py | 8 +------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 0f423655..432f22b9 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -148,10 +148,7 @@ def call_zone_method(context, method_name, errors_to_ignore=None, except Exception as e: if type(e) in errors_to_ignore: return None - # TODO (dabo) - want to be able to re-raise here. - # Returning a string now; raising was causing issues. - # raise e - return "ERROR", "%s" % e + raise e res = pool.spawn(_error_trap, *args, **kwargs) results.append((zone, res)) diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 54b3f80f..5d8f34ef 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -1109,10 +1109,4 @@ class CallZoneMethodTest(test.TestCase): def test_call_zone_method_generates_exception(self): context = {} method = 'raises_exception' - results = api.call_zone_method(context, method) - - # FIXME(sirp): for now the _error_trap code is catching errors and - # converting them to a ("ERROR", "string") tuples. The code (and this - # test) should eventually handle real exceptions. - expected = [(1, ('ERROR', 'testing'))] - self.assertEqual(expected, results) + self.assertRaises(Exception, api.call_zone_method, context, method) From 574e707992c76c391e665af62e8860de461d0d80 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 2 Jun 2011 07:57:35 -0700 Subject: [PATCH 06/47] get_all with reservation id across zone tests --- nova/scheduler/api.py | 7 ------- nova/scheduler/zone_aware_scheduler.py | 6 ------ 2 files changed, 13 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 432f22b9..78999389 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -87,13 +87,6 @@ def select(context, specs=None): params={"request_spec": specs}) -def should_create_all_at_once(context): - """Returns a tuple of rules for how instances should - be created given the current Scheduler driver being used.""" - return _call_scheduler('should_create_all_at_once', context=context, - params={}) - - def update_service_capabilities(context, service_name, host, capabilities): """Send an update to all the scheduler services informing them of the capabilities of this service.""" diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index 4b96f987..5e8d6303 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -159,12 +159,6 @@ class ZoneAwareScheduler(driver.Scheduler): self._provision_resource_from_blob(context, item, instance_id, request_spec, kwargs) - def should_create_all_at_once(self, context=None, *args, **kwargs): - """ - This driver prefers all-at-once requests. - """ - return True - def schedule_run_instance(self, context, instance_id, request_spec, *args, **kwargs): """This method is called from nova.compute.api to provision From 0fb759e39ed1d630c45f1aa83dd9e0764ba6a7a4 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 7 Jun 2011 13:32:06 -0400 Subject: [PATCH 07/47] removing local image service --- nova/tests/fake_flags.py | 2 +- nova/tests/test_cloud.py | 30 +++++++++++++++--------------- nova/tests/test_compute.py | 14 +++++++------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index ecefc464..2297d2f0 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -32,7 +32,7 @@ flags.DECLARE('fake_network', 'nova.network.manager') FLAGS['network_size'].SetDefault(8) FLAGS['num_networks'].SetDefault(2) FLAGS['fake_network'].SetDefault(True) -FLAGS['image_service'].SetDefault('nova.image.local.LocalImageService') +FLAGS['image_service'].SetDefault('nova.image.fake.FakeImageService') flags.DECLARE('num_shelves', 'nova.volume.driver') flags.DECLARE('blades_per_shelf', 'nova.volume.driver') flags.DECLARE('iscsi_num_targets', 'nova.volume.driver') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index a58e8bc3..d1f02d69 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -35,7 +35,7 @@ from nova import utils from nova.auth import manager from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils -from nova.image import local +from nova.image import fake FLAGS = flags.FLAGS @@ -69,8 +69,8 @@ class CloudTestCase(test.TestCase): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, 'type': 'machine', 'image_state': 'available'}} - self.stubs.Set(local.LocalImageService, 'show', fake_show) - self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show) # NOTE(vish): set up a manual wait so rpc.cast has a chance to finish rpc_cast = rpc.cast @@ -291,7 +291,7 @@ class CloudTestCase(test.TestCase): def fake_show_none(meh, context, id): raise exception.ImageNotFound(image_id='bad_image_id') - self.stubs.Set(local.LocalImageService, 'detail', fake_detail) + self.stubs.Set(fake._FakeImageService, 'detail', fake_detail) # list all result1 = describe_images(self.context) result1 = result1['imagesSet'][0] @@ -305,8 +305,8 @@ class CloudTestCase(test.TestCase): self.assertEqual(2, len(result3['imagesSet'])) # provide an non-existing image_id self.stubs.UnsetAll() - self.stubs.Set(local.LocalImageService, 'show', fake_show_none) - self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show_none) + self.stubs.Set(fake._FakeImageService, 'show', fake_show_none) + self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show_none) self.assertRaises(exception.ImageNotFound, describe_images, self.context, ['ami-fake']) @@ -317,8 +317,8 @@ class CloudTestCase(test.TestCase): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, 'type': 'machine'}, 'is_public': True} - self.stubs.Set(local.LocalImageService, 'show', fake_show) - self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show) result = describe_image_attribute(self.context, 'ami-00000001', 'launchPermission') self.assertEqual([{'group': 'all'}], result['launchPermission']) @@ -333,9 +333,9 @@ class CloudTestCase(test.TestCase): def fake_update(meh, context, image_id, metadata, data=None): return metadata - self.stubs.Set(local.LocalImageService, 'show', fake_show) - self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) - self.stubs.Set(local.LocalImageService, 'update', fake_update) + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show) + self.stubs.Set(fake._FakeImageService, 'update', fake_update) result = modify_image_attribute(self.context, 'ami-00000001', 'launchPermission', 'add', user_group=['all']) @@ -347,7 +347,7 @@ class CloudTestCase(test.TestCase): def fake_delete(self, context, id): return None - self.stubs.Set(local.LocalImageService, 'delete', fake_delete) + self.stubs.Set(fake._FakeImageService, 'delete', fake_delete) # valid image result = deregister_image(self.context, 'ami-00000001') self.assertEqual(result['imageId'], 'ami-00000001') @@ -357,7 +357,7 @@ class CloudTestCase(test.TestCase): def fake_detail_empty(self, context): return [] - self.stubs.Set(local.LocalImageService, 'detail', fake_detail_empty) + self.stubs.Set(fake._FakeImageService, 'detail', fake_detail_empty) self.assertRaises(exception.ImageNotFound, deregister_image, self.context, 'ami-bad001') @@ -468,7 +468,7 @@ class CloudTestCase(test.TestCase): 'type': 'machine'}} self.stubs.UnsetAll() - self.stubs.Set(local.LocalImageService, 'show', fake_show_no_state) + self.stubs.Set(fake._FakeImageService, 'show', fake_show_no_state) self.assertRaises(exception.ApiError, run_instances, self.context, **kwargs) @@ -483,7 +483,7 @@ class CloudTestCase(test.TestCase): 'type': 'machine', 'image_state': 'decrypting'}} self.stubs.UnsetAll() - self.stubs.Set(local.LocalImageService, 'show', fake_show_decrypt) + self.stubs.Set(fake._FakeImageService, 'show', fake_show_decrypt) self.assertRaises(exception.ApiError, run_instances, self.context, **kwargs) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index b4ac2dbc..2a68df2f 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -22,21 +22,21 @@ Tests For Compute import mox import stubout +from nova.auth import manager from nova import compute +from nova.compute import instance_types +from nova.compute import manager as compute_manager +from nova.compute import power_state from nova import context from nova import db +from nova.db.sqlalchemy import models from nova import exception from nova import flags +import nova.image.fake from nova import log as logging from nova import rpc from nova import test from nova import utils -from nova.auth import manager -from nova.compute import instance_types -from nova.compute import manager as compute_manager -from nova.compute import power_state -from nova.db.sqlalchemy import models -from nova.image import local LOG = logging.getLogger('nova.tests.compute') FLAGS = flags.FLAGS @@ -73,7 +73,7 @@ class ComputeTestCase(test.TestCase): def fake_show(meh, context, id): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} - self.stubs.Set(local.LocalImageService, 'show', fake_show) + self.stubs.Set(nova.image.fake._FakeImageService, 'show', fake_show) def tearDown(self): self.manager.delete_user(self.user) From 411f8ae6b1ea2449b375c8e264e04c65e60e25b2 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 9 Jun 2011 15:31:10 -0400 Subject: [PATCH 08/47] further changes --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index d5090edb..ebdfeb7a 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -362,7 +362,7 @@ DEFINE_string('scheduler_manager', 'nova.scheduler.manager.SchedulerManager', 'Manager for scheduler') # The service to use for image search and retrieval -DEFINE_string('image_service', 'nova.image.local.LocalImageService', +DEFINE_string('image_service', 'nova.image.glance.GlanceImageService', 'The service to use for retrieving and searching for images.') DEFINE_string('host', socket.gethostname(), From 7612141d8fd7509d2db95d3918b1b4705bd94793 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 9 Jun 2011 20:11:55 +0000 Subject: [PATCH 09/47] Record architecture of image for matching to agent build later. Add code to automatically update agent running on instance on instance creation. --- bin/nova-manage | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 0147ae21..c004a36c 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1082,6 +1082,50 @@ class ImageCommands(object): self._convert_images(machine_images) +class AgentBuildCommands(object): + """Class for managing agent builds.""" + + def create(self, os, architecture, version, url, md5hash, hypervisor='xen'): + """creates a new agent build + arguments: os architecture version url md5hash [hypervisor='xen']""" + ctxt = context.get_admin_context() + agent_build = db.agent_build_create(ctxt, + {'hypervisor': hypervisor, + 'os': os, + 'architecture': architecture, + 'version': version, + 'url': url, + 'md5hash': md5hash}) + + def delete(self, os, architecture, hypervisor='xen'): + """deletes an existing agent build + arguments: os architecture [hypervisor='xen']""" + ctxt = context.get_admin_context() + agent_build_ref = db.agent_build_get_by_triple(ctxt, + hypervisor, os, architecture) + db.agent_build_destroy(ctxt, agent_build_ref['id']) + + def list(self): + """lists all agent builds + arguments: """ + # TODO(johannes.erdfelt): Make the output easier to read + ctxt = context.get_admin_context() + for agent_build in db.agent_build_get_all(ctxt): + print agent_build.hypervisor, agent_build.os, agent_build.architecture, agent_build.version, agent_build.url, agent_build.md5hash + + def modify(self, os, architecture, version, url, md5hash, hypervisor='xen'): + """update an existing agent build + arguments: os architecture version url md5hash [hypervisor='xen'] + """ + ctxt = context.get_admin_context() + agent_build_ref = db.agent_build_get_by_triple(ctxt, + hypervisor, os, architecture) + db.agent_build_update(ctxt, agent_build_ref['id'], + {'version': version, + 'url': url, + 'md5hash': md5hash}) + + class ConfigCommands(object): """Class for exposing the flags defined by flag_file(s).""" @@ -1094,6 +1138,7 @@ class ConfigCommands(object): CATEGORIES = [ ('account', AccountCommands), + ('agent', AgentBuildCommands), ('config', ConfigCommands), ('db', DbCommands), ('fixed', FixedIpCommands), From 11e295b8cb93a1eb1c7151e11518ba6c95de7a14 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 9 Jun 2011 22:04:32 +0000 Subject: [PATCH 10/47] Add test for agent update --- nova/tests/test_compute.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index b4ac2dbc..dd06e571 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -266,6 +266,14 @@ class ComputeTestCase(test.TestCase): "File Contents") self.compute.terminate_instance(self.context, instance_id) + def test_agent_update(self): + """Ensure instance can have its agent updated""" + instance_id = self._create_instance() + self.compute.run_instance(self.context, instance_id) + self.compute.agent_update(self.context, instance_id, + 'http://127.0.0.1/agent', '00112233445566778899aabbccddeeff') + self.compute.terminate_instance(self.context, instance_id) + def test_snapshot(self): """Ensure instance can be snapshotted""" instance_id = self._create_instance() From 3f8e0184ca45626c26892e6aa68b46adb043c57f Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Fri, 10 Jun 2011 05:27:05 -0700 Subject: [PATCH 11/47] fixed reraise in trap_error --- nova/scheduler/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 78999389..ffe59d2c 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -141,7 +141,7 @@ def call_zone_method(context, method_name, errors_to_ignore=None, except Exception as e: if type(e) in errors_to_ignore: return None - raise e + raise res = pool.spawn(_error_trap, *args, **kwargs) results.append((zone, res)) From c18e2619dd7136c982178a231d27d7dcc38b4f6d Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 10 Jun 2011 10:12:57 -0400 Subject: [PATCH 12/47] removing LocalImageService from nova-manage --- bin/nova-manage | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b0cd343f..0bfc63bd 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -96,7 +96,6 @@ flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('vlan_start', 'nova.network.manager') flags.DECLARE('vpn_start', 'nova.network.manager') flags.DECLARE('fixed_range_v6', 'nova.network.manager') -flags.DECLARE('images_path', 'nova.image.local') flags.DECLARE('libvirt_type', 'nova.virt.libvirt.connection') flags.DEFINE_flag(flags.HelpFlag()) flags.DEFINE_flag(flags.HelpshortFlag()) @@ -1055,16 +1054,6 @@ class ImageCommands(object): machine_images = {} other_images = {} directory = os.path.abspath(directory) - # NOTE(vish): If we're importing from the images path dir, attempt - # to move the files out of the way before importing - # so we aren't writing to the same directory. This - # may fail if the dir was a mointpoint. - if (FLAGS.image_service == 'nova.image.local.LocalImageService' - and directory == os.path.abspath(FLAGS.images_path)): - new_dir = "%s_bak" % directory - os.rename(directory, new_dir) - os.mkdir(directory) - directory = new_dir for fn in glob.glob("%s/*/info.json" % directory): try: image_path = os.path.join(fn.rpartition('/')[0], 'image') From ea04aefe3971ad7fe8d8c694ae46e2dad6f366e7 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 13 Jun 2011 15:20:43 -0500 Subject: [PATCH 13/47] Removed clocksource=jiffies from PV_args. --- nova/tests/test_xenapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 3a175b10..d1c88287 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -331,7 +331,7 @@ class XenAPIVMTestCase(test.TestCase): def check_vm_params_for_linux(self): self.assertEquals(self.vm['platform']['nx'], 'false') - self.assertEquals(self.vm['PV_args'], 'clocksource=jiffies') + self.assertEquals(self.vm['PV_args'], '') self.assertEquals(self.vm['PV_bootloader'], 'pygrub') # check that these are not set From 7153b380fc33595545d9c33a5c43146c72958f9b Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Mon, 13 Jun 2011 16:41:31 -0400 Subject: [PATCH 14/47] Test now passes even if the rpc call does not complete on time --- nova/tests/test_cloud.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 13046f86..b491448e 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -466,7 +466,8 @@ class CloudTestCase(test.TestCase): self.assertEqual(instance['imageId'], 'ami-00000001') self.assertEqual(instance['displayName'], 'Server 1') self.assertEqual(instance['instanceId'], 'i-00000001') - self.assertEqual(instance['instanceState']['name'], 'networking') + self.assertTrue(instance['instanceState']['name'] in + ['networking', 'scheduling']) self.assertEqual(instance['instanceType'], 'm1.small') def test_run_instances_image_state_none(self): From 20ce24c63e839f101f0a45a235102a4c8a5a0ba3 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Mon, 13 Jun 2011 21:14:26 -0400 Subject: [PATCH 15/47] pep8 --- nova/tests/scheduler/test_host_filter.py | 8 ++++---- nova/tests/scheduler/test_least_cost_scheduler.py | 8 ++++---- nova/tests/scheduler/test_zone_aware_scheduler.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/nova/tests/scheduler/test_host_filter.py b/nova/tests/scheduler/test_host_filter.py index 07817cc5..10eafde0 100644 --- a/nova/tests/scheduler/test_host_filter.py +++ b/nova/tests/scheduler/test_host_filter.py @@ -133,11 +133,11 @@ class HostFilterTestCase(test.TestCase): raw = ['or', ['and', ['<', '$compute.host_memory_free', 30], - ['<', '$compute.disk_available', 300] + ['<', '$compute.disk_available', 300], ], ['and', ['>', '$compute.host_memory_free', 70], - ['>', '$compute.disk_available', 700] + ['>', '$compute.disk_available', 700], ] ] cooked = json.dumps(raw) @@ -183,12 +183,12 @@ class HostFilterTestCase(test.TestCase): self.assertTrue(hf.filter_hosts(self.zone_manager, json.dumps([]))) self.assertTrue(hf.filter_hosts(self.zone_manager, json.dumps({}))) self.assertTrue(hf.filter_hosts(self.zone_manager, json.dumps( - ['not', True, False, True, False] + ['not', True, False, True, False], ))) try: hf.filter_hosts(self.zone_manager, json.dumps( - 'not', True, False, True, False + 'not', True, False, True, False, )) self.fail("Should give KeyError") except KeyError, e: diff --git a/nova/tests/scheduler/test_least_cost_scheduler.py b/nova/tests/scheduler/test_least_cost_scheduler.py index 506fa62f..9a5318ae 100644 --- a/nova/tests/scheduler/test_least_cost_scheduler.py +++ b/nova/tests/scheduler/test_least_cost_scheduler.py @@ -44,7 +44,7 @@ class WeightedSumTestCase(test.TestCase): hosts = [ FakeHost(1, 512 * MB, 100), FakeHost(2, 256 * MB, 400), - FakeHost(3, 512 * MB, 100) + FakeHost(3, 512 * MB, 100), ] weighted_fns = [ @@ -96,7 +96,7 @@ class LeastCostSchedulerTestCase(test.TestCase): def test_noop_cost_fn(self): FLAGS.least_cost_scheduler_cost_functions = [ - 'nova.scheduler.least_cost.noop_cost_fn' + 'nova.scheduler.least_cost.noop_cost_fn', ] FLAGS.noop_cost_fn_weight = 1 @@ -110,7 +110,7 @@ class LeastCostSchedulerTestCase(test.TestCase): def test_cost_fn_weights(self): FLAGS.least_cost_scheduler_cost_functions = [ - 'nova.scheduler.least_cost.noop_cost_fn' + 'nova.scheduler.least_cost.noop_cost_fn', ] FLAGS.noop_cost_fn_weight = 2 @@ -124,7 +124,7 @@ class LeastCostSchedulerTestCase(test.TestCase): def test_fill_first_cost_fn(self): FLAGS.least_cost_scheduler_cost_functions = [ - 'nova.scheduler.least_cost.fill_first_cost_fn' + 'nova.scheduler.least_cost.fill_first_cost_fn', ] FLAGS.fill_first_cost_fn_weight = 1 diff --git a/nova/tests/scheduler/test_zone_aware_scheduler.py b/nova/tests/scheduler/test_zone_aware_scheduler.py index 9f70b9db..37c6488c 100644 --- a/nova/tests/scheduler/test_zone_aware_scheduler.py +++ b/nova/tests/scheduler/test_zone_aware_scheduler.py @@ -201,7 +201,7 @@ class ZoneAwareSchedulerTestCase(test.TestCase): 'instance_properties': {}, 'instance_type': {}, 'filter_driver': 'nova.scheduler.host_filter.AllHostsFilter', - 'blob': "Non-None blob data" + 'blob': "Non-None blob data", } result = sched.schedule_run_instance(None, 1, request_spec) From 6fe7cfa50f5f8edb03a87b79d641fc4be8b8df1a Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 14 Jun 2011 01:21:08 +0000 Subject: [PATCH 16/47] Adds --show-elapsed option for run_tests --- run_tests.py | 82 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 19 deletions(-) diff --git a/run_tests.py b/run_tests.py index d5d8acd1..ace91653 100644 --- a/run_tests.py +++ b/run_tests.py @@ -56,9 +56,11 @@ To run a single test module: """ import gettext +import heapq import os import unittest import sys +import time gettext.install('nova', unicode=1) @@ -185,7 +187,10 @@ class _NullColorizer(object): class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): + self.show_elapsed = kw.pop('show_elapsed') result.TextTestResult.__init__(self, *args, **kw) + self.num_slow_tests = 5 + self.slow_tests = [] # this is a fixed-sized heap self._last_case = None self.colorizer = None # NOTE(vish): reset stdout for the terminal check @@ -200,25 +205,48 @@ class NovaTestResult(result.TextTestResult): def getDescription(self, test): return str(test) + def _handleElapsedTime(self, test): + self.elapsed_time = time.time() - self.start_time + item = (self.elapsed_time, test) + # Record only the n-slowest tests using heap + if len(self.slow_tests) >= self.num_slow_tests: + heapq.heappushpop(self.slow_tests, item) + else: + heapq.heappush(self.slow_tests, item) + + def _writeElapsedTime(self, test): + if self.elapsed_time >= 3.0: + color = 'red' + elif self.elapsed_time >= 1.0: + color = 'yellow' + else: + color = 'green' + + self.stream.write(' ' * 10) + self.colorizer.write("%.2f" % self.elapsed_time, color) + self.stream.write(' secs') + + def _writeResult(self, test, long_result, color, short_result): + if self.showAll: + self.colorizer.write(long_result, color) + if self.show_elapsed: + self._writeElapsedTime(test) + self.stream.writeln() + elif self.dots: + self.stream.write(short_result) + self.stream.flush() + # NOTE(vish): copied from unittest with edit to add color def addSuccess(self, test): unittest.TestResult.addSuccess(self, test) - if self.showAll: - self.colorizer.write("OK", 'green') - self.stream.writeln() - elif self.dots: - self.stream.write('.') - self.stream.flush() + self._handleElapsedTime(test) + self._writeResult(test, 'OK', 'green', '.') # NOTE(vish): copied from unittest with edit to add color def addFailure(self, test, err): unittest.TestResult.addFailure(self, test, err) - if self.showAll: - self.colorizer.write("FAIL", 'red') - self.stream.writeln() - elif self.dots: - self.stream.write('F') - self.stream.flush() + self._handleElapsedTime(test) + self._writeResult(test, 'FAIL', 'red', 'F') # NOTE(vish): copied from nose with edit to add color def addError(self, test, err): @@ -226,6 +254,7 @@ class NovaTestResult(result.TextTestResult): errorClasses. If the exception is a registered class, the error will be added to the list for that class, not errors. """ + self._handleElapsedTime(test) stream = getattr(self, 'stream', None) ec, ev, tb = err try: @@ -252,14 +281,11 @@ class NovaTestResult(result.TextTestResult): self.errors.append((test, exc_info)) test.passed = False if stream is not None: - if self.showAll: - self.colorizer.write("ERROR", 'red') - self.stream.writeln() - elif self.dots: - stream.write('E') + self._writeResult(test, 'ERROR', 'red', 'E') def startTest(self, test): unittest.TestResult.startTest(self, test) + self.start_time = time.time() current_case = test.test.__class__.__name__ if self.showAll: @@ -273,21 +299,38 @@ class NovaTestResult(result.TextTestResult): class NovaTestRunner(core.TextTestRunner): + def __init__(self, *args, **kwargs): + self.show_elapsed = kwargs.pop('show_elapsed') + core.TextTestRunner.__init__(self, *args, **kwargs) + def _makeResult(self): return NovaTestResult(self.stream, self.descriptions, self.verbosity, - self.config) + self.config, + show_elapsed=self.show_elapsed) + + def run(self, test): + result_ = core.TextTestRunner.run(self, test) + if self.show_elapsed: + self.stream.writeln("Slowest %i tests:" % result_.num_slow_tests) + for elapsed_time, test in reversed(sorted(result_.slow_tests)): + time_str = "%.2f secs" % elapsed_time + self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) + return result_ if __name__ == '__main__': logging.setup() # If any argument looks like a test name but doesn't have "nova.tests" in # front of it, automatically add that so we don't have to type as much + show_elapsed = False argv = [] for x in sys.argv: if x.startswith('test_'): argv.append('nova.tests.%s' % x) + elif x.startswith('--show-elapsed'): + show_elapsed = True else: argv.append(x) @@ -300,5 +343,6 @@ if __name__ == '__main__': runner = NovaTestRunner(stream=c.stream, verbosity=c.verbosity, - config=c) + config=c, + show_elapsed=show_elapsed) sys.exit(not core.run(config=c, testRunner=runner, argv=argv)) From 969db3d6440e7ecffa55137d765deb6249708df5 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 14 Jun 2011 16:39:37 +0000 Subject: [PATCH 17/47] Making timing points stricter, only show slow/sluggish tests in summary --- run_tests.py | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/run_tests.py b/run_tests.py index ace91653..3ecc146f 100644 --- a/run_tests.py +++ b/run_tests.py @@ -185,6 +185,22 @@ class _NullColorizer(object): self.stream.write(text) +def classify_test_speed(elapsed_time): + if elapsed_time > 1.0: + return 'slow' + elif elapsed_time > 0.25: + return 'sluggish' + else: + return 'fast' + + +def get_elapsed_time_color(elapsed_time): + color_map = {'slow': 'red', 'sluggish': 'yellow', 'fast': 'green'} + slowness = classify_test_speed(elapsed_time) + color = color_map[slowness] + return color + + class NovaTestResult(result.TextTestResult): def __init__(self, *args, **kw): self.show_elapsed = kw.pop('show_elapsed') @@ -215,15 +231,8 @@ class NovaTestResult(result.TextTestResult): heapq.heappush(self.slow_tests, item) def _writeElapsedTime(self, test): - if self.elapsed_time >= 3.0: - color = 'red' - elif self.elapsed_time >= 1.0: - color = 'yellow' - else: - color = 'green' - - self.stream.write(' ' * 10) - self.colorizer.write("%.2f" % self.elapsed_time, color) + color = get_elapsed_time_color(self.elapsed_time) + self.colorizer.write(" %.2f" % self.elapsed_time, color) self.stream.write(' secs') def _writeResult(self, test, long_result, color, short_result): @@ -310,13 +319,22 @@ class NovaTestRunner(core.TextTestRunner): self.config, show_elapsed=self.show_elapsed) + def _writeSlowTests(self, result_): + # Pare out 'fast' tests + slow_tests = [item for item in result_.slow_tests + if classify_test_speed(item[0]) != 'fast'] + + slow_total_time = sum(item[0] for item in slow_tests) + self.stream.writeln("Slowest %i tests took %.2f secs:" + % (len(slow_tests), slow_total_time)) + for elapsed_time, test in sorted(slow_tests, reverse=True): + time_str = "%.2f secs" % elapsed_time + self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) + def run(self, test): result_ = core.TextTestRunner.run(self, test) if self.show_elapsed: - self.stream.writeln("Slowest %i tests:" % result_.num_slow_tests) - for elapsed_time, test in reversed(sorted(result_.slow_tests)): - time_str = "%.2f secs" % elapsed_time - self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) + self._writeSlowTests(result_) return result_ From 006630fe3b237b058532d6bb676f89021b8a8e88 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Tue, 14 Jun 2011 13:14:00 -0400 Subject: [PATCH 18/47] Stub out the rpc call in a unit test to avoid a race condition --- nova/tests/test_cloud.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index b491448e..afc66163 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -457,6 +457,12 @@ class CloudTestCase(test.TestCase): self.cloud.delete_key_pair(self.context, 'test') def test_run_instances(self): + # stub out the rpc call + def stub_cast(*args, **kwargs): + pass + + self.stubs.Set(rpc, 'cast', stub_cast) + kwargs = {'image_id': FLAGS.default_image, 'instance_type': FLAGS.default_instance_type, 'max_count': 1} @@ -466,8 +472,7 @@ class CloudTestCase(test.TestCase): self.assertEqual(instance['imageId'], 'ami-00000001') self.assertEqual(instance['displayName'], 'Server 1') self.assertEqual(instance['instanceId'], 'i-00000001') - self.assertTrue(instance['instanceState']['name'] in - ['networking', 'scheduling']) + self.assertEqual(instance['instanceState']['name'], 'scheduling') self.assertEqual(instance['instanceType'], 'm1.small') def test_run_instances_image_state_none(self): From f1d5cac6b22e95d947a3527f26d31a83637aed70 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Tue, 14 Jun 2011 13:17:13 -0400 Subject: [PATCH 19/47] pep8 fix --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index afc66163..d2ff14f2 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -462,7 +462,7 @@ class CloudTestCase(test.TestCase): pass self.stubs.Set(rpc, 'cast', stub_cast) - + kwargs = {'image_id': FLAGS.default_image, 'instance_type': FLAGS.default_instance_type, 'max_count': 1} From 483d9bdaaadf2724368e2703e6b462ac189a1102 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 15 Jun 2011 00:08:19 +0000 Subject: [PATCH 20/47] Removing seconds unit --- run_tests.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/run_tests.py b/run_tests.py index 3ecc146f..c29a3da4 100644 --- a/run_tests.py +++ b/run_tests.py @@ -185,20 +185,13 @@ class _NullColorizer(object): self.stream.write(text) -def classify_test_speed(elapsed_time): - if elapsed_time > 1.0: - return 'slow' - elif elapsed_time > 0.25: - return 'sluggish' - else: - return 'fast' - - def get_elapsed_time_color(elapsed_time): - color_map = {'slow': 'red', 'sluggish': 'yellow', 'fast': 'green'} - slowness = classify_test_speed(elapsed_time) - color = color_map[slowness] - return color + if elapsed_time > 1.0: + return 'red' + elif elapsed_time > 0.25: + return 'yellow' + else: + return 'green' class NovaTestResult(result.TextTestResult): @@ -233,7 +226,6 @@ class NovaTestResult(result.TextTestResult): def _writeElapsedTime(self, test): color = get_elapsed_time_color(self.elapsed_time) self.colorizer.write(" %.2f" % self.elapsed_time, color) - self.stream.write(' secs') def _writeResult(self, test, long_result, color, short_result): if self.showAll: @@ -322,13 +314,13 @@ class NovaTestRunner(core.TextTestRunner): def _writeSlowTests(self, result_): # Pare out 'fast' tests slow_tests = [item for item in result_.slow_tests - if classify_test_speed(item[0]) != 'fast'] + if get_elapsed_time_color(item[0]) != 'green'] slow_total_time = sum(item[0] for item in slow_tests) self.stream.writeln("Slowest %i tests took %.2f secs:" % (len(slow_tests), slow_total_time)) for elapsed_time, test in sorted(slow_tests, reverse=True): - time_str = "%.2f secs" % elapsed_time + time_str = "%.2f" % elapsed_time self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) def run(self, test): From f931a0e31d26b13ca9e9f7c1f218f4b0ba34ce92 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 15 Jun 2011 01:21:11 +0000 Subject: [PATCH 21/47] Showing elapsed time is now default --- run_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/run_tests.py b/run_tests.py index c29a3da4..601c41e4 100644 --- a/run_tests.py +++ b/run_tests.py @@ -334,13 +334,13 @@ if __name__ == '__main__': logging.setup() # If any argument looks like a test name but doesn't have "nova.tests" in # front of it, automatically add that so we don't have to type as much - show_elapsed = False + show_elapsed = True argv = [] for x in sys.argv: if x.startswith('test_'): argv.append('nova.tests.%s' % x) - elif x.startswith('--show-elapsed'): - show_elapsed = True + elif x.startswith('--hide-elapsed'): + show_elapsed = False else: argv.append(x) From 1054258981da6982811b1b848c2fa16495e5a62c Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 15 Jun 2011 04:05:37 +0000 Subject: [PATCH 22/47] Show only if we have slow tests, elapsed only if test success --- run_tests.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/run_tests.py b/run_tests.py index 601c41e4..0944bb58 100644 --- a/run_tests.py +++ b/run_tests.py @@ -227,10 +227,10 @@ class NovaTestResult(result.TextTestResult): color = get_elapsed_time_color(self.elapsed_time) self.colorizer.write(" %.2f" % self.elapsed_time, color) - def _writeResult(self, test, long_result, color, short_result): + def _writeResult(self, test, long_result, color, short_result, success): if self.showAll: self.colorizer.write(long_result, color) - if self.show_elapsed: + if self.show_elapsed and success: self._writeElapsedTime(test) self.stream.writeln() elif self.dots: @@ -241,13 +241,13 @@ class NovaTestResult(result.TextTestResult): def addSuccess(self, test): unittest.TestResult.addSuccess(self, test) self._handleElapsedTime(test) - self._writeResult(test, 'OK', 'green', '.') + self._writeResult(test, 'OK', 'green', '.', True) # NOTE(vish): copied from unittest with edit to add color def addFailure(self, test, err): unittest.TestResult.addFailure(self, test, err) self._handleElapsedTime(test) - self._writeResult(test, 'FAIL', 'red', 'F') + self._writeResult(test, 'FAIL', 'red', 'F', False) # NOTE(vish): copied from nose with edit to add color def addError(self, test, err): @@ -282,7 +282,7 @@ class NovaTestResult(result.TextTestResult): self.errors.append((test, exc_info)) test.passed = False if stream is not None: - self._writeResult(test, 'ERROR', 'red', 'E') + self._writeResult(test, 'ERROR', 'red', 'E', False) def startTest(self, test): unittest.TestResult.startTest(self, test) @@ -315,13 +315,13 @@ class NovaTestRunner(core.TextTestRunner): # Pare out 'fast' tests slow_tests = [item for item in result_.slow_tests if get_elapsed_time_color(item[0]) != 'green'] - - slow_total_time = sum(item[0] for item in slow_tests) - self.stream.writeln("Slowest %i tests took %.2f secs:" - % (len(slow_tests), slow_total_time)) - for elapsed_time, test in sorted(slow_tests, reverse=True): - time_str = "%.2f" % elapsed_time - self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) + if slow_tests: + slow_total_time = sum(item[0] for item in slow_tests) + self.stream.writeln("Slowest %i tests took %.2f secs:" + % (len(slow_tests), slow_total_time)) + for elapsed_time, test in sorted(slow_tests, reverse=True): + time_str = "%.2f" % elapsed_time + self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) def run(self, test): result_ = core.TextTestRunner.run(self, test) From 09da35f84d78b7cb4cc85977ff5bca7e177b74e7 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 15 Jun 2011 14:41:29 +0900 Subject: [PATCH 23/47] api/ec2: make the parameter parser an independent method Following the review, make the parser of argument items an independent method for readability. --- nova/tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 7c0331ef..20b20fcb 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -89,7 +89,7 @@ class FakeHttplibConnection(object): class XmlConversionTestCase(test.TestCase): """Unit test api xml conversion""" def test_number_conversion(self): - conv = apirequest._try_convert + conv = ec2utils._try_convert self.assertEqual(conv('None'), None) self.assertEqual(conv('True'), True) self.assertEqual(conv('False'), False) From dbe3729971977391e7476b07ed5e6aa6fb1555fd Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 15 Jun 2011 06:40:42 -0700 Subject: [PATCH 24/47] None project_id now default --- nova/scheduler/api.py | 5 +++-- nova/scheduler/zone_aware_scheduler.py | 3 ++- nova/scheduler/zone_manager.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index ffe59d2c..3b3195c2 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -106,7 +106,8 @@ def _wrap_method(function, self): def _process(func, zone): """Worker stub for green thread pool. Give the worker an authenticated nova client and zone info.""" - nova = novaclient.OpenStack(zone.username, zone.password, zone.api_url) + nova = novaclient.OpenStack(zone.username, zone.password, None, + zone.api_url) nova.authenticate() return func(nova, zone) @@ -122,7 +123,7 @@ def call_zone_method(context, method_name, errors_to_ignore=None, results = [] for zone in db.zone_get_all(context): try: - nova = novaclient.OpenStack(zone.username, zone.password, + nova = novaclient.OpenStack(zone.username, zone.password, None, zone.api_url) nova.authenticate() except novaclient.exceptions.BadRequest, e: diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index f04defa6..69d4c603 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -105,7 +105,8 @@ class ZoneAwareScheduler(driver.Scheduler): % locals()) nova = None try: - nova = novaclient.OpenStack(zone.username, zone.password, url) + nova = novaclient.OpenStack(zone.username, zone.password, None, + url) nova.authenticate() except novaclient.exceptions.BadRequest, e: raise exception.NotAuthorized(_("Bad credentials attempting " diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 3f483adf..ba7403c1 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -89,7 +89,8 @@ class ZoneState(object): def _call_novaclient(zone): """Call novaclient. Broken out for testing purposes.""" - client = novaclient.OpenStack(zone.username, zone.password, zone.api_url) + client = novaclient.OpenStack(zone.username, zone.password, None, + zone.api_url) return client.zones.info()._info From bf586e4b144e567ed1220b87b4f89db2c3ef43ab Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 15 Jun 2011 22:58:22 +0900 Subject: [PATCH 25/47] typo --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index c9b75f96..1e8b8e84 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -535,7 +535,7 @@ class CloudTestCase(test.TestCase): self._wait_for_state(elevated, instance_id, is_deleted) def test_stop_start_instance(self): - """Makes sure stop/start instnace works""" + """Makes sure stop/start instance works""" # enforce periodic tasks run in short time to avoid wait for 60s. self._restart_compute_service(periodic_interval=0.3) From 2244a92ebfbad56ce4488b6c9022d6418adc2830 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 15 Jun 2011 23:11:03 +0900 Subject: [PATCH 26/47] pep8 --- nova/tests/test_cloud.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 1e8b8e84..3d91eb2b 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -541,7 +541,7 @@ class CloudTestCase(test.TestCase): kwargs = {'image_id': 'ami-1', 'instance_type': FLAGS.default_instance_type, - 'max_count': 1,} + 'max_count': 1, } instance_id = self._run_instance_wait(**kwargs) # a running instance can't be started. It is just ignored. @@ -553,7 +553,7 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) self.assertTrue(result) self._wait_for_stopped(instance_id) - + result = self.cloud.start_instances(self.context, [instance_id]) greenthread.sleep(0.3) self.assertTrue(result) @@ -563,18 +563,18 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) self.assertTrue(result) self._wait_for_stopped(instance_id) - + result = self.cloud.terminate_instances(self.context, [instance_id]) greenthread.sleep(0.3) self.assertTrue(result) - + self._restart_compute_service() def _volume_create(self): kwargs = {'status': 'available', 'host': self.volume.host, 'size': 1, - 'attach_status': 'detached',} + 'attach_status': 'detached', } return db.volume_create(self.context, kwargs) def _assert_volume_attached(self, vol, instance_id, mountpoint): @@ -582,7 +582,7 @@ class CloudTestCase(test.TestCase): self.assertEqual(vol['mountpoint'], mountpoint) self.assertEqual(vol['status'], "in-use") self.assertEqual(vol['attach_status'], "attached") - + def _assert_volume_detached(self, vol): self.assertEqual(vol['instance_id'], None) self.assertEqual(vol['mountpoint'], None) @@ -604,8 +604,8 @@ class CloudTestCase(test.TestCase): 'volume_id': vol1['id'], 'delete_on_termination': False,}, {'device_name': '/dev/vdc', - 'volume_id': vol2['id'], - 'delete_on_termination': True,}, + 'volume_id': vol2['id'], + 'delete_on_termination': True, }, ]} ec2_instance_id = self._run_instance_wait(**kwargs) instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) @@ -629,7 +629,7 @@ class CloudTestCase(test.TestCase): self._assert_volume_detached(vol) vol = db.volume_get(self.context, vol2['id']) self._assert_volume_detached(vol) - + self.cloud.start_instances(self.context, [ec2_instance_id]) self._wait_for_running(ec2_instance_id) vols = db.volume_get_all_by_instance(self.context, instance_id) @@ -654,7 +654,7 @@ class CloudTestCase(test.TestCase): admin_ctxt = context.get_admin_context(read_deleted=True) vol = db.volume_get(admin_ctxt, vol2['id']) self.assertTrue(vol['deleted']) - + self._restart_compute_service() def test_stop_with_attached_volume(self): @@ -669,7 +669,7 @@ class CloudTestCase(test.TestCase): 'max_count': 1, 'block_device_mapping': [{'device_name': '/dev/vdb', 'volume_id': vol1['id'], - 'delete_on_termination': True,},]} + 'delete_on_termination': True}]} ec2_instance_id = self._run_instance_wait(**kwargs) instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) @@ -695,7 +695,7 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) vol = db.volume_get(self.context, vol1['id']) self._assert_volume_detached(vol) - + result = self.cloud.stop_instances(self.context, [ec2_instance_id]) self.assertTrue(result) self._wait_for_stopped(ec2_instance_id) @@ -703,7 +703,7 @@ class CloudTestCase(test.TestCase): for vol_id in (vol1['id'], vol2['id']): vol = db.volume_get(self.context, vol_id) self._assert_volume_detached(vol) - + self.cloud.start_instances(self.context, [ec2_instance_id]) self._wait_for_running(ec2_instance_id) vols = db.volume_get_all_by_instance(self.context, instance_id) @@ -723,15 +723,15 @@ class CloudTestCase(test.TestCase): self.assertEqual(vol['id'], vol_id) self._assert_volume_detached(vol) db.volume_destroy(self.context, vol_id) - + self._restart_compute_service() - + def _create_snapshot(self, ec2_volume_id): result = self.cloud.create_snapshot(self.context, volume_id=ec2_volume_id) greenthread.sleep(0.3) return result['snapshotId'] - + def test_run_with_snapshot(self): """Makes sure run/stop/start instance with snapshot works.""" vol = self._volume_create() @@ -747,10 +747,10 @@ class CloudTestCase(test.TestCase): 'max_count': 1, 'block_device_mapping': [{'device_name': '/dev/vdb', 'snapshot_id': snapshot1_id, - 'delete_on_termination': False,}, + 'delete_on_termination': False, }, {'device_name': '/dev/vdc', 'snapshot_id': snapshot2_id, - 'delete_on_termination': True,},],} + 'delete_on_termination': True}]} ec2_instance_id = self._run_instance_wait(**kwargs) instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) @@ -778,14 +778,14 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) self._wait_for_terminate(ec2_instance_id) - greenthread.sleep(0.3) + greenthread.sleep(0.3) admin_ctxt = context.get_admin_context(read_deleted=False) vol = db.volume_get(admin_ctxt, vol1_id) self._assert_volume_detached(vol) self.assertFalse(vol['deleted']) db.volume_destroy(self.context, vol1_id) - greenthread.sleep(0.3) + greenthread.sleep(0.3) admin_ctxt = context.get_admin_context(read_deleted=True) vol = db.volume_get(admin_ctxt, vol2_id) self.assertTrue(vol['deleted']) From 2cf42caded6c6a8f693459a6a3105ff2c0d8d510 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 15 Jun 2011 14:41:09 +0000 Subject: [PATCH 27/47] PEP8 cleanups --- bin/nova-manage | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index c004a36c..62eb8bf1 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1085,7 +1085,8 @@ class ImageCommands(object): class AgentBuildCommands(object): """Class for managing agent builds.""" - def create(self, os, architecture, version, url, md5hash, hypervisor='xen'): + def create(self, os, architecture, version, url, md5hash, + hypervisor='xen'): """creates a new agent build arguments: os architecture version url md5hash [hypervisor='xen']""" ctxt = context.get_admin_context() From 36f2848b4f63aa051647c426b3d4cdcd5e73370b Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 15 Jun 2011 15:21:20 +0000 Subject: [PATCH 28/47] Print list of agent builds a bit prettier --- bin/nova-manage | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 62eb8bf1..184d1d73 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1106,15 +1106,34 @@ class AgentBuildCommands(object): hypervisor, os, architecture) db.agent_build_destroy(ctxt, agent_build_ref['id']) - def list(self): + def list(self, hypervisor=None): """lists all agent builds arguments: """ - # TODO(johannes.erdfelt): Make the output easier to read + fmt = "%-10s %-8s %12s %s" ctxt = context.get_admin_context() + by_hypervisor = {} for agent_build in db.agent_build_get_all(ctxt): - print agent_build.hypervisor, agent_build.os, agent_build.architecture, agent_build.version, agent_build.url, agent_build.md5hash + buildlist = by_hypervisor.get(agent_build.hypervisor) + if not buildlist: + buildlist = by_hypervisor[agent_build.hypervisor] = [] - def modify(self, os, architecture, version, url, md5hash, hypervisor='xen'): + buildlist.append(agent_build) + + for key, buildlist in by_hypervisor.iteritems(): + if hypervisor and key != hypervisor: + continue + + print "Hypervisor: %s" % key + print fmt % ('-' * 10, '-' * 8, '-' * 12, '-' * 32) + for agent_build in buildlist: + print fmt % (agent_build.os, agent_build.architecture, + agent_build.version, agent_build.md5hash) + print ' %s' % agent_build.url + + print + + def modify(self, os, architecture, version, url, md5hash, + hypervisor='xen'): """update an existing agent build arguments: os architecture version url md5hash [hypervisor='xen'] """ From 8a6791a646956954dc4cdbfaa4add25096212ab4 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 15 Jun 2011 09:45:22 -0700 Subject: [PATCH 29/47] fixed up some little project_id things with new novaclient --- nova/scheduler/zone_aware_scheduler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index 69d4c603..0ec83ec2 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -88,7 +88,7 @@ class ZoneAwareScheduler(driver.Scheduler): instance_properties = request_spec['instance_properties'] name = instance_properties['display_name'] - image_id = instance_properties['image_id'] + image_ref = instance_properties['image_ref'] meta = instance_properties['metadata'] flavor_id = instance_type['flavorid'] reservation_id = instance_properties['reservation_id'] @@ -112,7 +112,7 @@ class ZoneAwareScheduler(driver.Scheduler): raise exception.NotAuthorized(_("Bad credentials attempting " "to talk to zone at %(url)s.") % locals()) - nova.servers.create(name, image_id, flavor_id, ipgroup, meta, files, + nova.servers.create(name, image_ref, flavor_id, ipgroup, meta, files, child_blob, reservation_id=reservation_id) def _provision_resource_from_blob(self, context, item, instance_id, From 1323711eb6d7ba749872f32dcff752de5d2703e4 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Wed, 15 Jun 2011 10:21:41 -0700 Subject: [PATCH 30/47] don't provision to all child zones --- nova/scheduler/zone_aware_scheduler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nova/scheduler/zone_aware_scheduler.py b/nova/scheduler/zone_aware_scheduler.py index 0ec83ec2..e7bff2fa 100644 --- a/nova/scheduler/zone_aware_scheduler.py +++ b/nova/scheduler/zone_aware_scheduler.py @@ -185,7 +185,11 @@ class ZoneAwareScheduler(driver.Scheduler): if not build_plan: raise driver.NoValidHost(_('No hosts were available')) - for item in build_plan: + for num in xrange(request_spec['num_instances']): + if not build_plan: + break + + item = build_plan.pop(0) self._provision_resource(context, item, instance_id, request_spec, kwargs) From 266b876efaa3d6e94302bc30a951755af5e28558 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 15 Jun 2011 21:12:37 +0000 Subject: [PATCH 31/47] Prep-work to begin on reroute_compute --- nova/scheduler/api.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index ffe59d2c..28410f53 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -200,9 +200,27 @@ class RedirectResult(exception.Error): class reroute_compute(object): - """Decorator used to indicate that the method should - delegate the call the child zones if the db query - can't find anything.""" + """ + reroute_compute is responsible for trying to lookup a resource in the + current zone and if it's not found there, delegating the call to the + child zones. + + Since reroute_compute will be making 'cross-zone' calls, the ID for the + object must come in as a UUID-- if we receive an integer ID, we bail. + + The steps involved are: + + 1. Validate that item_id is UUID like + + 2. Lookup item by UUID in the zone local database + + 3. If the item was found, then extract integer ID, and pass that to + the wrapped method. (This ensures that zone-local code can + continue to use integer IDs). + + 4. If the item was not found, we delgate the call to a child zone + using the UUID. + """ def __init__(self, method_name): self.method_name = method_name From 0267e05f1ff77b5ee3df104337104e9a7de6a1c6 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 15 Jun 2011 21:50:36 +0000 Subject: [PATCH 32/47] First attempt to rewrite reroute_compute --- nova/scheduler/api.py | 80 +++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 28410f53..0cc8a113 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -24,6 +24,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import rpc +from nova import utils from eventlet import greenpool @@ -224,32 +225,57 @@ class reroute_compute(object): def __init__(self, method_name): self.method_name = method_name + + def _route_local(): + pass + + def _route_to_child_zones(context, collection, item_uuid): + if not FLAGS.enable_zone_routing: + raise InstanceNotFound(instance_id=item_uuid) + + zones = db.zone_get_all(context) + if not zones: + raise InstanceNotFound(instance_id=item_uuid) + + # Ask the children to provide an answer ... + LOG.debug(_("Asking child zones ...")) + result = self._call_child_zones(zones, + wrap_novaclient_function(_issue_novaclient_command, + collection, self.method_name, item_uuid)) + # Scrub the results and raise another exception + # so the API layers can bail out gracefully ... + raise RedirectResult(self.unmarshall_result(result)) + def __call__(self, f): def wrapped_f(*args, **kwargs): - collection, context, item_id = \ + collection, context, item_id_or_uuid = \ self.get_collection_context_and_id(args, kwargs) - try: - # Call the original function ... + + attempt_reroute = False + if utils.is_uuid_like(item_id_or_uuid): + item_uuid = item_id_or_uuid + try: + instance = self.db.instance_get_by_uuid( + context, item_uuid) + except exception.InstanceNotFound, e: + # NOTE(sirp): since a UUID was passed in, we can attempt + # to reroute to a child zone + attempt_reroute = True + LOG.debug(_("Instance %(item_uuid)s not found " + "locally: '%(e)s'" % locals())) + else: + # NOTE(sirp): since we're not re-routing in this case, and we + # we were passed a UUID, we need to replace that UUID with an + # integer ID in the argument list so that the zone-local code + # can continue to use integer IDs. + item_id = instance['id'] + self.replace_uuid_with_id(args, kwargs, replacement_id) + + if attempt_reroute: + self._route_to_child_zones(context, collection, item_uuid) + else: return f(*args, **kwargs) - except exception.InstanceNotFound, e: - LOG.debug(_("Instance %(item_id)s not found " - "locally: '%(e)s'" % locals())) - if not FLAGS.enable_zone_routing: - raise - - zones = db.zone_get_all(context) - if not zones: - raise - - # Ask the children to provide an answer ... - LOG.debug(_("Asking child zones ...")) - result = self._call_child_zones(zones, - wrap_novaclient_function(_issue_novaclient_command, - collection, self.method_name, item_id)) - # Scrub the results and raise another exception - # so the API layers can bail out gracefully ... - raise RedirectResult(self.unmarshall_result(result)) return wrapped_f def _call_child_zones(self, zones, function): @@ -268,6 +294,18 @@ class reroute_compute(object): instance_id = args[2] return ("servers", context, instance_id) + @staticmethod + def replace_uuid_with_id(args, kwargs, replacement_id): + """ + Extracts the UUID parameter from the arg or kwarg list and replaces + it with an integer ID. + """ + if 'instance_id' in kwargs: + kwargs['instance_id'] = replacement_id + elif len(args) > 1: + args.pop(2) + args.insert(2, replacement_id) + def unmarshall_result(self, zone_responses): """Result is a list of responses from each child zone. Each decorator derivation is responsible to turning this From 38249457dbf1b28d194efb5311ce99f9f3f44691 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 16 Jun 2011 15:02:18 +0000 Subject: [PATCH 33/47] Small tweaks --- nova/scheduler/api.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 0cc8a113..8bf6a159 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -225,10 +225,6 @@ class reroute_compute(object): def __init__(self, method_name): self.method_name = method_name - - def _route_local(): - pass - def _route_to_child_zones(context, collection, item_uuid): if not FLAGS.enable_zone_routing: raise InstanceNotFound(instance_id=item_uuid) @@ -255,8 +251,7 @@ class reroute_compute(object): if utils.is_uuid_like(item_id_or_uuid): item_uuid = item_id_or_uuid try: - instance = self.db.instance_get_by_uuid( - context, item_uuid) + instance = db.instance_get_by_uuid(context, item_uuid) except exception.InstanceNotFound, e: # NOTE(sirp): since a UUID was passed in, we can attempt # to reroute to a child zone @@ -269,10 +264,11 @@ class reroute_compute(object): # integer ID in the argument list so that the zone-local code # can continue to use integer IDs. item_id = instance['id'] - self.replace_uuid_with_id(args, kwargs, replacement_id) + args = list(args) # needs to be mutable to replace + self.replace_uuid_with_id(args, kwargs, item_id) if attempt_reroute: - self._route_to_child_zones(context, collection, item_uuid) + return self._route_to_child_zones(context, collection, item_uuid) else: return f(*args, **kwargs) @@ -303,6 +299,8 @@ class reroute_compute(object): if 'instance_id' in kwargs: kwargs['instance_id'] = replacement_id elif len(args) > 1: + # NOTE(sirp): args comes in as a tuple, so we need to convert it + # to a list to mutate it, and then convert it back to a tuple args.pop(2) args.insert(2, replacement_id) From 4ec4e388a99de1767e3413d57b569b4ca8e320ec Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 16 Jun 2011 15:56:06 +0000 Subject: [PATCH 34/47] Clean up docstrings to match HACKING --- bin/nova-manage | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 184d1d73..04d4ae48 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1087,7 +1087,7 @@ class AgentBuildCommands(object): def create(self, os, architecture, version, url, md5hash, hypervisor='xen'): - """creates a new agent build + """Creates a new agent build. arguments: os architecture version url md5hash [hypervisor='xen']""" ctxt = context.get_admin_context() agent_build = db.agent_build_create(ctxt, @@ -1099,7 +1099,7 @@ class AgentBuildCommands(object): 'md5hash': md5hash}) def delete(self, os, architecture, hypervisor='xen'): - """deletes an existing agent build + """Deletes an existing agent build. arguments: os architecture [hypervisor='xen']""" ctxt = context.get_admin_context() agent_build_ref = db.agent_build_get_by_triple(ctxt, @@ -1107,7 +1107,7 @@ class AgentBuildCommands(object): db.agent_build_destroy(ctxt, agent_build_ref['id']) def list(self, hypervisor=None): - """lists all agent builds + """Lists all agent builds. arguments: """ fmt = "%-10s %-8s %12s %s" ctxt = context.get_admin_context() @@ -1134,7 +1134,7 @@ class AgentBuildCommands(object): def modify(self, os, architecture, version, url, md5hash, hypervisor='xen'): - """update an existing agent build + """Update an existing agent build. arguments: os architecture version url md5hash [hypervisor='xen'] """ ctxt = context.get_admin_context() From 711e7633fff1cb5097572bd09493abf61e873658 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 16 Jun 2011 17:27:36 +0000 Subject: [PATCH 35/47] Fixing test_servers_by_uuid --- nova/scheduler/api.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 8bf6a159..a363a311 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -218,20 +218,20 @@ class reroute_compute(object): 3. If the item was found, then extract integer ID, and pass that to the wrapped method. (This ensures that zone-local code can continue to use integer IDs). - + 4. If the item was not found, we delgate the call to a child zone using the UUID. """ def __init__(self, method_name): self.method_name = method_name - def _route_to_child_zones(context, collection, item_uuid): + def _route_to_child_zones(self, context, collection, item_uuid): if not FLAGS.enable_zone_routing: - raise InstanceNotFound(instance_id=item_uuid) + raise exception.InstanceNotFound(instance_id=item_uuid) zones = db.zone_get_all(context) if not zones: - raise InstanceNotFound(instance_id=item_uuid) + raise exception.InstanceNotFound(instance_id=item_uuid) # Ask the children to provide an answer ... LOG.debug(_("Asking child zones ...")) @@ -247,7 +247,7 @@ class reroute_compute(object): collection, context, item_id_or_uuid = \ self.get_collection_context_and_id(args, kwargs) - attempt_reroute = False + attempt_reroute = False if utils.is_uuid_like(item_id_or_uuid): item_uuid = item_id_or_uuid try: From 4166cf9416d258f1144140bdee24c954bdff509e Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 16 Jun 2011 17:52:34 +0000 Subject: [PATCH 36/47] PEP8 cleanup. --- nova/scheduler/api.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index a363a311..6e5471d7 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -259,16 +259,17 @@ class reroute_compute(object): LOG.debug(_("Instance %(item_uuid)s not found " "locally: '%(e)s'" % locals())) else: - # NOTE(sirp): since we're not re-routing in this case, and we - # we were passed a UUID, we need to replace that UUID with an - # integer ID in the argument list so that the zone-local code - # can continue to use integer IDs. + # NOTE(sirp): since we're not re-routing in this case, and + # we we were passed a UUID, we need to replace that UUID + # with an integer ID in the argument list so that the + # zone-local code can continue to use integer IDs. item_id = instance['id'] args = list(args) # needs to be mutable to replace self.replace_uuid_with_id(args, kwargs, item_id) if attempt_reroute: - return self._route_to_child_zones(context, collection, item_uuid) + return self._route_to_child_zones(context, collection, + item_uuid) else: return f(*args, **kwargs) From b7eda2552912e3a9016b9047ab8dbb9ca4101b2f Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Thu, 16 Jun 2011 13:56:52 -0500 Subject: [PATCH 37/47] Added a new test for confirming failure when no primary VDI is present --- nova/tests/test_xenapi.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index d1c88287..c0213250 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -33,6 +33,7 @@ from nova import utils from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state +from nova import exception from nova.virt import xenapi_conn from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import volume_utils @@ -228,6 +229,23 @@ class XenAPIVMTestCase(test.TestCase): instance = self._create_instance() self.conn.get_diagnostics(instance) + def test_instance_snapshot_fails_with_no_primary_vdi(self): + def create_bad_vbd(vm_ref, vdi_ref): + vbd_rec = {'VM': vm_ref, + 'VDI': vdi_ref, + 'userdevice': 'fake', + 'currently_attached': False} + vbd_ref = xenapi_fake._create_object('VBD', vbd_rec) + xenapi_fake.after_VBD_create(vbd_ref, vbd_rec) + return vbd_ref + + self.stubs.Set(xenapi_fake, 'create_vbd', create_bad_vbd) + stubs.stubout_instance_snapshot(self.stubs) + instance = self._create_instance() + + name = "MySnapshot" + self.assertRaises(exception.Error, self.conn.snapshot, instance, name) + def test_instance_snapshot(self): stubs.stubout_instance_snapshot(self.stubs) instance = self._create_instance() From 2a276dde81b9a638427e25c06436e73dc4b77a06 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 16 Jun 2011 14:42:50 -0500 Subject: [PATCH 38/47] Renaming to _build_instance_get --- nova/scheduler/api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 6e5471d7..8ef893be 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -300,8 +300,6 @@ class reroute_compute(object): if 'instance_id' in kwargs: kwargs['instance_id'] = replacement_id elif len(args) > 1: - # NOTE(sirp): args comes in as a tuple, so we need to convert it - # to a list to mutate it, and then convert it back to a tuple args.pop(2) args.insert(2, replacement_id) From 9727d0c66698a2e1e03c2ce177ca7f429f237372 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 16 Jun 2011 21:21:01 +0000 Subject: [PATCH 39/47] Glance host defaults to rather than localhost --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index acfcf8d6..f6f12e3b 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -272,7 +272,7 @@ DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID') DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key') # NOTE(sirp): my_ip interpolation doesn't work within nested structures DEFINE_list('glance_api_servers', - ['127.0.0.1:9292'], + ['%s:9292' % _get_my_ip()], 'list of glance api servers available to nova (host:port)') DEFINE_integer('s3_port', 3333, 's3 port') DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)') From de7ff05481194c5384707c6de3015b399d8797da Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 17 Jun 2011 14:20:18 +0200 Subject: [PATCH 40/47] Fix unitttest so that it actually fails without the fix --- nova/tests/test_cloud.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index ba133c86..c61968d2 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -526,7 +526,9 @@ class CloudTestCase(test.TestCase): def test_update_of_instance_wont_update_private_fields(self): inst = db.instance_create(self.context, {}) - self.cloud.update_instance(self.context, inst['id'], + ec2_id = ec2utils.id_to_ec2_id(inst['id']) + self.cloud.update_instance(self.context, ec2_id, + display_name='c00l 1m4g3', mac_address='DE:AD:BE:EF') inst = db.instance_get(self.context, inst['id']) self.assertEqual(None, inst['mac_address']) From 67adeb8db0c79d20cfd721724da85306a4da7c89 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Fri, 17 Jun 2011 10:17:07 -0400 Subject: [PATCH 41/47] Fix for a problem where run_tests.sh would output a seemingly unrelated error message when there was a sqlalchemy-migrate version number conflict --- run_tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/run_tests.py b/run_tests.py index 0944bb58..bb33f913 100644 --- a/run_tests.py +++ b/run_tests.py @@ -211,6 +211,12 @@ class NovaTestResult(result.TextTestResult): break sys.stdout = stdout + # NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate + # error results in it failing to be initialized later. Otherwise, + # _handleElapsedTime will fail, causing the wrong error message to + # be outputted. + self.start_time = time.time() + def getDescription(self, test): return str(test) From 31d7aa83ae0ab574a37e1ac0fb28a39215285391 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 17 Jun 2011 13:15:49 -0400 Subject: [PATCH 42/47] fixing test case --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 094fd394..8ba2164e 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -515,7 +515,7 @@ class CloudTestCase(test.TestCase): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, 'type': 'machine'}, 'status': 'active'} - self.stubs.Set(local.LocalImageService, 'show', fake_show_stat_active) + self.stubs.Set(fake._FakeImageService, 'show', fake_show_stat_active) result = run_instances(self.context, **kwargs) self.assertEqual(len(result['instancesSet']), 1) From 2fb55611baa5b008f592824362423a4dd039bfc4 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Fri, 17 Jun 2011 17:32:45 +0000 Subject: [PATCH 43/47] Add some documentation for cmp_version Add test cases for cmp_version --- nova/tests/test_xenapi.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index d1c88287..5504e802 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -36,9 +36,8 @@ from nova.compute import power_state from nova.virt import xenapi_conn from nova.virt.xenapi import fake as xenapi_fake from nova.virt.xenapi import volume_utils +from nova.virt.xenapi import vmops from nova.virt.xenapi import vm_utils -from nova.virt.xenapi.vmops import SimpleDH -from nova.virt.xenapi.vmops import VMOps from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs from nova.tests.glance import stubs as glance_stubs @@ -191,7 +190,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_get_this_vm_uuid(self.stubs) stubs.stubout_stream_disk(self.stubs) stubs.stubout_is_vdi_pv(self.stubs) - self.stubs.Set(VMOps, 'reset_network', reset_network) + self.stubs.Set(vmops.VMOps, 'reset_network', reset_network) stubs.stub_out_vm_methods(self.stubs) glance_stubs.stubout_glance_client(self.stubs) fake_utils.stub_out_utils_execute(self.stubs) @@ -581,8 +580,8 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): """Unit tests for Diffie-Hellman code.""" def setUp(self): super(XenAPIDiffieHellmanTestCase, self).setUp() - self.alice = SimpleDH() - self.bob = SimpleDH() + self.alice = vmops.SimpleDH() + self.bob = vmops.SimpleDH() def test_shared(self): alice_pub = self.alice.get_public() @@ -729,6 +728,28 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): self.assert_disk_type(vm_utils.ImageType.DISK_VHD) +class CompareVersionTestCase(test.TestCase): + def test_less_than(self): + """Test that cmp_version compares a as less than b""" + self.assert_(vmops.cmp_version('1.2.3.4', '1.2.3.5') < 0) + + def test_greater_than(self): + """Test that cmp_version compares a as greater than b""" + self.assert_(vmops.cmp_version('1.2.3.5', '1.2.3.4') > 0) + + def test_equal(self): + """Test that cmp_version compares a as equal to b""" + self.assert_(vmops.cmp_version('1.2.3.4', '1.2.3.4') == 0) + + def test_non_lexical(self): + """Test that cmp_version compares non-lexically""" + self.assert_(vmops.cmp_version('1.2.3.10', '1.2.3.4') > 0) + + def test_length(self): + """Test that cmp_version compares by length as last resort""" + self.assert_(vmops.cmp_version('1.2.3', '1.2.3.4') < 0) + + class FakeXenApi(object): """Fake XenApi for testing HostState.""" From 2160a99d3f59025d0421142ae13666d70970dcdc Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Fri, 17 Jun 2011 18:47:57 +0000 Subject: [PATCH 44/47] Add new architecture attribute along with os_type --- nova/tests/test_xenapi.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 5504e802..b3364a45 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -83,7 +83,8 @@ class XenAPIVolumeTestCase(test.TestCase): 'ramdisk_id': 3, 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', - 'os_type': 'linux'} + 'os_type': 'linux', + 'architecture': 'x86-64'} def _create_volume(self, size='0'): """Create a volume object.""" @@ -210,7 +211,8 @@ class XenAPIVMTestCase(test.TestCase): 'ramdisk_id': 3, 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', - 'os_type': 'linux'} + 'os_type': 'linux', + 'architecture': 'x86-64'} instance = db.instance_create(self.context, values) self.conn.spawn(instance) @@ -351,7 +353,8 @@ class XenAPIVMTestCase(test.TestCase): def _test_spawn(self, image_ref, kernel_id, ramdisk_id, instance_type_id="3", os_type="linux", - instance_id=1, check_injection=False): + architecture="x86-64", instance_id=1, + check_injection=False): stubs.stubout_loopingcall_start(self.stubs) values = {'id': instance_id, 'project_id': self.project.id, @@ -361,7 +364,8 @@ class XenAPIVMTestCase(test.TestCase): 'ramdisk_id': ramdisk_id, 'instance_type_id': instance_type_id, 'mac_address': 'aa:bb:cc:dd:ee:ff', - 'os_type': os_type} + 'os_type': os_type, + 'architecture': architecture} instance = db.instance_create(self.context, values) self.conn.spawn(instance) self.create_vm_record(self.conn, os_type, instance_id) @@ -390,7 +394,7 @@ class XenAPIVMTestCase(test.TestCase): def test_spawn_vhd_glance_linux(self): FLAGS.xenapi_image_service = 'glance' self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None, - os_type="linux") + os_type="linux", architecture="x86-64") self.check_vm_params_for_linux() def test_spawn_vhd_glance_swapdisk(self): @@ -419,7 +423,7 @@ class XenAPIVMTestCase(test.TestCase): def test_spawn_vhd_glance_windows(self): FLAGS.xenapi_image_service = 'glance' self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None, - os_type="windows") + os_type="windows", architecture="i386") self.check_vm_params_for_windows() def test_spawn_glance(self): @@ -570,7 +574,8 @@ class XenAPIVMTestCase(test.TestCase): 'ramdisk_id': 3, 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', - 'os_type': 'linux'} + 'os_type': 'linux', + 'architecture': 'x86-64'} instance = db.instance_create(self.context, values) self.conn.spawn(instance) return instance @@ -645,7 +650,8 @@ class XenAPIMigrateInstance(test.TestCase): 'local_gb': 5, 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', - 'os_type': 'linux'} + 'os_type': 'linux', + 'architecture': 'x86-64'} fake_utils.stub_out_utils_execute(self.stubs) stubs.stub_out_migration_methods(self.stubs) @@ -684,6 +690,7 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): self.fake_instance = FakeInstance() self.fake_instance.id = 42 self.fake_instance.os_type = 'linux' + self.fake_instance.architecture = 'x86-64' def assert_disk_type(self, disk_type): dt = vm_utils.VMHelper.determine_disk_image_type( From 84efb31e4b8f889603ad50e88b5ec7a720ca38c6 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Fri, 17 Jun 2011 21:03:12 +0000 Subject: [PATCH 45/47] Ensure os_type and architecture get set correctly --- nova/tests/test_xenapi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index b3364a45..54e82592 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -370,6 +370,8 @@ class XenAPIVMTestCase(test.TestCase): self.conn.spawn(instance) self.create_vm_record(self.conn, os_type, instance_id) self.check_vm_record(self.conn, check_injection) + self.assert_(instance.os_type) + self.assert_(instance.architecture) def test_spawn_not_enough_memory(self): FLAGS.xenapi_image_service = 'glance' From c025f01925825817a895236a5e32f7cd4d2f3ad4 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Sat, 18 Jun 2011 08:34:20 +0900 Subject: [PATCH 46/47] pep8: white space/blank lines --- nova/tests/test_cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 6a6256c2..09e26df4 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -671,7 +671,7 @@ class CloudTestCase(test.TestCase): 'max_count': 1, 'block_device_mapping': [{'device_name': '/dev/vdb', 'volume_id': vol1['id'], - 'delete_on_termination': False,}, + 'delete_on_termination': False, }, {'device_name': '/dev/vdc', 'volume_id': vol2['id'], 'delete_on_termination': True, }, From 7ada255d992fbd0516b2302ef366f896d75e0a8d Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Mon, 20 Jun 2011 18:42:04 +0000 Subject: [PATCH 47/47] assert_ -> assertTrue since assert_ is deprecated --- nova/tests/test_xenapi.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 54e82592..93e6e544 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -370,8 +370,8 @@ class XenAPIVMTestCase(test.TestCase): self.conn.spawn(instance) self.create_vm_record(self.conn, os_type, instance_id) self.check_vm_record(self.conn, check_injection) - self.assert_(instance.os_type) - self.assert_(instance.architecture) + self.assertTrue(instance.os_type) + self.assertTrue(instance.architecture) def test_spawn_not_enough_memory(self): FLAGS.xenapi_image_service = 'glance' @@ -740,23 +740,23 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): class CompareVersionTestCase(test.TestCase): def test_less_than(self): """Test that cmp_version compares a as less than b""" - self.assert_(vmops.cmp_version('1.2.3.4', '1.2.3.5') < 0) + self.assertTrue(vmops.cmp_version('1.2.3.4', '1.2.3.5') < 0) def test_greater_than(self): """Test that cmp_version compares a as greater than b""" - self.assert_(vmops.cmp_version('1.2.3.5', '1.2.3.4') > 0) + self.assertTrue(vmops.cmp_version('1.2.3.5', '1.2.3.4') > 0) def test_equal(self): """Test that cmp_version compares a as equal to b""" - self.assert_(vmops.cmp_version('1.2.3.4', '1.2.3.4') == 0) + self.assertTrue(vmops.cmp_version('1.2.3.4', '1.2.3.4') == 0) def test_non_lexical(self): """Test that cmp_version compares non-lexically""" - self.assert_(vmops.cmp_version('1.2.3.10', '1.2.3.4') > 0) + self.assertTrue(vmops.cmp_version('1.2.3.10', '1.2.3.4') > 0) def test_length(self): """Test that cmp_version compares by length as last resort""" - self.assert_(vmops.cmp_version('1.2.3', '1.2.3.4') < 0) + self.assertTrue(vmops.cmp_version('1.2.3', '1.2.3.4') < 0) class FakeXenApi(object):