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:
|
except exception.InstanceNotFound:
|
||||||
LOG.exception(_("Instance %s not found.") % instance_uuid)
|
LOG.exception(_("Instance %s not found.") % instance_uuid)
|
||||||
return # assuming the instance was already deleted
|
return # assuming the instance was already deleted
|
||||||
except Exception:
|
except Exception as e:
|
||||||
with utils.save_and_reraise_exception():
|
with utils.save_and_reraise_exception():
|
||||||
self._instance_update(context, instance_uuid,
|
self._instance_update(context, instance_uuid,
|
||||||
vm_state=vm_states.ERROR)
|
vm_state=vm_states.ERROR)
|
||||||
|
self.add_instance_fault_from_exc(context, instance_uuid, e)
|
||||||
|
|
||||||
def _check_instance_not_already_created(self, context, instance):
|
def _check_instance_not_already_created(self, context, instance):
|
||||||
"""Ensure an instance with the same name is not already present."""
|
"""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"),
|
LOG.info(_("Reclaiming deleted instance %(instance_id)s"),
|
||||||
locals())
|
locals())
|
||||||
self._delete_instance(context, instance)
|
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):
|
def sm_volume_get_all(context):
|
||||||
"""Get all child Zones."""
|
"""Get all child Zones."""
|
||||||
return IMPL.sm_volume_get_all(context)
|
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):
|
def sm_volume_get_all(context):
|
||||||
return model_query(context, models.SMVolume, read_deleted="yes").all()
|
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))
|
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():
|
def register_models():
|
||||||
"""Register Models and create metadata.
|
"""Register Models and create metadata.
|
||||||
|
|
||||||
@@ -927,7 +938,8 @@ def register_models():
|
|||||||
Project, Certificate, ConsolePool, Console, Zone,
|
Project, Certificate, ConsolePool, Console, Zone,
|
||||||
VolumeMetadata, VolumeTypes, VolumeTypeExtraSpecs,
|
VolumeMetadata, VolumeTypes, VolumeTypeExtraSpecs,
|
||||||
AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration,
|
AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration,
|
||||||
VirtualStorageArray, SMFlavors, SMBackendConf, SMVolume)
|
VirtualStorageArray, SMFlavors, SMBackendConf, SMVolume,
|
||||||
|
InstanceFault)
|
||||||
engine = create_engine(FLAGS.sql_connection, echo=False)
|
engine = create_engine(FLAGS.sql_connection, echo=False)
|
||||||
for model in models:
|
for model in models:
|
||||||
model.metadata.create_all(engine)
|
model.metadata.create_all(engine)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Tests For Compute
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
import mox
|
import mox
|
||||||
|
|
||||||
@@ -1063,6 +1064,60 @@ class ComputeTestCase(BaseTestCase):
|
|||||||
self.assertEqual(len(instances), 1)
|
self.assertEqual(len(instances), 1)
|
||||||
self.assertEqual(power_state.NOSTATE, instances[0]['power_state'])
|
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):
|
class ComputeAPITestCase(BaseTestCase):
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from nova import test
|
|||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import flags
|
from nova import flags
|
||||||
|
from nova import utils
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
@@ -192,3 +193,74 @@ class DbApiTestCase(test.TestCase):
|
|||||||
# Retrieve the metadata to ensure it was successfully updated
|
# Retrieve the metadata to ensure it was successfully updated
|
||||||
instance_meta = db.instance_metadata_get(ctxt, instance.id)
|
instance_meta = db.instance_metadata_get(ctxt, instance.id)
|
||||||
self.assertEqual('bar', instance_meta['host'])
|
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