From 55cc0a44d56be13d3cf735c67c19a5d9849086cf Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Fri, 7 Jan 2011 16:34:19 +0100 Subject: [PATCH] Use new console information in hypervisor abstraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes use of the new way of returning console information from the master daemon. Unittests are included. Signed-off-by: Michael Hanselmann <hansmi@google.com> Reviewed-by: RenΓ© Nussbaumer <rn@google.com> --- Makefile.am | 4 ++ lib/cmdlib.py | 9 +--- lib/hypervisor/hv_base.py | 4 +- lib/hypervisor/hv_chroot.py | 21 +++++--- lib/hypervisor/hv_fake.py | 10 ++-- lib/hypervisor/hv_kvm.py | 38 ++++++++------ lib/hypervisor/hv_lxc.py | 9 +++- lib/hypervisor/hv_xen.py | 10 ++-- test/ganeti.hypervisor.hv_chroot_unittest.py | 54 ++++++++++++++++++++ test/ganeti.hypervisor.hv_fake_unittest.py | 44 ++++++++++++++++ test/ganeti.hypervisor.hv_kvm_unittest.py | 46 ++++++++++++++++- test/ganeti.hypervisor.hv_lxc_unittest.py | 46 +++++++++++++++++ test/ganeti.hypervisor.hv_xen_unittest.py | 48 +++++++++++++++++ 13 files changed, 301 insertions(+), 42 deletions(-) create mode 100755 test/ganeti.hypervisor.hv_chroot_unittest.py create mode 100755 test/ganeti.hypervisor.hv_fake_unittest.py create mode 100755 test/ganeti.hypervisor.hv_lxc_unittest.py create mode 100755 test/ganeti.hypervisor.hv_xen_unittest.py diff --git a/Makefile.am b/Makefile.am index 6c25dcaaa..612a98a5e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -449,7 +449,11 @@ python_tests = \ test/ganeti.ht_unittest.py \ test/ganeti.http_unittest.py \ test/ganeti.hypervisor_unittest.py \ + test/ganeti.hypervisor.hv_chroot_unittest.py \ + test/ganeti.hypervisor.hv_fake_unittest.py \ test/ganeti.hypervisor.hv_kvm_unittest.py \ + test/ganeti.hypervisor.hv_lxc_unittest.py \ + test/ganeti.hypervisor.hv_xen_unittest.py \ test/ganeti.impexpd_unittest.py \ test/ganeti.jqueue_unittest.py \ test/ganeti.locking_unittest.py \ diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 996489e5c..5a4a08fa5 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -7691,14 +7691,9 @@ class LUConnectConsole(NoHooksLU): # instance and then saving the defaults in the instance itself. hvparams = cluster.FillHV(instance) beparams = cluster.FillBE(instance) - console_cmd = hyper.GetShellCommandForConsole(instance, hvparams, beparams) - - console = objects.InstanceConsole(instance=instance.name, - kind=constants.CONS_SSH, - host=node, - user="root", - command=console_cmd) + console = hyper.GetInstanceConsole(instance, hvparams, beparams) + assert console.instance == instance.name assert console.Validate() return console.ToDict() diff --git a/lib/hypervisor/hv_base.py b/lib/hypervisor/hv_base.py index 80675eedd..ec474005c 100644 --- a/lib/hypervisor/hv_base.py +++ b/lib/hypervisor/hv_base.py @@ -206,8 +206,8 @@ class BaseHypervisor(object): raise NotImplementedError @classmethod - def GetShellCommandForConsole(cls, instance, hvparams, beparams): - """Return a command for connecting to the console of an instance. + def GetInstanceConsole(cls, instance, hvparams, beparams): + """Return information for connecting to the console of an instance. """ raise NotImplementedError diff --git a/lib/hypervisor/hv_chroot.py b/lib/hypervisor/hv_chroot.py index f33fed204..5b26e8b44 100644 --- a/lib/hypervisor/hv_chroot.py +++ b/lib/hypervisor/hv_chroot.py @@ -31,6 +31,7 @@ import logging from ganeti import constants from ganeti import errors # pylint: disable-msg=W0611 from ganeti import utils +from ganeti import objects from ganeti.hypervisor import hv_base from ganeti.errors import HypervisorError @@ -246,15 +247,21 @@ class ChrootManager(hv_base.BaseHypervisor): return self.GetLinuxNodeInfo() @classmethod - def GetShellCommandForConsole(cls, instance, hvparams, beparams): - """Return a command for connecting to the console of an instance. + def GetInstanceConsole(cls, instance, # pylint: disable-msg=W0221 + hvparams, beparams, root_dir=None): + """Return information for connecting to the console of an instance. """ - root_dir = cls._InstanceDir(instance.name) - if not os.path.ismount(root_dir): - raise HypervisorError("Instance %s is not running" % instance.name) - - return "chroot %s" % root_dir + if root_dir is None: + root_dir = cls._InstanceDir(instance.name) + if not os.path.ismount(root_dir): + raise HypervisorError("Instance %s is not running" % instance.name) + + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_SSH, + host=instance.primary_node, + user=constants.GANETI_RUNAS, + command=["chroot", root_dir]) def Verify(self): """Verify the hypervisor. diff --git a/lib/hypervisor/hv_fake.py b/lib/hypervisor/hv_fake.py index 2d386d2e3..9e5f96e32 100644 --- a/lib/hypervisor/hv_fake.py +++ b/lib/hypervisor/hv_fake.py @@ -30,6 +30,7 @@ import logging from ganeti import utils from ganeti import constants from ganeti import errors +from ganeti import objects from ganeti.hypervisor import hv_base @@ -204,11 +205,14 @@ class FakeHypervisor(hv_base.BaseHypervisor): return result @classmethod - def GetShellCommandForConsole(cls, instance, hvparams, beparams): - """Return a command for connecting to the console of an instance. + def GetInstanceConsole(cls, instance, hvparams, beparams): + """Return information for connecting to the console of an instance. """ - return "echo Console not available for fake hypervisor" + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_MESSAGE, + message=("Console not available for fake" + " hypervisor")) def Verify(self): """Verify the hypervisor. diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index 0538ffb62..46f41c5b4 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -1035,28 +1035,33 @@ class KVMHypervisor(hv_base.BaseHypervisor): return self.GetLinuxNodeInfo() @classmethod - def GetShellCommandForConsole(cls, instance, hvparams, beparams): + def GetInstanceConsole(cls, instance, hvparams, beparams): """Return a command for connecting to the console of an instance. """ if hvparams[constants.HV_SERIAL_CONSOLE]: - shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" % - (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(), - utils.ShellQuote(cls._InstanceSerial(instance.name)))) - else: - shell_command = "echo 'No serial shell for instance %s'" % instance.name + cmd = [constants.SOCAT_PATH, + "STDIO,%s" % cls._SocatUnixConsoleParams(), + "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)] + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_SSH, + host=instance.primary_node, + user=constants.GANETI_RUNAS, + command=cmd) vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS] - if vnc_bind_address: - if instance.network_port > constants.VNC_BASE_PORT: - display = instance.network_port - constants.VNC_BASE_PORT - vnc_command = ("echo 'Instance has VNC listening on %s:%d" - " (display: %d)'" % (vnc_bind_address, - instance.network_port, - display)) - shell_command = "%s; %s" % (vnc_command, shell_command) - - return shell_command + if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT: + display = instance.network_port - constants.VNC_BASE_PORT + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_VNC, + host=vnc_bind_address, + port=instance.network_port, + display=display) + + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_MESSAGE, + message=("No serial shell for instance %s" % + instance.name)) def Verify(self): """Verify the hypervisor. @@ -1069,7 +1074,6 @@ class KVMHypervisor(hv_base.BaseHypervisor): if not os.path.exists(constants.SOCAT_PATH): return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH - @classmethod def CheckParameterSyntax(cls, hvparams): """Check the given parameters for validity. diff --git a/lib/hypervisor/hv_lxc.py b/lib/hypervisor/hv_lxc.py index 3779a68cd..78c1f3467 100644 --- a/lib/hypervisor/hv_lxc.py +++ b/lib/hypervisor/hv_lxc.py @@ -31,6 +31,7 @@ import logging from ganeti import constants from ganeti import errors # pylint: disable-msg=W0611 from ganeti import utils +from ganeti import objects from ganeti.hypervisor import hv_base from ganeti.errors import HypervisorError @@ -374,11 +375,15 @@ class LXCHypervisor(hv_base.BaseHypervisor): return self.GetLinuxNodeInfo() @classmethod - def GetShellCommandForConsole(cls, instance, hvparams, beparams): + def GetInstanceConsole(cls, instance, hvparams, beparams): """Return a command for connecting to the console of an instance. """ - return "lxc-console -n %s" % instance.name + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_SSH, + host=instance.primary_node, + user=constants.GANETI_RUNAS, + command=["lxc-console", "-n", instance.name]) def Verify(self): """Verify the hypervisor. diff --git a/lib/hypervisor/hv_xen.py b/lib/hypervisor/hv_xen.py index 5d6de5cc8..8d36e202a 100644 --- a/lib/hypervisor/hv_xen.py +++ b/lib/hypervisor/hv_xen.py @@ -31,6 +31,7 @@ from ganeti import errors from ganeti import utils from ganeti.hypervisor import hv_base from ganeti import netutils +from ganeti import objects class XenHypervisor(hv_base.BaseHypervisor): @@ -294,12 +295,15 @@ class XenHypervisor(hv_base.BaseHypervisor): return result @classmethod - def GetShellCommandForConsole(cls, instance, hvparams, beparams): + def GetInstanceConsole(cls, instance, hvparams, beparams): """Return a command for connecting to the console of an instance. """ - return "xm console %s" % instance.name - + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_SSH, + host=instance.primary_node, + user=constants.GANETI_RUNAS, + command=["xm", "console", instance.name]) def Verify(self): """Verify the hypervisor. diff --git a/test/ganeti.hypervisor.hv_chroot_unittest.py b/test/ganeti.hypervisor.hv_chroot_unittest.py new file mode 100755 index 000000000..233808c4a --- /dev/null +++ b/test/ganeti.hypervisor.hv_chroot_unittest.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# + +# Copyright (C) 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing ganeti.hypervisor.hv_chroot""" + +import unittest +import tempfile +import shutil + +from ganeti import constants +from ganeti import objects +from ganeti import hypervisor + +from ganeti.hypervisor import hv_chroot + +import testutils + + +class TestConsole(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tmpdir) + + def test(self): + instance = objects.Instance(name="fake.example.com", primary_node="node837") + cons = hv_chroot.ChrootManager.GetInstanceConsole(instance, {}, {}, + root_dir=self.tmpdir) + self.assertTrue(cons.Validate()) + self.assertEqual(cons.kind, constants.CONS_SSH) + self.assertEqual(cons.host, instance.primary_node) + + +if __name__ == "__main__": + testutils.GanetiTestProgram() diff --git a/test/ganeti.hypervisor.hv_fake_unittest.py b/test/ganeti.hypervisor.hv_fake_unittest.py new file mode 100755 index 000000000..e0c42407b --- /dev/null +++ b/test/ganeti.hypervisor.hv_fake_unittest.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# + +# Copyright (C) 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing ganeti.hypervisor.hv_fake""" + +import unittest + +from ganeti import constants +from ganeti import objects +from ganeti import hypervisor + +from ganeti.hypervisor import hv_fake + +import testutils + + +class TestConsole(unittest.TestCase): + def test(self): + instance = objects.Instance(name="fake.example.com") + cons = hv_fake.FakeHypervisor.GetInstanceConsole(instance, {}, {}) + self.assertTrue(cons.Validate()) + self.assertEqual(cons.kind, constants.CONS_MESSAGE) + + +if __name__ == "__main__": + testutils.GanetiTestProgram() diff --git a/test/ganeti.hypervisor.hv_kvm_unittest.py b/test/ganeti.hypervisor.hv_kvm_unittest.py index 28a936be1..c2a964f7c 100755 --- a/test/ganeti.hypervisor.hv_kvm_unittest.py +++ b/test/ganeti.hypervisor.hv_kvm_unittest.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -# Copyright (C) 2010 Google Inc. +# Copyright (C) 2010, 2011 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -80,5 +80,49 @@ class TestWriteNetScript(unittest.TestCase): inst, nic, 2) +class TestConsole(unittest.TestCase): + def _Test(self, instance, hvparams): + cons = hv_kvm.KVMHypervisor.GetInstanceConsole(instance, hvparams, {}) + self.assertTrue(cons.Validate()) + return cons + + def testSerial(self): + instance = objects.Instance(name="kvm.example.com", + primary_node="node6017") + hvparams = { + constants.HV_SERIAL_CONSOLE: True, + constants.HV_VNC_BIND_ADDRESS: None, + } + cons = self._Test(instance, hvparams) + self.assertEqual(cons.kind, constants.CONS_SSH) + self.assertEqual(cons.host, instance.primary_node) + self.assertEqual(cons.command[0], constants.SOCAT_PATH) + + def testVnc(self): + instance = objects.Instance(name="kvm.example.com", + primary_node="node7235", + network_port=constants.VNC_BASE_PORT + 10) + hvparams = { + constants.HV_SERIAL_CONSOLE: False, + constants.HV_VNC_BIND_ADDRESS: "192.0.2.1", + } + cons = self._Test(instance, hvparams) + self.assertEqual(cons.kind, constants.CONS_VNC) + self.assertEqual(cons.host, "192.0.2.1") + self.assertEqual(cons.port, constants.VNC_BASE_PORT + 10) + self.assertEqual(cons.display, 10) + + def testNoConsole(self): + instance = objects.Instance(name="kvm.example.com", + primary_node="node24325", + network_port=0) + hvparams = { + constants.HV_SERIAL_CONSOLE: False, + constants.HV_VNC_BIND_ADDRESS: None, + } + cons = self._Test(instance, hvparams) + self.assertEqual(cons.kind, constants.CONS_MESSAGE) + + if __name__ == "__main__": testutils.GanetiTestProgram() diff --git a/test/ganeti.hypervisor.hv_lxc_unittest.py b/test/ganeti.hypervisor.hv_lxc_unittest.py new file mode 100755 index 000000000..9d9e441ed --- /dev/null +++ b/test/ganeti.hypervisor.hv_lxc_unittest.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# + +# Copyright (C) 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing ganeti.hypervisor.hv_lxc""" + +import unittest + +from ganeti import constants +from ganeti import objects +from ganeti import hypervisor + +from ganeti.hypervisor import hv_lxc + +import testutils + + +class TestConsole(unittest.TestCase): + def test(self): + instance = objects.Instance(name="lxc.example.com", primary_node="node199") + cons = hv_lxc.LXCHypervisor.GetInstanceConsole(instance, {}, {}) + self.assertTrue(cons.Validate()) + self.assertEqual(cons.kind, constants.CONS_SSH) + self.assertEqual(cons.host, instance.primary_node) + self.assertEqual(cons.command[-1], instance.name) + + +if __name__ == "__main__": + testutils.GanetiTestProgram() diff --git a/test/ganeti.hypervisor.hv_xen_unittest.py b/test/ganeti.hypervisor.hv_xen_unittest.py new file mode 100755 index 000000000..dc868396e --- /dev/null +++ b/test/ganeti.hypervisor.hv_xen_unittest.py @@ -0,0 +1,48 @@ +#!/usr/bin/python +# + +# Copyright (C) 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing ganeti.hypervisor.hv_lxc""" + +import unittest + +from ganeti import constants +from ganeti import objects +from ganeti import hypervisor + +from ganeti.hypervisor import hv_xen + +import testutils + + +class TestConsole(unittest.TestCase): + def test(self): + for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]: + instance = objects.Instance(name="xen.example.com", + primary_node="node24828") + cons = cls.GetInstanceConsole(instance, {}, {}) + self.assertTrue(cons.Validate()) + self.assertEqual(cons.kind, constants.CONS_SSH) + self.assertEqual(cons.host, instance.primary_node) + self.assertEqual(cons.command[-1], instance.name) + + +if __name__ == "__main__": + testutils.GanetiTestProgram() -- GitLab