31
32
from ironic.common import images
32
33
from ironic.common import utils
33
34
from ironic.openstack.common import fileutils
34
from ironic.openstack.common import lockutils
35
35
from ironic.openstack.common import log as logging
72
72
if master_dir is not None:
73
73
fileutils.ensure_tree(master_dir)
75
def fetch_image(self, uuid, dest_path, ctx=None):
76
"""Fetch image with given uuid to the destination path.
75
def fetch_image(self, href, dest_path, ctx=None, force_raw=True):
76
"""Fetch image by given href to the destination path.
78
78
Does nothing if destination path exists.
79
79
Only creates a link if master image for this UUID is already in cache.
80
80
Otherwise downloads an image and also stores it in cache.
82
:param uuid: image UUID or href to fetch
82
:param href: image UUID or href to fetch
83
83
:param dest_path: destination file path
84
84
:param ctx: context
85
:param force_raw: boolean value, whether to convert the image to raw
86
88
img_download_lock_name = 'download-image'
87
89
if self.master_dir is None:
88
#NOTE(ghe): We don't share images between instances/hosts
90
# NOTE(ghe): We don't share images between instances/hosts
89
91
if not CONF.parallel_image_downloads:
90
92
with lockutils.lock(img_download_lock_name, 'ironic-'):
91
_fetch_to_raw(ctx, uuid, dest_path, self._image_service)
93
_fetch(ctx, href, dest_path, self._image_service,
93
_fetch_to_raw(ctx, uuid, dest_path, self._image_service)
96
_fetch(ctx, href, dest_path, self._image_service, force_raw)
96
#TODO(ghe): have hard links and counts the same behaviour in all fs
99
# TODO(ghe): have hard links and counts the same behaviour in all fs
98
master_file_name = service_utils.parse_image_ref(uuid)[0]
101
master_file_name = service_utils.parse_image_ref(href)[0]
99
102
master_path = os.path.join(self.master_dir, master_file_name)
101
104
if CONF.parallel_image_downloads:
118
121
LOG.info(_LI("Master cache miss for image %(uuid)s, "
119
122
"starting download"),
122
125
LOG.debug("Master cache hit for image %(uuid)s",
126
self._download_image(uuid, master_path, dest_path, ctx=ctx)
129
self._download_image(
130
href, master_path, dest_path, ctx=ctx, force_raw=force_raw)
128
132
# NOTE(dtantsur): we increased cache size - time to clean up
131
def _download_image(self, uuid, master_path, dest_path, ctx=None):
132
"""Download image from Glance and store at a given path.
135
def _download_image(self, href, master_path, dest_path, ctx=None,
137
"""Download image by href and store at a given path.
133
139
This method should be called with uuid-specific lock taken.
135
:param uuid: image UUID or href to fetch
141
:param href: image UUID or href to fetch
136
142
:param master_path: destination master path
137
143
:param dest_path: destination file path
138
144
:param ctx: context
145
:param force_raw: boolean value, whether to convert the image to raw
140
#TODO(ghe): timeout and retry for downloads
141
#TODO(ghe): logging when image cannot be created
148
# TODO(ghe): timeout and retry for downloads
149
# TODO(ghe): logging when image cannot be created
142
150
tmp_dir = tempfile.mkdtemp(dir=self.master_dir)
143
tmp_path = os.path.join(tmp_dir, uuid)
151
tmp_path = os.path.join(tmp_dir, href.split('/')[-1])
145
_fetch_to_raw(ctx, uuid, tmp_path, self._image_service)
154
_fetch(ctx, href, tmp_path, self._image_service, force_raw)
146
155
# NOTE(dtantsur): no need for global lock here - master_path
147
156
# will have link count >1 at any moment, so won't be cleaned up
148
157
os.link(tmp_path, master_path)
218
227
def _clean_up_ensure_cache_size(self, listing, amount):
219
228
"""Clean up stage 2: try to ensure cache size < threshold.
220
230
Try to delete the oldest files until conditions is satisfied
221
231
or no more files are eligable for delition.
281
291
return stat.f_frsize * stat.f_bavail
284
def _fetch_to_raw(context, image_href, path, image_service=None):
294
def _fetch(context, image_href, path, image_service=None, force_raw=False):
285
295
"""Fetch image and convert to raw format if needed."""
286
296
path_tmp = "%s.part" % path
287
images.fetch(context, image_href, path_tmp, image_service)
288
required_space = images.converted_size(path_tmp)
289
directory = os.path.dirname(path_tmp)
290
_clean_up_caches(directory, required_space)
291
images.image_to_raw(image_href, path, path_tmp)
297
images.fetch(context, image_href, path_tmp, image_service,
299
# Notes(yjiang5): If glance can provide the virtual size information,
300
# then we can firstly clean cach and then invoke images.fetch().
302
required_space = images.converted_size(path_tmp)
303
directory = os.path.dirname(path_tmp)
304
_clean_up_caches(directory, required_space)
305
images.image_to_raw(image_href, path, path_tmp)
307
os.rename(path_tmp, path)
294
310
def _clean_up_caches(directory, amount):