Commit 7f798c24 authored by Hrvoje Ribicic's avatar Hrvoje Ribicic

Introduce socat as a way of doing xl migrations

This patch introduces support for socat as a means of doing xl
migrations. The primary reason for doing so is that Ganeti no longer
handles SSH key distribution across nodes which are not master
candidates. By relying on SSH as the only means of doing migrations,
we could only migrate instances off of master candidates.
Signed-off-by: default avatarHrvoje Ribicic <riba@google.com>
Reviewed-by: default avatarKlaus Aehlig <aehlig@google.com>
parent aaecd556
......@@ -57,17 +57,6 @@ DAEMONS_EXTRA_LOGFILES = \
for extra in DAEMONS_EXTRA_LOGBASE[daemon]))
for daemon in DAEMONS_EXTRA_LOGBASE)
# When the Xen toolstack used is "xl", live migration requires the source host
# to connect to the target host via ssh (xl runs this command). We need to pass
# the command xl runs some extra info so that it can use Ganeti's key
# verification and not fail. Note that this string is incomplete: it must be
# filled with the cluster name before being used.
XL_SSH_CMD = ("ssh -l %s -oGlobalKnownHostsFile=%s"
" -oUserKnownHostsFile=/dev/null"
" -oCheckHostIp=no -oStrictHostKeyChecking=yes"
" -oHostKeyAlias=%%s") % (SSH_LOGIN_USER,
pathutils.SSH_KNOWN_HOSTS_FILE)
IE_MAGIC_RE = re.compile(r"^[-_.a-zA-Z0-9]{5,100}$")
# External script validation mask
......
......@@ -1072,6 +1072,14 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""
return self._ReadConfigFile(instance.name)
def _UseMigrationDaemon(self, hvparams):
"""Whether to start a socat daemon when accepting an instance.
@rtype: bool
"""
return self._GetCommand(hvparams) == constants.XEN_CMD_XL
def AcceptInstance(self, instance, info, target):
"""Prepare to accept an instance.
......@@ -1083,7 +1091,10 @@ class XenHypervisor(hv_base.BaseHypervisor):
@param target: target host (usually ip), on this node
"""
pass
if self._UseMigrationDaemon(instance.hvparams):
port = instance.hvparams[constants.HV_MIGRATION_PORT]
utils.StartDaemon(["socat", "TCP-LISTEN:%d,bind=%s" % (port, target),
"SYSTEM:'xl migrate-receive'"])
def FinalizeMigrationDst(self, instance, info, success):
"""Finalize an instance migration.
......@@ -1102,7 +1113,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
if success:
self._WriteConfigFile(instance.name, info)
def MigrateInstance(self, cluster_name, instance, target, live):
def MigrateInstance(self, _cluster_name, instance, target, live):
"""Migrate an instance to a target node.
The migration will not be attempted if the instance is not
......@@ -1118,11 +1129,11 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""
port = instance.hvparams[constants.HV_MIGRATION_PORT]
return self._MigrateInstance(cluster_name, instance.name, target, port,
live, instance.hvparams)
return self._MigrateInstance(instance.name, target, port, live,
instance.hvparams)
def _MigrateInstance(self, cluster_name, instance_name, target, port, live,
hvparams, _ping_fn=netutils.TcpPing):
def _MigrateInstance(self, instance_name, target, port, live, hvparams,
_ping_fn=netutils.TcpPing):
"""Migrate an instance to a target node.
@see: L{MigrateInstance} for details
......@@ -1136,26 +1147,28 @@ class XenHypervisor(hv_base.BaseHypervisor):
cmd = self._GetCommand(hvparams)
if (cmd == constants.XEN_CMD_XM and
not _ping_fn(target, port, live_port_needed=True)):
raise errors.HypervisorError("Remote host %s not listening on port"
" %s, cannot migrate" % (target, port))
args = ["migrate"]
if cmd == constants.XEN_CMD_XM:
# Try and see if xm is listening on the specified port
if not _ping_fn(target, port, live_port_needed=True):
raise errors.HypervisorError("Remote host %s not listening on port"
" %s, cannot migrate" % (target, port))
args.extend(["-p", "%d" % port])
if live:
args.append("-l")
elif cmd == constants.XEN_CMD_XL:
# Rather than using SSH, use socat as Ganeti cannot guarantee the presence
# of usable SSH keys as of 2.13
args.extend([
"-s", constants.XL_SSH_CMD % cluster_name,
"-s", constants.XL_SOCAT_CMD % (target, port),
"-C", self._ConfigFileName(instance_name),
])
else:
raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
raise errors.HypervisorError("Unsupported Xen command: %s" % cmd)
args.extend([instance_name, target])
......
......@@ -503,6 +503,12 @@ xenInitrd = AutoConf.xenInitrd
xenKernel :: String
xenKernel = AutoConf.xenKernel
xlSocatCmd :: String
xlSocatCmd = "socat - tcp:%s:%d #"
xlPidfileSuffix :: String
xlPidfileSuffix = ".pid"
-- FIXME: perhaps rename to 'validXenCommands' for consistency with
-- other constants
knownXenCommands :: FrozenSet String
......
......@@ -871,8 +871,8 @@ class _TestXenHypervisor(object):
for live in [False, True]:
try:
hv._MigrateInstance(NotImplemented, name, target, port, live,
self.VALID_HVPARAMS, _ping_fn=NotImplemented)
hv._MigrateInstance(name, target, port, live, self.VALID_HVPARAMS,
_ping_fn=NotImplemented)
except errors.HypervisorError, err:
self.assertEqual(str(err), "Instance not running, cannot migrate")
else:
......@@ -900,8 +900,7 @@ class _TestXenHypervisor(object):
pass
else:
try:
hv._MigrateInstance(NotImplemented, name, target, port, live,
hvparams,
hv._MigrateInstance(name, target, port, live, hvparams,
_ping_fn=compat.partial(self._FakeTcpPing,
(target, port), False))
except errors.HypervisorError, err:
......@@ -910,7 +909,7 @@ class _TestXenHypervisor(object):
else:
self.fail("Exception was not raised")
def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port,
def _MigrateInstanceCmd(self, instance_name, target, port,
live, fail, cmd):
if cmd == [self.CMD, "list"]:
output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
......@@ -923,7 +922,7 @@ class _TestXenHypervisor(object):
elif self.CMD == constants.XEN_CMD_XL:
args = [
"-s", constants.XL_SSH_CMD % cluster_name,
"-s", constants.XL_SOCAT_CMD % (target, port),
"-C", utils.PathJoin(self.tmpdir, instance_name),
]
......@@ -943,7 +942,6 @@ class _TestXenHypervisor(object):
return self._SuccessCommand(output, cmd)
def testMigrateInstance(self):
clustername = "cluster.example.com"
instname = "server01.example.com"
target = constants.IP4_ADDRESS_LOCALHOST
port = 22364
......@@ -958,21 +956,20 @@ class _TestXenHypervisor(object):
run_cmd = \
compat.partial(self._MigrateInstanceCmd,
clustername, instname, target, port, live,
fail)
instname, target, port, live, fail)
hv = self._GetHv(run_cmd=run_cmd)
if fail:
try:
hv._MigrateInstance(clustername, instname, target, port, live,
hvparams, _ping_fn=ping_fn)
hv._MigrateInstance(instname, target, port, live, hvparams,
_ping_fn=ping_fn)
except errors.HypervisorError, err:
self.assertTrue(str(err).startswith("Failed to migrate instance"))
else:
self.fail("Exception was not raised")
else:
hv._MigrateInstance(clustername, instname, target, port, live,
hv._MigrateInstance(instname, target, port, live,
hvparams, _ping_fn=ping_fn)
if self.CMD == constants.XEN_CMD_XM:
......
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