prometheanfire    14/03/25 20:51:33

  Added:                2013.2.2-CVE-2014-0134.patch
  Log:
  fix for nova CVE-2014-0134
  
  (Portage version: 2.2.8-r1/cvs/Linux x86_64, signed Manifest commit with key 
0x2471eb3e40ac5ac3)

Revision  Changes    Path
1.1                  sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch

file : 
http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch?rev=1.1&view=markup
plain: 
http://sources.gentoo.org/viewvc.cgi/gentoo-x86/sys-cluster/nova/files/2013.2.2-CVE-2014-0134.patch?rev=1.1&content-type=text/plain

Index: 2013.2.2-CVE-2014-0134.patch
===================================================================
>From e2527e64f77ca0211c744908031cf056cb144f07 Mon Sep 17 00:00:00 2001
From: David Ripton <[email protected]>
Date: Mon, 17 Mar 2014 22:18:05 -0400
Subject: [PATCH] Persist image format to a file, to prevent attacks based on
 changing it

The attack is based on creating a raw image that looks like a qcow2
image, and taking advantage of the code that used 'qemu-img info' to
autodetect the image format.

Now we store the image format to a 'disk.info' file, for Qcow2 and Raw
images, and only autodetect for images that have never been written to
that file.  Since glance takes the appropriate security precautions on
image upload, this should be sufficient and backward-compatible.

Manually resolved one conflict in nova/virt/libvirt/imagebackend.py:
snapshot_delete was removed in icehouse, but is still there in havana

Change-Id: I2016efdb3f49a44ec4d677ac596eacc97871f30a
Closes-bug: #1221190
---
 nova/tests/virt/libvirt/test_imagebackend.py | 331 ++++++++++++++++++++++-----
 nova/virt/libvirt/imagebackend.py            |  57 ++++-
 2 files changed, 324 insertions(+), 64 deletions(-)

diff --git a/nova/tests/virt/libvirt/test_imagebackend.py 
b/nova/tests/virt/libvirt/test_imagebackend.py
index 2455ec8..3df22fa 100644
--- a/nova/tests/virt/libvirt/test_imagebackend.py
+++ b/nova/tests/virt/libvirt/test_imagebackend.py
@@ -16,6 +16,8 @@
 #    under the License.
 
 import os
+import shutil
+import tempfile
 
 import fixtures
 from oslo.config import cfg
@@ -31,7 +33,6 @@ CONF = cfg.CONF
 
 
 class _ImageTestCase(object):
-    INSTANCES_PATH = '/instances_path'
 
     def mock_create_image(self, image):
         def create_image(fn, base, size, *args, **kwargs):
@@ -40,10 +41,13 @@ class _ImageTestCase(object):
 
     def setUp(self):
         super(_ImageTestCase, self).setUp()
+        self.INSTANCES_PATH = tempfile.mkdtemp(suffix='instances')
         self.flags(disable_process_locking=True,
                    instances_path=self.INSTANCES_PATH)
         self.INSTANCE = {'name': 'instance',
                          'uuid': uuidutils.generate_uuid()}
+        self.DISK_INFO_PATH = os.path.join(self.INSTANCES_PATH,
+                                           self.INSTANCE['uuid'], 'disk.info')
         self.NAME = 'fake.vm'
         self.TEMPLATE = 'template'
 
@@ -61,6 +65,70 @@ class _ImageTestCase(object):
             'nova.virt.libvirt.imagebackend.libvirt_utils',
             fake_libvirt_utils))
 
+    def tearDown(self):
+        super(_ImageTestCase, self).tearDown()
+        shutil.rmtree(self.INSTANCES_PATH)
+
+    def test_prealloc_image(self):
+        CONF.set_override('preallocate_images', 'space')
+
+        fake_processutils.fake_execute_clear_log()
+        fake_processutils.stub_out_processutils_execute(self.stubs)
+        image = self.image_class(self.INSTANCE, self.NAME)
+
+        def fake_fetch(target, *args, **kwargs):
+            return
+
+        self.stubs.Set(os.path, 'exists', lambda _: True)
+        self.stubs.Set(os, 'access', lambda p, w: True)
+
+        # Call twice to verify testing fallocate is only called once.
+        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
+        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
+
+        self.assertEqual(fake_processutils.fake_execute_get_log(),
+            ['fallocate -n -l 1 %s.fallocate_test' % self.PATH,
+             'fallocate -n -l %s %s' % (self.SIZE, self.PATH),
+             'fallocate -n -l %s %s' % (self.SIZE, self.PATH)])
+
+    def test_prealloc_image_without_write_access(self):
+        CONF.set_override('preallocate_images', 'space')
+
+        fake_processutils.fake_execute_clear_log()
+        fake_processutils.stub_out_processutils_execute(self.stubs)
+        image = self.image_class(self.INSTANCE, self.NAME)
+
+        def fake_fetch(target, *args, **kwargs):
+            return
+
+        self.stubs.Set(image, 'check_image_exists', lambda: True)
+        self.stubs.Set(image, '_can_fallocate', lambda: True)
+        self.stubs.Set(os.path, 'exists', lambda _: True)
+        self.stubs.Set(os, 'access', lambda p, w: False)
+
+        # Testing fallocate is only called when user has write access.
+        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
+
+        self.assertEqual(fake_processutils.fake_execute_get_log(), [])
+
+
+class RawTestCase(_ImageTestCase, test.NoDBTestCase):
+
+    SIZE = 1024
+
+    def setUp(self):
+        self.image_class = imagebackend.Raw
+        super(RawTestCase, self).setUp()
+        self.stubs.Set(imagebackend.Raw, 'correct_format', lambda _: None)
+
+    def prepare_mocks(self):
+        fn = self.mox.CreateMockAnything()
+        self.mox.StubOutWithMock(imagebackend.utils.synchronized,
+                                 '__call__')
+        self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
+        self.mox.StubOutWithMock(imagebackend.disk, 'extend')
+        return fn
+
     def test_cache(self):
         self.mox.StubOutWithMock(os.path, 'exists')
         if self.OLD_STYLE_INSTANCE_PATH:
@@ -128,66 +196,6 @@ class _ImageTestCase(object):
 
         self.mox.VerifyAll()
 
-    def test_prealloc_image(self):
-        CONF.set_override('preallocate_images', 'space')
-
-        fake_processutils.fake_execute_clear_log()
-        fake_processutils.stub_out_processutils_execute(self.stubs)
-        image = self.image_class(self.INSTANCE, self.NAME)
-
-        def fake_fetch(target, *args, **kwargs):
-            return
-
-        self.stubs.Set(os.path, 'exists', lambda _: True)
-        self.stubs.Set(os, 'access', lambda p, w: True)
-
-        # Call twice to verify testing fallocate is only called once.
-        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
-        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
-
-        self.assertEqual(fake_processutils.fake_execute_get_log(),
-            ['fallocate -n -l 1 %s.fallocate_test' % self.PATH,
-             'fallocate -n -l %s %s' % (self.SIZE, self.PATH),
-             'fallocate -n -l %s %s' % (self.SIZE, self.PATH)])
-
-    def test_prealloc_image_without_write_access(self):
-        CONF.set_override('preallocate_images', 'space')
-
-        fake_processutils.fake_execute_clear_log()
-        fake_processutils.stub_out_processutils_execute(self.stubs)
-        image = self.image_class(self.INSTANCE, self.NAME)
-
-        def fake_fetch(target, *args, **kwargs):
-            return
-
-        self.stubs.Set(image, 'check_image_exists', lambda: True)
-        self.stubs.Set(image, '_can_fallocate', lambda: True)
-        self.stubs.Set(os.path, 'exists', lambda _: True)
-        self.stubs.Set(os, 'access', lambda p, w: False)
-
-        # Testing fallocate is only called when user has write access.
-        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
-
-        self.assertEqual(fake_processutils.fake_execute_get_log(), [])
-
-
-class RawTestCase(_ImageTestCase, test.NoDBTestCase):
-
-    SIZE = 1024
-
-    def setUp(self):
-        self.image_class = imagebackend.Raw
-        super(RawTestCase, self).setUp()
-        self.stubs.Set(imagebackend.Raw, 'correct_format', lambda _: None)
-
-    def prepare_mocks(self):
-        fn = self.mox.CreateMockAnything()
-        self.mox.StubOutWithMock(imagebackend.utils.synchronized,
-                                 '__call__')
-        self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
-        self.mox.StubOutWithMock(imagebackend.disk, 'extend')
-        return fn
-
     def test_create_image(self):
         fn = self.prepare_mocks()
         fn(target=self.TEMPLATE_PATH, max_size=None, image_id=None)
@@ -222,16 +230,17 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase):
         self.mox.VerifyAll()
 
     def test_correct_format(self):
-        info = self.mox.CreateMockAnything()
         self.stubs.UnsetAll()
 
         self.mox.StubOutWithMock(os.path, 'exists')
         self.mox.StubOutWithMock(imagebackend.images, 'qemu_img_info')
 
         os.path.exists(self.PATH).AndReturn(True)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
         info = self.mox.CreateMockAnything()
         info.file_format = 'foo'
         imagebackend.images.qemu_img_info(self.PATH).AndReturn(info)
+        os.path.exists(CONF.instances_path).AndReturn(True)
         self.mox.ReplayAll()
 
         image = self.image_class(self.INSTANCE, self.NAME, path=self.PATH)
@@ -239,6 +248,11 @@ class RawTestCase(_ImageTestCase, test.NoDBTestCase):
 
         self.mox.VerifyAll()
 
+    def test_resolve_driver_format(self):
+        image = self.image_class(self.INSTANCE, self.NAME)
+        driver_format = image.resolve_driver_format()
+        self.assertEqual(driver_format, 'raw')
+
 
 class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
     SIZE = 1024 * 1024 * 1024
@@ -259,6 +273,77 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
         self.mox.StubOutWithMock(imagebackend.disk, 'extend')
         return fn
 
+    def test_cache(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(CONF.instances_path).AndReturn(True)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
+        os.path.exists(self.PATH).AndReturn(False)
+        fn = self.mox.CreateMockAnything()
+        fn(target=self.TEMPLATE_PATH)
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        self.mock_create_image(image)
+        image.cache(fn, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
+    def test_cache_image_exists(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
+        os.path.exists(self.PATH).AndReturn(True)
+        os.path.exists(self.TEMPLATE_PATH).AndReturn(True)
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        image.cache(None, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
+    def test_cache_base_dir_exists(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
+        os.path.exists(self.PATH).AndReturn(False)
+        fn = self.mox.CreateMockAnything()
+        fn(target=self.TEMPLATE_PATH)
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        self.mock_create_image(image)
+        image.cache(fn, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
+    def test_cache_template_exists(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
+        os.path.exists(self.PATH).AndReturn(False)
+        fn = self.mox.CreateMockAnything()
+        fn(target=self.TEMPLATE_PATH)
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        self.mock_create_image(image)
+        image.cache(fn, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
     def test_create_image(self):
         fn = self.prepare_mocks()
         fn(max_size=None, target=self.TEMPLATE_PATH)
@@ -277,6 +362,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
         self.mox.StubOutWithMock(os.path, 'exists')
         if self.OLD_STYLE_INSTANCE_PATH:
             os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
         os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
         os.path.exists(self.PATH).AndReturn(False)
         os.path.exists(self.PATH).AndReturn(False)
@@ -296,6 +383,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
         self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
         if self.OLD_STYLE_INSTANCE_PATH:
             os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
         os.path.exists(self.TEMPLATE_PATH).AndReturn(True)
         imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
                                        ).AndReturn(self.SIZE)
@@ -314,6 +403,8 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
                                  'get_disk_backing_file')
         if self.OLD_STYLE_INSTANCE_PATH:
             os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(CONF.instances_path).AndReturn(True)
         os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
         os.path.exists(self.PATH).AndReturn(True)
 
@@ -340,6 +431,9 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
                                  'get_disk_backing_file')
         if self.OLD_STYLE_INSTANCE_PATH:
             os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.DISK_INFO_PATH).AndReturn(False)
+        os.path.exists(self.INSTANCES_PATH).AndReturn(True)
+
         os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
         os.path.exists(self.PATH).AndReturn(True)
 
@@ -353,6 +447,55 @@ class Qcow2TestCase(_ImageTestCase, test.NoDBTestCase):
 
         self.mox.VerifyAll()
 
+    def test_resolve_driver_format(self):
+        image = self.image_class(self.INSTANCE, self.NAME)
+        driver_format = image.resolve_driver_format()
+        self.assertEqual(driver_format, 'qcow2')
+
+    def test_prealloc_image(self):
+        CONF.set_override('preallocate_images', 'space')
+
+        fake_processutils.fake_execute_clear_log()
+        fake_processutils.stub_out_processutils_execute(self.stubs)
+        image = self.image_class(self.INSTANCE, self.NAME)
+
+        def fake_fetch(target, *args, **kwargs):
+            return
+
+        self.stubs.Set(os.path, 'exists', lambda _: True)
+        self.stubs.Set(os, 'access', lambda p, w: True)
+
+        # Call twice to verify testing fallocate is only called once.
+        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
+        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
+
+        self.assertEqual(fake_processutils.fake_execute_get_log(),
+            ['chown root:root %s' % self.DISK_INFO_PATH,
+             'fallocate -n -l 1 %s.fallocate_test' % self.PATH,
+             'fallocate -n -l %s %s' % (self.SIZE, self.PATH),
+             'fallocate -n -l %s %s' % (self.SIZE, self.PATH)])
+
+    def test_prealloc_image_without_write_access(self):
+        CONF.set_override('preallocate_images', 'space')
+
+        fake_processutils.fake_execute_clear_log()
+        fake_processutils.stub_out_processutils_execute(self.stubs)
+        image = self.image_class(self.INSTANCE, self.NAME)
+
+        def fake_fetch(target, *args, **kwargs):
+            return
+
+        self.stubs.Set(image, 'check_image_exists', lambda: True)
+        self.stubs.Set(image, '_can_fallocate', lambda: True)
+        self.stubs.Set(os.path, 'exists', lambda _: True)
+        self.stubs.Set(os, 'access', lambda p, w: False)
+
+        # Testing fallocate is only called when user has write access.
+        image.cache(fake_fetch, self.TEMPLATE_PATH, self.SIZE)
+
+        self.assertEqual(fake_processutils.fake_execute_get_log(),
+            ['chown root:root %s' % self.DISK_INFO_PATH])
+
 
 class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
     VG = 'FakeVG'
@@ -429,6 +572,56 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
 
         self.mox.VerifyAll()
 
+    def test_cache(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(False)
+        os.path.exists(self.PATH).AndReturn(False)
+
+        fn = self.mox.CreateMockAnything()
+        fn(target=self.TEMPLATE_PATH)
+        self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree')
+        imagebackend.fileutils.ensure_tree(self.TEMPLATE_DIR)
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        self.mock_create_image(image)
+        image.cache(fn, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
+    def test_cache_image_exists(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
+        os.path.exists(self.PATH).AndReturn(True)
+        os.path.exists(self.TEMPLATE_PATH).AndReturn(True)
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        image.cache(None, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
+    def test_cache_base_dir_exists(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        if self.OLD_STYLE_INSTANCE_PATH:
+            os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
+        os.path.exists(self.PATH).AndReturn(False)
+        fn = self.mox.CreateMockAnything()
+        fn(target=self.TEMPLATE_PATH)
+        self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree')
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        self.mock_create_image(image)
+        image.cache(fn, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
     def test_create_image(self):
         self._create_image(False)
 
@@ -594,6 +787,20 @@ class RbdTestCase(_ImageTestCase, test.NoDBTestCase):
 
         self.mox.VerifyAll()
 
+    def test_cache_base_dir_exists(self):
+        self.mox.StubOutWithMock(os.path, 'exists')
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
+        fn = self.mox.CreateMockAnything()
+        fn(target=self.TEMPLATE_PATH)
+        self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree')
+        self.mox.ReplayAll()
+
+        image = self.image_class(self.INSTANCE, self.NAME)
+        self.mock_create_image(image)
+        image.cache(fn, self.TEMPLATE)
+
+        self.mox.VerifyAll()
+
     def test_create_image(self):
         fn = self.prepare_mocks()
         fn(max_size=None, rbd=self.rbd, target=self.TEMPLATE_PATH)
diff --git a/nova/virt/libvirt/imagebackend.py 
b/nova/virt/libvirt/imagebackend.py
index e900789..21c7641 100644
--- a/nova/virt/libvirt/imagebackend.py
+++ b/nova/virt/libvirt/imagebackend.py
@@ -88,6 +88,11 @@ class Image(object):
         self.is_block_dev = is_block_dev
         self.preallocate = False
 
+        # NOTE(dripton): We store lines of json (path, disk_format) in this
+        # file, for some image types, to prevent attacks based on changing the
+        # disk_format.
+        self.disk_info_path = None
+
         # NOTE(mikal): We need a lock directory which is shared along with
         # instance files, to cover the scenario where multiple compute nodes
         # are trying to create a base file at the same time
@@ -232,6 +237,46 @@ class Image(object):
     def snapshot_delete(self):
         raise NotImplementedError()
 
+    def _get_driver_format(self):
+        return self.driver_format
+
+    def resolve_driver_format(self):
+        """Return the driver format for self.path.
+
+        First checks self.disk_info_path for an entry.
+        If it's not there, calls self._get_driver_format(), and then
+        stores the result in self.disk_info_path
+
+        See https://bugs.launchpad.net/nova/+bug/1221190
+        """
+        @utils.synchronized(self.disk_info_path, external=False,
+                            lock_path=self.lock_path)
+        def write_to_disk_info_file():
+            with open(self.disk_info_path, "w") as disk_info_file:
+                disk_info_file.write('%s\n' %
+                                  jsonutils.dumps((self.path, driver_format)))
+            # Ensure the file is always owned by root so qemu can't write it.
+            utils.execute('chown', 'root:root', self.disk_info_path,
+                          run_as_root=True)
+
+        if (self.disk_info_path is not None and
+                    os.path.exists(self.disk_info_path)):
+            with open(self.disk_info_path) as disk_info_file:
+                line = disk_info_file.read().rstrip()
+                try:
+                    parts = jsonutils.loads(line)
+                except (TypeError, ValueError):
+                    parts = []
+                if len(parts) == 2:
+                    (path, driver_format) = parts
+                    if path == self.path:
+                        return driver_format
+        driver_format = self._get_driver_format()
+        if self.disk_info_path is not None:
+            fileutils.ensure_tree(os.path.dirname(self.disk_info_path))
+            write_to_disk_info_file()
+        return driver_format
+
 
 class Raw(Image):
     def __init__(self, instance=None, disk_name=None, path=None,
@@ -243,12 +288,17 @@ class Raw(Image):
                                   disk_name))
         self.snapshot_name = snapshot_name
         self.preallocate = CONF.preallocate_images != 'none'
+        self.disk_info_path = os.path.join(os.path.dirname(self.path),
+                                           'disk.info')
         self.correct_format()
 
+    def _get_driver_format(self):
+        data = images.qemu_img_info(self.path)
+        return data.file_format or 'raw'
+
     def correct_format(self):
         if os.path.exists(self.path):
-            data = images.qemu_img_info(self.path)
-            self.driver_format = data.file_format or 'raw'
+            self.driver_format = self.resolve_driver_format()
 
     def create_image(self, prepare_template, base, size, *args, **kwargs):
         @utils.synchronized(base, external=True, lock_path=self.lock_path)
@@ -291,6 +341,9 @@ class Qcow2(Image):
                                   disk_name))
         self.snapshot_name = snapshot_name
         self.preallocate = CONF.preallocate_images != 'none'
+        self.disk_info_path = os.path.join(os.path.dirname(self.path),
+                                           'disk.info')
+        self.resolve_driver_format()
 
     def create_image(self, prepare_template, base, size, *args, **kwargs):
         @utils.synchronized(base, external=True, lock_path=self.lock_path)
-- 
1.8.5.3







Reply via email to