Commit 30e42c4e authored by Guido Trotter's avatar Guido Trotter
Browse files

KVM: instance migration

The tcp port used for migrating KVM instances is selectable at
./configure time. We use a single port as nodes are locked anyway during
a migration, so no two migrations can happen at the same time to the
same node.

Reviewed-by: iustinp
parent 1f8b3a27
......@@ -295,6 +295,7 @@ lib/ Makefile stamp-directories
echo "KVM_PATH = '$(KVM_PATH)'"; \
echo "SOCAT_PATH = '$(SOCAT_PATH)'"; \
} > $@
......@@ -99,6 +99,16 @@ AC_ARG_WITH([kvm-path],
AC_SUBST(KVM_PATH, $kvm_path)
# --with-kvm-migration-port=...
[tcp port used for kvm instance live migration]
[ (default is 8102)]
AC_SUBST(KVM_MIGRATION_PORT, $kvm_migration_port)
# --with-socat-path=...
......@@ -134,6 +134,7 @@ XEN_INITRD = _autoconf.XEN_INITRD
KVM_PATH = _autoconf.KVM_PATH
VALUE_DEFAULT = "default"
VALUE_AUTO = "auto"
......@@ -28,6 +28,7 @@ import os.path
import re
import tempfile
import time
import logging
from cStringIO import StringIO
from ganeti import utils
......@@ -53,6 +54,9 @@ class KVMHypervisor(hv_base.BaseHypervisor):
_MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
re.M | re.I)
def __init__(self):
# Let's make sure the directories we need exist, even if the RUN_DIR lives
......@@ -284,19 +288,23 @@ class KVMHypervisor(hv_base.BaseHypervisor):
serialized_form = serializer.Dump((kvm_cmd, serialized_nics))
self._WriteKVMRuntime(, serialized_form)
def _LoadKVMRuntime(self, instance):
def _LoadKVMRuntime(self, instance, serialized_runtime=None):
"""Load an instance's KVM runtime
serialized_form = self._ReadKVMRuntime(
loaded_runtime = serializer.Load(serialized_form)
if not serialized_runtime:
serialized_runtime = self._ReadKVMRuntime(
loaded_runtime = serializer.Load(serialized_runtime)
kvm_cmd, serialized_nics = loaded_runtime
kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
return (kvm_cmd, kvm_nics)
def _ExecuteKVMRuntime(self, instance, kvm_runtime):
def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
"""Execute a KVM cmd, after completing it with some last minute data
@type incoming: tuple of strings
@param incoming: (target_host_ip, port)
pidfile, pid, alive = self._InstancePidAlive(
if alive:
......@@ -317,6 +325,10 @@ class KVMHypervisor(hv_base.BaseHypervisor):
kvm_cmd.extend(['-net', 'tap,script=%s' % script])
if incoming:
target, port = incoming
kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
result = utils.RunCmd(kvm_cmd)
if result.failed:
raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
......@@ -415,6 +427,95 @@ class KVMHypervisor(hv_base.BaseHypervisor):
self._SaveKVMRuntime(instance, kvm_runtime)
self._ExecuteKVMRuntime(instance, kvm_runtime)
def MigrationInfo(self, instance):
"""Get instance information to perform a migration.
@type instance: L{objects.Instance}
@param instance: instance to be migrated
@rtype: string
@return: content of the KVM runtime file
return self._ReadKVMRuntime(
def AcceptInstance(self, instance, info, target):
"""Prepare to accept an instance.
@type instance: L{objects.Instance}
@param instance: instance to be accepted
@type info: string
@param info: content of the KVM runtime file on the source node
@type target: string
@param target: target host (usually ip), on this node
kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
incoming_address = (target, constants.KVM_MIGRATION_PORT)
self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
def FinalizeMigration(self, instance, info, success):
"""Finalize an instance migration.
Stop the incoming mode KVM.
@type instance: L{objects.Instance}
@param instance: instance whose migration is being aborted
if success:
self._WriteKVMRuntime(, info)
self.StopInstance(instance, force=True)
def MigrateInstance(self, instance_name, target, live):
"""Migrate an instance to a target node.
The migration will not be attempted if the instance is not
currently running.
@type instance_name: string
@param instance_name: name of the instance to be migrated
@type target: string
@param target: ip address of the target node
@type live: boolean
@param live: perform a live migration
pidfile, pid, alive = self._InstancePidAlive(instance_name)
if not alive:
raise errors.HypervisorError("Instance not running, cannot migrate")
if not live:
self._CallMonitorCommand(instance_name, 'stop')
migrate_command = ('migrate -d tcp:%s:%s' %
(target, constants.KVM_MIGRATION_PORT))
self._CallMonitorCommand(instance_name, migrate_command)
info_command = 'info migrate'
done = False
while not done:
result = self._CallMonitorCommand(instance_name, info_command)
match =
if not match:
raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
status =
if status == 'completed':
done = True
elif status == 'active':
else:"KVM: unknown migration status '%s'" % status)
def GetNodeInfo(self):
"""Return information about the node.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment