X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/94fed7dabe521ca18aa9aaa47f22d744b05ee8cc..ffc271168b940658a28a0fbd663003e583faeb15:/lib/hypervisor/hv_chroot.py diff --git a/lib/hypervisor/hv_chroot.py b/lib/hypervisor/hv_chroot.py index 1a9e4a3..5d88447 100644 --- a/lib/hypervisor/hv_chroot.py +++ b/lib/hypervisor/hv_chroot.py @@ -27,11 +27,12 @@ import os import os.path import time import logging -from cStringIO import StringIO from ganeti import constants -from ganeti import errors +from ganeti import errors # pylint: disable=W0611 from ganeti import utils +from ganeti import objects +from ganeti import pathutils from ganeti.hypervisor import hv_base from ganeti.errors import HypervisorError @@ -58,7 +59,7 @@ class ChrootManager(hv_base.BaseHypervisor): - instance alive check is based on whether any process is using the chroot """ - _ROOT_DIR = constants.RUN_GANETI_DIR + "/chroot-hypervisor" + _ROOT_DIR = pathutils.RUN_DIR + "/chroot-hypervisor" PARAMETERS = { constants.HV_INIT_SCRIPT: (True, utils.IsNormAbsPath, @@ -68,11 +69,7 @@ class ChrootManager(hv_base.BaseHypervisor): def __init__(self): hv_base.BaseHypervisor.__init__(self) - if not os.path.exists(self._ROOT_DIR): - os.mkdir(self._ROOT_DIR) - if not os.path.isdir(self._ROOT_DIR): - raise HypervisorError("Needed path %s is not a directory" % - self._ROOT_DIR) + utils.EnsureDirs([(self._ROOT_DIR, constants.RUN_DIRS_MODE)]) @staticmethod def _IsDirLive(path): @@ -88,30 +85,29 @@ class ChrootManager(hv_base.BaseHypervisor): def _GetMountSubdirs(path): """Return the list of mountpoints under a given path. - This function is Linux-specific. + """ + result = [] + for _, mountpoint, _, _ in utils.GetMounts(): + if (mountpoint.startswith(path) and + mountpoint != path): + result.append(mountpoint) + + result.sort(key=lambda x: x.count("/"), reverse=True) + return result + + @classmethod + def _InstanceDir(cls, instance_name): + """Return the root directory for an instance. """ - #TODO(iustin): investigate and document non-linux options - #(e.g. via mount output) - data = [] - fh = open("/proc/mounts", "r") - try: - for line in fh: - _, mountpoint, _ = line.split(" ", 2) - if (mountpoint.startswith(path) and - mountpoint != path): - data.append(mountpoint) - finally: - fh.close() - data.sort(key=lambda x: x.count("/"), reverse=True) - return data + return utils.PathJoin(cls._ROOT_DIR, instance_name) def ListInstances(self): """Get the list of running instances. """ return [name for name in os.listdir(self._ROOT_DIR) - if self._IsDirLive(os.path.join(self._ROOT_DIR, name))] + if self._IsDirLive(utils.PathJoin(self._ROOT_DIR, name))] def GetInstanceInfo(self, instance_name): """Get instance properties. @@ -122,7 +118,7 @@ class ChrootManager(hv_base.BaseHypervisor): @return: (name, id, memory, vcpus, stat, times) """ - dir_name = "%s/%s" % (self._ROOT_DIR, instance_name) + dir_name = self._InstanceDir(instance_name) if not self._IsDirLive(dir_name): raise HypervisorError("Instance %s is not running" % instance_name) return (instance_name, 0, 0, 0, 0, 0) @@ -135,19 +131,19 @@ class ChrootManager(hv_base.BaseHypervisor): """ data = [] for file_name in os.listdir(self._ROOT_DIR): - path = os.path.join(self._ROOT_DIR, file_name) + path = utils.PathJoin(self._ROOT_DIR, file_name) if self._IsDirLive(path): data.append((file_name, 0, 0, 0, 0, 0)) return data - def StartInstance(self, instance, block_devices): + def StartInstance(self, instance, block_devices, startup_paused): """Start an instance. For the chroot manager, we try to mount the block device and execute '/ganeti-chroot start'. """ - root_dir = "%s/%s" % (self._ROOT_DIR, instance.name) + root_dir = self._InstanceDir(instance.name) if not os.path.exists(root_dir): try: os.mkdir(root_dir) @@ -171,7 +167,7 @@ class ChrootManager(hv_base.BaseHypervisor): raise HypervisorError("Can't run the chroot start script: %s" % result.output) - def StopInstance(self, instance, force=False, retry=False): + def StopInstance(self, instance, force=False, retry=False, name=None): """Stop an instance. This method has complicated cleanup tests, as we must: @@ -180,7 +176,10 @@ class ChrootManager(hv_base.BaseHypervisor): - finally unmount the instance dir """ - root_dir = "%s/%s" % (self._ROOT_DIR, instance.name) + if name is None: + name = instance.name + + root_dir = self._InstanceDir(name) if not os.path.exists(root_dir) or not self._IsDirLive(root_dir): return @@ -203,11 +202,23 @@ class ChrootManager(hv_base.BaseHypervisor): raise HypervisorError("Can't stop the processes using the chroot") return + def CleanupInstance(self, instance_name): + """Cleanup after a stopped instance + + """ + root_dir = self._InstanceDir(instance_name) + + if not os.path.exists(root_dir): + return + + if self._IsDirLive(root_dir): + raise HypervisorError("Processes are still using the chroot") + for mpath in self._GetMountSubdirs(root_dir): utils.RunCmd(["umount", mpath]) result = utils.RunCmd(["umount", root_dir]) - if result.failed and force: + if result.failed: msg = ("Processes still alive in the chroot: %s" % utils.RunCmd("fuser -vm %s" % root_dir).output) logging.error(msg) @@ -223,6 +234,18 @@ class ChrootManager(hv_base.BaseHypervisor): raise HypervisorError("The chroot manager doesn't implement the" " reboot functionality") + def BalloonInstanceMemory(self, instance, mem): + """Balloon an instance memory to a certain value. + + @type instance: L{objects.Instance} + @param instance: instance to be accepted + @type mem: int + @param mem: actual memory size to use for instance runtime + + """ + # Currently chroots don't have memory limits + pass + def GetNodeInfo(self): """Return information about the node. @@ -237,24 +260,34 @@ 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=W0221 + hvparams, beparams, root_dir=None): + """Return information for connecting to the console of an instance. """ - root_dir = "%s/%s" % (cls._ROOT_DIR, instance.name) - if not os.path.ismount(root_dir): - raise HypervisorError("Instance %s is not running" % instance.name) + 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 "chroot %s" % root_dir + return objects.InstanceConsole(instance=instance.name, + kind=constants.CONS_SSH, + host=instance.primary_node, + user=constants.SSH_CONSOLE_USER, + command=["chroot", root_dir]) def Verify(self): """Verify the hypervisor. For the chroot manager, it just checks the existence of the base dir. + @return: Problem description if something is wrong, C{None} otherwise + """ - if not os.path.exists(self._ROOT_DIR): - return "The required directory '%s' does not exist." % self._ROOT_DIR + if os.path.exists(self._ROOT_DIR): + return None + else: + return "The required directory '%s' does not exist" % self._ROOT_DIR @classmethod def PowercycleNode(cls): @@ -266,7 +299,7 @@ class ChrootManager(hv_base.BaseHypervisor): def MigrateInstance(self, instance, target, live): """Migrate an instance. - @type instance: L{object.Instance} + @type instance: L{objects.Instance} @param instance: the instance to be migrated @type target: string @param target: hostname (usually ip) of the target node @@ -275,3 +308,16 @@ class ChrootManager(hv_base.BaseHypervisor): """ raise HypervisorError("Migration not supported by the chroot hypervisor") + + def GetMigrationStatus(self, instance): + """Get the migration status + + @type instance: L{objects.Instance} + @param instance: the instance that is being migrated + @rtype: L{objects.MigrationStatus} + @return: the status of the current migration (one of + L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional + progress info that can be retrieved from the hypervisor + + """ + raise HypervisorError("Migration not supported by the chroot hypervisor")