diff --git a/nova/rootwrap/compute.py b/nova/rootwrap/compute.py index 31c0ad91d5b3..9985edb3a16d 100755 --- a/nova/rootwrap/compute.py +++ b/nova/rootwrap/compute.py @@ -179,6 +179,10 @@ filterlist = [ # nova/virt/xenapi/vm_utils.py: 'mkfs' filters.CommandFilter("/sbin/mkfs", "root"), + # nova/virt/libvirt/utils.py: 'qemu-img' + filters.CommandFilter("/usr/bin/qemu-img", "root"), + # nova/virt/libvirt/connection.py: filters.ReadFileFilter("/etc/iscsi/initiatorname.iscsi"), + ] diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py index 726e28a1d035..bdd24e4bd08e 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/fake_libvirt_utils.py @@ -61,6 +61,14 @@ def chown(path, owner): pass +def create_snapshot(disk_path, snapshot_name): + pass + + +def delete_snapshot(disk_path, snapshot_name): + pass + + def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt): files[out_path] = '' diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index d8a44eec923f..2de87f2da81c 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -96,8 +96,14 @@ class FakeVirtDomain(object): def name(self): return "fake-domain %s" % self - def snapshotCreateXML(self, *args): - return FakeVirDomainSnapshot(self) + def info(self): + return [power_state.RUNNING, None, None, None, None] + + def create(self): + pass + + def managedSave(self, *args): + pass def createWithFlags(self, launch_flags): pass diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index ae2a2b665ac2..15bfddd7ca05 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -616,21 +616,20 @@ class LibvirtConnection(driver.ComputeDriver): if 'container_format' in base: metadata['container_format'] = base['container_format'] - # Make the snapshot - snapshot_name = uuid.uuid4().hex - snapshot_xml = """ - - %s - - """ % snapshot_name - snapshot_ptr = virt_dom.snapshotCreateXML(snapshot_xml, 0) - # Find the disk xml_desc = virt_dom.XMLDesc(0) domain = ElementTree.fromstring(xml_desc) source = domain.find('devices/disk/source') disk_path = source.get('file') + snapshot_name = uuid.uuid4().hex + + (state, _max_mem, _mem, _cpus, _t) = virt_dom.info() + if state == power_state.RUNNING: + virt_dom.managedSave(0) + # Make the snapshot + libvirt_utils.create_snapshot(disk_path, snapshot_name) + # Export the snapshot to a raw image with utils.tempdir() as tmpdir: try: @@ -638,15 +637,17 @@ class LibvirtConnection(driver.ComputeDriver): libvirt_utils.extract_snapshot(disk_path, source_format, snapshot_name, out_path, image_format) - # Upload that image to the image service - with libvirt_utils.file_open(out_path) as image_file: - image_service.update(context, - image_href, - metadata, - image_file) - finally: - snapshot_ptr.delete(0) + libvirt_utils.delete_snapshot(disk_path, snapshot_name) + if state == power_state.RUNNING: + virt_dom.create() + + # Upload that image to the image service + with libvirt_utils.file_open(out_path) as image_file: + image_service.update(context, + image_href, + metadata, + image_file) @exception.wrap_exception() def reboot(self, instance, network_info, reboot_type='SOFT'): diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 329f76eb13cb..0eec1db1a5e1 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -24,17 +24,11 @@ import random from nova import exception from nova import flags -from nova.openstack.common import cfg from nova import utils from nova.virt import images -qemu_img_opt = cfg.StrOpt('qemu_img', - default='qemu-img', - help='binary to use for qemu-img commands') - FLAGS = flags.FLAGS -FLAGS.register_opt(qemu_img_opt) def execute(*args, **kwargs): @@ -63,7 +57,7 @@ def create_image(disk_format, path, size): for megabytes, 'g' for gigabytes, 't' for terabytes). If no prefix is given, it will be interpreted as bytes. """ - execute(FLAGS.qemu_img, 'create', '-f', disk_format, path, size) + execute('qemu-img', 'create', '-f', disk_format, path, size) def create_cow_image(backing_file, path): @@ -74,7 +68,7 @@ def create_cow_image(backing_file, path): :param backing_file: Existing image on which to base the COW image :param path: Desired location of the COW image """ - execute(FLAGS.qemu_img, 'create', '-f', 'qcow2', '-o', + execute('qemu-img', 'create', '-f', 'qcow2', '-o', 'cluster_size=2M,backing_file=%s' % backing_file, path) @@ -85,7 +79,7 @@ def get_disk_size(path): :returns: Size (in bytes) of the given disk image as it would be seen by a virtual machine. """ - out, err = execute(FLAGS.qemu_img, 'info', path) + out, err = execute('qemu-img', 'info', path) size = [i.split('(')[1].split()[0] for i in out.split('\n') if i.strip().find('virtual size') >= 0] return int(size[0]) @@ -97,7 +91,7 @@ def get_disk_backing_file(path): :param path: Path to the disk image :returns: a path to the image's backing store """ - out, err = execute(FLAGS.qemu_img, 'info', path) + out, err = execute('qemu-img', 'info', path) backing_file = [i.split('actual path:')[1].strip()[:-1] for i in out.split('\n') if 0 <= i.find('backing file')] if backing_file: @@ -168,7 +162,37 @@ def chown(path, owner): :param path: File or directory whose ownership to change :param owner: Desired new owner (given as uid or username) """ - utils.execute('chown', owner, path, run_as_root=True) + execute('chown', owner, path, run_as_root=True) + + +def create_snapshot(disk_path, snapshot_name): + """Create a snapshot in a disk image + + :param disk_path: Path to disk image + :param snapshot_name: Name of snapshot in disk image + """ + qemu_img_cmd = ('qemu-img', + 'snapshot', + '-c', + snapshot_name, + disk_path) + # NOTE(vish): libvirt changes ownership of images + execute(*qemu_img_cmd, run_as_root=True) + + +def delete_snapshot(disk_path, snapshot_name): + """Create a snapshot in a disk image + + :param disk_path: Path to disk image + :param snapshot_name: Name of snapshot in disk image + """ + qemu_img_cmd = ('qemu-img', + 'snapshot', + '-d', + snapshot_name, + disk_path) + # NOTE(vish): libvirt changes ownership of images + execute(*qemu_img_cmd, run_as_root=True) def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt): @@ -178,7 +202,7 @@ def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt): :param snapshot_name: Name of snapshot in disk image :param out_path: Desired path of extracted snapshot """ - qemu_img_cmd = (FLAGS.qemu_img, + qemu_img_cmd = ('qemu-img', 'convert', '-f', source_fmt,