X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/07813a9efd0baac14437ebcc9841c66e3131a265..6f4070cd32d7bf651d86cdac886162e1353a9d91:/lib/hypervisor/hv_fake.py diff --git a/lib/hypervisor/hv_fake.py b/lib/hypervisor/hv_fake.py index 48e645b..0f85e8c 100644 --- a/lib/hypervisor/hv_fake.py +++ b/lib/hypervisor/hv_fake.py @@ -25,11 +25,13 @@ 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 @@ -40,12 +42,13 @@ class FakeHypervisor(hv_base.BaseHypervisor): 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): """Get the list of running instances. @@ -61,7 +64,7 @@ class FakeHypervisor(hv_base.BaseHypervisor): @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: @@ -69,7 +72,7 @@ class FakeHypervisor(hv_base.BaseHypervisor): 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) @@ -88,7 +91,7 @@ class FakeHypervisor(hv_base.BaseHypervisor): 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 @@ -107,7 +110,45 @@ class FakeHypervisor(hv_base.BaseHypervisor): 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, @@ -115,34 +156,28 @@ class FakeHypervisor(hv_base.BaseHypervisor): 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. @@ -152,72 +187,51 @@ class FakeHypervisor(hv_base.BaseHypervisor): """ 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): """Verify the hypervisor. @@ -228,3 +242,98 @@ class FakeHypervisor(hv_base.BaseHypervisor): """ if not os.path.exists(self._ROOT_DIR): 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 + + """ + return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)