diff --git a/daemons/daemon-util.in b/daemons/daemon-util.in index f9251747f442bcaa7668407430812d523b9eb988..4d1d7c582a1582047401650547245d6600d1681c 100644 --- a/daemons/daemon-util.in +++ b/daemons/daemon-util.in @@ -32,6 +32,7 @@ DAEMONS=( ganeti-masterd ganeti-rapi ganeti-luxid + ganeti-kvmd ) _confd_enabled() { diff --git a/lib/backend.py b/lib/backend.py index fe9ce4eaf2b38f50a13a42d5bc04eb58a1b46c6a..fb34c4c16b2924824377f251fd6baad8d40a8304 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -557,6 +557,7 @@ def LeaveCluster(modify_ssh_setup): logging.exception("Error while removing cluster secrets") utils.StopDaemon(constants.CONFD) + utils.StopDaemon(constants.KVMD) # Raise a custom exception (handled in ganeti-noded) raise errors.QuitGanetiException(True, "Shutdown scheduled") diff --git a/lib/cmdlib/cluster.py b/lib/cmdlib/cluster.py index 2c480adeb6d95f6805a55fa8d6a8f8fc11a97d28..4e0a175b792050eb55d301b067489c3b2836cd4a 100644 --- a/lib/cmdlib/cluster.py +++ b/lib/cmdlib/cluster.py @@ -56,7 +56,7 @@ from ganeti.cmdlib.common import ShareAll, RunPostHook, \ CheckOSParams, CheckHVParams, AdjustCandidatePool, CheckNodePVs, \ ComputeIPolicyInstanceViolation, AnnotateDiskParams, SupportsOob, \ CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \ - CheckDiskAccessModeConsistency, CreateNewClientCert + CheckDiskAccessModeConsistency, CreateNewClientCert, EnsureKvmdOnNodes import ganeti.masterd.instance @@ -1332,6 +1332,8 @@ class LUClusterSetParams(LogicalUnit): self._SetSharedFileStorageDir(feedback_fn) self._SetDrbdHelper(feedback_fn) + ensure_kvmd = False + if self.op.hvparams: self.cluster.hvparams = self.new_hvparams if self.op.os_hvp: @@ -1339,6 +1341,7 @@ class LUClusterSetParams(LogicalUnit): if self.op.enabled_hypervisors is not None: self.cluster.hvparams = self.new_hvparams self.cluster.enabled_hypervisors = self.op.enabled_hypervisors + ensure_kvmd = True if self.op.beparams: self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams if self.op.nicparams: @@ -1397,8 +1400,10 @@ class LUClusterSetParams(LogicalUnit): if self.op.use_external_mip_script is not None: self.cluster.use_external_mip_script = self.op.use_external_mip_script - if self.op.enabled_user_shutdown is not None: + if self.op.enabled_user_shutdown is not None and \ + self.cluster.enabled_user_shutdown != self.op.enabled_user_shutdown: self.cluster.enabled_user_shutdown = self.op.enabled_user_shutdown + ensure_kvmd = True def helper_os(aname, mods, desc): desc += " OS list" @@ -1463,6 +1468,13 @@ class LUClusterSetParams(LogicalUnit): result.Warn("Could not re-enable the master ip on the master," " please restart manually", self.LogWarning) + # Even though 'self.op.enabled_user_shutdown' is being tested + # above, the RPCs can only be done after 'self.cfg.Update' because + # this will update the cluster object and sync 'Ssconf', and kvmd + # uses 'Ssconf'. + if ensure_kvmd: + EnsureKvmdOnNodes(self, feedback_fn) + class LUClusterVerify(NoHooksLU): """Submits all jobs necessary to verify the cluster. diff --git a/lib/cmdlib/common.py b/lib/cmdlib/common.py index 443a164487be11e1aed6a0b6a39fa2d0e08043f0..073d4f5e9ddc9c2531e7f8de7f68a79597f893ac 100644 --- a/lib/cmdlib/common.py +++ b/lib/cmdlib/common.py @@ -1279,3 +1279,65 @@ def CreateNewClientCert(lu, node_uuid, filename=None): ((crypto_type, new_digest), ) = result.payload assert crypto_type == constants.CRYPTO_TYPE_SSL_DIGEST return new_digest + + +def EnsureKvmdOnNodes(lu, feedback_fn, nodes=None): + """Ensure KVM daemon is running on nodes with KVM instances. + + If user shutdown is enabled in the cluster: + - The KVM daemon will be started on VM capable nodes containing + KVM instances. + - The KVM daemon will be stopped on non VM capable nodes. + + If user shutdown is disabled in the cluster: + - The KVM daemon will be stopped on all nodes + + Issues a warning for each failed RPC call. + + @type lu: L{LogicalUnit} + @param lu: logical unit on whose behalf we execute + + @type feedback_fn: callable + @param feedback_fn: feedback function + + @type nodes: list of string + @param nodes: if supplied, it overrides the node uuids to start/stop; + this is used mainly for optimization + + """ + cluster = lu.cfg.GetClusterInfo() + + # Either use the passed nodes or consider all cluster nodes + if nodes is not None: + node_uuids = set(nodes) + else: + node_uuids = lu.cfg.GetNodeList() + + # Determine in which nodes should the KVM daemon be started/stopped + if constants.HT_KVM in cluster.enabled_hypervisors and \ + cluster.enabled_user_shutdown: + start_nodes = [] + stop_nodes = [] + + for node_uuid in node_uuids: + if lu.cfg.GetNodeInfo(node_uuid).vm_capable: + start_nodes.append(node_uuid) + else: + stop_nodes.append(node_uuid) + else: + start_nodes = [] + stop_nodes = node_uuids + + # Start KVM where necessary + if start_nodes: + results = lu.rpc.call_node_ensure_daemon(start_nodes, constants.KVMD, True) + for node_uuid in start_nodes: + results[node_uuid].Warn("Failed to start KVM daemon in node '%s'" % + node_uuid, feedback_fn) + + # Stop KVM where necessary + if stop_nodes: + results = lu.rpc.call_node_ensure_daemon(stop_nodes, constants.KVMD, False) + for node_uuid in stop_nodes: + results[node_uuid].Warn("Failed to stop KVM daemon in node '%s'" % + node_uuid, feedback_fn) diff --git a/lib/cmdlib/node.py b/lib/cmdlib/node.py index 15fb83e8c0308c9884af6bb560f1ca22bb0ed57b..8f7064654244e84aafd4cbb40035e46c6b71c1ee 100644 --- a/lib/cmdlib/node.py +++ b/lib/cmdlib/node.py @@ -43,7 +43,8 @@ from ganeti.cmdlib.common import CheckParamsNotGlobal, \ AdjustCandidatePool, CheckIAllocatorOrNode, LoadNodeEvacResult, \ GetWantedNodes, MapInstanceLvsToNodes, RunPostHook, \ FindFaultyInstanceDisks, CheckStorageTypeEnabled, CreateNewClientCert, \ - AddNodeCertToCandidateCerts, RemoveNodeCertFromCandidateCerts + AddNodeCertToCandidateCerts, RemoveNodeCertFromCandidateCerts, \ + EnsureKvmdOnNodes def _DecideSelfPromotion(lu, exceptions=None): @@ -432,6 +433,8 @@ class LUNodeAdd(LogicalUnit): cluster.candidate_certs) self.cfg.Update(cluster, feedback_fn) + EnsureKvmdOnNodes(self, feedback_fn, nodes=[self.new_node.uuid]) + class LUNodeSetParams(LogicalUnit): """Modifies the parameters of a node. @@ -808,6 +811,8 @@ class LUNodeSetParams(LogicalUnit): if [self.old_role, self.new_role].count(self._ROLE_CANDIDATE) == 1: self.context.ReaddNode(node) + EnsureKvmdOnNodes(self, feedback_fn, nodes=[node.uuid]) + return result diff --git a/lib/watcher/__init__.py b/lib/watcher/__init__.py index d60fc3e9e0361397c1f22cf5ba5406b720577f3d..3a34d2013d2447cfe4b995b4fa2333f27d08563e 100644 --- a/lib/watcher/__init__.py +++ b/lib/watcher/__init__.py @@ -100,6 +100,8 @@ def StartNodeDaemons(): # start mond as well: all nodes need monitoring if constants.ENABLE_MOND: utils.EnsureDaemon(constants.MOND) + # start kvmd, which will quit if not needed to run + utils.EnsureDaemon(constants.KVMD) def RunWatcherHooks(): diff --git a/test/py/daemon-util_unittest.bash b/test/py/daemon-util_unittest.bash index f5b98f0b2fd390c2a969507211eb70833fb478cc..faacaedff77c91cbbd7819f620b05b87c6421477 100755 --- a/test/py/daemon-util_unittest.bash +++ b/test/py/daemon-util_unittest.bash @@ -36,8 +36,8 @@ if ! grep -q '^ENABLE_MOND = ' lib/_constants.py; then err "Please update $0, mond enable feature is missing" fi -DAEMONS_LIST="noded masterd rapi luxid" -STOPDAEMONS_LIST="luxid rapi masterd noded" +DAEMONS_LIST="noded masterd rapi luxid kvmd" +STOPDAEMONS_LIST="kvmd luxid rapi masterd noded" if grep -q '^ENABLE_CONFD = True' lib/_constants.py; then DAEMONS_LIST="$DAEMONS_LIST confd"