Add preparation for asynchronous instance faults
Add InstanceFault model Migration for new instance_faults table Functions to add and get instance faults A single example of how faults should be added Change-Id: I439e2419240de24a728045046153451eb8a0d267
This commit is contained in:
		@@ -323,10 +323,11 @@ class ComputeManager(manager.SchedulerDependentManager):
 | 
			
		||||
        except exception.InstanceNotFound:
 | 
			
		||||
            LOG.exception(_("Instance %s not found.") % instance_uuid)
 | 
			
		||||
            return  # assuming the instance was already deleted
 | 
			
		||||
        except Exception:
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            with utils.save_and_reraise_exception():
 | 
			
		||||
                self._instance_update(context, instance_uuid,
 | 
			
		||||
                                      vm_state=vm_states.ERROR)
 | 
			
		||||
                self.add_instance_fault_from_exc(context, instance_uuid, e)
 | 
			
		||||
 | 
			
		||||
    def _check_instance_not_already_created(self, context, instance):
 | 
			
		||||
        """Ensure an instance with the same name is not already present."""
 | 
			
		||||
@@ -1917,3 +1918,29 @@ class ComputeManager(manager.SchedulerDependentManager):
 | 
			
		||||
                LOG.info(_("Reclaiming deleted instance %(instance_id)s"),
 | 
			
		||||
                         locals())
 | 
			
		||||
                self._delete_instance(context, instance)
 | 
			
		||||
 | 
			
		||||
    def add_instance_fault_from_exc(self, context, instance_uuid, fault):
 | 
			
		||||
        """Adds the specified fault to the database."""
 | 
			
		||||
        if hasattr(fault, "code"):
 | 
			
		||||
            code = fault.code
 | 
			
		||||
        else:
 | 
			
		||||
            code = 500
 | 
			
		||||
 | 
			
		||||
        values = {
 | 
			
		||||
            'instance_uuid': instance_uuid,
 | 
			
		||||
            'code': code,
 | 
			
		||||
            'message': fault.__class__.__name__,
 | 
			
		||||
            'details': fault.message,
 | 
			
		||||
        }
 | 
			
		||||
        self.db.instance_fault_create(context, values)
 | 
			
		||||
 | 
			
		||||
    def add_instance_fault(self, context, instance_uuid, code=500,
 | 
			
		||||
                           message='', details=''):
 | 
			
		||||
        """Adds a fault to the database using the specified values."""
 | 
			
		||||
        values = {
 | 
			
		||||
            'instance_uuid': instance_uuid,
 | 
			
		||||
            'code': code,
 | 
			
		||||
            'message': message,
 | 
			
		||||
            'details': details,
 | 
			
		||||
        }
 | 
			
		||||
        self.db.instance_fault_create(context, values)
 | 
			
		||||
 
 | 
			
		||||
@@ -1756,3 +1756,16 @@ def sm_volume_get(context, volume_id):
 | 
			
		||||
def sm_volume_get_all(context):
 | 
			
		||||
    """Get all child Zones."""
 | 
			
		||||
    return IMPL.sm_volume_get_all(context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
####################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def instance_fault_create(context, values):
 | 
			
		||||
    """Create a new Instance Fault."""
 | 
			
		||||
    return IMPL.instance_fault_create(context, values)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def instance_fault_get_by_instance(context, instance_uuid):
 | 
			
		||||
    """Get first instance fault with the given instance uuid."""
 | 
			
		||||
    return IMPL.instance_fault_get_by_instance(context, instance_uuid)
 | 
			
		||||
 
 | 
			
		||||
@@ -4024,3 +4024,22 @@ def sm_volume_get(context, volume_id):
 | 
			
		||||
 | 
			
		||||
def sm_volume_get_all(context):
 | 
			
		||||
    return model_query(context, models.SMVolume, read_deleted="yes").all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def instance_fault_create(context, values):
 | 
			
		||||
    """Create a new Instance Fault."""
 | 
			
		||||
    fault_ref = models.InstanceFault()
 | 
			
		||||
    fault_ref.update(values)
 | 
			
		||||
    fault_ref.save()
 | 
			
		||||
    return fault_ref
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def instance_fault_get_by_instance(context, instance_uuid):
 | 
			
		||||
    """Get first instance fault with the given instance uuid."""
 | 
			
		||||
    return model_query(context, models.InstanceFault, read_deleted='no').\
 | 
			
		||||
                filter_by(instance_uuid=instance_uuid).\
 | 
			
		||||
                order_by(desc("created_at")).\
 | 
			
		||||
                first()
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
# Copyright 2011 OpenStack LLC.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from sqlalchemy import Boolean, Column, DateTime, Integer, ForeignKey
 | 
			
		||||
from sqlalchemy import MetaData, String, Table, Text
 | 
			
		||||
from nova import log as logging
 | 
			
		||||
 | 
			
		||||
meta = MetaData()
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# New Tables
 | 
			
		||||
#
 | 
			
		||||
instance_faults = Table('instance_faults', meta,
 | 
			
		||||
        Column('created_at', DateTime(timezone=False)),
 | 
			
		||||
        Column('updated_at', DateTime(timezone=False)),
 | 
			
		||||
        Column('deleted_at', DateTime(timezone=False)),
 | 
			
		||||
        Column('deleted', Boolean(create_constraint=True, name=None),
 | 
			
		||||
                default=False),
 | 
			
		||||
        Column('id', Integer(), primary_key=True, nullable=False),
 | 
			
		||||
        Column('instance_uuid', String(36, ForeignKey('instances.uuid'))),
 | 
			
		||||
        Column('code', Integer(), nullable=False),
 | 
			
		||||
        Column('message',
 | 
			
		||||
               String(length=255, convert_unicode=False, assert_unicode=None,
 | 
			
		||||
                      unicode_error=None, _warn_on_bytestring=False)),
 | 
			
		||||
        Column('details',
 | 
			
		||||
               Text(length=None, convert_unicode=False, assert_unicode=None,
 | 
			
		||||
                    unicode_error=None, _warn_on_bytestring=False)),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Tables to alter
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# (none currently)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upgrade(migrate_engine):
 | 
			
		||||
    # Upgrade operations go here. Don't create your own engine;
 | 
			
		||||
    # bind migrate_engine to your metadata
 | 
			
		||||
    meta.bind = migrate_engine
 | 
			
		||||
    try:
 | 
			
		||||
        instance_faults.create()
 | 
			
		||||
    except Exception:
 | 
			
		||||
        logging.info(repr(instance_faults))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def downgrade(migrate_engine):
 | 
			
		||||
    # Operations to reverse the above upgrade go here.
 | 
			
		||||
    instance_faults.drop()
 | 
			
		||||
@@ -912,6 +912,17 @@ class SMVolume(BASE, NovaBase):
 | 
			
		||||
    vdi_uuid = Column(String(255))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InstanceFault(BASE, NovaBase):
 | 
			
		||||
    __tablename__ = 'instance_faults'
 | 
			
		||||
    id = Column(Integer(), primary_key=True, autoincrement=True)
 | 
			
		||||
    instance_uuid = Column(String(36),
 | 
			
		||||
                           ForeignKey('instances.uuid'),
 | 
			
		||||
                           nullable=False)
 | 
			
		||||
    code = Column(Integer(), nullable=False)
 | 
			
		||||
    message = Column(String(255))
 | 
			
		||||
    details = Column(Text)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_models():
 | 
			
		||||
    """Register Models and create metadata.
 | 
			
		||||
 | 
			
		||||
@@ -927,7 +938,8 @@ def register_models():
 | 
			
		||||
              Project, Certificate, ConsolePool, Console, Zone,
 | 
			
		||||
              VolumeMetadata, VolumeTypes, VolumeTypeExtraSpecs,
 | 
			
		||||
              AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration,
 | 
			
		||||
              VirtualStorageArray, SMFlavors, SMBackendConf, SMVolume)
 | 
			
		||||
              VirtualStorageArray, SMFlavors, SMBackendConf, SMVolume,
 | 
			
		||||
              InstanceFault)
 | 
			
		||||
    engine = create_engine(FLAGS.sql_connection, echo=False)
 | 
			
		||||
    for model in models:
 | 
			
		||||
        model.metadata.create_all(engine)
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ Tests For Compute
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from copy import copy
 | 
			
		||||
from webob import exc
 | 
			
		||||
 | 
			
		||||
import mox
 | 
			
		||||
 | 
			
		||||
@@ -1063,6 +1064,60 @@ class ComputeTestCase(BaseTestCase):
 | 
			
		||||
        self.assertEqual(len(instances), 1)
 | 
			
		||||
        self.assertEqual(power_state.NOSTATE, instances[0]['power_state'])
 | 
			
		||||
 | 
			
		||||
    def test_add_instance_fault(self):
 | 
			
		||||
        instance_uuid = str(utils.gen_uuid())
 | 
			
		||||
 | 
			
		||||
        def fake_db_fault_create(ctxt, values):
 | 
			
		||||
            expected = {
 | 
			
		||||
                'code': 404,
 | 
			
		||||
                'message': 'HTTPNotFound',
 | 
			
		||||
                'details': 'Error Details',
 | 
			
		||||
                'instance_uuid': instance_uuid,
 | 
			
		||||
            }
 | 
			
		||||
            self.assertEquals(expected, values)
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
 | 
			
		||||
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        self.compute.add_instance_fault(ctxt, instance_uuid, 404,
 | 
			
		||||
                                        'HTTPNotFound', 'Error Details')
 | 
			
		||||
 | 
			
		||||
    def test_add_instance_fault_error(self):
 | 
			
		||||
        instance_uuid = str(utils.gen_uuid())
 | 
			
		||||
 | 
			
		||||
        def fake_db_fault_create(ctxt, values):
 | 
			
		||||
            expected = {
 | 
			
		||||
                'code': 500,
 | 
			
		||||
                'message': 'NotImplementedError',
 | 
			
		||||
                'details': '',
 | 
			
		||||
                'instance_uuid': instance_uuid,
 | 
			
		||||
            }
 | 
			
		||||
            self.assertEquals(expected, values)
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
 | 
			
		||||
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        self.compute.add_instance_fault_from_exc(ctxt, instance_uuid,
 | 
			
		||||
                                                 NotImplementedError())
 | 
			
		||||
 | 
			
		||||
    def test_add_instance_fault_http_exception(self):
 | 
			
		||||
        instance_uuid = str(utils.gen_uuid())
 | 
			
		||||
 | 
			
		||||
        def fake_db_fault_create(ctxt, values):
 | 
			
		||||
            expected = {
 | 
			
		||||
                'code': 404,
 | 
			
		||||
                'message': 'HTTPNotFound',
 | 
			
		||||
                'details': 'Error Details',
 | 
			
		||||
                'instance_uuid': instance_uuid,
 | 
			
		||||
            }
 | 
			
		||||
            self.assertEquals(expected, values)
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
 | 
			
		||||
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        self.compute.add_instance_fault_from_exc(ctxt, instance_uuid,
 | 
			
		||||
                                        exc.HTTPNotFound("Error Details"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ComputeAPITestCase(BaseTestCase):
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ from nova import test
 | 
			
		||||
from nova import context
 | 
			
		||||
from nova import db
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import utils
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
@@ -192,3 +193,74 @@ class DbApiTestCase(test.TestCase):
 | 
			
		||||
        # Retrieve the metadata to ensure it was successfully updated
 | 
			
		||||
        instance_meta = db.instance_metadata_get(ctxt, instance.id)
 | 
			
		||||
        self.assertEqual('bar', instance_meta['host'])
 | 
			
		||||
 | 
			
		||||
    def test_instance_fault_create(self):
 | 
			
		||||
        """Ensure we can create an instance fault"""
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
        uuid = str(utils.gen_uuid())
 | 
			
		||||
 | 
			
		||||
        # Create a fault
 | 
			
		||||
        fault_values = {
 | 
			
		||||
            'message': 'message',
 | 
			
		||||
            'details': 'detail',
 | 
			
		||||
            'instance_uuid': uuid,
 | 
			
		||||
            'code': 404,
 | 
			
		||||
        }
 | 
			
		||||
        db.instance_fault_create(ctxt, fault_values)
 | 
			
		||||
 | 
			
		||||
        # Retrieve the fault to ensure it was successfully added
 | 
			
		||||
        instance_fault = db.instance_fault_get_by_instance(ctxt, uuid)
 | 
			
		||||
        self.assertEqual(404, instance_fault['code'])
 | 
			
		||||
 | 
			
		||||
    def test_instance_fault_get_by_instance(self):
 | 
			
		||||
        """ ensure we can retrieve an instance fault by  instance UUID """
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
 | 
			
		||||
        # Create faults
 | 
			
		||||
        uuid = str(utils.gen_uuid())
 | 
			
		||||
        fault_values = {
 | 
			
		||||
            'message': 'message',
 | 
			
		||||
            'details': 'detail',
 | 
			
		||||
            'instance_uuid': uuid,
 | 
			
		||||
            'code': 404,
 | 
			
		||||
        }
 | 
			
		||||
        db.instance_fault_create(ctxt, fault_values)
 | 
			
		||||
 | 
			
		||||
        uuid2 = str(utils.gen_uuid())
 | 
			
		||||
        fault_values = {
 | 
			
		||||
            'message': 'message',
 | 
			
		||||
            'details': 'detail',
 | 
			
		||||
            'instance_uuid': uuid2,
 | 
			
		||||
            'code': 500,
 | 
			
		||||
        }
 | 
			
		||||
        db.instance_fault_create(ctxt, fault_values)
 | 
			
		||||
 | 
			
		||||
        # Retrieve the fault to ensure it was successfully added
 | 
			
		||||
        instance_fault = db.instance_fault_get_by_instance(ctxt, uuid2)
 | 
			
		||||
        self.assertEqual(500, instance_fault['code'])
 | 
			
		||||
 | 
			
		||||
    def test_instance_fault_get_by_instance_first_fault(self):
 | 
			
		||||
        """Instance_fault_get_by_instance should return the latest fault """
 | 
			
		||||
        ctxt = context.get_admin_context()
 | 
			
		||||
 | 
			
		||||
        # Create faults
 | 
			
		||||
        uuid = str(utils.gen_uuid())
 | 
			
		||||
        fault_values = {
 | 
			
		||||
            'message': 'message',
 | 
			
		||||
            'details': 'detail',
 | 
			
		||||
            'instance_uuid': uuid,
 | 
			
		||||
            'code': 404,
 | 
			
		||||
        }
 | 
			
		||||
        db.instance_fault_create(ctxt, fault_values)
 | 
			
		||||
 | 
			
		||||
        fault_values = {
 | 
			
		||||
            'message': 'message',
 | 
			
		||||
            'details': 'detail',
 | 
			
		||||
            'instance_uuid': uuid,
 | 
			
		||||
            'code': 500,
 | 
			
		||||
        }
 | 
			
		||||
        db.instance_fault_create(ctxt, fault_values)
 | 
			
		||||
 | 
			
		||||
        # Retrieve the fault to ensure it was successfully added
 | 
			
		||||
        instance_fault = db.instance_fault_get_by_instance(ctxt, uuid)
 | 
			
		||||
        self.assertEqual(500, instance_fault['code'])
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user