summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/conductor/api.py12
-rw-r--r--nova/conductor/manager.py8
-rw-r--r--nova/conductor/rpcapi.py9
-rw-r--r--nova/db/api.py41
-rw-r--r--nova/db/sqlalchemy/api.py119
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/148_add_instance_actions.py101
-rw-r--r--nova/db/sqlalchemy/models.py29
-rw-r--r--nova/exception.py9
-rw-r--r--nova/tests/conductor/test_conductor.py12
-rw-r--r--nova/tests/test_db_api.py219
10 files changed, 558 insertions, 1 deletions
diff --git a/nova/conductor/api.py b/nova/conductor/api.py
index a95332f08..87e75f274 100644
--- a/nova/conductor/api.py
+++ b/nova/conductor/api.py
@@ -237,6 +237,12 @@ class LocalAPI(object):
def service_get_all_compute_by_host(self, context, host):
return self._manager.service_get_all_by(context, 'compute', host)
+ def action_event_start(self, context, values):
+ return self._manager.action_event_start(context, values)
+
+ def action_event_finish(self, context, values):
+ return self._manager.action_event_finish(context, values)
+
class API(object):
"""Conductor API that does updates via RPC to the ConductorManager"""
@@ -428,3 +434,9 @@ class API(object):
def service_get_all_compute_by_host(self, context, host):
return self.conductor_rpcapi.service_get_all_by(context, 'compute',
host)
+
+ def action_event_start(self, context, values):
+ return self.conductor_rpcapi.action_event_start(context, values)
+
+ def action_event_finish(self, context, values):
+ return self.conductor_rpcapi.action_event_finish(context, values)
diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py
index 9a1a62712..96443c834 100644
--- a/nova/conductor/manager.py
+++ b/nova/conductor/manager.py
@@ -43,7 +43,7 @@ datetime_fields = ['launched_at', 'terminated_at']
class ConductorManager(manager.SchedulerDependentManager):
"""Mission: TBD"""
- RPC_API_VERSION = '1.24'
+ RPC_API_VERSION = '1.25'
def __init__(self, *args, **kwargs):
super(ConductorManager, self).__init__(service_name='conductor',
@@ -260,3 +260,9 @@ class ConductorManager(manager.SchedulerDependentManager):
result = self.db.service_get_all_by_host(context, host)
return jsonutils.to_primitive(result)
+
+ def action_event_start(self, context, values):
+ return self.db.action_event_start(context, values)
+
+ def action_event_finish(self, context, values):
+ return self.db.action_event_finish(context, values)
diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py
index c7143ade9..f11208e2f 100644
--- a/nova/conductor/rpcapi.py
+++ b/nova/conductor/rpcapi.py
@@ -57,6 +57,7 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
1.23 - Added instance_get_all
Un-Deprecate instance_get_all_by_host
1.24 - Added instance_get
+ 1.25 - Added action_event_start and action_event_finish
"""
BASE_RPC_API_VERSION = '1.0'
@@ -261,3 +262,11 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
def instance_get_all_by_host(self, context, host):
msg = self.make_msg('instance_get_all_by_host', host=host)
return self.call(context, msg, version='1.23')
+
+ def action_event_start(self, context, values):
+ msg = self.make_msg('action_event_start', values=values)
+ return self.call(context, msg, version='1.25')
+
+ def action_event_finish(self, context, values):
+ msg = self.make_msg('action_event_finish', values=values)
+ return self.call(context, msg, version='1.25')
diff --git a/nova/db/api.py b/nova/db/api.py
index 7f202862e..b9f188a45 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1621,6 +1621,47 @@ def instance_fault_get_by_instance_uuids(context, instance_uuids):
####################
+def action_start(context, values):
+ """Start an action for an instance"""
+ return IMPL.action_start(context, values)
+
+
+def action_finish(context, values):
+ """Finish an action for an instance"""
+ return IMPL.action_finish(context, values)
+
+
+def actions_get(context, uuid):
+ """Get all instance actions for the provided instance"""
+ return IMPL.actions_get(context, uuid)
+
+
+def action_get_by_id(context, uuid, action_id):
+ """Get the action by id and given instance"""
+ return IMPL.action_get_by_id(context, uuid, action_id)
+
+
+def action_event_start(context, values):
+ """Start an event on an instance action"""
+ return IMPL.action_event_start(context, values)
+
+
+def action_event_finish(context, values):
+ """Finish an event on an instance action"""
+ return IMPL.action_event_finish(context, values)
+
+
+def action_events_get(context, action_id):
+ return IMPL.action_events_get(context, action_id)
+
+
+def action_event_get_by_id(context, action_id, event_id):
+ return IMPL.action_event_get_by_id(context, action_id, event_id)
+
+
+####################
+
+
def get_ec2_instance_id_by_uuid(context, instance_id):
"""Get ec2 id through uuid from instance_id_mappings table"""
return IMPL.get_ec2_instance_id_by_uuid(context, instance_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 66ecc8bf6..8687ab87b 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -4560,6 +4560,125 @@ def instance_fault_get_by_instance_uuids(context, instance_uuids):
##################
+def action_start(context, values):
+ action_ref = models.InstanceAction()
+ action_ref.update(values)
+ action_ref.save()
+ return action_ref
+
+
+def action_finish(context, values):
+ session = get_session()
+ with session.begin():
+ action_ref = model_query(context, models.InstanceAction,
+ session=session).\
+ filter_by(instance_uuid=values['instance_uuid']).\
+ filter_by(request_id=values['request_id']).\
+ first()
+
+ if not action_ref:
+ raise exception.InstanceActionNotFound(
+ request_id=values['request_id'],
+ instance_uuid=values['instance_uuid'])
+
+ action_ref.update(values)
+ return action_ref
+
+
+def actions_get(context, instance_uuid):
+ """Get all instance actions for the provided uuid."""
+ actions = model_query(context, models.InstanceAction).\
+ filter_by(instance_uuid=instance_uuid).\
+ order_by(desc("created_at")).\
+ all()
+ return actions
+
+
+def action_get_by_id(context, instance_uuid, action_id):
+ """Get the action by id and given instance"""
+ action = model_query(context, models.InstanceAction).\
+ filter_by(instance_uuid=instance_uuid).\
+ filter_by(id=action_id).\
+ first()
+
+ return action
+
+
+def _action_get_by_request_id(context, instance_uuid, request_id,
+ session=None):
+ result = model_query(context, models.InstanceAction, session=session).\
+ filter_by(instance_uuid=instance_uuid).\
+ filter_by(request_id=request_id).\
+ first()
+ return result
+
+
+def action_event_start(context, values):
+ """Start an event on an instance action"""
+ session = get_session()
+ with session.begin():
+ action = _action_get_by_request_id(context, values['instance_uuid'],
+ values['request_id'], session)
+
+ if not action:
+ raise exception.InstanceActionNotFound(
+ request_id=values['request_id'],
+ instance_uuid=values['instance_uuid'])
+
+ values['action_id'] = action['id']
+
+ event_ref = models.InstanceActionEvent()
+ event_ref.update(values)
+ event_ref.save(session=session)
+ return event_ref
+
+
+def action_event_finish(context, values):
+ """Finish an event on an instance action"""
+ session = get_session()
+ with session.begin():
+ action = _action_get_by_request_id(context, values['instance_uuid'],
+ values['request_id'], session)
+
+ if not action:
+ raise exception.InstanceActionNotFound(
+ request_id=values['request_id'],
+ instance_uuid=values['instance_uuid'])
+
+ event_ref = model_query(context, models.InstanceActionEvent,
+ session=session).\
+ filter_by(action_id=action['id']).\
+ filter_by(event=values['event']).\
+ first()
+
+ if not event_ref:
+ raise exception.InstanceActionEventNotFound(action_id=action['id'],
+ event=values['event'])
+ event_ref.update(values)
+ return event_ref
+
+
+def action_events_get(context, action_id):
+ events = model_query(context, models.InstanceActionEvent).\
+ filter_by(action_id=action_id).\
+ order_by(desc("created_at")).\
+ all()
+
+ return events
+
+
+def action_event_get_by_id(context, action_id, event_id):
+ event = model_query(context, models.InstanceActionEvent).\
+ filter_by(action_id=action_id).\
+ filter_by(id=event_id).\
+ first()
+
+ return event
+
+
+##################
+
+
@require_context
def ec2_instance_create(context, instance_uuid, id=None):
"""Create ec2 compatable instance by provided uuid"""
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/148_add_instance_actions.py b/nova/db/sqlalchemy/migrate_repo/versions/148_add_instance_actions.py
new file mode 100644
index 000000000..6adfb1dc1
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/148_add_instance_actions.py
@@ -0,0 +1,101 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC.
+#
+# 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
+from sqlalchemy import Column
+from sqlalchemy import DateTime
+from sqlalchemy import ForeignKey
+from sqlalchemy import Index
+from sqlalchemy import Integer
+from sqlalchemy import MetaData
+from sqlalchemy import String
+from sqlalchemy import Table
+from sqlalchemy import Text
+
+from nova.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ instance_actions = Table('instance_actions', meta,
+ Column('created_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('deleted_at', DateTime),
+ Column('deleted', Boolean),
+ Column('id', Integer, primary_key=True, nullable=False),
+ Column('action', String(length=255)),
+ Column('instance_uuid', String(length=36)),
+ Column('request_id', String(length=255)),
+ Column('user_id', String(length=255)),
+ Column('project_id', String(length=255)),
+ Column('start_time', DateTime),
+ Column('finish_time', DateTime),
+ Column('message', String(length=255)),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8',
+ )
+
+ instance_actions_events = Table('instance_actions_events', meta,
+ Column('created_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('deleted_at', DateTime),
+ Column('deleted', Boolean),
+ Column('id', Integer, primary_key=True, nullable=False),
+ Column('event', String(length=255)),
+ Column('action_id', Integer, ForeignKey('instance_actions.id')),
+ Column('start_time', DateTime),
+ Column('finish_time', DateTime),
+ Column('result', String(length=255)),
+ Column('traceback', Text),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8',
+ )
+
+ try:
+ instance_actions.create()
+ instance_actions_events.create()
+ except Exception:
+ LOG.exception("Exception while creating table 'instance_actions' or "
+ "'instance_actions_events'")
+ meta.drop_all(tables=[instance_actions, instance_actions_events])
+ raise
+
+ Index('instance_uuid_idx',
+ instance_actions.c.instance_uuid).create(migrate_engine)
+ Index('request_id_idx',
+ instance_actions.c.request_id).create(migrate_engine)
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ try:
+ instance_actions = Table('instance_actions', meta, autoload=True)
+ instance_actions.drop()
+ except Exception:
+ LOG.exception("Exception dropping table 'instance_actions'")
+
+ try:
+ instance_actions_events = Table('instance_actions_events', meta,
+ autoload=True)
+ instance_actions_events.drop()
+ except Exception:
+ LOG.exception("Exception dropping table 'instance_actions_events")
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 2d3e23c26..01251cd42 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -984,6 +984,35 @@ class InstanceFault(BASE, NovaBase):
details = Column(Text)
+class InstanceAction(BASE, NovaBase):
+ """Track client actions on an instance"""
+ __tablename__ = 'instance_actions'
+ id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
+ action = Column(String(255))
+ instance_uuid = Column(String(36),
+ ForeignKey('instances.uuid'),
+ nullable=False)
+ request_id = Column(String(255))
+ user_id = Column(String(255))
+ project_id = Column(String(255))
+ start_time = Column(DateTime, default=timeutils.utcnow)
+ finish_time = Column(DateTime)
+ message = Column(String(255))
+
+
+class InstanceActionEvent(BASE, NovaBase):
+ """Track events that occur during an InstanceAction"""
+ __tablename__ = 'instance_actions_events'
+ id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
+ event = Column(String(255))
+ action_id = Column(Integer, ForeignKey('instance_actions.id'),
+ nullable=False)
+ start_time = Column(DateTime, default=timeutils.utcnow)
+ finish_time = Column(DateTime)
+ result = Column(String(255))
+ traceback = Column(Text)
+
+
class InstanceIdMapping(BASE, NovaBase):
"""Compatibility layer for the EC2 instance service"""
__tablename__ = 'instance_id_mappings'
diff --git a/nova/exception.py b/nova/exception.py
index 9507a0088..7ec23d32d 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -1074,6 +1074,15 @@ class UnexpectedTaskStateError(NovaException):
"the actual state is %(actual)s")
+class InstanceActionNotFound(NovaException):
+ message = _("Action for request_id %(request_id)s on instance"
+ " %(instance_uuid)s not found")
+
+
+class InstanceActionEventNotFound(NovaException):
+ message = _("Event %(event)s not found for action id %(action_id)s")
+
+
class CryptoCAFileNotFound(FileNotFound):
message = _("The CA file for %(project)s could not be found")
diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py
index fd87e420b..6b579e131 100644
--- a/nova/tests/conductor/test_conductor.py
+++ b/nova/tests/conductor/test_conductor.py
@@ -86,6 +86,18 @@ class _BaseTestCase(object):
self.assertEqual(instance['vm_state'], vm_states.STOPPED)
self.assertEqual(new_inst['vm_state'], instance['vm_state'])
+ def test_action_event_start(self):
+ self.mox.StubOutWithMock(db, 'action_event_start')
+ db.action_event_start(self.context, mox.IgnoreArg())
+ self.mox.ReplayAll()
+ self.conductor.action_event_start(self.context, {})
+
+ def test_action_event_finish(self):
+ self.mox.StubOutWithMock(db, 'action_event_finish')
+ db.action_event_finish(self.context, mox.IgnoreArg())
+ self.mox.ReplayAll()
+ self.conductor.action_event_finish(self.context, {})
+
def test_instance_update_invalid_key(self):
# NOTE(danms): the real DB API call ignores invalid keys
if self.db == None:
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index 0aaa42a11..3ea853f0e 100644
--- a/nova/tests/test_db_api.py
+++ b/nova/tests/test_db_api.py
@@ -539,6 +539,225 @@ class DbApiTestCase(test.TestCase):
expected = {uuids[0]: [], uuids[1]: []}
self.assertEqual(expected, instance_faults)
+ def test_instance_action_start(self):
+ """Create an instance action"""
+ ctxt = context.get_admin_context()
+ uuid = str(stdlib_uuid.uuid4())
+
+ start_time = timeutils.utcnow()
+ action_values = {'action': 'run_instance',
+ 'instance_uuid': uuid,
+ 'request_id': ctxt.request_id,
+ 'user_id': ctxt.user_id,
+ 'project_id': ctxt.project_id,
+ 'start_time': start_time}
+ db.action_start(ctxt, action_values)
+
+ # Retrieve the action to ensure it was successfully added
+ actions = db.actions_get(ctxt, uuid)
+ self.assertEqual(1, len(actions))
+ self.assertEqual('run_instance', actions[0]['action'])
+ self.assertEqual(start_time, actions[0]['start_time'])
+ self.assertEqual(ctxt.request_id, actions[0]['request_id'])
+ self.assertEqual(ctxt.user_id, actions[0]['user_id'])
+ self.assertEqual(ctxt.project_id, actions[0]['project_id'])
+
+ def test_instance_action_finish(self):
+ """Create an instance action"""
+ ctxt = context.get_admin_context()
+ uuid = str(stdlib_uuid.uuid4())
+
+ start_time = timeutils.utcnow()
+ action_start_values = {'action': 'run_instance',
+ 'instance_uuid': uuid,
+ 'request_id': ctxt.request_id,
+ 'user_id': ctxt.user_id,
+ 'project_id': ctxt.project_id,
+ 'start_time': start_time}
+ db.action_start(ctxt, action_start_values)
+
+ finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5)
+ action_finish_values = {'instance_uuid': uuid,
+ 'request_id': ctxt.request_id,
+ 'finish_time': finish_time}
+ db.action_finish(ctxt, action_finish_values)
+
+ # Retrieve the action to ensure it was successfully added
+ actions = db.actions_get(ctxt, uuid)
+ self.assertEqual(1, len(actions))
+ self.assertEqual('run_instance', actions[0]['action'])
+ self.assertEqual(start_time, actions[0]['start_time'])
+ self.assertEqual(finish_time, actions[0]['finish_time'])
+ self.assertEqual(ctxt.request_id, actions[0]['request_id'])
+ self.assertEqual(ctxt.user_id, actions[0]['user_id'])
+ self.assertEqual(ctxt.project_id, actions[0]['project_id'])
+
+ def test_instance_actions_get_by_instance(self):
+ """Ensure we can get actions by UUID"""
+ ctxt1 = context.get_admin_context()
+ ctxt2 = context.get_admin_context()
+ uuid1 = str(stdlib_uuid.uuid4())
+ uuid2 = str(stdlib_uuid.uuid4())
+
+ action_values = {'action': 'run_instance',
+ 'instance_uuid': uuid1,
+ 'request_id': ctxt1.request_id,
+ 'user_id': ctxt1.user_id,
+ 'project_id': ctxt1.project_id,
+ 'start_time': timeutils.utcnow()}
+ db.action_start(ctxt1, action_values)
+ action_values['action'] = 'resize'
+ db.action_start(ctxt1, action_values)
+
+ action_values = {'action': 'reboot',
+ 'instance_uuid': uuid2,
+ 'request_id': ctxt2.request_id,
+ 'user_id': ctxt2.user_id,
+ 'project_id': ctxt2.project_id,
+ 'start_time': timeutils.utcnow()}
+ db.action_start(ctxt2, action_values)
+ db.action_start(ctxt2, action_values)
+
+ # Retrieve the action to ensure it was successfully added
+ actions = db.actions_get(ctxt1, uuid1)
+ self.assertEqual(2, len(actions))
+ self.assertEqual('resize', actions[0]['action'])
+ self.assertEqual('run_instance', actions[1]['action'])
+
+ def test_instance_action_get_by_instance_and_action(self):
+ """Ensure we can get an action by instance UUID and action id"""
+ ctxt1 = context.get_admin_context()
+ ctxt2 = context.get_admin_context()
+ uuid1 = str(stdlib_uuid.uuid4())
+ uuid2 = str(stdlib_uuid.uuid4())
+
+ action_values = {'action': 'run_instance',
+ 'instance_uuid': uuid1,
+ 'request_id': ctxt1.request_id,
+ 'user_id': ctxt1.user_id,
+ 'project_id': ctxt1.project_id,
+ 'start_time': timeutils.utcnow()}
+ db.action_start(ctxt1, action_values)
+ action_values['action'] = 'resize'
+ db.action_start(ctxt1, action_values)
+
+ action_values = {'action': 'reboot',
+ 'instance_uuid': uuid2,
+ 'request_id': ctxt2.request_id,
+ 'user_id': ctxt2.user_id,
+ 'project_id': ctxt2.project_id,
+ 'start_time': timeutils.utcnow()}
+ db.action_start(ctxt2, action_values)
+ db.action_start(ctxt2, action_values)
+
+ actions = db.actions_get(ctxt1, uuid1)
+ action_id = actions[0]['id']
+ action = db.action_get_by_id(ctxt1, uuid1, action_id)
+ self.assertEqual('resize', action['action'])
+ self.assertEqual(ctxt1.request_id, action['request_id'])
+
+ def test_instance_action_event_start(self):
+ """Create an instance action event"""
+ ctxt = context.get_admin_context()
+ uuid = str(stdlib_uuid.uuid4())
+
+ start_time = timeutils.utcnow()
+ action_values = {'action': 'run_instance',
+ 'instance_uuid': uuid,
+ 'request_id': ctxt.request_id,
+ 'user_id': ctxt.user_id,
+ 'project_id': ctxt.project_id,
+ 'start_time': start_time}
+ action = db.action_start(ctxt, action_values)
+
+ event_values = {'event': 'schedule',
+ 'instance_uuid': uuid,
+ 'request_id': ctxt.request_id,
+ 'start_time': start_time}
+ db.action_event_start(ctxt, event_values)
+
+ # Retrieve the event to ensure it was successfully added
+ events = db.action_events_get(ctxt, action['id'])
+ self.assertEqual(1, len(events))
+ self.assertEqual('schedule', events[0]['event'])
+ self.assertEqual(start_time, events[0]['start_time'])
+
+ def test_instance_action_event_finish(self):
+ """Finish an instance action event"""
+ ctxt = context.get_admin_context()
+ uuid = str(stdlib_uuid.uuid4())
+
+ start_time = timeutils.utcnow()
+ action_values = {'action': 'run_instance',
+ 'instance_uuid': uuid,
+ 'request_id': ctxt.request_id,
+ 'user_id': ctxt.user_id,
+ 'project_id': ctxt.project_id,
+ 'start_time': start_time}
+ action = db.action_start(ctxt, action_values)
+
+ event_values = {'event': 'schedule',
+ 'request_id': ctxt.request_id,
+ 'instance_uuid': uuid,
+ 'start_time': start_time}
+ db.action_event_start(ctxt, event_values)
+
+ finish_time = timeutils.utcnow() + datetime.timedelta(seconds=5)
+ event_finish_values = {'event': 'schedule',
+ 'request_id': ctxt.request_id,
+ 'instance_uuid': uuid,
+ 'finish_time': finish_time}
+ db.action_event_finish(ctxt, event_finish_values)
+
+ # Retrieve the event to ensure it was successfully added
+ events = db.action_events_get(ctxt, action['id'])
+ self.assertEqual(1, len(events))
+ self.assertEqual('schedule', events[0]['event'])
+ self.assertEqual(start_time, events[0]['start_time'])
+ self.assertEqual(finish_time, events[0]['finish_time'])
+
+ def test_instance_action_event_get_by_id(self):
+ """Get a specific instance action event"""
+ ctxt1 = context.get_admin_context()
+ ctxt2 = context.get_admin_context()
+ uuid1 = str(stdlib_uuid.uuid4())
+ uuid2 = str(stdlib_uuid.uuid4())
+
+ action_values = {'action': 'run_instance',
+ 'instance_uuid': uuid1,
+ 'request_id': ctxt1.request_id,
+ 'user_id': ctxt1.user_id,
+ 'project_id': ctxt1.project_id,
+ 'start_time': timeutils.utcnow()}
+ added_action = db.action_start(ctxt1, action_values)
+
+ action_values = {'action': 'reboot',
+ 'instance_uuid': uuid2,
+ 'request_id': ctxt2.request_id,
+ 'user_id': ctxt2.user_id,
+ 'project_id': ctxt2.project_id,
+ 'start_time': timeutils.utcnow()}
+ db.action_start(ctxt2, action_values)
+
+ start_time = timeutils.utcnow()
+ event_values = {'event': 'schedule',
+ 'instance_uuid': uuid1,
+ 'request_id': ctxt1.request_id,
+ 'start_time': start_time}
+ added_event = db.action_event_start(ctxt1, event_values)
+
+ event_values = {'event': 'reboot',
+ 'instance_uuid': uuid2,
+ 'request_id': ctxt2.request_id,
+ 'start_time': timeutils.utcnow()}
+ db.action_event_start(ctxt2, event_values)
+
+ # Retrieve the event to ensure it was successfully added
+ event = db.action_event_get_by_id(ctxt1, added_action['id'],
+ added_event['id'])
+ self.assertEqual('schedule', event['event'])
+ self.assertEqual(start_time, event['start_time'])
+
def test_dns_registration(self):
domain1 = 'test.domain.one'
domain2 = 'test.domain.two'