diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-02-15 00:27:39 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-02-15 00:27:39 +0000 |
| commit | cc12819adef88983ef78e9bfb18745df9c39b561 (patch) | |
| tree | 754f450506eb0ec29831d6b4246767b05ff8a8f2 /nova/virt | |
| parent | 9e4131741b3c767f97408442048a639a66a3d780 (diff) | |
| parent | cb1c1d406b98e492931ff19e29735aa592a99f15 (diff) | |
Merge "Added resize support for Libvirt/KVM."
Diffstat (limited to 'nova/virt')
| -rw-r--r-- | nova/virt/driver.py | 4 | ||||
| -rw-r--r-- | nova/virt/fake.py | 5 | ||||
| -rw-r--r-- | nova/virt/libvirt/connection.py | 142 | ||||
| -rw-r--r-- | nova/virt/libvirt/utils.py | 3 | ||||
| -rw-r--r-- | nova/virt/xenapi_conn.py | 6 |
5 files changed, 154 insertions, 6 deletions
diff --git a/nova/virt/driver.py b/nova/virt/driver.py index ad2e82a31..821455b45 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -258,7 +258,7 @@ class ComputeDriver(object): raise NotImplementedError() def migrate_disk_and_power_off(self, context, instance, dest, - instance_type): + instance_type, network_info): """ Transfers the disk of a running instance in multiple phases, turning off the instance before the end. @@ -293,7 +293,7 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def finish_revert_migration(self, instance): + def finish_revert_migration(self, instance, network_info): """Finish reverting a resize, powering back on the instance""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 204ffb56c..53a1ea4c9 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -144,7 +144,10 @@ class FakeConnection(driver.ComputeDriver): pass def migrate_disk_and_power_off(self, context, instance, dest, - instance_type): + instance_type, network_info): + pass + + def finish_revert_migration(self, instance, network_info): pass def poll_unconfirmed_resizes(self, resize_confirm_window): diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index efd972b4b..77b53e9fb 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -63,6 +63,7 @@ from nova import flags import nova.image from nova import log as logging from nova.openstack.common import cfg +from nova import network from nova import utils from nova.virt import driver from nova.virt import images @@ -437,6 +438,12 @@ class LibvirtConnection(driver.ComputeDriver): 'initiator': self._initiator, } + def _cleanup_resize(self, instance): + target = os.path.join(FLAGS.instances_path, + instance['name'] + "_resize") + if os.path.exists(target): + shutil.rmtree(target) + def volume_driver_method(self, method_name, connection_info, *args, **kwargs): driver_type = connection_info.get('driver_volume_type') @@ -2081,6 +2088,141 @@ class LibvirtConnection(driver.ComputeDriver): """Manage the local cache of images.""" self.image_cache_manager.verify_base_images(context) + @exception.wrap_exception() + def migrate_disk_and_power_off(self, context, instance, dest, + instance_type, network_info): + LOG.debug(_("Instance %s: Starting migrate_disk_and_power_off"), + instance['name']) + disk_info_text = self.get_instance_disk_info(instance['name']) + disk_info = utils.loads(disk_info_text) + + self._destroy(instance, network_info, cleanup=False) + + # copy disks to destination + # if disk type is qcow2, convert to raw then send to dest. + # rename instance dir to +_resize at first for using + # shared storage for instance dir (eg. NFS). + same_host = (dest == self.get_host_ip_addr()) + inst_base = "%s/%s" % (FLAGS.instances_path, instance['name']) + inst_base_resize = inst_base + "_resize" + try: + utils.execute('mv', inst_base, inst_base_resize) + if same_host: + utils.execute('mkdir', '-p', inst_base) + else: + utils.execute('ssh', dest, 'mkdir', '-p', inst_base) + for info in disk_info: + # assume inst_base == dirname(info['path']) + to_path = "%s:%s" % (dest, info['path']) + fname = os.path.basename(info['path']) + from_path = os.path.join(inst_base_resize, fname) + if info['type'] == 'qcow2': + tmp_path = from_path + "_rbase" + utils.execute('qemu-img', 'convert', '-f', 'qcow2', + '-O', 'raw', from_path, tmp_path) + if same_host: + utils.execute('mv', tmp_path, info['path']) + else: + utils.execute('scp', tmp_path, to_path) + utils.execute('rm', '-f', tmp_path) + else: # raw + if same_host: + utils.execute('cp', from_path, info['path']) + else: + utils.execute('scp', from_path, to_path) + except Exception, e: + try: + if os.path.exists(inst_base_resize): + utils.execute('rm', '-rf', inst_base) + utils.execute('mv', inst_base_resize, inst_base) + utils.execute('ssh', dest, 'rm', '-rf', inst_base) + except: + pass + raise e + + return disk_info_text + + def _wait_for_running(self, instance_name): + try: + state = self.get_info(instance_name)['state'] + except exception.NotFound: + msg = _("During wait running, %s disappeared.") % instance_name + LOG.error(msg) + raise utils.LoopingCallDone + + if state == power_state.RUNNING: + msg = _("Instance %s running successfully.") % instance_name + LOG.info(msg) + raise utils.LoopingCallDone + + @exception.wrap_exception() + def finish_migration(self, context, migration, instance, disk_info, + network_info, image_meta, resize_instance): + LOG.debug(_("Instance %s: Starting finish_migration"), + instance['name']) + + # resize disks. only "disk" and "disk.local" are necessary. + disk_info = utils.loads(disk_info) + for info in disk_info: + fname = os.path.basename(info['path']) + if fname == 'disk': + disk.extend(info['path'], + instance['root_gb'] * 1024 * 1024 * 1024) + elif fname == 'disk.local': + disk.extend(info['path'], + instance['ephemeral_gb'] * 1024 * 1024 * 1024) + if FLAGS.use_cow_images: + # back to qcow2 (no backing_file though) so that snapshot + # will be available + path_qcow = info['path'] + '_qcow' + utils.execute('qemu-img', 'convert', '-f', 'raw', + '-O', 'qcow2', info['path'], path_qcow) + utils.execute('mv', path_qcow, info['path']) + + xml = self.to_xml(instance, network_info) + + self.plug_vifs(instance, network_info) + self.firewall_driver.setup_basic_filtering(instance, network_info) + self.firewall_driver.prepare_instance_filter(instance, network_info) + # assume _create_image do nothing if a target file exists. + # TODO(oda): injecting files is not necessary + self._create_image(context, instance, xml, + network_info=network_info, + block_device_info=None) + + domain = self._create_new_domain(xml) + + self.firewall_driver.apply_instance_filter(instance, network_info) + + timer = utils.LoopingCall(self._wait_for_running, instance['name']) + return timer.start(interval=0.5, now=True) + + @exception.wrap_exception() + def finish_revert_migration(self, instance, network_info): + LOG.debug(_("Instance %s: Starting finish_revert_migration"), + instance['name']) + + inst_base = "%s/%s" % (FLAGS.instances_path, instance['name']) + inst_base_resize = inst_base + "_resize" + utils.execute('mv', inst_base_resize, inst_base) + + xml_path = os.path.join(inst_base, 'libvirt.xml') + xml = open(xml_path).read() + + self.plug_vifs(instance, network_info) + self.firewall_driver.setup_basic_filtering(instance, network_info) + self.firewall_driver.prepare_instance_filter(instance, network_info) + # images already exist + domain = self._create_new_domain(xml) + self.firewall_driver.apply_instance_filter(instance, network_info) + + timer = utils.LoopingCall(self._wait_for_running, instance['name']) + return timer.start(interval=0.5, now=True) + + def confirm_migration(self, migration, instance, network_info): + """Confirms a resize, destroying the source VM""" + self._cleanup_resize(instance) + class HostState(object): """Manages information about the compute node through libvirt""" diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 0b94a173f..080b58736 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -102,7 +102,8 @@ def get_disk_backing_file(path): out, err = execute(FLAGS.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')] - backing_file = os.path.basename(backing_file[0]) + if backing_file: + backing_file = os.path.basename(backing_file[0]) return backing_file diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 7c419824a..1e2d11bb0 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -211,8 +211,9 @@ class XenAPIConnection(driver.ComputeDriver): # TODO(Vek): Need to pass context in for access to auth_token self._vmops.confirm_migration(migration, instance, network_info) - def finish_revert_migration(self, instance): + def finish_revert_migration(self, instance, network_info): """Finish reverting a resize, powering back on the instance""" + # NOTE(vish): Xen currently does not use network info. self._vmops.finish_revert_migration(instance) def finish_migration(self, context, migration, instance, disk_info, @@ -252,9 +253,10 @@ class XenAPIConnection(driver.ComputeDriver): self._vmops.unpause(instance) def migrate_disk_and_power_off(self, context, instance, dest, - instance_type): + instance_type, network_info): """Transfers the VHD of a running instance to another host, then shuts off the instance copies over the COW disk""" + # NOTE(vish): Xen currently does not use network info. return self._vmops.migrate_disk_and_power_off(context, instance, dest, instance_type) |
