import os
import os.path
-import re
+import logging
from ganeti import utils
from ganeti import constants
from ganeti import errors
+from ganeti import objects
+from ganeti import pathutils
from ganeti.hypervisor import hv_base
a real virtualisation software installed.
"""
- _ROOT_DIR = constants.RUN_DIR + "/ganeti-fake-hypervisor"
+ CAN_MIGRATE = True
+
+ _ROOT_DIR = pathutils.RUN_DIR + "/fake-hypervisor"
def __init__(self):
hv_base.BaseHypervisor.__init__(self)
- if not os.path.exists(self._ROOT_DIR):
- os.mkdir(self._ROOT_DIR)
+ utils.EnsureDirs([(self._ROOT_DIR, constants.RUN_DIRS_MODE)])
- def ListInstances(self):
+ def ListInstances(self, hvparams=None):
"""Get the list of running instances.
"""
@return: tuple of (name, id, memory, vcpus, stat, times)
"""
- file_name = "%s/%s" % (self._ROOT_DIR, instance_name)
+ file_name = self._InstanceFile(instance_name)
if not os.path.exists(file_name):
return None
try:
try:
inst_id = fh.readline().strip()
memory = utils.TryConvert(int, fh.readline().strip())
- vcpus = utils.TryConvert(fh.readline().strip())
+ vcpus = utils.TryConvert(int, fh.readline().strip())
stat = "---b-"
times = "0"
return (instance_name, inst_id, memory, vcpus, stat, times)
data = []
for file_name in os.listdir(self._ROOT_DIR):
try:
- fh = open(self._ROOT_DIR+"/"+file_name, "r")
+ fh = open(utils.PathJoin(self._ROOT_DIR, file_name), "r")
inst_id = "-1"
memory = 0
vcpus = 1
raise errors.HypervisorError("Failed to list instances: %s" % err)
return data
- def StartInstance(self, instance, block_devices):
+ @classmethod
+ def _InstanceFile(cls, instance_name):
+ """Compute the instance file for an instance name.
+
+ """
+ return utils.PathJoin(cls._ROOT_DIR, instance_name)
+
+ def _IsAlive(self, instance_name):
+ """Checks if an instance is alive.
+
+ """
+ file_name = self._InstanceFile(instance_name)
+ return os.path.exists(file_name)
+
+ def _MarkUp(self, instance, memory):
+ """Mark the instance as running.
+
+ This does no checks, which should be done by its callers.
+
+ """
+ file_name = self._InstanceFile(instance.name)
+ fh = file(file_name, "w")
+ try:
+ fh.write("0\n%d\n%d\n" %
+ (memory,
+ instance.beparams[constants.BE_VCPUS]))
+ finally:
+ fh.close()
+
+ def _MarkDown(self, instance_name):
+ """Mark the instance as running.
+
+ This does no checks, which should be done by its callers.
+
+ """
+ file_name = self._InstanceFile(instance_name)
+ utils.RemoveFile(file_name)
+
+ def StartInstance(self, instance, block_devices, startup_paused):
"""Start an instance.
For the fake hypervisor, it just creates a file in the base dir,
handle race conditions properly, since these are *FAKE* instances.
"""
- file_name = self._ROOT_DIR + "/%s" % instance.name
- if os.path.exists(file_name):
+ if self._IsAlive(instance.name):
raise errors.HypervisorError("Failed to start instance %s: %s" %
(instance.name, "already running"))
try:
- fh = file(file_name, "w")
- try:
- fh.write("0\n%d\n%d\n" %
- (instance.beparams[constants.BE_MEMORY],
- instance.beparams[constants.BE_VCPUS]))
- finally:
- fh.close()
+ self._MarkUp(instance, self._InstanceStartupMemory(instance))
except IOError, err:
raise errors.HypervisorError("Failed to start instance %s: %s" %
(instance.name, err))
- def StopInstance(self, instance, force=False):
+ def StopInstance(self, instance, force=False, retry=False, name=None):
"""Stop an instance.
For the fake hypervisor, this just removes the file in the base
dir, if it exist, otherwise we raise an exception.
"""
- file_name = self._ROOT_DIR + "/%s" % instance.name
- if not os.path.exists(file_name):
+ if name is None:
+ name = instance.name
+ if not self._IsAlive(name):
raise errors.HypervisorError("Failed to stop instance %s: %s" %
- (instance.name, "not running"))
- utils.RemoveFile(file_name)
+ (name, "not running"))
+ self._MarkDown(name)
def RebootInstance(self, instance):
"""Reboot an instance.
"""
return
+ 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
+
+ """
+ if not self._IsAlive(instance.name):
+ raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
+ (instance.name, "not running"))
+ try:
+ self._MarkUp(instance, mem)
+ except EnvironmentError, err:
+ raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
+ (instance.name, utils.ErrnoOrStr(err)))
+
def GetNodeInfo(self):
"""Return information about the node.
+ This is just a wrapper over the base GetLinuxNodeInfo method.
+
@return: a dict with the following keys (values in MiB):
- memory_total: the total memory size on the node
- memory_free: the available memory on the node for instances
- memory_dom0: the memory used by the node itself, if available
"""
- # global ram usage from the xm info command
- # memory : 3583
- # free_memory : 747
- # note: in xen 3, memory has changed to total_memory
- try:
- fh = file("/proc/meminfo")
- try:
- data = fh.readlines()
- finally:
- fh.close()
- except IOError, err:
- raise errors.HypervisorError("Failed to list node info: %s" % err)
-
- result = {}
- sum_free = 0
- for line in data:
- splitfields = line.split(":", 1)
-
- if len(splitfields) > 1:
- key = splitfields[0].strip()
- val = splitfields[1].strip()
- if key == 'MemTotal':
- result['memory_total'] = int(val.split()[0])/1024
- elif key in ('MemFree', 'Buffers', 'Cached'):
- sum_free += int(val.split()[0])/1024
- elif key == 'Active':
- result['memory_dom0'] = int(val.split()[0])/1024
- result['memory_free'] = sum_free
-
+ result = self.GetLinuxNodeInfo()
# substract running instances
all_instances = self.GetAllInstancesInfo()
- result['memory_free'] -= min(result['memory_free'],
+ result["memory_free"] -= min(result["memory_free"],
sum([row[2] for row in all_instances]))
-
- cpu_total = 0
- try:
- fh = open("/proc/cpuinfo")
- try:
- cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
- fh.read()))
- finally:
- fh.close()
- except EnvironmentError, err:
- raise errors.HypervisorError("Failed to list node info: %s" % err)
- result['cpu_total'] = cpu_total
- # FIXME: export correct data here
- result['cpu_nodes'] = 1
- result['cpu_sockets'] = 1
-
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):
+ def Verify(self, hvparams=None):
"""Verify the hypervisor.
For the fake hypervisor, it just checks the existence of the base
dir.
+ @type hvparams: dict of strings
+ @param hvparams: hypervisor parameters to be verified against; not used
+ for fake hypervisors
+
+ @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):
+ """Fake hypervisor powercycle, just a wrapper over Linux powercycle.
+
+ """
+ cls.LinuxPowercycle()
+
+ 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: instance info, not used
+ @type target: string
+ @param target: target host (usually ip), on this node
+
+ """
+ if self._IsAlive(instance.name):
+ raise errors.HypervisorError("Can't accept instance, already running")
+
+ 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
+
+ """
+ logging.debug("Fake hypervisor migrating %s to %s (live=%s)",
+ instance, target, live)
+
+ def FinalizeMigrationDst(self, instance, info, success):
+ """Finalize the instance migration on the target node.
+
+ For the fake hv, this just marks the instance up.
+
+ @type instance: L{objects.Instance}
+ @param instance: instance whose migration is being finalized
+ @type info: string/data (opaque)
+ @param info: migration information, from the source node
+ @type success: boolean
+ @param success: whether the migration was a success or a failure
+
+ """
+ if success:
+ self._MarkUp(instance, self._InstanceStartupMemory(instance))
+ else:
+ # ensure it's down
+ self._MarkDown(instance.name)
+
+ def PostMigrationCleanup(self, instance):
+ """Clean-up after a migration.
+
+ To be executed on the source node.
+
+ @type instance: L{objects.Instance}
+ @param instance: the instance that was migrated
+
+ """
+ pass
+
+ def FinalizeMigrationSource(self, instance, success, live):
+ """Finalize the instance migration on the source node.
+
+ @type instance: L{objects.Instance}
+ @param instance: the instance that was migrated
+ @type success: bool
+ @param success: whether the migration succeeded or not
+ @type live: bool
+ @param live: whether the user requested a live migration or not
+
+ """
+ # pylint: disable=W0613
+ if success:
+ self._MarkDown(instance.name)
+
+ def GetMigrationStatus(self, instance):
+ """Get the migration status
+
+ The fake hypervisor migration always succeeds.
+
+ @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
+ return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)