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
- 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,
- ]
+ PARAMETERS = {
+ constants.HV_INIT_SCRIPT: (True, utils.IsNormAbsPath,
+ "must be an absolute normalized path",
+ None, None),
+ }
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):
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:
- fstype, mountpoint, rest = 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.
- Args:
- instance_name: the instance name
+ @type instance_name: string
+ @param instance_name: the instance name
+
+ @return: (name, id, memory, vcpus, stat, times)
- Returns:
- (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)
def GetAllInstancesInfo(self):
"""Get properties of all instances.
- Returns:
- [(name, id, memory, vcpus, stat, times),...]
+ @return: [(name, id, memory, vcpus, stat, times),...]
+
"""
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)
raise HypervisorError("Can't run the chroot start script: %s" %
result.output)
- def StopInstance(self, instance, force=False):
+ def StopInstance(self, instance, force=False, retry=False, name=None):
"""Stop an instance.
This method has complicated cleanup tests, as we must:
- finally unmount the instance dir
"""
- root_dir = "%s/%s" % (self._ROOT_DIR, instance.name)
- if not os.path.exists(root_dir):
+ 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
- if self._IsDirLive(root_dir):
+ # Run the chroot stop script only once
+ if not retry and not force:
result = utils.RunCmd(["chroot", root_dir, "/ganeti-chroot", "stop"])
if result.failed:
raise HypervisorError("Can't run the chroot stop script: %s" %
result.output)
- retry = 20
- while not force and self._IsDirLive(root_dir) and retry > 0:
- time.sleep(1)
- retry -= 1
- if retry < 10:
- result = utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir])
- retry = 5
- while force and self._IsDirLive(root_dir) and retry > 0:
- time.sleep(1)
- retry -= 1
- utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir])
- if self._IsDirLive(root_dir):
+
+ if not force:
+ utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir])
+ else:
+ utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir])
+ # 2 seconds at most should be enough for KILL to take action
+ time.sleep(2)
+
+ if self._IsDirLive(root_dir):
+ if force:
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])
- retry = 10
- while retry > 0:
- result = utils.RunCmd(["umount", root_dir])
- if not result.failed:
- break
- retry -= 1
- time.sleep(1)
+
+ result = utils.RunCmd(["umount", root_dir])
if result.failed:
- logging.error("Processes still alive in the chroot: %s",
- utils.RunCmd("fuser -vm %s" % root_dir).output)
- raise HypervisorError("Can't umount the chroot dir: %s" % result.output)
+ msg = ("Processes still alive in the chroot: %s" %
+ utils.RunCmd("fuser -vm %s" % root_dir).output)
+ logging.error(msg)
+ raise HypervisorError("Can't umount the chroot dir: %s (%s)" %
+ (result.output, msg))
def RebootInstance(self, instance):
"""Reboot an instance.
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.
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.
+ For the chroot manager, it just checks the existence of the base dir.
+
+ @return: Problem description if something is wrong, C{None} otherwise
+
+ """
+ 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):
+ """Chroot powercycle, just a wrapper over Linux powercycle.
+
+ """
+ cls.LinuxPowercycle()
+
+ def MigrateInstance(self, instance, target, live):
+ """Migrate an 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
+ @type live: boolean
+ @param live: whether to do a live or non-live migration
+
+ """
+ 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
"""
- if not os.path.exists(self._ROOT_DIR):
- return "The required directory '%s' does not exist." % self._ROOT_DIR
+ raise HypervisorError("Migration not supported by the chroot hypervisor")