unittest: tests for boot from volume and stop/start instances
This commit is contained in:
		@@ -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'])
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user