1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2012 Grid Dynamics
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
10
# http://www.apache.org/licenses/LICENSE-2.0
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
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
30
__imagebackend_opts = [
31
cfg.StrOpt('libvirt_images_type',
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',
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',
42
help='Create sparse logical volumes (with virtualsize)'
43
' if this flag is set to True.'),
47
FLAGS.register_opts(__imagebackend_opts)
51
__metaclass__ = abc.ABCMeta
54
def __init__(self, instance, name, suffix):
55
"""Image initialization.
57
:instance: Instance name.
59
:suffix: Suffix for image name.
64
def create_image(self, prepare_template, base, size, *args, **kwargs):
65
"""Create image from template.
67
Contains specific behavior for each image type.
69
:prepare_template: function, that creates template.
70
Should accept `target` argument.
72
:size: Size of created image in bytes
77
def libvirt_info(self, device_type):
78
"""Get `LibvirtConfigGuestDisk` filled for this image.
80
:device_type: Device type for this image.
84
def cache(self, fn, fname, size=None, *args, **kwargs):
85
"""Creates image from template.
87
Ensures that template and image not already exists.
88
Ensures that base directory exists.
89
Synchronizes on template fetching.
91
:fn: function, that creates template.
92
Should accept `target` argument.
94
:size: Size of created image in bytes (optional)
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)
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)
107
self.create_image(call_if_not_exists, base, size,
112
def __init__(self, instance, name, suffix):
115
self.path = os.path.join(FLAGS.instances_path,
116
instance, name + suffix)
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
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)
131
disk.extend(target, size)
133
generating = 'image_id' not in kwargs
135
#Generating image in place
136
prepare_template(target=self.path, *args, **kwargs)
138
prepare_template(target=base, *args, **kwargs)
139
with utils.remove_path_on_error(self.path):
140
copy_raw_image(base, self.path, size)
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
152
def create_image(self, prepare_template, base, size, *args, **kwargs):
153
@utils.synchronized(base)
154
def copy_qcow2_image(base, target, 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)
165
prepare_template(target=base, *args, **kwargs)
166
with utils.remove_path_on_error(self.path):
167
copy_qcow2_image(base, self.path, size)
173
return fname.replace('_', '__')
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
183
def __init__(self, instance, name, 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
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)
207
disk.resize2fs(self.path)
209
generated = 'ephemeral_size' in kwargs
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)
218
prepare_template(target=base, *args, **kwargs)
219
with self.remove_volume_on_error(self.path):
220
create_lvm_image(base, size)
222
@contextlib.contextmanager
223
def remove_volume_on_error(self, path):
227
with excutils.save_and_reraise_exception():
228
libvirt_utils.remove_logical_volumes(path)
231
class Backend(object):
232
def __init__(self, use_cow):
237
'default': Qcow2 if use_cow else Raw
240
def image(self, instance, name,
241
suffix=None, image_type=None):
242
"""Constructs image for selected backend
244
:instance: Instance name.
246
:suffix: Suffix for image name (optional).
247
:image_type: Image type.
248
Optional, is FLAGS.libvirt_images_type by default.
251
image_type = FLAGS.libvirt_images_type
252
image = self.BACKEND.get(image_type)
254
raise RuntimeError(_('Unknown image_type=%s') % image_type)
255
return image(instance, name, suffix)