Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

blueprint lvm-disk-images

Add ability to use LVM volumes for VM disks.

Implements LVM disks support for libvirt driver.

VM disks will be stored on LVM volumes in volume group
 specified by `libvirt_images_volume_group` option.
 Another option `libvirt_local_images_type` specify which storage
 type will be used. Supported values are `raw`, `lvm`, `qcow2`,
 `default`. If `libvirt_local_images_type` = `default`, usual
 logic with `use_cow_images` flag is used.
Boolean option `libvirt_sparse_logical_volumes` controls which type
 of logical volumes will be created (sparsed with virtualsize or
 usual logical volumes with full space allocation). Default value
 for this option is `False`.
Commit introduce three classes: `Raw`, `Qcow2` and `Lvm`. They contain
 image creation logic, that was stored in
 `LibvirtConnection._cache_image` and `libvirt_info` methods,
 that produce right `LibvirtGuestConfigDisk` configurations for
 libvirt. `Backend` class choose which image type to use.

Change-Id: I0d01cb7d2fd67de2565b8d45d34f7846ad4112c2
  • Loading branch information...
commit e0540dfed1c1276106105aea8d5765356961ef3d 1 parent 6555c5a
Boris Filippov authored May 16, 2012
1  Authors
@@ -23,6 +23,7 @@ Asbjørn Sannes <asbjorn.sannes@interhost.no>
23 23
 Ben McGraw <ben@pistoncloud.com>
24 24
 Ben Swartzlander <bswartz@netapp.com>
25 25
 Bilal Akhtar <bilalakhtar@ubuntu.com>
  26
+Boris Filippov <bfilippov@griddynamics.com>
26 27
 Brad Hall <brad@nicira.com>
27 28
 Brad McConnell <bmcconne@rackspace.com>
28 29
 Brendan Maguire <B_Maguire@Dell.com>
9  nova/rootwrap/compute.py
@@ -188,4 +188,13 @@
188 188
     # nova/virt/libvirt/connection.py:
189 189
     filters.ReadFileFilter("/etc/iscsi/initiatorname.iscsi"),
190 190
 
  191
+    # nova/virt/libvirt/connection.py:
  192
+    filters.CommandFilter("/sbin/lvremove", "root"),
  193
+
  194
+    # nova/virt/libvirt/utils.py:
  195
+    filters.CommandFilter("/sbin/lvcreate", "root"),
  196
+
  197
+    # nova/virt/libvirt/utils.py:
  198
+    filters.CommandFilter("/sbin/vgs", "root")
  199
+
191 200
     ]
48  nova/tests/fake_imagebackend.py
... ...
@@ -0,0 +1,48 @@
  1
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
  2
+
  3
+# Copyright 2012 Grid Dynamics
  4
+# All Rights Reserved.
  5
+#
  6
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
  7
+#    not use this file except in compliance with the License. You may obtain
  8
+#    a copy of the License at
  9
+#
  10
+#         http://www.apache.org/licenses/LICENSE-2.0
  11
+#
  12
+#    Unless required by applicable law or agreed to in writing, software
  13
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15
+#    License for the specific language governing permissions and limitations
  16
+#    under the License.
  17
+
  18
+import os
  19
+
  20
+from nova.virt.libvirt import config
  21
+from nova.virt.libvirt import imagebackend
  22
+
  23
+
  24
+class Backend(object):
  25
+    def __init__(self, use_cow):
  26
+        pass
  27
+
  28
+    def image(self, instance, name, suffix='', image_type=''):
  29
+        class FakeImage(imagebackend.Image):
  30
+            def __init__(self, instance, name, suffix=''):
  31
+                self.path = os.path.join(instance, name + suffix)
  32
+
  33
+            def create_image(self, prepare_template, base,
  34
+                              size, *args, **kwargs):
  35
+                pass
  36
+
  37
+            def cache(self, fn, fname, size=None, *args, **kwargs):
  38
+                pass
  39
+
  40
+            def libvirt_info(self, device_type):
  41
+                info = config.LibvirtConfigGuestDisk()
  42
+                info.source_type = 'file'
  43
+                info.source_device = device_type
  44
+                info.driver_format = 'raw'
  45
+                info.source_path = self.path
  46
+                return info
  47
+
  48
+        return FakeImage(instance, name, suffix)
16  nova/tests/fake_libvirt_utils.py
@@ -50,6 +50,22 @@ def mkfs(fs, path):
50 50
     pass
51 51
 
52 52
 
  53
+def resize2fs(path):
  54
+    pass
  55
+
  56
+
  57
+def create_lvm_image(vg, lv, size, sparse=False):
  58
+    pass
  59
+
  60
+
  61
+def volume_group_free_space(vg):
  62
+    pass
  63
+
  64
+
  65
+def remove_logical_volumes(*paths):
  66
+    pass
  67
+
  68
+
53 69
 def ensure_tree(path):
54 70
     pass
55 71
 
392  nova/tests/test_imagebackend.py
... ...
@@ -0,0 +1,392 @@
  1
+#!/usr/bin/python
  2
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
  3
+
  4
+# Copyright 2012 Grid Dynamics
  5
+# All Rights Reserved.
  6
+#
  7
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
  8
+#    not use this file except in compliance with the License. You may obtain
  9
+#    a copy of the License at
  10
+#
  11
+#         http://www.apache.org/licenses/LICENSE-2.0
  12
+#
  13
+#    Unless required by applicable law or agreed to in writing, software
  14
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16
+#    License for the specific language governing permissions and limitations
  17
+#    under the License.
  18
+
  19
+import os
  20
+
  21
+from nova import flags
  22
+from nova import test
  23
+from nova.tests import fake_libvirt_utils
  24
+from nova.virt.libvirt import imagebackend
  25
+
  26
+FLAGS = flags.FLAGS
  27
+
  28
+
  29
+class _ImageTestCase(test.TestCase):
  30
+    INSTANCES_PATH = '/fake'
  31
+
  32
+    def mock_create_image(self, image):
  33
+        def create_image(fn, base, size, *args, **kwargs):
  34
+            fn(target=base, *args, **kwargs)
  35
+        image.create_image = create_image
  36
+
  37
+    def setUp(self):
  38
+        super(_ImageTestCase, self).setUp()
  39
+        self.flags(instances_path=self.INSTANCES_PATH)
  40
+        self.INSTANCE = 'instance'
  41
+        self.NAME = 'fake'
  42
+        self.SUFFIX = 'vm'
  43
+        self.TEMPLATE = 'template'
  44
+
  45
+        self.PATH = os.path.join(FLAGS.instances_path, self.INSTANCE,
  46
+                                 self.NAME + self.SUFFIX)
  47
+        self.TEMPLATE_DIR = os.path.join(FLAGS.instances_path,
  48
+                                         '_base')
  49
+        self.TEMPLATE_PATH = os.path.join(self.TEMPLATE_DIR, 'template')
  50
+
  51
+        imagebackend.libvirt_utils = fake_libvirt_utils
  52
+
  53
+    def test_cache(self):
  54
+        self.mox.StubOutWithMock(os.path, 'exists')
  55
+        os.path.exists(self.PATH).AndReturn(False)
  56
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(False)
  57
+        os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
  58
+        fn = self.mox.CreateMockAnything()
  59
+        fn(target=self.TEMPLATE_PATH)
  60
+        self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'ensure_tree')
  61
+        imagebackend.libvirt_utils.ensure_tree(self.TEMPLATE_DIR)
  62
+        self.mox.ReplayAll()
  63
+
  64
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  65
+        self.mock_create_image(image)
  66
+        image.cache(fn, self.TEMPLATE)
  67
+
  68
+        self.mox.VerifyAll()
  69
+
  70
+    def test_cache_image_exists(self):
  71
+        self.mox.StubOutWithMock(os.path, 'exists')
  72
+        os.path.exists(self.PATH).AndReturn(True)
  73
+        self.mox.ReplayAll()
  74
+
  75
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  76
+        image.cache(None, self.TEMPLATE)
  77
+
  78
+        self.mox.VerifyAll()
  79
+
  80
+    def test_cache_base_dir_exists(self):
  81
+        self.mox.StubOutWithMock(os.path, 'exists')
  82
+        os.path.exists(self.PATH).AndReturn(False)
  83
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
  84
+        os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
  85
+        fn = self.mox.CreateMockAnything()
  86
+        fn(target=self.TEMPLATE_PATH)
  87
+        self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'ensure_tree')
  88
+        self.mox.ReplayAll()
  89
+
  90
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  91
+        self.mock_create_image(image)
  92
+        image.cache(fn, self.TEMPLATE)
  93
+
  94
+        self.mox.VerifyAll()
  95
+
  96
+    def test_cache_template_exists(self):
  97
+        self.mox.StubOutWithMock(os.path, 'exists')
  98
+        os.path.exists(self.PATH).AndReturn(False)
  99
+        os.path.exists(self.TEMPLATE_DIR).AndReturn(True)
  100
+        os.path.exists(self.TEMPLATE_PATH).AndReturn(True)
  101
+        fn = self.mox.CreateMockAnything()
  102
+        self.mox.ReplayAll()
  103
+
  104
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  105
+        self.mock_create_image(image)
  106
+        image.cache(fn, self.TEMPLATE)
  107
+
  108
+        self.mox.VerifyAll()
  109
+
  110
+
  111
+class RawTestCase(_ImageTestCase):
  112
+
  113
+    SIZE = 1024
  114
+
  115
+    def setUp(self):
  116
+        self.image_class = imagebackend.Raw
  117
+        super(RawTestCase, self).setUp()
  118
+
  119
+    def prepare_mocks(self):
  120
+        fn = self.mox.CreateMockAnything()
  121
+        self.mox.StubOutWithMock(imagebackend.utils.synchronized, '__call__')
  122
+        self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
  123
+        self.mox.StubOutWithMock(imagebackend.disk, 'extend')
  124
+        return fn
  125
+
  126
+    def test_create_image(self):
  127
+        fn = self.prepare_mocks()
  128
+        fn(target=self.TEMPLATE_PATH, image_id=None)
  129
+        imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH)
  130
+        self.mox.ReplayAll()
  131
+
  132
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  133
+        image.create_image(fn, self.TEMPLATE_PATH, None, image_id=None)
  134
+
  135
+        self.mox.VerifyAll()
  136
+
  137
+    def test_create_image_generated(self):
  138
+        fn = self.prepare_mocks()
  139
+        fn(target=self.PATH)
  140
+        self.mox.ReplayAll()
  141
+
  142
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  143
+        image.create_image(fn, self.TEMPLATE_PATH, None)
  144
+
  145
+        self.mox.VerifyAll()
  146
+
  147
+    def test_create_image_extend(self):
  148
+        fn = self.prepare_mocks()
  149
+        fn(target=self.TEMPLATE_PATH, image_id=None)
  150
+        imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, self.PATH)
  151
+        imagebackend.disk.extend(self.PATH, self.SIZE)
  152
+        self.mox.ReplayAll()
  153
+
  154
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  155
+        image.create_image(fn, self.TEMPLATE_PATH, self.SIZE, image_id=None)
  156
+
  157
+        self.mox.VerifyAll()
  158
+
  159
+
  160
+class Qcow2TestCase(_ImageTestCase):
  161
+    SIZE = 1024 * 1024 * 1024
  162
+
  163
+    def setUp(self):
  164
+        self.image_class = imagebackend.Qcow2
  165
+        super(Qcow2TestCase, self).setUp()
  166
+        self.QCOW2_BASE = (self.TEMPLATE_PATH +
  167
+                           '_%d' % (self.SIZE / (1024 * 1024 * 1024)))
  168
+
  169
+    def prepare_mocks(self):
  170
+        fn = self.mox.CreateMockAnything()
  171
+        self.mox.StubOutWithMock(imagebackend.utils.synchronized, '__call__')
  172
+        self.mox.StubOutWithMock(imagebackend.libvirt_utils,
  173
+                                 'create_cow_image')
  174
+        self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
  175
+        self.mox.StubOutWithMock(imagebackend.disk, 'extend')
  176
+        return fn
  177
+
  178
+    def test_create_image(self):
  179
+        fn = self.prepare_mocks()
  180
+        fn(target=self.TEMPLATE_PATH)
  181
+        imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH,
  182
+                                                    self.PATH)
  183
+        self.mox.ReplayAll()
  184
+
  185
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  186
+        image.create_image(fn, self.TEMPLATE_PATH, None)
  187
+
  188
+        self.mox.VerifyAll()
  189
+
  190
+    def test_create_image_with_size(self):
  191
+        fn = self.prepare_mocks()
  192
+        fn(target=self.TEMPLATE_PATH)
  193
+        self.mox.StubOutWithMock(os.path, 'exists')
  194
+        os.path.exists(self.QCOW2_BASE).AndReturn(False)
  195
+        imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH,
  196
+                                              self.QCOW2_BASE)
  197
+        imagebackend.disk.extend(self.QCOW2_BASE, self.SIZE)
  198
+        imagebackend.libvirt_utils.create_cow_image(self.QCOW2_BASE,
  199
+                                                    self.PATH)
  200
+        self.mox.ReplayAll()
  201
+
  202
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  203
+        image.create_image(fn, self.TEMPLATE_PATH, self.SIZE)
  204
+
  205
+        self.mox.VerifyAll()
  206
+
  207
+    def test_create_image_with_size_template_exists(self):
  208
+        fn = self.prepare_mocks()
  209
+        fn(target=self.TEMPLATE_PATH)
  210
+        self.mox.StubOutWithMock(os.path, 'exists')
  211
+        os.path.exists(self.QCOW2_BASE).AndReturn(True)
  212
+        imagebackend.libvirt_utils.create_cow_image(self.QCOW2_BASE,
  213
+                                                    self.PATH)
  214
+        self.mox.ReplayAll()
  215
+
  216
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  217
+        image.create_image(fn, self.TEMPLATE_PATH, self.SIZE)
  218
+
  219
+        self.mox.VerifyAll()
  220
+
  221
+
  222
+class LvmTestCase(_ImageTestCase):
  223
+    VG = 'FakeVG'
  224
+    TEMPLATE_SIZE = 512
  225
+    SIZE = 1024
  226
+
  227
+    def setUp(self):
  228
+        self.image_class = imagebackend.Lvm
  229
+        super(LvmTestCase, self).setUp()
  230
+        self.flags(libvirt_images_volume_group=self.VG)
  231
+        self.LV = '%s_%s' % (self.INSTANCE, self.NAME + self.SUFFIX)
  232
+        self.PATH = os.path.join('/dev', self.VG, self.LV)
  233
+
  234
+        self.disk = imagebackend.disk
  235
+        self.utils = imagebackend.utils
  236
+        self.libvirt_utils = imagebackend.libvirt_utils
  237
+
  238
+    def prepare_mocks(self):
  239
+        fn = self.mox.CreateMockAnything()
  240
+        self.mox.StubOutWithMock(self.disk, 'get_image_virtual_size')
  241
+        self.mox.StubOutWithMock(self.disk, 'resize2fs')
  242
+        self.mox.StubOutWithMock(self.libvirt_utils, 'create_lvm_image')
  243
+        self.mox.StubOutWithMock(self.utils, 'execute')
  244
+        return fn
  245
+
  246
+    def _create_image(self, sparse):
  247
+        fn = self.prepare_mocks()
  248
+        fn(target=self.TEMPLATE_PATH)
  249
+        self.disk.get_image_virtual_size(self.TEMPLATE_PATH
  250
+                                         ).AndReturn(self.TEMPLATE_SIZE)
  251
+        self.libvirt_utils.create_lvm_image(self.VG,
  252
+                                            self.LV,
  253
+                                            self.TEMPLATE_SIZE,
  254
+                                            sparse=sparse)
  255
+        cmd = ('dd', 'if=%s' % self.TEMPLATE_PATH,
  256
+               'of=%s' % self.PATH, 'bs=4M')
  257
+        self.utils.execute(*cmd, run_as_root=True)
  258
+        self.mox.ReplayAll()
  259
+
  260
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  261
+        image.create_image(fn, self.TEMPLATE_PATH, None)
  262
+
  263
+        self.mox.VerifyAll()
  264
+
  265
+    def _create_image_generated(self, sparse):
  266
+        fn = self.prepare_mocks()
  267
+        self.libvirt_utils.create_lvm_image(self.VG, self.LV,
  268
+                                            self.SIZE, sparse=sparse)
  269
+        fn(target=self.PATH, ephemeral_size=None)
  270
+        self.mox.ReplayAll()
  271
+
  272
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  273
+        image.create_image(fn, self.TEMPLATE_PATH,
  274
+                self.SIZE, ephemeral_size=None)
  275
+
  276
+        self.mox.VerifyAll()
  277
+
  278
+    def _create_image_resize(self, sparse):
  279
+        fn = self.prepare_mocks()
  280
+        fn(target=self.TEMPLATE_PATH)
  281
+        self.disk.get_image_virtual_size(self.TEMPLATE_PATH
  282
+                                         ).AndReturn(self.TEMPLATE_SIZE)
  283
+        self.libvirt_utils.create_lvm_image(self.VG, self.LV,
  284
+                                            self.SIZE, sparse=sparse)
  285
+        cmd = ('dd', 'if=%s' % self.TEMPLATE_PATH,
  286
+               'of=%s' % self.PATH, 'bs=4M')
  287
+        self.utils.execute(*cmd, run_as_root=True)
  288
+        self.disk.resize2fs(self.PATH)
  289
+        self.mox.ReplayAll()
  290
+
  291
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  292
+        image.create_image(fn, self.TEMPLATE_PATH, self.SIZE)
  293
+
  294
+        self.mox.VerifyAll()
  295
+
  296
+    def test_create_image(self):
  297
+        self._create_image(False)
  298
+
  299
+    def test_create_image_sparsed(self):
  300
+        self.flags(libvirt_sparse_logical_volumes=True)
  301
+        self._create_image(True)
  302
+
  303
+    def test_create_image_generated(self):
  304
+        self._create_image_generated(False)
  305
+
  306
+    def test_create_image_generated_sparsed(self):
  307
+        self.flags(libvirt_sparse_logical_volumes=True)
  308
+        self._create_image_generated(True)
  309
+
  310
+    def test_create_image_resize(self):
  311
+        self._create_image_resize(False)
  312
+
  313
+    def test_create_image_resize_sparsed(self):
  314
+        self.flags(libvirt_sparse_logical_volumes=True)
  315
+        self._create_image_resize(True)
  316
+
  317
+    def test_create_image_negative(self):
  318
+        fn = self.prepare_mocks()
  319
+        fn(target=self.TEMPLATE_PATH)
  320
+        self.disk.get_image_virtual_size(self.TEMPLATE_PATH
  321
+                                         ).AndReturn(self.TEMPLATE_SIZE)
  322
+        self.libvirt_utils.create_lvm_image(self.VG,
  323
+                                            self.LV,
  324
+                                            self.SIZE,
  325
+                                            sparse=False
  326
+                                            ).AndRaise(RuntimeError())
  327
+        self.mox.StubOutWithMock(self.libvirt_utils, 'remove_logical_volumes')
  328
+        self.libvirt_utils.remove_logical_volumes(self.PATH)
  329
+        self.mox.ReplayAll()
  330
+
  331
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  332
+
  333
+        self.assertRaises(RuntimeError, image.create_image, fn,
  334
+                          self.TEMPLATE_PATH, self.SIZE)
  335
+        self.mox.VerifyAll()
  336
+
  337
+    def test_create_image_generated_negative(self):
  338
+        fn = self.prepare_mocks()
  339
+        fn(target=self.PATH,
  340
+           ephemeral_size=None).AndRaise(RuntimeError())
  341
+        self.libvirt_utils.create_lvm_image(self.VG,
  342
+                                            self.LV,
  343
+                                            self.SIZE,
  344
+                                            sparse=False)
  345
+        self.mox.StubOutWithMock(self.libvirt_utils, 'remove_logical_volumes')
  346
+        self.libvirt_utils.remove_logical_volumes(self.PATH)
  347
+        self.mox.ReplayAll()
  348
+
  349
+        image = self.image_class(self.INSTANCE, self.NAME, self.SUFFIX)
  350
+
  351
+        self.assertRaises(RuntimeError, image.create_image, fn,
  352
+                          self.TEMPLATE_PATH, self.SIZE,
  353
+                          ephemeral_size=None)
  354
+        self.mox.VerifyAll()
  355
+
  356
+
  357
+class BackendTestCase(test.TestCase):
  358
+    INSTANCE = 'fake-instance'
  359
+    NAME = 'fake-name'
  360
+    SUFFIX = 'suffix'
  361
+
  362
+    def get_image(self, use_cow, image_type):
  363
+        return imagebackend.Backend(use_cow).image(self.INSTANCE,
  364
+                                                   self.NAME,
  365
+                                                   self.SUFFIX,
  366
+                                                   image_type)
  367
+
  368
+    def _test_image(self, image_type, image_not_cow, image_cow):
  369
+        image1 = self.get_image(False, image_type)
  370
+        image2 = self.get_image(True, image_type)
  371
+
  372
+        def assertIsInstance(instance, class_object):
  373
+            failure = ('Expected %s,' +
  374
+                       ' but got %s.') % (class_object.__name__,
  375
+                                          instance.__class__.__name__)
  376
+            self.assertTrue(isinstance(instance, class_object), failure)
  377
+
  378
+        assertIsInstance(image1, image_not_cow)
  379
+        assertIsInstance(image2, image_cow)
  380
+
  381
+    def test_image_raw(self):
  382
+        self._test_image('raw', imagebackend.Raw, imagebackend.Raw)
  383
+
  384
+    def test_image_qcow2(self):
  385
+        self._test_image('qcow2', imagebackend.Qcow2, imagebackend.Qcow2)
  386
+
  387
+    def test_image_lvm(self):
  388
+        self.flags(libvirt_images_volume_group='FakeVG')
  389
+        self._test_image('lvm', imagebackend.Lvm, imagebackend.Lvm)
  390
+
  391
+    def test_image_default(self):
  392
+        self._test_image('default', imagebackend.Raw, imagebackend.Qcow2)
27  nova/tests/test_libvirt.py
@@ -47,6 +47,7 @@
47 47
 from nova.virt.libvirt import config
48 48
 from nova.virt.libvirt import connection
49 49
 from nova.virt.libvirt import firewall
  50
+from nova.virt.libvirt import imagebackend
50 51
 from nova.virt.libvirt import utils as libvirt_utils
51 52
 from nova.virt.libvirt import volume
52 53
 from nova.volume import driver as volume_driver
@@ -342,24 +343,24 @@ def fake_extend(image, size):
342 343
 
343 344
         self.stubs.Set(os.path, 'exists', fake_exists)
344 345
         self.stubs.Set(utils, 'execute', fake_execute)
345  
-        self.stubs.Set(connection.disk, 'extend', fake_extend)
346  
-        connection.libvirt_utils = fake_libvirt_utils
  346
+        self.stubs.Set(imagebackend.disk, 'extend', fake_extend)
  347
+        imagebackend.libvirt_utils = fake_libvirt_utils
347 348
 
348 349
     def tearDown(self):
349  
-        connection.libvirt_utils = libvirt_utils
  350
+        imagebackend.libvirt_utils = libvirt_utils
350 351
         super(CacheConcurrencyTestCase, self).tearDown()
351 352
 
352 353
     def test_same_fname_concurrency(self):
353 354
         """Ensures that the same fname cache runs at a sequentially"""
354  
-        conn = connection.LibvirtDriver
  355
+        backend = imagebackend.Backend(False)
355 356
         wait1 = eventlet.event.Event()
356 357
         done1 = eventlet.event.Event()
357  
-        eventlet.spawn(conn._cache_image, _concurrency,
358  
-                       'target', 'fname', False, None, wait1, done1)
  358
+        eventlet.spawn(backend.image('instance', 'name').cache,
  359
+                _concurrency, 'fname', None, wait=wait1, done=done1)
359 360
         wait2 = eventlet.event.Event()
360 361
         done2 = eventlet.event.Event()
361  
-        eventlet.spawn(conn._cache_image, _concurrency,
362  
-                       'target', 'fname', False, None, wait2, done2)
  362
+        eventlet.spawn(backend.image('instance', 'name').cache,
  363
+                _concurrency, 'fname', None, wait=wait2, done=done2)
363 364
         wait2.send()
364 365
         eventlet.sleep(0)
365 366
         try:
@@ -372,15 +373,15 @@ def test_same_fname_concurrency(self):
372 373
 
373 374
     def test_different_fname_concurrency(self):
374 375
         """Ensures that two different fname caches are concurrent"""
375  
-        conn = connection.LibvirtDriver
  376
+        backend = imagebackend.Backend(False)
376 377
         wait1 = eventlet.event.Event()
377 378
         done1 = eventlet.event.Event()
378  
-        eventlet.spawn(conn._cache_image, _concurrency,
379  
-                       'target', 'fname2', False, None, wait1, done1)
  379
+        eventlet.spawn(backend.image('instance', 'name').cache,
  380
+                _concurrency, 'fname2', None, wait=wait1, done=done1)
380 381
         wait2 = eventlet.event.Event()
381 382
         done2 = eventlet.event.Event()
382  
-        eventlet.spawn(conn._cache_image, _concurrency,
383  
-                       'target', 'fname1', False, None, wait2, done2)
  383
+        eventlet.spawn(backend.image('instance', 'name').cache,
  384
+                _concurrency, 'fname1', None, wait=wait2, done=done2)
384 385
         wait2.send()
385 386
         eventlet.sleep(0)
386 387
         try:
2  nova/tests/test_virt_drivers.py
@@ -63,6 +63,7 @@ def _setup_fakelibvirt(self):
63 63
         else:
64 64
             self.saved_libvirt = None
65 65
 
  66
+        import fake_imagebackend
66 67
         import fake_libvirt_utils
67 68
         import fakelibvirt
68 69
 
@@ -70,6 +71,7 @@ def _setup_fakelibvirt(self):
70 71
         import nova.virt.libvirt.connection
71 72
         import nova.virt.libvirt.firewall
72 73
 
  74
+        nova.virt.libvirt.connection.imagebackend = fake_imagebackend
73 75
         nova.virt.libvirt.connection.libvirt = fakelibvirt
74 76
         nova.virt.libvirt.connection.libvirt_utils = fake_libvirt_utils
75 77
         nova.virt.libvirt.firewall.libvirt = fakelibvirt
8  nova/virt/disk/api.py
@@ -108,6 +108,11 @@ def get_image_virtual_size(image):
108 108
     return int(m.group(2))
109 109
 
110 110
 
  111
+def resize2fs(image, check_exit_code=False):
  112
+    utils.execute('e2fsck', '-fp', image, check_exit_code=check_exit_code)
  113
+    utils.execute('resize2fs', image, check_exit_code=check_exit_code)
  114
+
  115
+
111 116
 def extend(image, size):
112 117
     """Increase image to size"""
113 118
     # NOTE(MotoKen): check image virtual size before resize
@@ -116,8 +121,7 @@ def extend(image, size):
116 121
         return
117 122
     utils.execute('qemu-img', 'resize', image, size)
118 123
     # NOTE(vish): attempts to resize filesystem
119  
-    utils.execute('e2fsck', '-fp', image, check_exit_code=False)
120  
-    utils.execute('resize2fs', image, check_exit_code=False)
  124
+    resize2fs(image)
121 125
 
122 126
 
123 127
 def bind(src, target, instance_name):
269  nova/virt/libvirt/connection.py
@@ -71,6 +71,7 @@
71 71
 from nova.virt import driver
72 72
 from nova.virt.libvirt import config
73 73
 from nova.virt.libvirt import firewall
  74
+from nova.virt.libvirt import imagebackend
74 75
 from nova.virt.libvirt import imagecache
75 76
 from nova.virt.libvirt import utils as libvirt_utils
76 77
 
@@ -271,6 +272,7 @@ def __init__(self, read_only=False):
271 272
 
272 273
         self._disk_cachemode = None
273 274
         self.image_cache_manager = imagecache.ImageCacheManager()
  275
+        self.image_backend = imagebackend.Backend(FLAGS.use_cow_images)
274 276
 
275 277
     @property
276 278
     def disk_cachemode(self):
@@ -506,6 +508,34 @@ def _cleanup(self, instance, network_info, block_device_info):
506 508
         if os.path.exists(target):
507 509
             shutil.rmtree(target)
508 510
 
  511
+        #NOTE(bfilippov): destroy all LVM disks for this instance
  512
+        self._cleanup_lvm(instance)
  513
+
  514
+    def _cleanup_lvm(self, instance):
  515
+        """Delete all LVM disks for given instance object"""
  516
+        disks = self._lvm_disks(instance)
  517
+        if disks:
  518
+            libvirt_utils.remove_logical_volumes(*disks)
  519
+
  520
+    def _lvm_disks(self, instance):
  521
+        """Returns all LVM disks for given instance object"""
  522
+        if FLAGS.libvirt_images_volume_group:
  523
+            vg = os.path.join('/dev', FLAGS.libvirt_images_volume_group)
  524
+            if not os.path.exists(vg):
  525
+                return []
  526
+            pattern = '%s_' % instance['name']
  527
+
  528
+            def belongs_to_instance(disk):
  529
+                return disk.startswith(pattern)
  530
+
  531
+            def fullpath(name):
  532
+                return os.path.join(vg, name)
  533
+
  534
+            disk_names = filter(belongs_to_instance, os.listdir(vg))
  535
+            disks = map(fullpath, disk_names)
  536
+            return disks
  537
+        return []
  538
+
509 539
     def get_volume_connector(self, instance):
510 540
         if not self._initiator:
511 541
             self._initiator = libvirt_utils.get_iscsi_initiator()
@@ -681,7 +711,10 @@ def snapshot(self, context, instance, image_href):
681 711
             # NOTE(vish): assume amis are raw
682 712
             source_format = 'raw'
683 713
         image_format = FLAGS.snapshot_image_format or source_format
684  
-        if FLAGS.use_cow_images:
  714
+        use_qcow2 = ((FLAGS.libvirt_images_type == 'default' and
  715
+                FLAGS.use_cow_images) or
  716
+                FLAGS.libvirt_images_type == 'qcow2')
  717
+        if use_qcow2:
685 718
             source_format = 'qcow2'
686 719
         # NOTE(vish): glance forces ami disk format to be ami
687 720
         if base.get('disk_format') == 'ami':
@@ -957,8 +990,8 @@ def _append_to_file(self, data, fpath):
957 990
         return fpath
958 991
 
959 992
     def _inject_files(self, instance, files, partition):
960  
-        disk_path = os.path.join(FLAGS.instances_path,
961  
-                                 instance['name'], 'disk')
  993
+        disk_path = self.image_backend.image(instance['name'],
  994
+                                             'disk').path
962 995
         disk.inject_files(disk_path, files, partition=partition,
963 996
                           use_cow=FLAGS.use_cow_images)
964 997
 
@@ -1071,72 +1104,6 @@ def _supports_direct_io(dirpath):
1071 1104
         return hasDirectIO
1072 1105
 
1073 1106
     @staticmethod
1074  
-    def _cache_image(fn, target, fname, cow=False, size=None, *args, **kwargs):
1075  
-        """Wrapper for a method that creates an image that caches the image.
1076  
-
1077  
-        This wrapper will save the image into a common store and create a
1078  
-        copy for use by the hypervisor.
1079  
-
1080  
-        The underlying method should specify a kwarg of target representing
1081  
-        where the image will be saved.
1082  
-
1083  
-        fname is used as the filename of the base image.  The filename needs
1084  
-        to be unique to a given image.
1085  
-
1086  
-        If cow is True, it will make a CoW image instead of a copy.
1087  
-
1088  
-        If size is specified, we attempt to resize up to that size.
1089  
-        """
1090  
-
1091  
-        # NOTE(mikal): Checksums aren't created here, even if the image cache
1092  
-        # manager is enabled, as that would slow down VM startup. If both
1093  
-        # cache management and checksumming are enabled, then the checksum
1094  
-        # will be created on the first pass of the image cache manager.
1095  
-
1096  
-        generating = 'image_id' not in kwargs
1097  
-        if not os.path.exists(target):
1098  
-            base_dir = os.path.join(FLAGS.instances_path, FLAGS.base_dir_name)
1099  
-
1100  
-            if not os.path.exists(base_dir):
1101  
-                libvirt_utils.ensure_tree(base_dir)
1102  
-            base = os.path.join(base_dir, fname)
1103  
-
1104  
-            @utils.synchronized(fname)
1105  
-            def call_if_not_exists(base, fn, *args, **kwargs):
1106  
-                if not os.path.exists(base):
1107  
-                    with utils.remove_path_on_error(base):
1108  
-                        fn(target=base, *args, **kwargs)
1109  
-
1110  
-            if cow or not generating:
1111  
-                call_if_not_exists(base, fn, *args, **kwargs)
1112  
-            elif generating:
1113  
-                # For raw it's quicker to just generate outside the cache
1114  
-                call_if_not_exists(target, fn, *args, **kwargs)
1115  
-
1116  
-            @utils.synchronized(base)
1117  
-            def copy_and_extend(cow, generating, base, target, size):
1118  
-                if cow:
1119  
-                    cow_base = base
1120  
-                    if size:
1121  
-                        size_gb = size / (1024 * 1024 * 1024)
1122  
-                        cow_base += "_%d" % size_gb
1123  
-                        if not os.path.exists(cow_base):
1124  
-                            with utils.remove_path_on_error(cow_base):
1125  
-                                libvirt_utils.copy_image(base, cow_base)
1126  
-                                disk.extend(cow_base, size)
1127  
-                    libvirt_utils.create_cow_image(cow_base, target)
1128  
-                elif not generating:
1129  
-                    libvirt_utils.copy_image(base, target)
1130  
-                    # Resize after the copy, as it's usually much faster
1131  
-                    # to make sparse updates, rather than potentially
1132  
-                    # naively copying the whole image file.
1133  
-                    if size:
1134  
-                        disk.extend(target, size)
1135  
-
1136  
-            with utils.remove_path_on_error(target):
1137  
-                copy_and_extend(cow, generating, base, target, size)
1138  
-
1139  
-    @staticmethod
1140 1107
     def _create_local(target, local_size, unit='G',
1141 1108
                       fs_format=None, label=None):
1142 1109
         """Create a blank image of specified size"""
@@ -1181,6 +1148,13 @@ def basepath(fname='', suffix=suffix):
1181 1148
                                 instance['name'],
1182 1149
                                 fname + suffix)
1183 1150
 
  1151
+        def image(fname, image_type=FLAGS.libvirt_images_type):
  1152
+            return self.image_backend.image(instance['name'],
  1153
+                                            fname, suffix, image_type)
  1154
+
  1155
+        def raw(fname):
  1156
+            return image(fname, image_type='raw')
  1157
+
1184 1158
         # ensure directories exist and are writable
1185 1159
         libvirt_utils.ensure_tree(basepath(suffix=''))
1186 1160
 
@@ -1204,22 +1178,20 @@ def basepath(fname='', suffix=suffix):
1204 1178
 
1205 1179
         if disk_images['kernel_id']:
1206 1180
             fname = disk_images['kernel_id']
1207  
-            self._cache_image(fn=libvirt_utils.fetch_image,
1208  
-                              context=context,
1209  
-                              target=basepath('kernel'),
1210  
-                              fname=fname,
1211  
-                              image_id=disk_images['kernel_id'],
1212  
-                              user_id=instance['user_id'],
1213  
-                              project_id=instance['project_id'])
  1181
+            raw('kernel').cache(fn=libvirt_utils.fetch_image,
  1182
+                                context=context,
  1183
+                                fname=fname,
  1184
+                                image_id=disk_images['kernel_id'],
  1185
+                                user_id=instance['user_id'],
  1186
+                                project_id=instance['project_id'])
1214 1187
             if disk_images['ramdisk_id']:
1215 1188
                 fname = disk_images['ramdisk_id']
1216  
-                self._cache_image(fn=libvirt_utils.fetch_image,
1217  
-                                  context=context,
1218  
-                                  target=basepath('ramdisk'),
1219  
-                                  fname=fname,
1220  
-                                  image_id=disk_images['ramdisk_id'],
1221  
-                                  user_id=instance['user_id'],
1222  
-                                  project_id=instance['project_id'])
  1189
+                raw('ramdisk').cache(fn=libvirt_utils.fetch_image,
  1190
+                                     context=context,
  1191
+                                     fname=fname,
  1192
+                                     image_id=disk_images['ramdisk_id'],
  1193
+                                     user_id=instance['user_id'],
  1194
+                                     project_id=instance['project_id'])
1223 1195
 
1224 1196
         root_fname = hashlib.sha1(str(disk_images['image_id'])).hexdigest()
1225 1197
         size = instance['root_gb'] * 1024 * 1024 * 1024
@@ -1231,15 +1203,13 @@ def basepath(fname='', suffix=suffix):
1231 1203
 
1232 1204
         if not self._volume_in_mapping(self.default_root_device,
1233 1205
                                        block_device_info):
1234  
-            self._cache_image(fn=libvirt_utils.fetch_image,
1235  
-                              context=context,
1236  
-                              target=basepath('disk'),
1237  
-                              fname=root_fname,
1238  
-                              cow=FLAGS.use_cow_images,
1239  
-                              image_id=disk_images['image_id'],
1240  
-                              user_id=instance['user_id'],
1241  
-                              project_id=instance['project_id'],
1242  
-                              size=size)
  1206
+            image('disk').cache(fn=libvirt_utils.fetch_image,
  1207
+                                context=context,
  1208
+                                fname=root_fname,
  1209
+                                size=size,
  1210
+                                image_id=disk_images['image_id'],
  1211
+                                user_id=instance['user_id'],
  1212
+                                project_id=instance['project_id'])
1243 1213
 
1244 1214
         ephemeral_gb = instance['ephemeral_gb']
1245 1215
         if ephemeral_gb and not self._volume_in_mapping(
@@ -1248,12 +1218,14 @@ def basepath(fname='', suffix=suffix):
1248 1218
             fn = functools.partial(self._create_ephemeral,
1249 1219
                                    fs_label='ephemeral0',
1250 1220
                                    os_type=instance.os_type)
1251  
-            self._cache_image(fn=fn,
1252  
-                              target=basepath('disk.local'),
1253  
-                              fname="ephemeral_%s_%s_%s" %
1254  
-                              ("0", ephemeral_gb, instance.os_type),
1255  
-                              cow=FLAGS.use_cow_images,
1256  
-                              ephemeral_size=ephemeral_gb)
  1221
+            fname = "ephemeral_%s_%s_%s" % ("0",
  1222
+                                            ephemeral_gb,
  1223
+                                            instance.os_type)
  1224
+            size = ephemeral_gb * 1024 * 1024 * 1024
  1225
+            image('disk.local').cache(fn=fn,
  1226
+                                      fname=fname,
  1227
+                                      size=size,
  1228
+                                      ephemeral_size=ephemeral_gb)
1257 1229
         else:
1258 1230
             swap_device = self.default_second_device
1259 1231
 
@@ -1261,12 +1233,14 @@ def basepath(fname='', suffix=suffix):
1261 1233
             fn = functools.partial(self._create_ephemeral,
1262 1234
                                    fs_label='ephemeral%d' % eph['num'],
1263 1235
                                    os_type=instance.os_type)
1264  
-            self._cache_image(fn=fn,
1265  
-                              target=basepath(_get_eph_disk(eph)),
1266  
-                              fname="ephemeral_%s_%s_%s" %
1267  
-                              (eph['num'], eph['size'], instance.os_type),
1268  
-                              cow=FLAGS.use_cow_images,
1269  
-                              ephemeral_size=eph['size'])
  1236
+            size = eph['size'] * 1024 * 1024 * 1024
  1237
+            fname = "ephemeral_%s_%s_%s" % (eph['num'],
  1238
+                                            eph['size'],
  1239
+                                            instance.os_type)
  1240
+            image(_get_eph_disk(eph)).cache(fn=fn,
  1241
+                                            fname=fname,
  1242
+                                            size=size,
  1243
+                                            ephemeral_size=eph['size'])
1270 1244
 
1271 1245
         swap_mb = 0
1272 1246
 
@@ -1278,11 +1252,11 @@ def basepath(fname='', suffix=suffix):
1278 1252
             swap_mb = inst_type['swap']
1279 1253
 
1280 1254
         if swap_mb > 0:
1281  
-            self._cache_image(fn=self._create_swap,
1282  
-                              target=basepath('disk.swap'),
1283  
-                              fname="swap_%s" % swap_mb,
1284  
-                              cow=FLAGS.use_cow_images,
1285  
-                              swap_mb=swap_mb)
  1255
+            size = swap_mb * 1024 * 1024
  1256
+            image('disk.swap').cache(fn=self._create_swap,
  1257
+                                     fname="swap_%s" % swap_mb,
  1258
+                                     size=size,
  1259
+                                     swap_mb=swap_mb)
1286 1260
 
1287 1261
         target_partition = None
1288 1262
         if not instance['kernel_id']:
@@ -1297,12 +1271,11 @@ def basepath(fname='', suffix=suffix):
1297 1271
 
1298 1272
         if config_drive_id:
1299 1273
             fname = config_drive_id
1300  
-            self._cache_image(fn=libvirt_utils.fetch_image,
1301  
-                              target=basepath('disk.config'),
1302  
-                              fname=fname,
1303  
-                              image_id=config_drive_id,
1304  
-                              user_id=instance['user_id'],
1305  
-                              project_id=instance['project_id'],)
  1274
+            raw('disk.config').cache(fn=libvirt_utils.fetch_image,
  1275
+                                     fname=fname,
  1276
+                                     image_id=config_drive_id,
  1277
+                                     user_id=instance['user_id'],
  1278
+                                     project_id=instance['project_id'])
1306 1279
         elif config_drive:
1307 1280
             label = 'config'
1308 1281
             with utils.remove_path_on_error(basepath('disk.config')):
@@ -1360,10 +1333,10 @@ def basepath(fname='', suffix=suffix):
1360 1333
 
1361 1334
         if any((key, net, metadata, admin_password)):
1362 1335
             if config_drive:  # Should be True or None by now.
1363  
-                injection_path = basepath('disk.config')
  1336
+                injection_path = raw('disk.config').path
1364 1337
                 img_id = 'config-drive'
1365 1338
             else:
1366  
-                injection_path = basepath('disk')
  1339
+                injection_path = image('disk').path
1367 1340
                 img_id = instance.image_ref
1368 1341
 
1369 1342
             for injection in ('metadata', 'key', 'net', 'admin_password'):
@@ -1511,16 +1484,16 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1511 1484
                                          "rootfs")
1512 1485
             guest.add_device(fs)
1513 1486
         else:
1514  
-            if FLAGS.use_cow_images:
1515  
-                driver_type = 'qcow2'
1516  
-            else:
1517  
-                driver_type = 'raw'
1518  
-
1519 1487
             if image_meta and image_meta.get('disk_format') == 'iso':
1520 1488
                 root_device_type = 'cdrom'
1521 1489
             else:
1522 1490
                 root_device_type = 'disk'
1523 1491
 
  1492
+            def disk_info(name, suffix=''):
  1493
+                image = self.image_backend.image(instance['name'],
  1494
+                                                 name, suffix)
  1495
+                return image.libvirt_info(root_device_type)
  1496
+
1524 1497
             if FLAGS.libvirt_type == "uml":
1525 1498
                 ephemeral_disk_bus = "uml"
1526 1499
             elif FLAGS.libvirt_type == "xen":
@@ -1529,23 +1502,13 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1529 1502
                 ephemeral_disk_bus = "virtio"
1530 1503
 
1531 1504
             if rescue:
1532  
-                diskrescue = config.LibvirtConfigGuestDisk()
1533  
-                diskrescue.source_type = "file"
1534  
-                diskrescue.source_path = os.path.join(FLAGS.instances_path,
1535  
-                                                      instance['name'],
1536  
-                                                      "disk.rescue")
1537  
-                diskrescue.driver_format = driver_type
  1505
+                diskrescue = disk_info('disk', '.rescue')
1538 1506
                 diskrescue.driver_cache = self.disk_cachemode
1539 1507
                 diskrescue.target_dev = self.default_root_device
1540 1508
                 diskrescue.target_bus = ephemeral_disk_bus
1541 1509
                 guest.add_device(diskrescue)
1542 1510
 
1543  
-                diskos = config.LibvirtConfigGuestDisk()
1544  
-                diskos.source_type = "file"
1545  
-                diskos.source_path = os.path.join(FLAGS.instances_path,
1546  
-                                                  instance['name'],
1547  
-                                                  "disk")
1548  
-                diskos.driver_format = driver_type
  1511
+                diskos = disk_info('disk')
1549 1512
                 diskos.driver_cache = self.disk_cachemode
1550 1513
                 diskos.target_dev = self.default_second_device
1551 1514
                 diskos.target_bus = ephemeral_disk_bus
@@ -1555,14 +1518,8 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1555 1518
                                                    block_device_info)
1556 1519
 
1557 1520
                 if not ebs_root:
1558  
-                    diskos = config.LibvirtConfigGuestDisk()
1559  
-                    diskos.source_type = "file"
1560  
-                    diskos.source_device = root_device_type
1561  
-                    diskos.driver_format = driver_type
  1521
+                    diskos = disk_info('disk')
1562 1522
                     diskos.driver_cache = self.disk_cachemode
1563  
-                    diskos.source_path = os.path.join(FLAGS.instances_path,
1564  
-                                                      instance['name'],
1565  
-                                                      "disk")
1566 1523
                     diskos.target_dev = root_device
1567 1524
                     if root_device_type == "cdrom":
1568 1525
                         diskos.target_bus = "ide"
@@ -1580,14 +1537,8 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1580 1537
                         ephemeral_device = self.default_second_device
1581 1538
 
1582 1539
                 if ephemeral_device is not None:
1583  
-                    disklocal = config.LibvirtConfigGuestDisk()
1584  
-                    disklocal.source_type = "file"
1585  
-                    disklocal.source_device = root_device_type
1586  
-                    disklocal.driver_format = driver_type
  1540
+                    disklocal = disk_info('disk.local')
1587 1541
                     disklocal.driver_cache = self.disk_cachemode
1588  
-                    disklocal.source_path = os.path.join(FLAGS.instances_path,
1589  
-                                                         instance['name'],
1590  
-                                                         "disk.local")
1591 1542
                     disklocal.target_dev = ephemeral_device
1592 1543
                     disklocal.target_bus = ephemeral_disk_bus
1593 1544
                     guest.add_device(disklocal)
@@ -1603,14 +1554,8 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1603 1554
 
1604 1555
                 for eph in driver.block_device_info_get_ephemerals(
1605 1556
                     block_device_info):
1606  
-                    diskeph = config.LibvirtConfigGuestDisk()
1607  
-                    diskeph.source_type = "block"
1608  
-                    diskeph.source_device = root_device_type
1609  
-                    diskeph.driver_format = driver_type
  1557
+                    diskeph = disk_info(_get_eph_disk(eph))
1610 1558
                     diskeph.driver_cache = self.disk_cachemode
1611  
-                    diskeph.source_path = os.path.join(FLAGS.instances_path,
1612  
-                                                       instance['name'],
1613  
-                                                       _get_eph_disk(eph))
1614 1559
                     diskeph.target_dev = block_device.strip_dev(
1615 1560
                         eph['device_name'])
1616 1561
                     diskeph.target_bus = ephemeral_disk_bus
@@ -1618,13 +1563,8 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1618 1563
 
1619 1564
                 swap = driver.block_device_info_get_swap(block_device_info)
1620 1565
                 if driver.swap_is_usable(swap):
1621  
-                    diskswap = config.LibvirtConfigGuestDisk()
1622  
-                    diskswap.disk_type = "file"
1623  
-                    diskswap.driver_format = driver_type
  1566
+                    diskswap = disk_info('disk.swap')
1624 1567
                     diskswap.driver_cache = self.disk_cachemode
1625  
-                    diskswap.source_path = os.path.join(FLAGS.instances_path,
1626  
-                                                        instance['name'],
1627  
-                                                        "disk.swap")
1628 1568
                     diskswap.target_dev = block_device.strip_dev(
1629 1569
                         swap['device_name'])
1630 1570
                     diskswap.target_bus = ephemeral_disk_bus
@@ -1632,13 +1572,8 @@ def get_guest_config(self, instance, network_info, image_meta, rescue=None,
1632 1572
                 elif (inst_type['swap'] > 0 and
1633 1573
                       not self._volume_in_mapping(swap_device,
1634 1574
                                                   block_device_info)):
1635  
-                    diskswap = config.LibvirtConfigGuestDisk()
1636  
-                    diskswap.disk_type = "file"
1637  
-                    diskswap.driver_format = driver_type
  1575
+                    diskswap = disk_info('disk.swap')
1638 1576
                     diskswap.driver_cache = self.disk_cachemode
1639  
-                    diskswap.source_path = os.path.join(FLAGS.instances_path,
1640  
-                                                        instance['name'],
1641  
-                                                        "disk.swap")
1642 1577
                     diskswap.target_dev = swap_device
1643 1578
                     diskswap.target_bus = ephemeral_disk_bus
1644 1579
                     guest.add_device(diskswap)
255  nova/virt/libvirt/imagebackend.py
... ...
@@ -0,0 +1,255 @@
  1
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
  2
+
  3
+# Copyright 2012 Grid Dynamics
  4
+# All Rights Reserved.
  5
+#
  6
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
  7
+#    not use this file except in compliance with the License. You may obtain
  8
+#    a copy of the License at
  9
+#
  10
+#         http://www.apache.org/licenses/LICENSE-2.0
  11
+#
  12
+#    Unless required by applicable law or agreed to in writing, software
  13
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15
+#    License for the specific language governing permissions and limitations
  16
+#    under the License.
  17
+
  18
+import abc
  19
+import contextlib
  20
+import os
  21
+
  22
+from nova import flags
  23
+from nova.openstack.common import cfg
  24
+from nova.openstack.common import excutils
  25
+from nova import utils
  26
+from nova.virt.disk import api as disk
  27
+from nova.virt.libvirt import config
  28
+from nova.virt.libvirt import utils as libvirt_utils
  29
+
  30
+__imagebackend_opts = [
  31
+    cfg.StrOpt('libvirt_images_type',
  32
+            default='default',
  33
+            help='VM Images format. Acceptable values are: raw, qcow2, lvm,'
  34
+                 ' default. If default is specified,'
  35
+                 ' then use_cow_images flag is used instead of this one.'),
  36
+    cfg.StrOpt('libvirt_images_volume_group',
  37
+            default=None,
  38
+            help='LVM Volume Group that is used for VM images, when you'
  39
+                 ' specify libvirt_images_type=lvm.'),
  40
+    cfg.BoolOpt('libvirt_sparse_logical_volumes',
  41
+            default=False,
  42
+            help='Create sparse logical volumes (with virtualsize)'
  43
+                 ' if this flag is set to True.'),
  44
+        ]
  45
+
  46
+FLAGS = flags.FLAGS
  47
+FLAGS.register_opts(__imagebackend_opts)
  48
+
  49
+
  50
+class Image(object):
  51
+    __metaclass__ = abc.ABCMeta
  52
+
  53
+    @abc.abstractmethod
  54
+    def __init__(self, instance, name, suffix):
  55
+        """Image initialization.
  56
+
  57
+        :instance: Instance name.
  58
+        :name: Image name.
  59
+        :suffix: Suffix for image name.
  60
+        """
  61
+        pass
  62
+
  63
+    @abc.abstractmethod
  64
+    def create_image(self, prepare_template, base, size, *args, **kwargs):
  65
+        """Create image from template.
  66
+
  67
+        Contains specific behavior for each image type.
  68
+
  69
+        :prepare_template: function, that creates template.
  70
+        Should accept `target` argument.
  71
+        :base: Template name
  72
+        :size: Size of created image in bytes
  73
+        """
  74
+        pass
  75
+
  76
+    @abc.abstractmethod
  77
+    def libvirt_info(self, device_type):
  78
+        """Get `LibvirtConfigGuestDisk` filled for this image.
  79
+
  80
+        :device_type: Device type for this image.
  81
+        """
  82
+        pass
  83
+
  84
+    def cache(self, fn, fname, size=None, *args, **kwargs):
  85
+        """Creates image from template.
  86
+
  87
+        Ensures that template and image not already exists.
  88
+        Ensures that base directory exists.
  89
+        Synchronizes on template fetching.
  90
+
  91
+        :fn: function, that creates template.
  92
+        Should accept `target` argument.
  93
+        :fname: Template name
  94
+        :size: Size of created image in bytes (optional)