summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/image/glance.py20
-rw-r--r--nova/tests/image/test_glance.py37
2 files changed, 56 insertions, 1 deletions
diff --git a/nova/image/glance.py b/nova/image/glance.py
index 75551d35c..1a6bba62f 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -22,6 +22,7 @@ from __future__ import absolute_import
import copy
import itertools
import random
+import shutil
import sys
import time
import urlparse
@@ -58,7 +59,12 @@ glance_opts = [
cfg.IntOpt('glance_num_retries',
default=0,
help='Number retries when downloading an image from glance'),
-]
+ cfg.ListOpt('allowed_direct_url_schemes',
+ default=[],
+ help='A list of url scheme that can be downloaded directly '
+ 'via the direct_url. Currently supported schemes: '
+ '[file].'),
+ ]
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
@@ -254,6 +260,18 @@ class GlanceImageService(object):
def download(self, context, image_id, data):
"""Calls out to Glance for metadata and data and writes data."""
+ if 'file' in CONF.allowed_direct_url_schemes:
+ location = self.get_location(context, image_id)
+ o = urlparse.urlparse(location)
+ if o.scheme == "file":
+ with open(o.path, "r") as f:
+ # FIXME(jbresnah) a system call to cp could have
+ # significant performance advantages, however we
+ # do not have the path to files at this point in
+ # the abstraction.
+ shutil.copyfileobj(f, data)
+ return
+
try:
image_chunks = self._client.call(context, 1, 'data', image_id)
except Exception:
diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py
index 7c13796a6..9dd9e5121 100644
--- a/nova/tests/image/test_glance.py
+++ b/nova/tests/image/test_glance.py
@@ -17,7 +17,10 @@
import datetime
+import filecmp
+import os
import random
+import tempfile
import time
import glanceclient.exc
@@ -468,6 +471,40 @@ class TestGlanceImageService(test.TestCase):
self.flags(glance_num_retries=1)
service.download(self.context, image_id, writer)
+ def test_download_file_url(self):
+ class MyGlanceStubClient(glance_stubs.StubGlanceClient):
+ """A client that returns a file url."""
+
+ (outfd, s_tmpfname) = tempfile.mkstemp(prefix='directURLsrc')
+ outf = os.fdopen(outfd, 'w')
+ inf = open('/dev/urandom', 'r')
+ for i in range(10):
+ _data = inf.read(1024)
+ outf.write(_data)
+ outf.close()
+
+ def get(self, image_id):
+ return type('GlanceTestDirectUrlMeta', (object,),
+ {'direct_url': 'file://%s' + self.s_tmpfname})
+
+ client = MyGlanceStubClient()
+ (outfd, tmpfname) = tempfile.mkstemp(prefix='directURLdst')
+ writer = os.fdopen(outfd, 'w')
+
+ service = self._create_image_service(client)
+ image_id = 1 # doesn't matter
+
+ self.flags(allowed_direct_url_schemes=['file'])
+ service.download(self.context, image_id, writer)
+ writer.close()
+
+ # compare the two files
+ rc = filecmp.cmp(tmpfname, client.s_tmpfname)
+ self.assertTrue(rc, "The file %s and %s should be the same" %
+ (tmpfname, client.s_tmpfname))
+ os.remove(client.s_tmpfname)
+ os.remove(tmpfname)
+
def test_client_forbidden_converts_to_imagenotauthed(self):
class MyGlanceStubClient(glance_stubs.StubGlanceClient):
"""A client that raises a Forbidden exception."""