summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCerberus <matt.dietz@rackspace.com>2011-02-09 15:26:37 -0600
committerCerberus <matt.dietz@rackspace.com>2011-02-09 15:26:37 -0600
commitce5e3bdd30712aa6704926e6cdeb5ae73ae8200b (patch)
treebf6617d2af1a303f2e285d815b224b05a9d909f9
parent089286802db0dca22cd67e46f26fab3ab0a3a73b (diff)
A lot of stuff
-rw-r--r--nova/compute/manager.py8
-rw-r--r--nova/db/sqlalchemy/models.py1
-rw-r--r--nova/virt/xenapi/vmops.py53
-rw-r--r--nova/virt/xenapi_conn.py8
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer37
5 files changed, 71 insertions, 36 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 4189c49a4..ac09f7c8c 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -423,12 +423,12 @@ class ComputeManager(manager.Manager):
migration_ref = self.db.migration_get(context, migration_id)
self.db.migration_update(context, migration_id,
{ 'status': 'migrating', })
- self.driver.transfer_disk(context, instance_id,
+
+ self.driver.migrate_disk_and_power_off(context, instance,
migration_ref['dest_host'])
+
self.db.migration_update(context, migration_id,
{ 'status': 'post-migrating', })
-
- self.driver.power_off(context, migration_ref['instance_id'])
# This is where we would update the VM record after resizing
service = self.db.service_get_by_host_and_topic(context,
@@ -449,7 +449,7 @@ class ComputeManager(manager.Manager):
migration_ref['instance_id'])
# this may get passed into the following spawn instead
- self.driver.attach_disk(context, migration_ref['instance_id'])
+ self.driver.attach_disk(context, instance_ref)
self.driver.spawn(context, instance_ref, preexisting=True)
self.db.migration_update(context, migration_id,
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 499275504..ebf3a382b 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -369,6 +369,7 @@ class KeyPair(BASE, NovaBase):
class Migration(BASE, NovaBase):
"""Represents a running host-to-host migration."""
__tablename__ = 'migrations'
+ id = Column(Integer, primary_key=True, nullable=False)
source_host = Column(String(255))
dest_host = Column(String(255))
instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 6a7621502..40b075b3d 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -196,6 +196,26 @@ class VMOps(object):
Glance.
"""
+ with self._get_snapshot(instance) as snapshot:
+ # call plugin to ship snapshot off to glance
+ VMHelper.upload_image(
+ self._session, instance.id, snapshot.vdi_uuids, image_id)
+
+ logging.debug(_("Finished snapshot and upload for VM %s"), instance)
+
+ def _get_snapshot(self, instance):
+ class Snapshot(object):
+ def __init__(self, virt, instance, vdis):
+ self.instance = instance
+ self.vdi_uuids = vdis
+ self.virt = virt
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.virt._destroy(self.instance, self.vm_ref, shutdown=False)
+
#TODO(sirp): Add quiesce and VSS locking support when Windows support
# is added
@@ -204,30 +224,41 @@ class VMOps(object):
label = "%s-snapshot" % instance.name
try:
- template_vm_ref, template_vdi_uuids = VMHelper.create_snapshot(
+ _, template_vdi_uuids = VMHelper.create_snapshot(
self._session, instance.id, vm_ref, label)
+ return Snapshot(self, instance, template_vdi_uuids)
except self.XenAPI.Failure, exc:
logging.error(_("Unable to Snapshot %(vm_ref)s: %(exc)s")
% locals())
return
- try:
- # call plugin to ship snapshot off to glance
- VMHelper.upload_image(
- self._session, instance.id, template_vdi_uuids, image_id)
- finally:
- self._destroy(instance, template_vm_ref, shutdown=False)
-
- logging.debug(_("Finished snapshot and upload for VM %s"), instance)
-
- def transfer_disk(self, instance, dest):
+ def migrate_disk_and_power_off(self, instance, dest):
""" Copies a VHD from one host machine to another
:param instance: the instance that owns the VHD in question
:param dest: the destination host machine
+ :param disk_type: values are 'primary' or 'cow'
"""
vm_ref = VMHelper.lookup(self._session, instance.name)
+ # The primary VDI becomes the COW after the snapshot. We can figure
+ # this out from the VBD. The base copy is the parent_uuid returned
+ # from the snapshot creation
+ with self._get_snapshot(instance) as snapshot:
+ params = {'host':dest, 'vdi_uuid':snapshot.vdi_uuids[1]}
+ kwargs = {'params': pickle.dumps(params)}
+ self._session.async_call_plugin('data_transfer', 'transfer_vhd',
+ kwargs)
+
+ # Now power down the instance and transfer the COW VHD
+ self._shutdown(instance, method='clean')
+
+ _, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref)
+ params = {'host':dest, 'vdi_uuid': vm_vdi_rec['uuid']}
+ kwargs = {'params': pickle.dumps(params)}
+ self._session.async_call_plugin('data_transfer', 'transfer_vhd',
+ kwargs)
+
def resize(self, instance, flavor):
"""Resize a running instance by changing it's RAM and disk size """
raise NotImplementedError()
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 2e587117a..98b5e7851 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -192,8 +192,14 @@ class XenAPIConnection(object):
"""powers on a powered off VM instance"""
self._vmops.power_on(instance)
- def transfer_disk(self, instance, dest, callback):
+ def migrate_disk_and_power_off(self, instance, dest):
+ """Transfers the VHD of a running instance to another host, then shuts
+ off the instance copies over the COW disk"""
self._vmops.transfer_disk(instance, dest)
+
+ def move_disk(self, instance_ref):
+ """Moves the copied VDIs into the SR"""
+ pass
def suspend(self, instance, callback):
"""suspend the specified instance"""
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer
index 2af4a758b..bd46e1c0b 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/data_transfer
@@ -21,6 +21,7 @@ XenAPI Plugin for transfering data between host nodes
"""
import os.path
+import pickle
import subprocess
import XenAPIPlugin
@@ -30,27 +31,23 @@ DEVNULL = '/dev/null'
KEYSCAN = '/usr/bin/ssh-keyscan'
RSYNC = '/usr/bin/rsync'
-def _key_scan_and_add(host):
- """SSH scans a remote host and writes the SSH key out to known_hosts"""
- # Touch the file if it doesn't yet exist
- open(SSH_HOSTS, 'a').close()
-
- null = open(DEVNULL, 'w')
- known_hosts = open(SSH_HOSTS, 'a')
- key = subprocess.Popen(['/usr/bin/ssh-keyscan', '-t', 'rsa', host],
- stdout=subprocess.PIPE, stderr=null).communicate()[0].strip()
- grep = subprocess.call(['/bin/grep', '-o', '%s' % key, SSH_HOSTS],
- stdout=null, stderr=null)
- if grep == 1:
- known_hosts.write(key)
- null.close()
- known_hosts.close()
-
-def transfer_file(host, file_path):
+
+def transfer_vhd(session, args):
"""Rsyncs a VHD to an adjacent host"""
- _key_scan_and_add(host)
- if subprocess.call([RSYNC, file_path, "%s:/root/" % host]) != 0:
+ params = pickle.dumps(args)
+ instance_id = params['instance_id']
+ host = params['host']
+ vdi_uuid = params['vdi_uuid']
+ sr_path = get_sr_path(session)
+ vhd_path = "%s.vhd" % vdi_uuid
+
+ source_path = "%s/%s" % (sr_path, vhd_path)
+ dest_path = '%s:/images/instance%d/' % (host, instance_id)
+ rsync_args = [['nohup', RSYNC, '-av', '--progress',
+ '-e "ssh -o StrictHostKeyChecking=no"', source_path, dest_path]
+
+ if subprocess.call(rsync_args) != 0:
raise Exception("Unexpected VHD transfer failure")
if __name__ == '__main__':
- XenAPIPlugin.dispatch({'transfer_file': transfer_file})
+ XenAPIPlugin.dispatch({'transfer_vhd': transfer_vhd})