summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRick Harris <rconradharris@gmail.com>2011-06-22 16:14:01 -0500
committerRick Harris <rconradharris@gmail.com>2011-06-22 16:14:01 -0500
commitab2a77d0c6f738fe70b5d5a77fa7f97bf1f1f88b (patch)
treef47e4b44081287e3bcc018b0ac82a17c9139ffb0
parent75a87df739effe840e6cb39c976002e99b49c796 (diff)
downloadnova-ab2a77d0c6f738fe70b5d5a77fa7f97bf1f1f88b.tar.gz
nova-ab2a77d0c6f738fe70b5d5a77fa7f97bf1f1f88b.tar.xz
nova-ab2a77d0c6f738fe70b5d5a77fa7f97bf1f1f88b.zip
Adding backup rotation
-rw-r--r--nova/compute/api.py7
-rw-r--r--nova/compute/manager.py40
-rw-r--r--nova/exception.py4
3 files changed, 41 insertions, 10 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 365aa1c5d..c0cb2e18a 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -722,7 +722,7 @@ class API(base.Base):
def _snapshot(self, context, instance_id, name, image_type, rotation=None):
"""Snapshot an instance on this host.
-
+
:param context: security context
:param instance_id: nova.db.sqlalchemy.models.Instance.Id
:param name: string for name of the snapshot
@@ -733,9 +733,8 @@ class API(base.Base):
instance = db.api.instance_get(context, instance_id)
properties = {'instance_uuid': instance['uuid'],
'user_id': str(context.user_id),
- 'image_state': 'creating'}
- if image_type != 'snapshot':
- properties['backup_type'] = image_type
+ 'image_state': 'creating',
+ 'image_type': image_type}
sent_meta = {'name': name, 'is_public': False,
'status': 'creating', 'properties': properties}
recv_meta = self.image_service.create(context, sent_meta)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index bc6981c58..44abd5d89 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -43,6 +43,7 @@ import time
import functools
from eventlet import greenthread
+from operator import itemgetter
from nova import exception
from nova import flags
@@ -476,7 +477,7 @@ class ComputeManager(manager.SchedulerDependentManager):
def snapshot_instance(self, context, instance_id, image_id,
image_type='snapshot', rotation=None):
"""Snapshot an instance on this host.
-
+
:param context: security context
:param instance_id: nova.db.sqlalchemy.models.Instance.Id
:param image_id: glance.db.sqlalchemy.models.Image.Id
@@ -502,13 +503,40 @@ class ComputeManager(manager.SchedulerDependentManager):
'expected: %(running)s)') % locals())
self.driver.snapshot(instance_ref, image_id)
- if rotation:
- self.rotate_backups(context, instance_id, image_type, rotation)
+ if rotation and image_type == 'snapshot':
+ raise exception.ImageRotationNotAllowed
+ elif rotation:
+ instance_uuid = instance_ref['uuid']
+ self.rotate_backups(context, instance_uuid, image_type, rotation)
- def rotate_backups(self, context, instance_id, image_type, rotation):
- """
+ def rotate_backups(self, context, instance_uuid, image_type, rotation):
+ """Delete excess backups associated to an instance.
+
+ Instances are allowed a fixed number of backups (the rotation number);
+ this method deletes the oldest backups that exceed the rotation
+ threshold.
+
+ :param context: security context
+ :param instance_uuid: string representing uuid of instance
+ :param image_type: snapshot | daily | weekly
+ :param rotation: int representing how many backups to keep around;
+ None if rotation shouldn't be used (as in the case of snapshots)
"""
- pass
+ image_service = nova.image.get_default_image_service()
+ filters = {'property-image-type': image_type,
+ 'property-instance-uuid': instance_uuid}
+ images = image_service.detail(context, filters=filters)
+ if len(images) > rotation:
+ # Sort oldest (by created_at) to end of list
+ images.sort(key=itemgetter('created_at'), reverse=True)
+
+ # NOTE(sirp): this deletes all backups that exceed the rotation
+ # limit
+ excess = len(images) - rotation
+ for i in xrange(excess):
+ image = images.pop()
+ image_id = image['id']
+ image_service.delete(context, image_id)
@exception.wrap_exception
@checks_instance_lock
diff --git a/nova/exception.py b/nova/exception.py
index f3a452228..a548a638c 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -549,6 +549,10 @@ class GlobalRoleNotAllowed(NotAllowed):
message = _("Unable to use global role %(role_id)s")
+class ImageRotationNotAllowed(NovaException):
+ message = _("Rotation is not allowed for snapshots")
+
+
#TODO(bcwaldon): EOL this exception!
class Duplicate(NovaException):
pass