29
from lxml import etree
27
31
from nova import exception
28
from nova import flags
29
32
from nova.openstack.common import cfg
33
from nova.openstack.common import fileutils
30
34
from nova.openstack.common import jsonutils
35
from nova.openstack.common import lockutils
31
36
from nova.openstack.common import log as logging
32
37
from nova import utils
33
38
from nova.virt import images
43
48
'non-standard locations')
46
flags.DECLARE('instances_path', 'nova.compute.manager')
47
flags.DECLARE('base_dir_name', 'nova.compute.manager')
49
FLAGS.register_opts(util_opts)
52
CONF.register_opts(util_opts)
53
CONF.import_opt('instances_path', 'nova.compute.manager')
54
CONF.import_opt('base_dir_name', 'nova.compute.manager')
52
57
def execute(*args, **kwargs):
86
91
:param backing_file: Existing image on which to base the COW image
87
92
:param path: Desired location of the COW image
89
execute('qemu-img', 'create', '-f', 'qcow2', '-o',
90
'backing_file=%s' % backing_file, path)
94
base_cmd = ['qemu-img', 'create', '-f', 'qcow2']
97
cow_opts += ['backing_file=%s' % backing_file]
98
base_details = images.qemu_img_info(backing_file)
101
# This doesn't seem to get inherited so force it to...
102
# http://paste.ubuntu.com/1213295/
103
# TODO(harlowja) probably file a bug against qemu-img/qemu
104
if base_details and base_details.cluster_size is not None:
105
cow_opts += ['cluster_size=%s' % base_details.cluster_size]
106
# For now don't inherit this due the following discussion...
107
# See: http://www.gossamer-threads.com/lists/openstack/dev/10592
108
# if 'preallocation' in base_details:
109
# cow_opts += ['preallocation=%s' % base_details['preallocation']]
110
if base_details and base_details.encryption:
111
cow_opts += ['encryption=%s' % base_details.encryption]
113
# Format as a comma separated list
114
csv_opts = ",".join(cow_opts)
115
cow_opts = ['-o', csv_opts]
116
cmd = base_cmd + cow_opts + [path]
93
120
def create_lvm_image(vg, lv, size, sparse=False):
144
171
:param vg: volume group name
146
out, err = execute('lvs', '--noheadings', '-o', 'lv_path', vg,
173
out, err = execute('lvs', '--noheadings', '-o', 'lv_name', vg,
147
174
run_as_root=True)
149
176
return [line.strip() for line in out.splitlines()]
179
def logical_volume_info(path):
180
"""Get logical volume info.
182
:param path: logical volume path
184
out, err = execute('lvs', '-o', 'vg_all,lv_all',
185
'--separator', '|', path, run_as_root=True)
187
info = [line.split('|') for line in out.splitlines()]
190
raise RuntimeError(_("Path %s must be LVM logical volume") % path)
192
return dict(zip(*info))
152
195
def remove_logical_volumes(*paths):
153
196
"""Remove one or more logical volume."""
169
212
:param is_block_dev:
170
213
:returns: driver_name or None
172
if FLAGS.libvirt_type == "xen":
215
if CONF.libvirt_type == "xen":
177
elif FLAGS.libvirt_type in ('kvm', 'qemu'):
220
elif CONF.libvirt_type in ('kvm', 'qemu'):
180
223
# UML doesn't want a driver_name set
188
231
:returns: Size (in bytes) of the given disk image as it would be seen
189
232
by a virtual machine.
191
size = images.qemu_img_info(path)['virtual size']
192
size = size.split('(')[1].split()[0]
234
size = images.qemu_img_info(path).virtual_size
199
241
:param path: Path to the disk image
200
242
:returns: a path to the image's backing store
202
backing_file = images.qemu_img_info(path).get('backing file')
244
backing_file = images.qemu_img_info(path).backing_file
205
if 'actual path: ' in backing_file:
206
backing_file = backing_file.split('actual path: ')[1][:-1]
207
246
backing_file = os.path.basename(backing_file)
209
248
return backing_file
240
279
execute('rsync', '--sparse', '--compress', src, dest)
243
def mkfs(fs, path, label=None):
244
"""Format a file or block device
246
:param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4'
248
:param path: Path to file or block device to format
249
:param label: Volume label to use
252
execute('mkswap', path)
254
args = ['mkfs', '-t', fs]
255
#add -F to force no interactive excute on non-block device.
256
if fs in ['ext3', 'ext4']:
259
args.extend(['-n', label])
264
282
def write_to_file(path, contents, umask=None):
265
283
"""Write the given contents to a file
372
390
return os.unlink(path)
393
def find_disk(virt_dom):
394
"""Find root device path for instance
396
May be file or device"""
397
xml_desc = virt_dom.XMLDesc(0)
398
domain = etree.fromstring(xml_desc)
399
if CONF.libvirt_type == 'lxc':
400
source = domain.find('devices/filesystem/source')
401
disk_path = source.get('dir')
402
disk_path = disk_path[0:disk_path.rfind('rootfs')]
403
disk_path = os.path.join(disk_path, 'disk')
405
source = domain.find('devices/disk/source')
406
disk_path = source.get('file') or source.get('dev')
409
raise RuntimeError(_("Can't retrieve root device path "
410
"from instance libvirt configuration"))
415
def get_disk_type(path):
416
"""Retrieve disk type (raw, qcow2, lvm) for given file"""
417
if path.startswith('/dev'):
420
return images.qemu_img_info(path).file_format
375
423
def get_fs_info(path):
376
424
"""Get free/used/total space info for a filesystem
412
460
"""Test if a given path matches the pattern for info files."""
414
462
digest_size = hashlib.sha1().digestsize * 2
415
regexp = (FLAGS.image_info_filename_pattern
463
regexp = (CONF.image_info_filename_pattern
416
464
% {'image': ('([0-9a-f]{%(digest_size)d}|'
417
465
'[0-9a-f]{%(digest_size)d}_sm|'
418
466
'[0-9a-f]{%(digest_size)d}_[0-9]+)'
426
def read_stored_info(base_path, field=None):
474
def _read_possible_json(serialized, info_file):
476
d = jsonutils.loads(serialized)
478
except ValueError, e:
479
LOG.error(_('Error reading image info file %(filename)s: '
481
{'filename': info_file,
488
def read_stored_info(target, field=None, timestamped=False):
427
489
"""Read information about an image.
429
491
Returns an empty dictionary if there is no info, just the field value if
430
492
a field is requested, or the entire dictionary otherwise.
433
info_file = get_info_filename(base_path)
495
info_file = get_info_filename(target)
434
496
if not os.path.exists(info_file):
435
# Special case to handle essex checksums being converted
436
old_filename = base_path + '.sha1'
497
# NOTE(mikal): Special case to handle essex checksums being converted.
498
# There is an assumption here that target is a base image filename.
499
old_filename = target + '.sha1'
437
500
if field == 'sha1' and os.path.exists(old_filename):
438
501
hash_file = open(old_filename)
439
502
hash_value = hash_file.read()
440
503
hash_file.close()
442
write_stored_info(base_path, field=field, value=hash_value)
505
write_stored_info(target, field=field, value=hash_value)
443
506
os.remove(old_filename)
444
507
d = {field: hash_value}
450
LOG.info(_('Reading image info file: %s'), info_file)
451
f = open(info_file, 'r')
452
serialized = f.read().rstrip()
454
LOG.info(_('Read: %s'), serialized)
457
d = jsonutils.loads(serialized)
459
except ValueError, e:
460
LOG.error(_('Error reading image info file %(filename)s: '
462
{'filename': info_file,
513
lock_name = 'info-%s' % os.path.split(target)[-1]
514
lock_path = os.path.join(CONF.instances_path, 'locks')
516
@lockutils.synchronized(lock_name, 'nova-', external=True,
518
def read_file(info_file):
519
LOG.debug(_('Reading image info file: %s'), info_file)
520
with open(info_file, 'r') as f:
521
return f.read().rstrip()
523
serialized = read_file(info_file)
524
d = _read_possible_json(serialized, info_file)
467
return d.get(field, None)
528
return (d.get(field, None), d.get('%s-timestamp' % field, None))
530
return d.get(field, None)
477
540
info_file = get_info_filename(target)
478
utils.ensure_tree(os.path.dirname(info_file))
480
d = read_stored_info(info_file)
482
serialized = jsonutils.dumps(d)
484
LOG.info(_('Writing image info file: %s'), info_file)
485
LOG.info(_('Wrote: %s'), serialized)
486
f = open(info_file, 'w')
541
LOG.info(_('Writing stored info to %s'), info_file)
542
fileutils.ensure_tree(os.path.dirname(info_file))
544
lock_name = 'info-%s' % os.path.split(target)[-1]
545
lock_path = os.path.join(CONF.instances_path, 'locks')
547
@lockutils.synchronized(lock_name, 'nova-', external=True,
549
def write_file(info_file, field, value):
552
if os.path.exists(info_file):
553
with open(info_file, 'r') as f:
554
d = _read_possible_json(f.read(), info_file)
557
d['%s-timestamp' % field] = time.time()
559
with open(info_file, 'w') as f:
560
f.write(json.dumps(d))
562
write_file(info_file, field, value)