Add fault object

add fault object ,it is a optional object_field of instance.
if error occured, we can save related message into fault.

Change-Id: I6fb5cde7e5752683facf6ce7375c354a19cfd81a
This commit is contained in:
Zhong Luyao 2017-01-17 16:51:39 +08:00
parent c105c98151
commit 4875b5676e
8 changed files with 198 additions and 11 deletions

View File

@ -115,3 +115,11 @@ class Connection(object):
This creates or updates a nic db entry.
"""
# Instances Faults
@abc.abstractmethod
def instance_fault_create(self, context, values):
"""Create a new Instance Fault."""
@abc.abstractmethod
def instance_fault_get_by_instance_uuids(self, context, instance_uuids):
"""Get all instance faults for the provided instance_uuids."""

View File

@ -94,7 +94,6 @@ def upgrade():
mysql_ENGINE='InnoDB',
mysql_DEFAULT_CHARSET='UTF8'
)
op.create_table(
'instance_nics',
sa.Column('created_at', sa.DateTime(), nullable=True),
@ -111,3 +110,17 @@ def upgrade():
mysql_ENGINE='InnoDB',
mysql_DEFAULT_CHARSET='UTF8'
)
op.create_table(
'instance_faults',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('instance_uuid', sa.String(length=36), nullable=True),
sa.Column('code', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('message', sa.String(length=255), nullable=True),
sa.Column('detail', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['instance_uuid'], ['instances.uuid']),
sa.PrimaryKeyConstraint('id'),
mysql_ENGINE='InnoDB',
mysql_DEFAULT_CHARSET='UTF8'
)

View File

@ -24,6 +24,7 @@ from oslo_utils import strutils
from oslo_utils import uuidutils
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import desc
from mogan.common import exception
from mogan.common.i18n import _
@ -315,6 +316,36 @@ class Connection(api.Connection):
return model_query(context, models.InstanceNic).filter_by(
instance_uuid=instance_uuid).all()
def instance_fault_create(context, values):
"""Create a new InstanceFault."""
fault = models.InstanceFault()
fault.update(values)
with _session_for_write() as session:
session.add(fault)
session.flush()
return fault
def instance_fault_get_by_instance_uuids(context, instance_uuids):
"""Get all instance faults for the provided instance_uuids."""
if not instance_uuids:
return {}
rows = model_query(context, models.InstanceFault).\
filter(models.InstanceFault.instance_uuid.in_(instance_uuids)).\
order_by(desc("created_at"), desc("id")).all()
output = {}
for instance_uuid in instance_uuids:
output[instance_uuid] = []
for row in rows:
data = dict(row)
output[row['instance_uuid']].append(data)
return output
def _type_get_id_from_type_query(context, type_id):
return model_query(context, models.InstanceTypes). \

View File

@ -21,9 +21,10 @@ from oslo_db import options as db_options
from oslo_db.sqlalchemy import models
from oslo_db.sqlalchemy import types as db_types
import six.moves.urllib.parse as urlparse
from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey
from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey, Text
from sqlalchemy import orm
from sqlalchemy import schema, String, Integer
from sqlalchemy.dialects.mysql import MEDIUMTEXT
from sqlalchemy.ext.declarative import declarative_base
from mogan.common import paths
@ -35,6 +36,10 @@ _DEFAULT_SQL_CONNECTION = 'sqlite:///' + paths.state_path_def('mogan.sqlite')
db_options.set_defaults(CONF, _DEFAULT_SQL_CONNECTION, 'mogan.sqlite')
def MediumText():
return Text().with_variant(MEDIUMTEXT(), 'mysql')
def table_args():
engine_name = urlparse.urlparse(CONF.database.connection).scheme
if engine_name == 'mysql':
@ -163,3 +168,21 @@ class InstanceTypeExtraSpecs(Base):
foreign_keys=instance_type_uuid,
primaryjoin='InstanceTypeExtraSpecs.instance_type_uuid '
'== InstanceTypes.uuid')
class InstanceFault(Base):
"""Represents fault info for instance"""
__tablename__ = "instance_faults"
id = Column(Integer, primary_key=True, nullable=False)
instance_uuid = Column(String(36),
ForeignKey('instances.uuid'))
code = Column(Integer(), nullable=False)
message = Column(String(255))
detail = Column(MediumText())
instance = orm.relationship(
Instance,
backref=orm.backref('instance_faults', uselist=False),
foreign_keys=instance_uuid,
primaryjoin='Instance.uuid == InstanceFault.instance_uuid')

View File

@ -28,3 +28,4 @@ def register_all():
__import__('mogan.objects.instance_type')
__import__('mogan.objects.instance')
__import__('mogan.objects.instance_nics')
__import__('mogan.objects.instance_fault')

View File

@ -24,7 +24,7 @@ from mogan import objects
from mogan.objects import base
from mogan.objects import fields as object_fields
OPTIONAL_ATTRS = ['nics', ]
OPTIONAL_ATTRS = ['nics', 'fault', ]
LOG = logging.getLogger(__name__)
@ -80,9 +80,7 @@ class Instance(base.MoganObject, object_base.VersionedObjectDictCompat):
:param db_inst: A DB Instance model of the object
:return: The object of the class with the database entity added
"""
for field in instance.fields:
if field in OPTIONAL_ATTRS:
continue
for field in set(instance.fields) - set(OPTIONAL_ATTRS):
instance[field] = db_inst[field]
if expected_attrs is None:
@ -91,6 +89,8 @@ class Instance(base.MoganObject, object_base.VersionedObjectDictCompat):
instance._load_instance_nics(instance._context, instance.uuid)
else:
instance.nics = None
if 'fault' in expected_attrs:
instance._load_fault(instance._context, instance.uuid)
instance.obj_reset_changes()
return instance
@ -102,9 +102,19 @@ class Instance(base.MoganObject, object_base.VersionedObjectDictCompat):
@staticmethod
def _from_db_object_list(db_objects, cls, context):
"""Converts a list of database entities to a list of formal objects."""
return [Instance._from_db_object(cls(context), obj,
('nics', ))
for obj in db_objects]
instances = []
for obj in db_objects:
expected_attrs = ['nics']
if obj["status"] == "error":
expected_attrs.append("fault")
instances.append(Instance._from_db_object(cls(context),
obj,
expected_attrs))
return instances
def _load_fault(self, context, instance_uuid):
self.fault = objects.InstanceFault.get_latest_for_instance(
context=context, instance_uuid=instance_uuid)
def _save_nics(self, context):
for nic_obj in self.nics or []:
@ -126,9 +136,13 @@ class Instance(base.MoganObject, object_base.VersionedObjectDictCompat):
@classmethod
def get(cls, context, uuid):
"""Find a instance and return a Instance object."""
expected_attrs = ['nics']
db_instance = cls.dbapi.instance_get(context, uuid)
instance = Instance._from_db_object(cls(context), db_instance,
('nics', ))
if db_instance["status"] == "error":
expected_attrs.append("fault")
instance = Instance._from_db_object(cls(context),
db_instance,
expected_attrs)
return instance
def create(self, context=None):

View File

@ -0,0 +1,95 @@
# Copyright 2017 Intel
# 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.
import itertools
from oslo_versionedobjects import base as object_base
from mogan.common import exception
from mogan.db import api as dbapi
from mogan import objects
from mogan.objects import base
from mogan.objects import fields as object_fields
@base.MoganObjectRegistry.register
class InstanceFault(base.MoganObject, object_base.VersionedObjectDictCompat):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = dbapi.get_instance()
fields = {
'id': object_fields.IntegerField(),
'instance_uuid': object_fields.UUIDField(),
'code': object_fields.IntegerField(),
'message': object_fields.StringField(nullable=True),
'detail': object_fields.StringField(nullable=True),
}
@staticmethod
def _from_db_object(context, fault, db_fault):
for key in fault.fields:
fault[key] = db_fault[key]
fault._context = context
fault.obj_reset_changes()
return fault
@classmethod
def get_latest_for_instance(cls, context, instance_uuid):
db_faults = cls.dbapi.instance_fault_get_by_instance_uuids(
[instance_uuid])
if instance_uuid in db_faults and db_faults[instance_uuid]:
return cls._from_db_object(context, cls(),
db_faults[instance_uuid][0])
def create(self):
if self.obj_attr_is_set('id'):
raise exception.ObjectActionError(action='create',
reason='already created')
values = {
'instance_uuid': self.instance_uuid,
'code': self.code,
'message': self.message,
'detail': self.detail,
'created_at': self.created_at,
}
db_fault = self.dbapi.instance_fault_create(self._context, values)
self._from_db_object(self._context, self, db_fault)
self.obj_reset_changes()
@base.MoganObjectRegistry.register
class InstanceFaultList(base.MoganObject,
object_base.VersionedObjectDictCompat):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = dbapi.get_instance()
fields = {
'objects': object_fields.ListOfObjectsField('InstanceFault')
}
@classmethod
def get_by_instance_uuids(cls, context, instance_uuids):
db_faultdict = cls.dbapi.instance_fault_get_by_instance_uuids(
context, instance_uuids)
db_faultlist = itertools.chain(*db_faultdict.values())
return object_base.obj_make_list(context, cls(context),
objects.InstanceFault,
db_faultlist)

View File

@ -383,6 +383,8 @@ class _TestObject(object):
# The fingerprint values should only be changed if there is a version bump.
expected_object_fingerprints = {
'Instance': '1.0-18d0ffc894a0f6b52df73a29919c035b',
'InstanceFault': '1.0-6b5b01b2cc7b6b547837acb168ec6eb9',
'InstanceFaultList': '1.0-43e8aad0258652921f929934e9e048fd',
'InstanceType': '1.0-589b096651fcdb30898ff50f748dd948',
'MyObj': '1.1-aad62eedc5a5cc8bcaf2982c285e753f',
'FakeNode': '1.0-07813a70fee67557d8a71ad96f31cee7',