summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@yahoo.com>2010-09-08 01:53:07 -0700
committerVishvananda Ishaya <vishvananda@yahoo.com>2010-09-08 01:53:07 -0700
commit607162ffe86d7d2b5bd9eb6f16a6ee4405892fc6 (patch)
tree3ee9e512c8e20b29f1cfb0f14757e0d68a6f0607
parent9cb96a14ed6328732ee0dbee08ad9fed5bde43d6 (diff)
make timestamps for instances and volumes, includes additions to get deleted objects from db using deleted flag.
-rw-r--r--nova/compute/manager.py5
-rw-r--r--nova/db/sqlalchemy/api.py42
-rw-r--r--nova/db/sqlalchemy/models.py20
-rw-r--r--nova/tests/compute_unittest.py20
4 files changed, 63 insertions, 24 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 878205a36..7f6b49f90 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -21,6 +21,7 @@ Handles all code relating to instances (guest vms)
"""
import base64
+import datetime
import logging
import os
@@ -83,6 +84,8 @@ class ComputeManager(manager.Manager):
try:
yield self.driver.spawn(instance_ref)
+ now = datetime.datetime.now()
+ self.db.instance_update(None, instance_id, {'launched_at': now})
except Exception: # pylint: disable-msg=W0702
logging.exception("instance %s: Failed to spawn",
instance_ref['name'])
@@ -107,6 +110,8 @@ class ComputeManager(manager.Manager):
power_state.NOSTATE,
'shutting_down')
yield self.driver.destroy(instance_ref)
+ now = datetime.datetime.now()
+ self.db.instance_update(None, instance_id, {'terminated_at': now})
# TODO(ja): should we keep it in a terminated state for a bit?
self.db.instance_destroy(context, instance_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 391892214..fa9c77181 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -28,9 +28,19 @@ from sqlalchemy import or_
FLAGS = flags.FLAGS
+
# NOTE(vish): disabling docstring pylint because the docstrings are
# in the interface definition
# pylint: disable-msg=C0111
+def _deleted(context):
+ """Calcultates whether to include deleted objects based on context.
+
+ Currently just looks for a flag called deleted in the context dict.
+ """
+ if not context:
+ return False
+ return context.get('deleted', False)
+
###################
@@ -236,19 +246,19 @@ def instance_destroy(_context, instance_id):
instance_ref.delete(session=session)
-def instance_get(_context, instance_id):
- return models.Instance.find(instance_id)
+def instance_get(context, instance_id):
+ return models.Instance.find(instance_id, deleted=_deleted(context))
-def instance_get_all(_context):
- return models.Instance.all()
+def instance_get_all(context):
+ return models.Instance.all(deleted=_deleted(context))
-def instance_get_by_project(_context, project_id):
+def instance_get_by_project(context, project_id):
session = get_session()
return session.query(models.Instance
).filter_by(project_id=project_id
- ).filter_by(deleted=False
+ ).filter_by(deleted=_deleted(context)
).all()
@@ -260,8 +270,8 @@ def instance_get_by_reservation(_context, reservation_id):
).all()
-def instance_get_by_str(_context, str_id):
- return models.Instance.find_by_str(str_id)
+def instance_get_by_str(context, str_id):
+ return models.Instance.find_by_str(str_id, deleted=_deleted(context))
def instance_get_fixed_address(_context, instance_id):
@@ -562,24 +572,24 @@ def volume_detached(_context, volume_id):
volume_ref.save(session=session)
-def volume_get(_context, volume_id):
- return models.Volume.find(volume_id)
+def volume_get(context, volume_id):
+ return models.Volume.find(volume_id, deleted=_deleted(context))
-def volume_get_all(_context):
- return models.Volume.all()
+def volume_get_all(context):
+ return models.Volume.all(deleted=_deleted(context))
-def volume_get_by_project(_context, project_id):
+def volume_get_by_project(context, project_id):
session = get_session()
return session.query(models.Volume
).filter_by(project_id=project_id
- ).filter_by(deleted=False
+ ).filter_by(deleted=_deleted(context)
).all()
-def volume_get_by_str(_context, str_id):
- return models.Volume.find_by_str(str_id)
+def volume_get_by_str(context, str_id):
+ return models.Volume.find_by_str(str_id, deleted=_deleted(context))
def volume_get_host(context, volume_id):
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index fe3a77a52..064894e97 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -48,45 +48,46 @@ class NovaBase(object):
__prefix__ = 'none'
created_at = Column(DateTime, default=func.now())
updated_at = Column(DateTime, onupdate=datetime.datetime.now)
+ deleted_at = Column(DateTime)
deleted = Column(Boolean, default=False)
@classmethod
- def all(cls, session=None):
+ def all(cls, session=None, deleted=False):
"""Get all objects of this type"""
if not session:
session = get_session()
return session.query(cls) \
- .filter_by(deleted=False) \
+ .filter_by(deleted=deleted) \
.all()
@classmethod
- def count(cls, session=None):
+ def count(cls, session=None, deleted=False):
"""Count objects of this type"""
if not session:
session = get_session()
return session.query(cls) \
- .filter_by(deleted=False) \
+ .filter_by(deleted=deleted) \
.count()
@classmethod
- def find(cls, obj_id, session=None):
+ def find(cls, obj_id, session=None, deleted=False):
"""Find object by id"""
if not session:
session = get_session()
try:
return session.query(cls) \
.filter_by(id=obj_id) \
- .filter_by(deleted=False) \
+ .filter_by(deleted=deleted) \
.one()
except exc.NoResultFound:
new_exc = exception.NotFound("No model for id %s" % obj_id)
raise new_exc.__class__, new_exc, sys.exc_info()[2]
@classmethod
- def find_by_str(cls, str_id, session=None):
+ def find_by_str(cls, str_id, session=None, deleted=False):
"""Find object by str_id"""
int_id = int(str_id.rpartition('-')[2])
- return cls.find(int_id, session=session)
+ return cls.find(int_id, session=session, deleted=deleted)
@property
def str_id(self):
@@ -103,6 +104,7 @@ class NovaBase(object):
def delete(self, session=None):
"""Delete this object"""
self.deleted = True
+ self.deleted_at = datetime.datetime.now()
self.save(session=session)
def __setitem__(self, key, value):
@@ -230,6 +232,8 @@ class Instance(BASE, NovaBase):
reservation_id = Column(String(255))
mac_address = Column(String(255))
+ launched_at = Column(DateTime)
+ terminated_at = Column(DateTime)
# TODO(vish): see Ewan's email about state improvements, probably
# should be in a driver base class or some such
# vmstate_state = running, halted, suspended, paused
diff --git a/nova/tests/compute_unittest.py b/nova/tests/compute_unittest.py
index 746c035d6..e5da6b054 100644
--- a/nova/tests/compute_unittest.py
+++ b/nova/tests/compute_unittest.py
@@ -18,6 +18,8 @@
"""
Tests For Compute
"""
+
+import datetime
import logging
from twisted.internet import defer
@@ -80,6 +82,24 @@ class ComputeTestCase(test.TrialTestCase):
self.assertEqual(len(instances), 0)
@defer.inlineCallbacks
+ def test_run_terminate_timestamps(self):
+ """Make sure it is possible to run and terminate instance"""
+ instance_id = self._create_instance()
+ instance_ref = db.instance_get(self.context, instance_id)
+ self.assertEqual(instance_ref['launched_at'], None)
+ self.assertEqual(instance_ref['terminated_at'], None)
+ launch = datetime.datetime.now()
+ yield self.compute.run_instance(self.context, instance_id)
+ instance_ref = db.instance_get(self.context, instance_id)
+ self.assert_(instance_ref['launched_at'] > launch)
+ self.assertEqual(instance_ref['terminated_at'], None)
+ terminate = datetime.datetime.now()
+ yield self.compute.terminate_instance(self.context, instance_id)
+ instance_ref = db.instance_get({'deleted': True}, instance_id)
+ self.assert_(instance_ref['launched_at'] < terminate)
+ self.assert_(instance_ref['terminated_at'] > terminate)
+
+ @defer.inlineCallbacks
def test_reboot(self):
"""Ensure instance can be rebooted"""
instance_id = self._create_instance()