~ubuntu-branches/ubuntu/quantal/nova/quantal-security

« back to all changes in this revision

Viewing changes to nova/virt/libvirt/imagebackend.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Adam Gandelman
  • Date: 2012-06-22 12:39:57 UTC
  • mfrom: (1.1.57)
  • Revision ID: package-import@ubuntu.com-20120622123957-hbzwg84nt9rqwg8r
Tags: 2012.2~f2~20120621.14517-0ubuntu1
[ Chuck Short ]
* New upstream version.

[ Adam Gandelman ]
* debian/rules: Temporarily disable test suite while blocking
  tests are investigated. 
* debian/patches/kombu_tests_timeout.patch: Dropped.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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)
 
95
        """
 
96
        @utils.synchronized(fname)
 
97
        def call_if_not_exists(target, *args, **kwargs):
 
98
            if not os.path.exists(target):
 
99
                fn(target=target, *args, **kwargs)
 
100
 
 
101
        if not os.path.exists(self.path):
 
102
            base_dir = os.path.join(FLAGS.instances_path, '_base')
 
103
            if not os.path.exists(base_dir):
 
104
                libvirt_utils.ensure_tree(base_dir)
 
105
            base = os.path.join(base_dir, fname)
 
106
 
 
107
            self.create_image(call_if_not_exists, base, size,
 
108
                               *args, **kwargs)
 
109
 
 
110
 
 
111
class Raw(Image):
 
112
    def __init__(self, instance, name, suffix):
 
113
        if not suffix:
 
114
            suffix = ''
 
115
        self.path = os.path.join(FLAGS.instances_path,
 
116
                                 instance, name + suffix)
 
117
 
 
118
    def libvirt_info(self, device_type):
 
119
        info = config.LibvirtConfigGuestDisk()
 
120
        info.source_type = 'file'
 
121
        info.source_device = device_type
 
122
        info.driver_format = 'raw'
 
123
        info.source_path = self.path
 
124
        return info
 
125
 
 
126
    def create_image(self, prepare_template, base, size, *args, **kwargs):
 
127
        @utils.synchronized(base)
 
128
        def copy_raw_image(base, target, size):
 
129
            libvirt_utils.copy_image(base, target)
 
130
            if size:
 
131
                disk.extend(target, size)
 
132
 
 
133
        generating = 'image_id' not in kwargs
 
134
        if generating:
 
135
            #Generating image in place
 
136
            prepare_template(target=self.path, *args, **kwargs)
 
137
        else:
 
138
            prepare_template(target=base, *args, **kwargs)
 
139
            with utils.remove_path_on_error(self.path):
 
140
                copy_raw_image(base, self.path, size)
 
141
 
 
142
 
 
143
class Qcow2(Raw):
 
144
    def libvirt_info(self, device_type):
 
145
        info = config.LibvirtConfigGuestDisk()
 
146
        info.source_type = 'file'
 
147
        info.source_device = device_type
 
148
        info.driver_format = 'qcow2'
 
149
        info.source_path = self.path
 
150
        return info
 
151
 
 
152
    def create_image(self, prepare_template, base, size, *args, **kwargs):
 
153
        @utils.synchronized(base)
 
154
        def copy_qcow2_image(base, target, size):
 
155
            qcow2_base = base
 
156
            if size:
 
157
                size_gb = size / (1024 * 1024 * 1024)
 
158
                qcow2_base += '_%d' % size_gb
 
159
                if not os.path.exists(qcow2_base):
 
160
                    with utils.remove_path_on_error(qcow2_base):
 
161
                        libvirt_utils.copy_image(base, qcow2_base)
 
162
                        disk.extend(qcow2_base, size)
 
163
            libvirt_utils.create_cow_image(qcow2_base, target)
 
164
 
 
165
        prepare_template(target=base, *args, **kwargs)
 
166
        with utils.remove_path_on_error(self.path):
 
167
            copy_qcow2_image(base, self.path, size)
 
168
 
 
169
 
 
170
class Lvm(Image):
 
171
    @staticmethod
 
172
    def escape(fname):
 
173
        return fname.replace('_', '__')
 
174
 
 
175
    def libvirt_info(self, device_type):
 
176
        info = config.LibvirtConfigGuestDisk()
 
177
        info.source_type = 'block'
 
178
        info.source_device = device_type
 
179
        info.driver_format = 'raw'
 
180
        info.source_path = self.path
 
181
        return info
 
182
 
 
183
    def __init__(self, instance, name, suffix):
 
184
        if not suffix:
 
185
            suffix = ''
 
186
        if not FLAGS.libvirt_images_volume_group:
 
187
            raise RuntimeError(_('You should specify'
 
188
                               ' libvirt_images_volume_group'
 
189
                               ' flag to use LVM images.'))
 
190
        self.vg = FLAGS.libvirt_images_volume_group
 
191
        self.lv = '%s_%s' % (self.escape(instance),
 
192
                             self.escape(name + suffix))
 
193
        self.path = os.path.join('/dev', self.vg, self.lv)
 
194
        self.sparse = FLAGS.libvirt_sparse_logical_volumes
 
195
 
 
196
    def create_image(self, prepare_template, base, size, *args, **kwargs):
 
197
        @utils.synchronized(base)
 
198
        def create_lvm_image(base, size):
 
199
            base_size = disk.get_image_virtual_size(base)
 
200
            resize = size > base_size
 
201
            size = size if resize else base_size
 
202
            libvirt_utils.create_lvm_image(self.vg, self.lv,
 
203
                                           size, sparse=self.sparse)
 
204
            cmd = ('dd', 'if=%s' % base, 'of=%s' % self.path, 'bs=4M')
 
205
            utils.execute(*cmd, run_as_root=True)
 
206
            if resize:
 
207
                disk.resize2fs(self.path)
 
208
 
 
209
        generated = 'ephemeral_size' in kwargs
 
210
 
 
211
        #Generate images with specified size right on volume
 
212
        if generated and size:
 
213
            libvirt_utils.create_lvm_image(self.vg, self.lv,
 
214
                                           size, sparse=self.sparse)
 
215
            with self.remove_volume_on_error(self.path):
 
216
                prepare_template(target=self.path, *args, **kwargs)
 
217
        else:
 
218
            prepare_template(target=base, *args, **kwargs)
 
219
            with self.remove_volume_on_error(self.path):
 
220
                create_lvm_image(base, size)
 
221
 
 
222
    @contextlib.contextmanager
 
223
    def remove_volume_on_error(self, path):
 
224
        try:
 
225
            yield
 
226
        except Exception:
 
227
            with excutils.save_and_reraise_exception():
 
228
                libvirt_utils.remove_logical_volumes(path)
 
229
 
 
230
 
 
231
class Backend(object):
 
232
    def __init__(self, use_cow):
 
233
        self.BACKEND = {
 
234
            'raw': Raw,
 
235
            'qcow2': Qcow2,
 
236
            'lvm': Lvm,
 
237
            'default': Qcow2 if use_cow else Raw
 
238
        }
 
239
 
 
240
    def image(self, instance, name,
 
241
              suffix=None, image_type=None):
 
242
        """Constructs image for selected backend
 
243
 
 
244
        :instance: Instance name.
 
245
        :name: Image name.
 
246
        :suffix: Suffix for image name (optional).
 
247
        :image_type: Image type.
 
248
        Optional, is FLAGS.libvirt_images_type by default.
 
249
        """
 
250
        if not image_type:
 
251
            image_type = FLAGS.libvirt_images_type
 
252
        image = self.BACKEND.get(image_type)
 
253
        if not image:
 
254
            raise RuntimeError(_('Unknown image_type=%s') % image_type)
 
255
        return image(instance, name, suffix)