diff --git a/smaug/db/api.py b/smaug/db/api.py index eea77848..8f383a23 100644 --- a/smaug/db/api.py +++ b/smaug/db/api.py @@ -122,3 +122,62 @@ def service_update(context, service_id, values): def get_by_id(context, model, id, *args, **kwargs): return IMPL.get_by_id(context, model, id, *args, **kwargs) + + +################### + + +def scheduled_operation_log_get(context, log_id): + """Get a scheduled operation log by its id. + + :param context: The security context + :param log_id: Log_id of the scheduled operation log + + :returns: Dictionary-like object containing properties of the scheduled + operation log + + Raises ScheduledOperationLogNotFound if scheduled operation log with + the given ID doesn't exist. + """ + return IMPL.scheduled_operation_log_get(context, log_id) + + +def scheduled_operation_log_create(context, values): + """Create a scheduled operation log from the values dictionary. + + :param context: The security context + :param values: Dictionary containing scheduled operation log properties + + :returns: Dictionary-like object containing the properties of the created + scheduled operation log + """ + return IMPL.scheduled_operation_log_create(context, values) + + +def scheduled_operation_log_update(context, log_id, values): + """Set the given properties on a scheduled operation log and update it. + + :param context: The security context + :param log_id: Log_id of the scheduled operation log + :param values: Dictionary containing scheduled operation log properties + to be updated + + :returns: Dictionary-like object containing the properties of the updated + scheduled operation log + + Raises ScheduledOperationLogNotFound if scheduled operation log with + the given ID doesn't exist. + """ + return IMPL.scheduled_operation_log_update(context, log_id, values) + + +def scheduled_operation_log_delete(context, log_id): + """Delete a scheduled operation log from the database. + + :param context: The security context + :param log_id: Log_id of the scheduled operation log + + Raises ScheduledOperationLogNotFound if scheduled operation log with + the given ID doesn't exist. + """ + return IMPL.scheduled_operation_log_delete(context, log_id) diff --git a/smaug/db/sqlalchemy/api.py b/smaug/db/sqlalchemy/api.py index acccc711..ea5f8beb 100644 --- a/smaug/db/sqlalchemy/api.py +++ b/smaug/db/sqlalchemy/api.py @@ -12,7 +12,6 @@ """Implementation of SQLAlchemy backend.""" - import functools import re import sys @@ -20,6 +19,7 @@ import threading import time from oslo_config import cfg +from oslo_db import api as oslo_db_api from oslo_db import exception as db_exc from oslo_db import options from oslo_db.sqlalchemy import session as db_session @@ -326,3 +326,50 @@ def get_by_id(context, model, id, *args, **kwargs): _GET_METHODS[model] = _get_get_method(model) return _GET_METHODS[model](context, id, *args, **kwargs) + + +################### + + +def scheduled_operation_log_get(context, log_id): + return _scheduled_operation_log_get(context, log_id) + + +def _scheduled_operation_log_get(context, log_id, session=None): + result = model_query(context, models.ScheduledOperationLog, + session=session).filter_by(id=log_id).first() + + if not result: + raise exception.ScheduledOperationLogNotFound(log_id=log_id) + + return result + + +def scheduled_operation_log_create(context, values): + log_ref = models.ScheduledOperationLog() + log_ref.update(values) + log_ref.save(get_session()) + return log_ref + + +@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True) +def scheduled_operation_log_update(context, log_id, values): + """Update the ScheduledOperationLog record with the most recent data.""" + + session = get_session() + with session.begin(): + log_ref = _scheduled_operation_log_get(context, log_id, + session=session) + log_ref.update(values) + log_ref.save(session) + return log_ref + + +def scheduled_operation_log_delete(context, log_id): + """Delete a ScheduledOperationLog record.""" + + session = get_session() + with session.begin(): + log_ref = _scheduled_operation_log_get(context, log_id, + session=session) + log_ref.delete(session=session) diff --git a/smaug/db/sqlalchemy/migrate_repo/versions/001_smaug_init.py b/smaug/db/sqlalchemy/migrate_repo/versions/001_smaug_init.py index 8daeeec7..f9b41174 100644 --- a/smaug/db/sqlalchemy/migrate_repo/versions/001_smaug_init.py +++ b/smaug/db/sqlalchemy/migrate_repo/versions/001_smaug_init.py @@ -109,12 +109,35 @@ def define_tables(meta): Column('deleted', Boolean), mysql_engine='InnoDB' ) + + scheduled_operation_logs = Table( + 'scheduled_operation_logs', + meta, + Column('created_at', DateTime), + Column('updated_at', DateTime), + Column('deleted_at', DateTime), + Column('deleted', Boolean), + Column('id', Integer, primary_key=True, nullable=False, + autoincrement=True), + Column('operation_id', String(length=36), + ForeignKey('scheduled_operations.id', ondelete='CASCADE'), + nullable=False), + Column('expect_start_time', DateTime), + Column('triggered_time', DateTime), + Column('actual_start_time', DateTime), + Column('end_time', DateTime), + Column('state', String(length=32), nullable=False), + Column('extend_info', Text), + mysql_engine='InnoDB' + ) + return [services, plans, resources, restores, triggers, - scheduled_operations] + scheduled_operations, + scheduled_operation_logs] def upgrade(migrate_engine): diff --git a/smaug/db/sqlalchemy/models.py b/smaug/db/sqlalchemy/models.py index 4c5ebf7e..01fcc5aa 100644 --- a/smaug/db/sqlalchemy/models.py +++ b/smaug/db/sqlalchemy/models.py @@ -16,10 +16,9 @@ SQLAlchemy models for smaug data. from oslo_config import cfg from oslo_db.sqlalchemy import models from oslo_utils import timeutils -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, Text from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import DateTime, Boolean - +from sqlalchemy import DateTime, Boolean, Index CONF = cfg.CONF BASE = declarative_base() @@ -62,6 +61,29 @@ class Service(BASE, SmaugBase): rpc_available_version = Column(String(36)) +class ScheduledOperationLog(BASE, SmaugBase): + """Represents a scheduled operation log.""" + + __tablename__ = 'scheduled_operation_logs' + __table_args__ = ( + Index('operation_id_idx', 'operation_id'), + ) + + id = Column(Integer, primary_key=True, nullable=False, autoincrement=True) + # TODO(chenzeng):add foreign key after scheduled_operations is defined. + # operation_id = Column(String(36), + # ForeignKey('scheduled_operations.id', + # ondelete='CASCADE'), + # nullable=False) + operation_id = Column(String(36), nullable=False) + expect_start_time = Column(DateTime) + triggered_time = Column(DateTime) + actual_start_time = Column(DateTime) + end_time = Column(DateTime) + state = Column(String(32), nullable=False) + extend_info = Column(Text) + + def register_models(): """Register Models and create metadata. diff --git a/smaug/exception.py b/smaug/exception.py index 0877651e..75758f4d 100644 --- a/smaug/exception.py +++ b/smaug/exception.py @@ -180,5 +180,9 @@ class HostBinaryNotFound(NotFound): message = _("Could not find binary %(binary)s on host %(host)s.") +class ScheduledOperationLogNotFound(NotFound): + message = _("Scheduled Operation Log %(log_id)s could not be found.") + + OrphanedObjectError = obj_exc.OrphanedObjectError ObjectActionError = obj_exc.ObjectActionError diff --git a/smaug/tests/unit/db/test_models.py b/smaug/tests/unit/db/test_models.py index d78de4b4..857c7d78 100644 --- a/smaug/tests/unit/db/test_models.py +++ b/smaug/tests/unit/db/test_models.py @@ -89,3 +89,58 @@ class ServicesDbTestCase(base.TestCase): 'topictest5') self.assertEqual(service_ref['host'], 'hosttest5') self.assertEqual(service_get_ref['host'], 'hosttest5') + + +class ScheduledOperationLogTestCase(base.TestCase): + """Test cases for scheduled_operation_logs table.""" + + def setUp(self): + super(ScheduledOperationLogTestCase, self).setUp() + self.ctxt = context.get_admin_context() + + def _create_scheduled_operation_log(self): + values = { + 'operation_id': '0354ca9ddcd046b693340d78759fd274', + 'state': 'in_progress', + } + return db.scheduled_operation_log_create(self.ctxt, values) + + def test_scheduled_operation_log_create(self): + log_ref = self._create_scheduled_operation_log() + self.assertEqual('in_progress', log_ref['state']) + + def test_scheduled_operation_log_delete(self): + log_ref = self._create_scheduled_operation_log() + db.scheduled_operation_log_delete(self.ctxt, log_ref['id']) + + self.assertRaises(exception.ScheduledOperationLogNotFound, + db.scheduled_operation_log_delete, + self.ctxt, log_ref['id']) + + self.assertRaises(exception.ScheduledOperationLogNotFound, + db.scheduled_operation_log_get, + self.ctxt, log_ref['id']) + + self.assertRaises(exception.ScheduledOperationLogNotFound, + db.scheduled_operation_log_delete, + self.ctxt, 100) + + def test_scheduled_operation_log_update(self): + log_ref = self._create_scheduled_operation_log() + log_id = log_ref['id'] + log_ref = db.scheduled_operation_log_update(self.ctxt, + log_id, + {"state": "success"}) + self.assertEqual('success', log_ref['state']) + + log_ref = db.scheduled_operation_log_get(self.ctxt, log_id) + self.assertEqual('success', log_ref['state']) + + self.assertRaises(exception.ScheduledOperationLogNotFound, + db.scheduled_operation_log_update, + self.ctxt, 100, {"state": "success"}) + + def test_scheduled_operation_log_get(self): + log_ref = self._create_scheduled_operation_log() + log_ref = db.scheduled_operation_log_get(self.ctxt, log_ref['id']) + self.assertEqual('in_progress', log_ref['state'])