summaryrefslogtreecommitdiff
path: root/ironic/drivers/modules/image_cache.py
diff options
context:
space:
mode:
authorDmitry Tantsur <dtantsur@redhat.com>2014-05-20 15:47:37 +0200
committerDmitry Tantsur <dtantsur@redhat.com>2014-06-23 14:09:40 +0200
commit581ffa98d2fe56e2919b06f2caec3043fd745339 (patch)
treea255d9e9ca958f22b56858b42c7b50649d96f063 /ironic/drivers/modules/image_cache.py
parent2a9c6acd57bba211af7e333017fc4667766af4ce (diff)
downloadironic-581ffa98d2fe56e2919b06f2caec3043fd745339.tar.gz
PXE to pass hints to ImageCache on how much space to reclaim
After previous patch PXE triggers cache clean up, if there's not enough disk space for deployment. However, standard clean up may reclaim too little space, due to configuration. This patch implements hints to ImageCache, so that it tries to reclaim exactly required amount of disk space. Note that this amount is calculated as total size of images multiplied by two as an attempt to account for images converting to raw format. Change-Id: I8f95b7e6334a37e9e6e98278a5b61a9ace7221b5 Closes-Bug: #1316168
Diffstat (limited to 'ironic/drivers/modules/image_cache.py')
-rw-r--r--ironic/drivers/modules/image_cache.py60
1 files changed, 50 insertions, 10 deletions
diff --git a/ironic/drivers/modules/image_cache.py b/ironic/drivers/modules/image_cache.py
index b4fd0b8c4..88a1e65f9 100644
--- a/ironic/drivers/modules/image_cache.py
+++ b/ironic/drivers/modules/image_cache.py
@@ -146,12 +146,16 @@ class ImageCache(object):
utils.rmtree_without_raise(tmp_dir)
@lockutils.synchronized('master_image', 'ironic-')
- def clean_up(self):
+ def clean_up(self, amount=None):
"""Clean up directory with images, keeping cache of the latest images.
Files with link count >1 are never deleted.
Protected by global lock, so that no one messes with master images
after we get listing and before we actually delete files.
+
+ :param amount: if present, amount of space to reclaim in bytes,
+ cleaning will stop, if this goal was reached,
+ even if it is possible to clean up more files
"""
if self.master_dir is None:
return
@@ -159,31 +163,63 @@ class ImageCache(object):
LOG.debug("Starting clean up for master image cache %(dir)s" %
{'dir': self.master_dir})
+ amount_copy = amount
listing = _find_candidates_for_deletion(self.master_dir)
- survived = self._clean_up_too_old(listing)
- self._clean_up_ensure_cache_size(survived)
-
- def _clean_up_too_old(self, listing):
+ survived, amount = self._clean_up_too_old(listing, amount)
+ if amount is not None and amount <= 0:
+ return
+ amount = self._clean_up_ensure_cache_size(survived, amount)
+ if amount is not None and amount > 0:
+ LOG.warn(_("Cache clean up was unable to reclaim %(required)d MiB "
+ "of disk space, still %(left)d MiB required"),
+ {'required': amount_copy / 1024 / 1024,
+ 'left': amount / 1024 / 1024})
+
+ def _clean_up_too_old(self, listing, amount):
"""Clean up stage 1: drop images that are older than TTL.
+ This method removes files all files older than TTL seconds
+ unless 'amount' is non-None. If 'amount' is non-None,
+ it starts removing files older than TTL seconds,
+ oldest first, until the required 'amount' of space is reclaimed.
+
:param listing: list of tuples (file name, last used time)
- :returns: list of files left after clean up
+ :param amount: if not None, amount of space to reclaim in bytes,
+ cleaning will stop, if this goal was reached,
+ even if it is possible to clean up more files
+ :returns: tuple (list of files left after clean up,
+ amount still to reclaim)
"""
threshold = time.time() - self._cache_ttl
survived = []
for file_name, last_used, stat in listing:
if last_used < threshold:
- utils.unlink_without_raise(file_name)
+ try:
+ os.unlink(file_name)
+ except EnvironmentError as exc:
+ LOG.warn(_("Unable to delete file %(name)s from "
+ "master image cache: %(exc)s") %
+ {'name': file_name, 'exc': exc})
+ else:
+ if amount is not None:
+ amount -= stat.st_size
+ if amount <= 0:
+ amount = 0
+ break
else:
survived.append((file_name, last_used, stat))
- return survived
+ return survived, amount
- def _clean_up_ensure_cache_size(self, listing):
+ def _clean_up_ensure_cache_size(self, listing, amount):
"""Clean up stage 2: try to ensure cache size < threshold.
Try to delete the oldest files until conditions is satisfied
or no more files are eligable for delition.
:param listing: list of tuples (file name, last used time)
+ :param amount: amount of space to reclaim, if possible.
+ if amount is not None, it has higher priority than
+ cache size in settings
+ :returns: amount of space still required after clean up
"""
# NOTE(dtantsur): Sort listing to delete the oldest files first
listing = sorted(listing,
@@ -193,7 +229,8 @@ class ImageCache(object):
for f in os.listdir(self.master_dir))
total_size = sum(os.path.getsize(f)
for f in total_listing)
- while total_size > self._cache_size and listing:
+ while listing and (total_size > self._cache_size or
+ (amount is not None and amount > 0)):
file_name, last_used, stat = listing.pop()
try:
os.unlink(file_name)
@@ -203,6 +240,8 @@ class ImageCache(object):
{'name': file_name, 'exc': exc})
else:
total_size -= stat.st_size
+ if amount is not None:
+ amount -= stat.st_size
if total_size > self._cache_size:
LOG.info(_("After cleaning up cache dir %(dir)s "
@@ -210,6 +249,7 @@ class ImageCache(object):
"threshold %(expected)d") %
{'dir': self.master_dir, 'actual': total_size,
'expected': self._cache_size})
+ return max(amount, 0)
def _find_candidates_for_deletion(master_dir):