X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/bcba4e013eee9eac35943efde857de13f6f8b878..8351df2fae34414d07c2ee326c53d7510088addb:/lib/hypervisor/hv_xen.py diff --git a/lib/hypervisor/hv_xen.py b/lib/hypervisor/hv_xen.py index 1d0b587..63ab47c 100644 --- a/lib/hypervisor/hv_xen.py +++ b/lib/hypervisor/hv_xen.py @@ -82,21 +82,22 @@ def _CreateConfigCpus(cpu_mask): return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list)) -def _RunXmList(fn, xmllist_errors): - """Helper function for L{_GetXmList} to run "xm list". +def _RunInstanceList(fn, instance_list_errors): + """Helper function for L{_GetInstanceList} to retrieve the list of instances + from xen. @type fn: callable - @param fn: Function returning result of running C{xm list} - @type xmllist_errors: list - @param xmllist_errors: Error list + @param fn: Function to query xen for the list of instances + @type instance_list_errors: list + @param instance_list_errors: Error list @rtype: list """ result = fn() if result.failed: - logging.error("xm list failed (%s): %s", result.fail_reason, - result.output) - xmllist_errors.append(result) + logging.error("Retrieving the instance list from xen failed (%s): %s", + result.fail_reason, result.output) + instance_list_errors.append(result) raise utils.RetryAgain() # skip over the heading @@ -141,24 +142,24 @@ def _ParseXmList(lines, include_node): return result -def _GetXmList(fn, include_node, _timeout=5): +def _GetInstanceList(fn, include_node, _timeout=5): """Return the list of running instances. - See L{_RunXmList} and L{_ParseXmList} for parameter details. + See L{_RunInstanceList} and L{_ParseXmList} for parameter details. """ - xmllist_errors = [] + instance_list_errors = [] try: - lines = utils.Retry(_RunXmList, (0.3, 1.5, 1.0), _timeout, - args=(fn, xmllist_errors)) + lines = utils.Retry(_RunInstanceList, (0.3, 1.5, 1.0), _timeout, + args=(fn, instance_list_errors)) except utils.RetryTimeout: - if xmllist_errors: - xmlist_result = xmllist_errors.pop() + if instance_list_errors: + instance_list_result = instance_list_errors.pop() - errmsg = ("xm list failed, timeout exceeded (%s): %s" % - (xmlist_result.fail_reason, xmlist_result.output)) + errmsg = ("listing instances failed, timeout exceeded (%s): %s" % + (instance_list_result.fail_reason, instance_list_result.output)) else: - errmsg = "xm list failed" + errmsg = "listing instances failed" raise errors.HypervisorError(errmsg) @@ -270,7 +271,7 @@ def _GetConfigFileDiskData(block_devices, blockdev_prefix, @param block_devices: list of tuples (cfdev, rldev): - cfdev: dict containing ganeti config disk part - - rldev: ganeti.bdev.BlockDev object + - rldev: ganeti.block.bdev.BlockDev object @param blockdev_prefix: a string containing blockdevice prefix, e.g. "sd" for /dev/sda @@ -335,13 +336,20 @@ class XenHypervisor(hv_base.BaseHypervisor): self._cmd = _cmd - def _GetCommand(self): + def _GetCommand(self, hvparams=None): """Returns Xen command to use. + @type hvparams: dict of strings + @param hvparams: hypervisor parameters + """ if self._cmd is None: - # TODO: Make command a hypervisor parameter - cmd = constants.XEN_CMD + if hvparams is not None: + cmd = hvparams[constants.HV_XEN_CMD] + else: + # TODO: Remove autoconf option once retrieving the command from + # the hvparams is fully implemented. + cmd = constants.XEN_CMD else: cmd = self._cmd @@ -350,13 +358,15 @@ class XenHypervisor(hv_base.BaseHypervisor): return cmd - def _RunXen(self, args): + def _RunXen(self, args, hvparams=None): """Wrapper around L{utils.process.RunCmd} to run Xen command. + @type hvparams: dict of strings + @param hvparams: dictionary of hypervisor params @see: L{utils.process.RunCmd} """ - cmd = [self._GetCommand()] + cmd = [self._GetCommand(hvparams=hvparams)] cmd.extend(args) return self._run_cmd_fn(cmd) @@ -414,18 +424,30 @@ class XenHypervisor(hv_base.BaseHypervisor): """ utils.RemoveFile(self._ConfigFileName(instance_name)) - def _GetXmList(self, include_node): - """Wrapper around module level L{_GetXmList}. + def _StashConfigFile(self, instance_name): + """Move the Xen config file to the log directory and return its new path. """ - return _GetXmList(lambda: self._RunXen(["list"]), include_node) + old_filename = self._ConfigFileName(instance_name) + base = ("%s-%s" % + (instance_name, utils.TimestampForFilename())) + new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base) + utils.RenameFile(old_filename, new_filename) + return new_filename + + def _GetInstanceList(self, include_node, hvparams=None): + """Wrapper around module level L{_GetInstanceList}. - def ListInstances(self): + """ + return _GetInstanceList(lambda: self._RunXen(["list"], hvparams=hvparams), + include_node) + + def ListInstances(self, hvparams=None): """Get the list of running instances. """ - xm_list = self._GetXmList(False) - names = [info[0] for info in xm_list] + instance_list = self._GetInstanceList(False, hvparams=hvparams) + names = [info[0] for info in instance_list] return names def GetInstanceInfo(self, instance_name): @@ -436,9 +458,9 @@ class XenHypervisor(hv_base.BaseHypervisor): @return: tuple (name, id, memory, vcpus, stat, times) """ - xm_list = self._GetXmList(instance_name == _DOM0_NAME) + instance_list = self._GetInstanceList(instance_name == _DOM0_NAME) result = None - for data in xm_list: + for data in instance_list: if data[0] == instance_name: result = data break @@ -450,8 +472,7 @@ class XenHypervisor(hv_base.BaseHypervisor): @return: list of tuples (name, id, memory, vcpus, stat, times) """ - xm_list = self._GetXmList(False) - return xm_list + return self._GetInstanceList(False) def _MakeConfigFile(self, instance, startup_memory, block_devices): """Gather configuration details and write to disk. @@ -480,11 +501,15 @@ class XenHypervisor(hv_base.BaseHypervisor): cmd.append("-p") cmd.append(self._ConfigFileName(instance.name)) - result = self._RunXen(cmd) + result = self._RunXen(cmd, hvparams=instance.hvparams) if result.failed: - raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % + # Move the Xen configuration file to the log directory to avoid + # leaving a stale config file behind. + stashed_config = self._StashConfigFile(instance.name) + raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved" + " config file to %s" % (instance.name, result.fail_reason, - result.output)) + result.output, stashed_config)) def StopInstance(self, instance, force=False, retry=False, name=None): """Stop an instance. @@ -493,18 +518,25 @@ class XenHypervisor(hv_base.BaseHypervisor): if name is None: name = instance.name - return self._StopInstance(name, force) + return self._StopInstance(name, force, instance.hvparams) - def _StopInstance(self, name, force): + def _StopInstance(self, name, force, hvparams): """Stop an instance. + @type name: string + @param name: name of the instance to be shutdown + @type force: boolean + @param force: flag specifying whether shutdown should be forced + @type hvparams: dict of string + @param hvparams: hypervisor parameters of the instance + """ if force: action = "destroy" else: action = "shutdown" - result = self._RunXen([action, name]) + result = self._RunXen([action, name], hvparams=hvparams) if result.failed: raise errors.HypervisorError("Failed to stop instance %s: %s, %s" % (name, result.fail_reason, result.output)) @@ -522,7 +554,7 @@ class XenHypervisor(hv_base.BaseHypervisor): raise errors.HypervisorError("Failed to reboot instance %s," " not running" % instance.name) - result = self._RunXen(["reboot", instance.name]) + result = self._RunXen(["reboot", instance.name], hvparams=instance.hvparams) if result.failed: raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" % (instance.name, result.fail_reason, @@ -555,7 +587,8 @@ class XenHypervisor(hv_base.BaseHypervisor): @param mem: actual memory size to use for instance runtime """ - result = self._RunXen(["mem-set", instance.name, mem]) + result = self._RunXen(["mem-set", instance.name, mem], + hvparams=instance.hvparams) if result.failed: raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" % (instance.name, result.fail_reason, @@ -583,7 +616,7 @@ class XenHypervisor(hv_base.BaseHypervisor): result.output) return None - return _GetNodeInfo(result.stdout, self._GetXmList) + return _GetNodeInfo(result.stdout, self._GetInstanceList) @classmethod def GetInstanceConsole(cls, instance, hvparams, beparams): @@ -597,17 +630,33 @@ class XenHypervisor(hv_base.BaseHypervisor): command=[pathutils.XEN_CONSOLE_WRAPPER, constants.XEN_CMD, instance.name]) - def Verify(self): + def Verify(self, hvparams=None): """Verify the hypervisor. For Xen, this verifies that the xend process is running. + @type hvparams: dict of strings + @param hvparams: hypervisor parameters to be verified against + @return: Problem description if something is wrong, C{None} otherwise """ - result = self._RunXen(["info"]) + if hvparams is None: + return "Could not verify the hypervisor, because no hvparams were" \ + " provided." + + if constants.HV_XEN_CMD in hvparams: + xen_cmd = hvparams[constants.HV_XEN_CMD] + try: + self._CheckToolstack(xen_cmd) + except errors.HypervisorError: + return "The configured xen toolstack '%s' is not available on this" \ + " node." % xen_cmd + + result = self._RunXen(["info"], hvparams=hvparams) if result.failed: - return "'xm info' failed: %s, %s" % (result.fail_reason, result.output) + return "Retrieving information from xen failed: %s, %s" % \ + (result.fail_reason, result.output) return None @@ -672,19 +721,22 @@ class XenHypervisor(hv_base.BaseHypervisor): cluster_name = ssconf.SimpleStore().GetClusterName() return self._MigrateInstance(cluster_name, instance.name, target, port, - live) + live, instance.hvparams) def _MigrateInstance(self, cluster_name, instance_name, target, port, live, - _ping_fn=netutils.TcpPing): + hvparams, _ping_fn=netutils.TcpPing): """Migrate an instance to a target node. @see: L{MigrateInstance} for details """ - if self.GetInstanceInfo(instance_name) is None: + if hvparams is None: + raise errors.HypervisorError("No hvparams provided.") + + if self.GetInstanceInfo(instance_name, hvparams=hvparams) is None: raise errors.HypervisorError("Instance not running, cannot migrate") - cmd = self._GetCommand() + cmd = self._GetCommand(hvparams=hvparams) if (cmd == constants.XEN_CMD_XM and not _ping_fn(target, port, live_port_needed=True)): @@ -709,7 +761,7 @@ class XenHypervisor(hv_base.BaseHypervisor): args.extend([instance_name, target]) - result = self._RunXen(args) + result = self._RunXen(args, hvparams=hvparams) if result.failed: raise errors.HypervisorError("Failed to migrate instance %s: %s" % (instance_name, result.output)) @@ -767,6 +819,48 @@ class XenHypervisor(hv_base.BaseHypervisor): finally: utils.RunCmd([constants.XEN_CMD, "debug", "R"]) + def _CheckToolstack(self, xen_cmd): + """Check whether the given toolstack is available on the node. + + @type xen_cmd: string + @param xen_cmd: xen command (e.g. 'xm' or 'xl') + + """ + binary_found = self._CheckToolstackBinary(xen_cmd) + if not binary_found: + raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd) + elif xen_cmd == constants.XEN_CMD_XL: + if not self._CheckToolstackXlConfigured(): + raise errors.HypervisorError("Toolstack '%s' is not enabled on this" + "node." % xen_cmd) + + def _CheckToolstackBinary(self, xen_cmd): + """Checks whether the xen command's binary is found on the machine. + + """ + if xen_cmd not in constants.KNOWN_XEN_COMMANDS: + raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd) + result = self._run_cmd_fn(["which", xen_cmd]) + return not result.failed + + def _CheckToolstackXlConfigured(self): + """Checks whether xl is enabled on an xl-capable node. + + @rtype: bool + @returns: C{True} if 'xl' is enabled, C{False} otherwise + + """ + result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"]) + if not result.failed: + return True + elif result.failed: + if "toolstack" in result.stderr: + return False + # xl fails for some other reason than the toolstack + else: + raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s." + % (constants.XEN_CMD_XL, result.stderr)) + class XenPvmHypervisor(XenHypervisor): """Xen PVM hypervisor interface""" @@ -789,6 +883,8 @@ class XenPvmHypervisor(XenHypervisor): constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK, constants.HV_CPU_WEIGHT: (False, lambda x: 0 < x < 65536, "invalid weight", None, None), + constants.HV_XEN_CMD: + hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS), } def _GetConfig(self, instance, startup_memory, block_devices): @@ -911,6 +1007,8 @@ class XenHvmHypervisor(XenHypervisor): (False, lambda x: 0 < x < 65535, "invalid weight", None, None), constants.HV_VIF_TYPE: hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES), + constants.HV_XEN_CMD: + hv_base.ParamInSet(True, constants.KNOWN_XEN_COMMANDS), } def _GetConfig(self, instance, startup_memory, block_devices):