Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ da27bc7d

History | View | Annotate | Download (129 kB)

1 7352d33b Thomas Thrainer
#
2 7352d33b Thomas Thrainer
#
3 7352d33b Thomas Thrainer
4 7352d33b Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 7352d33b Thomas Thrainer
#
6 7352d33b Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 7352d33b Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 7352d33b Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 7352d33b Thomas Thrainer
# (at your option) any later version.
10 7352d33b Thomas Thrainer
#
11 7352d33b Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 7352d33b Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 7352d33b Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 7352d33b Thomas Thrainer
# General Public License for more details.
15 7352d33b Thomas Thrainer
#
16 7352d33b Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 7352d33b Thomas Thrainer
# along with this program; if not, write to the Free Software
18 7352d33b Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 7352d33b Thomas Thrainer
# 02110-1301, USA.
20 7352d33b Thomas Thrainer
21 7352d33b Thomas Thrainer
22 7352d33b Thomas Thrainer
"""Logical units dealing with the cluster."""
23 7352d33b Thomas Thrainer
24 7352d33b Thomas Thrainer
import copy
25 7352d33b Thomas Thrainer
import itertools
26 7352d33b Thomas Thrainer
import logging
27 7352d33b Thomas Thrainer
import operator
28 7352d33b Thomas Thrainer
import os
29 7352d33b Thomas Thrainer
import re
30 7352d33b Thomas Thrainer
import time
31 7352d33b Thomas Thrainer
32 7352d33b Thomas Thrainer
from ganeti import compat
33 7352d33b Thomas Thrainer
from ganeti import constants
34 7352d33b Thomas Thrainer
from ganeti import errors
35 7352d33b Thomas Thrainer
from ganeti import hypervisor
36 7352d33b Thomas Thrainer
from ganeti import locking
37 7352d33b Thomas Thrainer
from ganeti import masterd
38 7352d33b Thomas Thrainer
from ganeti import netutils
39 7352d33b Thomas Thrainer
from ganeti import objects
40 7352d33b Thomas Thrainer
from ganeti import opcodes
41 7352d33b Thomas Thrainer
from ganeti import pathutils
42 7352d33b Thomas Thrainer
from ganeti import query
43 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
44 7352d33b Thomas Thrainer
from ganeti import runtime
45 7352d33b Thomas Thrainer
from ganeti import ssh
46 7352d33b Thomas Thrainer
from ganeti import uidpool
47 7352d33b Thomas Thrainer
from ganeti import utils
48 7352d33b Thomas Thrainer
from ganeti import vcluster
49 7352d33b Thomas Thrainer
50 5eacbcae Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, QueryBase, LogicalUnit, \
51 7352d33b Thomas Thrainer
  ResultWithJobs
52 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import ShareAll, RunPostHook, \
53 5eacbcae Thomas Thrainer
  ComputeAncillaryFiles, RedistributeAncillaryFiles, UploadHelper, \
54 5eacbcae Thomas Thrainer
  GetWantedInstances, MergeAndVerifyHvState, MergeAndVerifyDiskState, \
55 5eacbcae Thomas Thrainer
  GetUpdatedIPolicy, ComputeNewInstanceViolations, GetUpdatedParams, \
56 5eacbcae Thomas Thrainer
  CheckOSParams, CheckHVParams, AdjustCandidatePool, CheckNodePVs, \
57 4e771a95 Helga Velroyen
  ComputeIPolicyInstanceViolation, AnnotateDiskParams, SupportsOob, \
58 294254b1 Raffa Santi
  CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \
59 b3cc1646 Helga Velroyen
  CheckDiskAccessModeConsistency, CreateNewClientCert
60 7352d33b Thomas Thrainer
61 7352d33b Thomas Thrainer
import ganeti.masterd.instance
62 7352d33b Thomas Thrainer
63 7352d33b Thomas Thrainer
64 b3cc1646 Helga Velroyen
def _UpdateMasterClientCert(
65 b3cc1646 Helga Velroyen
    lu, master_uuid, cluster, feedback_fn,
66 b3cc1646 Helga Velroyen
    client_cert=pathutils.NODED_CLIENT_CERT_FILE,
67 b3cc1646 Helga Velroyen
    client_cert_tmp=pathutils.NODED_CLIENT_CERT_FILE_TMP):
68 b3cc1646 Helga Velroyen
  """Renews the master's client certificate and propagates the config.
69 b3cc1646 Helga Velroyen

70 b3cc1646 Helga Velroyen
  @type lu: C{LogicalUnit}
71 b3cc1646 Helga Velroyen
  @param lu: the logical unit holding the config
72 b3cc1646 Helga Velroyen
  @type master_uuid: string
73 b3cc1646 Helga Velroyen
  @param master_uuid: the master node's UUID
74 b3cc1646 Helga Velroyen
  @type cluster: C{objects.Cluster}
75 b3cc1646 Helga Velroyen
  @param cluster: the cluster's configuration
76 b3cc1646 Helga Velroyen
  @type feedback_fn: function
77 b3cc1646 Helga Velroyen
  @param feedback_fn: feedback functions for config updates
78 b3cc1646 Helga Velroyen
  @type client_cert: string
79 b3cc1646 Helga Velroyen
  @param client_cert: the path of the client certificate
80 b3cc1646 Helga Velroyen
  @type client_cert_tmp: string
81 b3cc1646 Helga Velroyen
  @param client_cert_tmp: the temporary path of the client certificate
82 b3cc1646 Helga Velroyen
  @rtype: string
83 b3cc1646 Helga Velroyen
  @return: the digest of the newly created client certificate
84 b3cc1646 Helga Velroyen

85 b3cc1646 Helga Velroyen
  """
86 b3cc1646 Helga Velroyen
  client_digest = CreateNewClientCert(lu, master_uuid, filename=client_cert_tmp)
87 b3cc1646 Helga Velroyen
  utils.AddNodeToCandidateCerts(master_uuid, client_digest,
88 b3cc1646 Helga Velroyen
                                cluster.candidate_certs)
89 b3cc1646 Helga Velroyen
  # This triggers an update of the config and distribution of it with the old
90 b3cc1646 Helga Velroyen
  # SSL certificate
91 b3cc1646 Helga Velroyen
  lu.cfg.Update(cluster, feedback_fn)
92 b3cc1646 Helga Velroyen
93 b3cc1646 Helga Velroyen
  utils.RemoveFile(client_cert)
94 b3cc1646 Helga Velroyen
  utils.RenameFile(client_cert_tmp, client_cert)
95 b3cc1646 Helga Velroyen
  return client_digest
96 b3cc1646 Helga Velroyen
97 b3cc1646 Helga Velroyen
98 b3cc1646 Helga Velroyen
class LUClusterRenewCrypto(NoHooksLU):
99 b3cc1646 Helga Velroyen
  """Renew the cluster's crypto tokens.
100 b3cc1646 Helga Velroyen

101 b3cc1646 Helga Velroyen
  Note that most of this operation is done in gnt_cluster.py, this LU only
102 b3cc1646 Helga Velroyen
  takes care of the renewal of the client SSL certificates.
103 b3cc1646 Helga Velroyen

104 b3cc1646 Helga Velroyen
  """
105 b3cc1646 Helga Velroyen
  def Exec(self, feedback_fn):
106 b3cc1646 Helga Velroyen
    master_uuid = self.cfg.GetMasterNode()
107 b3cc1646 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
108 b3cc1646 Helga Velroyen
109 b3cc1646 Helga Velroyen
    server_digest = utils.GetCertificateDigest(
110 b3cc1646 Helga Velroyen
      cert_filename=pathutils.NODED_CERT_FILE)
111 b3cc1646 Helga Velroyen
    utils.AddNodeToCandidateCerts("%s-SERVER" % master_uuid,
112 b3cc1646 Helga Velroyen
                                  server_digest,
113 b3cc1646 Helga Velroyen
                                  cluster.candidate_certs)
114 da27bc7d Helga Velroyen
    try:
115 da27bc7d Helga Velroyen
      old_master_digest = utils.GetCertificateDigest(
116 da27bc7d Helga Velroyen
        cert_filename=pathutils.NODED_CLIENT_CERT_FILE)
117 da27bc7d Helga Velroyen
      utils.AddNodeToCandidateCerts("%s-OLDMASTER" % master_uuid,
118 da27bc7d Helga Velroyen
                                    old_master_digest,
119 da27bc7d Helga Velroyen
                                    cluster.candidate_certs)
120 da27bc7d Helga Velroyen
    except IOError:
121 da27bc7d Helga Velroyen
      logging.info("No old certificate available.")
122 da27bc7d Helga Velroyen
123 b3cc1646 Helga Velroyen
    new_master_digest = _UpdateMasterClientCert(self, master_uuid, cluster,
124 b3cc1646 Helga Velroyen
                                                feedback_fn)
125 b3cc1646 Helga Velroyen
126 3e8a6f39 Helga Velroyen
    utils.AddNodeToCandidateCerts(master_uuid,
127 3e8a6f39 Helga Velroyen
                                  new_master_digest,
128 3e8a6f39 Helga Velroyen
                                  cluster.candidate_certs)
129 b3cc1646 Helga Velroyen
    nodes = self.cfg.GetAllNodesInfo()
130 b3cc1646 Helga Velroyen
    for (node_uuid, node_info) in nodes.items():
131 b3cc1646 Helga Velroyen
      if node_uuid != master_uuid:
132 b3cc1646 Helga Velroyen
        new_digest = CreateNewClientCert(self, node_uuid)
133 b3cc1646 Helga Velroyen
        if node_info.master_candidate:
134 3e8a6f39 Helga Velroyen
          utils.AddNodeToCandidateCerts(node_uuid,
135 3e8a6f39 Helga Velroyen
                                        new_digest,
136 3e8a6f39 Helga Velroyen
                                        cluster.candidate_certs)
137 3e8a6f39 Helga Velroyen
    utils.RemoveNodeFromCandidateCerts("%s-SERVER" % master_uuid,
138 3e8a6f39 Helga Velroyen
                                       cluster.candidate_certs)
139 3e8a6f39 Helga Velroyen
    utils.RemoveNodeFromCandidateCerts("%s-OLDMASTER" % master_uuid,
140 3e8a6f39 Helga Velroyen
                                       cluster.candidate_certs)
141 b3cc1646 Helga Velroyen
    # Trigger another update of the config now with the new master cert
142 b3cc1646 Helga Velroyen
    self.cfg.Update(cluster, feedback_fn)
143 b3cc1646 Helga Velroyen
144 b3cc1646 Helga Velroyen
145 7352d33b Thomas Thrainer
class LUClusterActivateMasterIp(NoHooksLU):
146 7352d33b Thomas Thrainer
  """Activate the master IP on the master node.
147 7352d33b Thomas Thrainer

148 7352d33b Thomas Thrainer
  """
149 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
150 7352d33b Thomas Thrainer
    """Activate the master IP.
151 7352d33b Thomas Thrainer

152 7352d33b Thomas Thrainer
    """
153 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
154 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
155 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_activate_master_ip(master_params.uuid,
156 7352d33b Thomas Thrainer
                                                   master_params, ems)
157 7352d33b Thomas Thrainer
    result.Raise("Could not activate the master IP")
158 7352d33b Thomas Thrainer
159 7352d33b Thomas Thrainer
160 7352d33b Thomas Thrainer
class LUClusterDeactivateMasterIp(NoHooksLU):
161 7352d33b Thomas Thrainer
  """Deactivate the master IP on the master node.
162 7352d33b Thomas Thrainer

163 7352d33b Thomas Thrainer
  """
164 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
165 7352d33b Thomas Thrainer
    """Deactivate the master IP.
166 7352d33b Thomas Thrainer

167 7352d33b Thomas Thrainer
    """
168 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
169 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
170 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
171 7352d33b Thomas Thrainer
                                                     master_params, ems)
172 7352d33b Thomas Thrainer
    result.Raise("Could not deactivate the master IP")
173 7352d33b Thomas Thrainer
174 7352d33b Thomas Thrainer
175 7352d33b Thomas Thrainer
class LUClusterConfigQuery(NoHooksLU):
176 7352d33b Thomas Thrainer
  """Return configuration values.
177 7352d33b Thomas Thrainer

178 7352d33b Thomas Thrainer
  """
179 7352d33b Thomas Thrainer
  REQ_BGL = False
180 7352d33b Thomas Thrainer
181 7352d33b Thomas Thrainer
  def CheckArguments(self):
182 5eacbcae Thomas Thrainer
    self.cq = ClusterQuery(None, self.op.output_fields, False)
183 7352d33b Thomas Thrainer
184 7352d33b Thomas Thrainer
  def ExpandNames(self):
185 7352d33b Thomas Thrainer
    self.cq.ExpandNames(self)
186 7352d33b Thomas Thrainer
187 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
188 7352d33b Thomas Thrainer
    self.cq.DeclareLocks(self, level)
189 7352d33b Thomas Thrainer
190 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
191 7352d33b Thomas Thrainer
    result = self.cq.OldStyleQuery(self)
192 7352d33b Thomas Thrainer
193 7352d33b Thomas Thrainer
    assert len(result) == 1
194 7352d33b Thomas Thrainer
195 7352d33b Thomas Thrainer
    return result[0]
196 7352d33b Thomas Thrainer
197 7352d33b Thomas Thrainer
198 7352d33b Thomas Thrainer
class LUClusterDestroy(LogicalUnit):
199 7352d33b Thomas Thrainer
  """Logical unit for destroying the cluster.
200 7352d33b Thomas Thrainer

201 7352d33b Thomas Thrainer
  """
202 7352d33b Thomas Thrainer
  HPATH = "cluster-destroy"
203 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
204 7352d33b Thomas Thrainer
205 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
206 7352d33b Thomas Thrainer
    """Build hooks env.
207 7352d33b Thomas Thrainer

208 7352d33b Thomas Thrainer
    """
209 7352d33b Thomas Thrainer
    return {
210 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
211 7352d33b Thomas Thrainer
      }
212 7352d33b Thomas Thrainer
213 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
214 7352d33b Thomas Thrainer
    """Build hooks nodes.
215 7352d33b Thomas Thrainer

216 7352d33b Thomas Thrainer
    """
217 7352d33b Thomas Thrainer
    return ([], [])
218 7352d33b Thomas Thrainer
219 7352d33b Thomas Thrainer
  def CheckPrereq(self):
220 7352d33b Thomas Thrainer
    """Check prerequisites.
221 7352d33b Thomas Thrainer

222 7352d33b Thomas Thrainer
    This checks whether the cluster is empty.
223 7352d33b Thomas Thrainer

224 7352d33b Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
225 7352d33b Thomas Thrainer

226 7352d33b Thomas Thrainer
    """
227 7352d33b Thomas Thrainer
    master = self.cfg.GetMasterNode()
228 7352d33b Thomas Thrainer
229 7352d33b Thomas Thrainer
    nodelist = self.cfg.GetNodeList()
230 7352d33b Thomas Thrainer
    if len(nodelist) != 1 or nodelist[0] != master:
231 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d node(s) in"
232 7352d33b Thomas Thrainer
                                 " this cluster." % (len(nodelist) - 1),
233 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
234 7352d33b Thomas Thrainer
    instancelist = self.cfg.GetInstanceList()
235 7352d33b Thomas Thrainer
    if instancelist:
236 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d instance(s) in"
237 7352d33b Thomas Thrainer
                                 " this cluster." % len(instancelist),
238 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
239 7352d33b Thomas Thrainer
240 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
241 7352d33b Thomas Thrainer
    """Destroys the cluster.
242 7352d33b Thomas Thrainer

243 7352d33b Thomas Thrainer
    """
244 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
245 7352d33b Thomas Thrainer
246 7352d33b Thomas Thrainer
    # Run post hooks on master node before it's removed
247 1c3231aa Thomas Thrainer
    RunPostHook(self, self.cfg.GetNodeName(master_params.uuid))
248 7352d33b Thomas Thrainer
249 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
250 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
251 7352d33b Thomas Thrainer
                                                     master_params, ems)
252 c7dd65be Klaus Aehlig
    result.Warn("Error disabling the master IP address", self.LogWarning)
253 1c3231aa Thomas Thrainer
    return master_params.uuid
254 7352d33b Thomas Thrainer
255 7352d33b Thomas Thrainer
256 7352d33b Thomas Thrainer
class LUClusterPostInit(LogicalUnit):
257 7352d33b Thomas Thrainer
  """Logical unit for running hooks after cluster initialization.
258 7352d33b Thomas Thrainer

259 7352d33b Thomas Thrainer
  """
260 7352d33b Thomas Thrainer
  HPATH = "cluster-init"
261 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
262 7352d33b Thomas Thrainer
263 25ea5b98 Sebastian Gebhard
  def CheckArguments(self):
264 25ea5b98 Sebastian Gebhard
    self.master_uuid = self.cfg.GetMasterNode()
265 25ea5b98 Sebastian Gebhard
    self.master_ndparams = self.cfg.GetNdParams(self.cfg.GetMasterNodeInfo())
266 25ea5b98 Sebastian Gebhard
267 25ea5b98 Sebastian Gebhard
    # TODO: When Issue 584 is solved, and None is properly parsed when used
268 25ea5b98 Sebastian Gebhard
    # as a default value, ndparams.get(.., None) can be changed to
269 25ea5b98 Sebastian Gebhard
    # ndparams[..] to access the values directly
270 25ea5b98 Sebastian Gebhard
271 25ea5b98 Sebastian Gebhard
    # OpenvSwitch: Warn user if link is missing
272 25ea5b98 Sebastian Gebhard
    if (self.master_ndparams[constants.ND_OVS] and not
273 25ea5b98 Sebastian Gebhard
        self.master_ndparams.get(constants.ND_OVS_LINK, None)):
274 25ea5b98 Sebastian Gebhard
      self.LogInfo("No physical interface for OpenvSwitch was given."
275 25ea5b98 Sebastian Gebhard
                   " OpenvSwitch will not have an outside connection. This"
276 25ea5b98 Sebastian Gebhard
                   " might not be what you want.")
277 25ea5b98 Sebastian Gebhard
278 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
279 7352d33b Thomas Thrainer
    """Build hooks env.
280 7352d33b Thomas Thrainer

281 7352d33b Thomas Thrainer
    """
282 7352d33b Thomas Thrainer
    return {
283 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
284 7352d33b Thomas Thrainer
      }
285 7352d33b Thomas Thrainer
286 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
287 7352d33b Thomas Thrainer
    """Build hooks nodes.
288 7352d33b Thomas Thrainer

289 7352d33b Thomas Thrainer
    """
290 7352d33b Thomas Thrainer
    return ([], [self.cfg.GetMasterNode()])
291 7352d33b Thomas Thrainer
292 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
293 25ea5b98 Sebastian Gebhard
    """Create and configure Open vSwitch
294 7352d33b Thomas Thrainer

295 7352d33b Thomas Thrainer
    """
296 25ea5b98 Sebastian Gebhard
    if self.master_ndparams[constants.ND_OVS]:
297 25ea5b98 Sebastian Gebhard
      result = self.rpc.call_node_configure_ovs(
298 25ea5b98 Sebastian Gebhard
                 self.master_uuid,
299 25ea5b98 Sebastian Gebhard
                 self.master_ndparams[constants.ND_OVS_NAME],
300 25ea5b98 Sebastian Gebhard
                 self.master_ndparams.get(constants.ND_OVS_LINK, None))
301 25ea5b98 Sebastian Gebhard
      result.Raise("Could not successully configure Open vSwitch")
302 5b6f9e35 Helga Velroyen
303 b3cc1646 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
304 b3cc1646 Helga Velroyen
    _UpdateMasterClientCert(self, self.master_uuid, cluster, feedback_fn)
305 5b6f9e35 Helga Velroyen
306 7352d33b Thomas Thrainer
    return True
307 7352d33b Thomas Thrainer
308 7352d33b Thomas Thrainer
309 5eacbcae Thomas Thrainer
class ClusterQuery(QueryBase):
310 7352d33b Thomas Thrainer
  FIELDS = query.CLUSTER_FIELDS
311 7352d33b Thomas Thrainer
312 7352d33b Thomas Thrainer
  #: Do not sort (there is only one item)
313 7352d33b Thomas Thrainer
  SORT_FIELD = None
314 7352d33b Thomas Thrainer
315 7352d33b Thomas Thrainer
  def ExpandNames(self, lu):
316 7352d33b Thomas Thrainer
    lu.needed_locks = {}
317 7352d33b Thomas Thrainer
318 7352d33b Thomas Thrainer
    # The following variables interact with _QueryBase._GetNames
319 7352d33b Thomas Thrainer
    self.wanted = locking.ALL_SET
320 7352d33b Thomas Thrainer
    self.do_locking = self.use_locking
321 7352d33b Thomas Thrainer
322 7352d33b Thomas Thrainer
    if self.do_locking:
323 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Can not use locking for cluster queries",
324 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
325 7352d33b Thomas Thrainer
326 7352d33b Thomas Thrainer
  def DeclareLocks(self, lu, level):
327 7352d33b Thomas Thrainer
    pass
328 7352d33b Thomas Thrainer
329 7352d33b Thomas Thrainer
  def _GetQueryData(self, lu):
330 7352d33b Thomas Thrainer
    """Computes the list of nodes and their attributes.
331 7352d33b Thomas Thrainer

332 7352d33b Thomas Thrainer
    """
333 7352d33b Thomas Thrainer
    # Locking is not used
334 7352d33b Thomas Thrainer
    assert not (compat.any(lu.glm.is_owned(level)
335 7352d33b Thomas Thrainer
                           for level in locking.LEVELS
336 7352d33b Thomas Thrainer
                           if level != locking.LEVEL_CLUSTER) or
337 7352d33b Thomas Thrainer
                self.do_locking or self.use_locking)
338 7352d33b Thomas Thrainer
339 7352d33b Thomas Thrainer
    if query.CQ_CONFIG in self.requested_data:
340 7352d33b Thomas Thrainer
      cluster = lu.cfg.GetClusterInfo()
341 1c3231aa Thomas Thrainer
      nodes = lu.cfg.GetAllNodesInfo()
342 7352d33b Thomas Thrainer
    else:
343 7352d33b Thomas Thrainer
      cluster = NotImplemented
344 1c3231aa Thomas Thrainer
      nodes = NotImplemented
345 7352d33b Thomas Thrainer
346 7352d33b Thomas Thrainer
    if query.CQ_QUEUE_DRAINED in self.requested_data:
347 7352d33b Thomas Thrainer
      drain_flag = os.path.exists(pathutils.JOB_QUEUE_DRAIN_FILE)
348 7352d33b Thomas Thrainer
    else:
349 7352d33b Thomas Thrainer
      drain_flag = NotImplemented
350 7352d33b Thomas Thrainer
351 7352d33b Thomas Thrainer
    if query.CQ_WATCHER_PAUSE in self.requested_data:
352 1c3231aa Thomas Thrainer
      master_node_uuid = lu.cfg.GetMasterNode()
353 7352d33b Thomas Thrainer
354 1c3231aa Thomas Thrainer
      result = lu.rpc.call_get_watcher_pause(master_node_uuid)
355 7352d33b Thomas Thrainer
      result.Raise("Can't retrieve watcher pause from master node '%s'" %
356 1c3231aa Thomas Thrainer
                   lu.cfg.GetMasterNodeName())
357 7352d33b Thomas Thrainer
358 7352d33b Thomas Thrainer
      watcher_pause = result.payload
359 7352d33b Thomas Thrainer
    else:
360 7352d33b Thomas Thrainer
      watcher_pause = NotImplemented
361 7352d33b Thomas Thrainer
362 1c3231aa Thomas Thrainer
    return query.ClusterQueryData(cluster, nodes, drain_flag, watcher_pause)
363 7352d33b Thomas Thrainer
364 7352d33b Thomas Thrainer
365 7352d33b Thomas Thrainer
class LUClusterQuery(NoHooksLU):
366 7352d33b Thomas Thrainer
  """Query cluster configuration.
367 7352d33b Thomas Thrainer

368 7352d33b Thomas Thrainer
  """
369 7352d33b Thomas Thrainer
  REQ_BGL = False
370 7352d33b Thomas Thrainer
371 7352d33b Thomas Thrainer
  def ExpandNames(self):
372 7352d33b Thomas Thrainer
    self.needed_locks = {}
373 7352d33b Thomas Thrainer
374 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
375 7352d33b Thomas Thrainer
    """Return cluster config.
376 7352d33b Thomas Thrainer

377 7352d33b Thomas Thrainer
    """
378 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
379 7352d33b Thomas Thrainer
    os_hvp = {}
380 7352d33b Thomas Thrainer
381 7352d33b Thomas Thrainer
    # Filter just for enabled hypervisors
382 7352d33b Thomas Thrainer
    for os_name, hv_dict in cluster.os_hvp.items():
383 7352d33b Thomas Thrainer
      os_hvp[os_name] = {}
384 7352d33b Thomas Thrainer
      for hv_name, hv_params in hv_dict.items():
385 7352d33b Thomas Thrainer
        if hv_name in cluster.enabled_hypervisors:
386 7352d33b Thomas Thrainer
          os_hvp[os_name][hv_name] = hv_params
387 7352d33b Thomas Thrainer
388 7352d33b Thomas Thrainer
    # Convert ip_family to ip_version
389 7352d33b Thomas Thrainer
    primary_ip_version = constants.IP4_VERSION
390 7352d33b Thomas Thrainer
    if cluster.primary_ip_family == netutils.IP6Address.family:
391 7352d33b Thomas Thrainer
      primary_ip_version = constants.IP6_VERSION
392 7352d33b Thomas Thrainer
393 7352d33b Thomas Thrainer
    result = {
394 7352d33b Thomas Thrainer
      "software_version": constants.RELEASE_VERSION,
395 7352d33b Thomas Thrainer
      "protocol_version": constants.PROTOCOL_VERSION,
396 7352d33b Thomas Thrainer
      "config_version": constants.CONFIG_VERSION,
397 7352d33b Thomas Thrainer
      "os_api_version": max(constants.OS_API_VERSIONS),
398 7352d33b Thomas Thrainer
      "export_version": constants.EXPORT_VERSION,
399 026f444f Thomas Thrainer
      "vcs_version": constants.VCS_VERSION,
400 7352d33b Thomas Thrainer
      "architecture": runtime.GetArchInfo(),
401 7352d33b Thomas Thrainer
      "name": cluster.cluster_name,
402 1c3231aa Thomas Thrainer
      "master": self.cfg.GetMasterNodeName(),
403 7352d33b Thomas Thrainer
      "default_hypervisor": cluster.primary_hypervisor,
404 7352d33b Thomas Thrainer
      "enabled_hypervisors": cluster.enabled_hypervisors,
405 7352d33b Thomas Thrainer
      "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
406 7352d33b Thomas Thrainer
                        for hypervisor_name in cluster.enabled_hypervisors]),
407 7352d33b Thomas Thrainer
      "os_hvp": os_hvp,
408 7352d33b Thomas Thrainer
      "beparams": cluster.beparams,
409 7352d33b Thomas Thrainer
      "osparams": cluster.osparams,
410 7352d33b Thomas Thrainer
      "ipolicy": cluster.ipolicy,
411 7352d33b Thomas Thrainer
      "nicparams": cluster.nicparams,
412 7352d33b Thomas Thrainer
      "ndparams": cluster.ndparams,
413 7352d33b Thomas Thrainer
      "diskparams": cluster.diskparams,
414 7352d33b Thomas Thrainer
      "candidate_pool_size": cluster.candidate_pool_size,
415 178ad717 Klaus Aehlig
      "max_running_jobs": cluster.max_running_jobs,
416 7352d33b Thomas Thrainer
      "master_netdev": cluster.master_netdev,
417 7352d33b Thomas Thrainer
      "master_netmask": cluster.master_netmask,
418 7352d33b Thomas Thrainer
      "use_external_mip_script": cluster.use_external_mip_script,
419 7352d33b Thomas Thrainer
      "volume_group_name": cluster.volume_group_name,
420 7352d33b Thomas Thrainer
      "drbd_usermode_helper": cluster.drbd_usermode_helper,
421 7352d33b Thomas Thrainer
      "file_storage_dir": cluster.file_storage_dir,
422 7352d33b Thomas Thrainer
      "shared_file_storage_dir": cluster.shared_file_storage_dir,
423 7352d33b Thomas Thrainer
      "maintain_node_health": cluster.maintain_node_health,
424 7352d33b Thomas Thrainer
      "ctime": cluster.ctime,
425 7352d33b Thomas Thrainer
      "mtime": cluster.mtime,
426 7352d33b Thomas Thrainer
      "uuid": cluster.uuid,
427 7352d33b Thomas Thrainer
      "tags": list(cluster.GetTags()),
428 7352d33b Thomas Thrainer
      "uid_pool": cluster.uid_pool,
429 7352d33b Thomas Thrainer
      "default_iallocator": cluster.default_iallocator,
430 0359e5d0 Spyros Trigazis
      "default_iallocator_params": cluster.default_iallocator_params,
431 7352d33b Thomas Thrainer
      "reserved_lvs": cluster.reserved_lvs,
432 7352d33b Thomas Thrainer
      "primary_ip_version": primary_ip_version,
433 7352d33b Thomas Thrainer
      "prealloc_wipe_disks": cluster.prealloc_wipe_disks,
434 7352d33b Thomas Thrainer
      "hidden_os": cluster.hidden_os,
435 7352d33b Thomas Thrainer
      "blacklisted_os": cluster.blacklisted_os,
436 fe782deb Helga Velroyen
      "enabled_disk_templates": cluster.enabled_disk_templates,
437 7352d33b Thomas Thrainer
      }
438 7352d33b Thomas Thrainer
439 7352d33b Thomas Thrainer
    return result
440 7352d33b Thomas Thrainer
441 7352d33b Thomas Thrainer
442 7352d33b Thomas Thrainer
class LUClusterRedistConf(NoHooksLU):
443 7352d33b Thomas Thrainer
  """Force the redistribution of cluster configuration.
444 7352d33b Thomas Thrainer

445 7352d33b Thomas Thrainer
  This is a very simple LU.
446 7352d33b Thomas Thrainer

447 7352d33b Thomas Thrainer
  """
448 7352d33b Thomas Thrainer
  REQ_BGL = False
449 7352d33b Thomas Thrainer
450 7352d33b Thomas Thrainer
  def ExpandNames(self):
451 7352d33b Thomas Thrainer
    self.needed_locks = {
452 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
453 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
454 7352d33b Thomas Thrainer
    }
455 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
456 7352d33b Thomas Thrainer
457 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
458 7352d33b Thomas Thrainer
    """Redistribute the configuration.
459 7352d33b Thomas Thrainer

460 7352d33b Thomas Thrainer
    """
461 7352d33b Thomas Thrainer
    self.cfg.Update(self.cfg.GetClusterInfo(), feedback_fn)
462 5eacbcae Thomas Thrainer
    RedistributeAncillaryFiles(self)
463 7352d33b Thomas Thrainer
464 7352d33b Thomas Thrainer
465 7352d33b Thomas Thrainer
class LUClusterRename(LogicalUnit):
466 7352d33b Thomas Thrainer
  """Rename the cluster.
467 7352d33b Thomas Thrainer

468 7352d33b Thomas Thrainer
  """
469 7352d33b Thomas Thrainer
  HPATH = "cluster-rename"
470 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
471 7352d33b Thomas Thrainer
472 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
473 7352d33b Thomas Thrainer
    """Build hooks env.
474 7352d33b Thomas Thrainer

475 7352d33b Thomas Thrainer
    """
476 7352d33b Thomas Thrainer
    return {
477 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
478 7352d33b Thomas Thrainer
      "NEW_NAME": self.op.name,
479 7352d33b Thomas Thrainer
      }
480 7352d33b Thomas Thrainer
481 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
482 7352d33b Thomas Thrainer
    """Build hooks nodes.
483 7352d33b Thomas Thrainer

484 7352d33b Thomas Thrainer
    """
485 7352d33b Thomas Thrainer
    return ([self.cfg.GetMasterNode()], self.cfg.GetNodeList())
486 7352d33b Thomas Thrainer
487 7352d33b Thomas Thrainer
  def CheckPrereq(self):
488 7352d33b Thomas Thrainer
    """Verify that the passed name is a valid one.
489 7352d33b Thomas Thrainer

490 7352d33b Thomas Thrainer
    """
491 7352d33b Thomas Thrainer
    hostname = netutils.GetHostname(name=self.op.name,
492 7352d33b Thomas Thrainer
                                    family=self.cfg.GetPrimaryIPFamily())
493 7352d33b Thomas Thrainer
494 7352d33b Thomas Thrainer
    new_name = hostname.name
495 7352d33b Thomas Thrainer
    self.ip = new_ip = hostname.ip
496 7352d33b Thomas Thrainer
    old_name = self.cfg.GetClusterName()
497 7352d33b Thomas Thrainer
    old_ip = self.cfg.GetMasterIP()
498 7352d33b Thomas Thrainer
    if new_name == old_name and new_ip == old_ip:
499 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Neither the name nor the IP address of the"
500 7352d33b Thomas Thrainer
                                 " cluster has changed",
501 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
502 7352d33b Thomas Thrainer
    if new_ip != old_ip:
503 7352d33b Thomas Thrainer
      if netutils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT):
504 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("The given cluster IP address (%s) is"
505 7352d33b Thomas Thrainer
                                   " reachable on the network" %
506 7352d33b Thomas Thrainer
                                   new_ip, errors.ECODE_NOTUNIQUE)
507 7352d33b Thomas Thrainer
508 7352d33b Thomas Thrainer
    self.op.name = new_name
509 7352d33b Thomas Thrainer
510 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
511 7352d33b Thomas Thrainer
    """Rename the cluster.
512 7352d33b Thomas Thrainer

513 7352d33b Thomas Thrainer
    """
514 7352d33b Thomas Thrainer
    clustername = self.op.name
515 7352d33b Thomas Thrainer
    new_ip = self.ip
516 7352d33b Thomas Thrainer
517 7352d33b Thomas Thrainer
    # shutdown the master IP
518 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
519 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
520 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
521 7352d33b Thomas Thrainer
                                                     master_params, ems)
522 7352d33b Thomas Thrainer
    result.Raise("Could not disable the master role")
523 7352d33b Thomas Thrainer
524 7352d33b Thomas Thrainer
    try:
525 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
526 7352d33b Thomas Thrainer
      cluster.cluster_name = clustername
527 7352d33b Thomas Thrainer
      cluster.master_ip = new_ip
528 7352d33b Thomas Thrainer
      self.cfg.Update(cluster, feedback_fn)
529 7352d33b Thomas Thrainer
530 7352d33b Thomas Thrainer
      # update the known hosts file
531 7352d33b Thomas Thrainer
      ssh.WriteKnownHostsFile(self.cfg, pathutils.SSH_KNOWN_HOSTS_FILE)
532 7352d33b Thomas Thrainer
      node_list = self.cfg.GetOnlineNodeList()
533 7352d33b Thomas Thrainer
      try:
534 1c3231aa Thomas Thrainer
        node_list.remove(master_params.uuid)
535 7352d33b Thomas Thrainer
      except ValueError:
536 7352d33b Thomas Thrainer
        pass
537 5eacbcae Thomas Thrainer
      UploadHelper(self, node_list, pathutils.SSH_KNOWN_HOSTS_FILE)
538 7352d33b Thomas Thrainer
    finally:
539 7352d33b Thomas Thrainer
      master_params.ip = new_ip
540 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
541 7352d33b Thomas Thrainer
                                                     master_params, ems)
542 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master role on the master,"
543 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
544 7352d33b Thomas Thrainer
545 7352d33b Thomas Thrainer
    return clustername
546 7352d33b Thomas Thrainer
547 7352d33b Thomas Thrainer
548 7352d33b Thomas Thrainer
class LUClusterRepairDiskSizes(NoHooksLU):
549 7352d33b Thomas Thrainer
  """Verifies the cluster disks sizes.
550 7352d33b Thomas Thrainer

551 7352d33b Thomas Thrainer
  """
552 7352d33b Thomas Thrainer
  REQ_BGL = False
553 7352d33b Thomas Thrainer
554 7352d33b Thomas Thrainer
  def ExpandNames(self):
555 7352d33b Thomas Thrainer
    if self.op.instances:
556 da4a52a3 Thomas Thrainer
      (_, self.wanted_names) = GetWantedInstances(self, self.op.instances)
557 7352d33b Thomas Thrainer
      # Not getting the node allocation lock as only a specific set of
558 7352d33b Thomas Thrainer
      # instances (and their nodes) is going to be acquired
559 7352d33b Thomas Thrainer
      self.needed_locks = {
560 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: [],
561 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: self.wanted_names,
562 7352d33b Thomas Thrainer
        }
563 7352d33b Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
564 7352d33b Thomas Thrainer
    else:
565 7352d33b Thomas Thrainer
      self.wanted_names = None
566 7352d33b Thomas Thrainer
      self.needed_locks = {
567 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: locking.ALL_SET,
568 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: locking.ALL_SET,
569 7352d33b Thomas Thrainer
570 7352d33b Thomas Thrainer
        # This opcode is acquires the node locks for all instances
571 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
572 7352d33b Thomas Thrainer
        }
573 7352d33b Thomas Thrainer
574 7352d33b Thomas Thrainer
    self.share_locks = {
575 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_RES: 1,
576 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: 0,
577 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: 1,
578 7352d33b Thomas Thrainer
      }
579 7352d33b Thomas Thrainer
580 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
581 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and self.wanted_names is not None:
582 7352d33b Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=level)
583 7352d33b Thomas Thrainer
584 7352d33b Thomas Thrainer
  def CheckPrereq(self):
585 7352d33b Thomas Thrainer
    """Check prerequisites.
586 7352d33b Thomas Thrainer

587 7352d33b Thomas Thrainer
    This only checks the optional instance list against the existing names.
588 7352d33b Thomas Thrainer

589 7352d33b Thomas Thrainer
    """
590 7352d33b Thomas Thrainer
    if self.wanted_names is None:
591 7352d33b Thomas Thrainer
      self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE)
592 7352d33b Thomas Thrainer
593 7352d33b Thomas Thrainer
    self.wanted_instances = \
594 da4a52a3 Thomas Thrainer
        map(compat.snd, self.cfg.GetMultiInstanceInfoByName(self.wanted_names))
595 7352d33b Thomas Thrainer
596 7352d33b Thomas Thrainer
  def _EnsureChildSizes(self, disk):
597 7352d33b Thomas Thrainer
    """Ensure children of the disk have the needed disk size.
598 7352d33b Thomas Thrainer

599 7352d33b Thomas Thrainer
    This is valid mainly for DRBD8 and fixes an issue where the
600 7352d33b Thomas Thrainer
    children have smaller disk size.
601 7352d33b Thomas Thrainer

602 7352d33b Thomas Thrainer
    @param disk: an L{ganeti.objects.Disk} object
603 7352d33b Thomas Thrainer

604 7352d33b Thomas Thrainer
    """
605 cd3b4ff4 Helga Velroyen
    if disk.dev_type == constants.DT_DRBD8:
606 7352d33b Thomas Thrainer
      assert disk.children, "Empty children for DRBD8?"
607 7352d33b Thomas Thrainer
      fchild = disk.children[0]
608 7352d33b Thomas Thrainer
      mismatch = fchild.size < disk.size
609 7352d33b Thomas Thrainer
      if mismatch:
610 7352d33b Thomas Thrainer
        self.LogInfo("Child disk has size %d, parent %d, fixing",
611 7352d33b Thomas Thrainer
                     fchild.size, disk.size)
612 7352d33b Thomas Thrainer
        fchild.size = disk.size
613 7352d33b Thomas Thrainer
614 7352d33b Thomas Thrainer
      # and we recurse on this child only, not on the metadev
615 7352d33b Thomas Thrainer
      return self._EnsureChildSizes(fchild) or mismatch
616 7352d33b Thomas Thrainer
    else:
617 7352d33b Thomas Thrainer
      return False
618 7352d33b Thomas Thrainer
619 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
620 7352d33b Thomas Thrainer
    """Verify the size of cluster disks.
621 7352d33b Thomas Thrainer

622 7352d33b Thomas Thrainer
    """
623 7352d33b Thomas Thrainer
    # TODO: check child disks too
624 7352d33b Thomas Thrainer
    # TODO: check differences in size between primary/secondary nodes
625 7352d33b Thomas Thrainer
    per_node_disks = {}
626 7352d33b Thomas Thrainer
    for instance in self.wanted_instances:
627 7352d33b Thomas Thrainer
      pnode = instance.primary_node
628 7352d33b Thomas Thrainer
      if pnode not in per_node_disks:
629 7352d33b Thomas Thrainer
        per_node_disks[pnode] = []
630 7352d33b Thomas Thrainer
      for idx, disk in enumerate(instance.disks):
631 7352d33b Thomas Thrainer
        per_node_disks[pnode].append((instance, idx, disk))
632 7352d33b Thomas Thrainer
633 7352d33b Thomas Thrainer
    assert not (frozenset(per_node_disks.keys()) -
634 7352d33b Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE_RES)), \
635 7352d33b Thomas Thrainer
      "Not owning correct locks"
636 7352d33b Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE)
637 7352d33b Thomas Thrainer
638 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
639 1c3231aa Thomas Thrainer
                                               per_node_disks.keys())
640 40d93e3b Bernardo Dal Seno
641 7352d33b Thomas Thrainer
    changed = []
642 1c3231aa Thomas Thrainer
    for node_uuid, dskl in per_node_disks.items():
643 0c3d9c7c Thomas Thrainer
      if not dskl:
644 0c3d9c7c Thomas Thrainer
        # no disks on the node
645 0c3d9c7c Thomas Thrainer
        continue
646 0c3d9c7c Thomas Thrainer
647 d66acf3d Thomas Thrainer
      newl = [([v[2].Copy()], v[0]) for v in dskl]
648 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
649 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_getdimensions(node_uuid, newl)
650 7352d33b Thomas Thrainer
      if result.fail_msg:
651 6ef8077e Bernardo Dal Seno
        self.LogWarning("Failure in blockdev_getdimensions call to node"
652 1c3231aa Thomas Thrainer
                        " %s, ignoring", node_name)
653 7352d33b Thomas Thrainer
        continue
654 7352d33b Thomas Thrainer
      if len(result.payload) != len(dskl):
655 7352d33b Thomas Thrainer
        logging.warning("Invalid result from node %s: len(dksl)=%d,"
656 1c3231aa Thomas Thrainer
                        " result.payload=%s", node_name, len(dskl),
657 1c3231aa Thomas Thrainer
                        result.payload)
658 7352d33b Thomas Thrainer
        self.LogWarning("Invalid result from node %s, ignoring node results",
659 1c3231aa Thomas Thrainer
                        node_name)
660 7352d33b Thomas Thrainer
        continue
661 6ef8077e Bernardo Dal Seno
      for ((instance, idx, disk), dimensions) in zip(dskl, result.payload):
662 6ef8077e Bernardo Dal Seno
        if dimensions is None:
663 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return size"
664 7352d33b Thomas Thrainer
                          " information, ignoring", idx, instance.name)
665 7352d33b Thomas Thrainer
          continue
666 6ef8077e Bernardo Dal Seno
        if not isinstance(dimensions, (tuple, list)):
667 6ef8077e Bernardo Dal Seno
          self.LogWarning("Disk %d of instance %s did not return valid"
668 6ef8077e Bernardo Dal Seno
                          " dimension information, ignoring", idx,
669 6ef8077e Bernardo Dal Seno
                          instance.name)
670 6ef8077e Bernardo Dal Seno
          continue
671 40d93e3b Bernardo Dal Seno
        (size, spindles) = dimensions
672 7352d33b Thomas Thrainer
        if not isinstance(size, (int, long)):
673 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return valid"
674 7352d33b Thomas Thrainer
                          " size information, ignoring", idx, instance.name)
675 7352d33b Thomas Thrainer
          continue
676 7352d33b Thomas Thrainer
        size = size >> 20
677 7352d33b Thomas Thrainer
        if size != disk.size:
678 7352d33b Thomas Thrainer
          self.LogInfo("Disk %d of instance %s has mismatched size,"
679 7352d33b Thomas Thrainer
                       " correcting: recorded %d, actual %d", idx,
680 7352d33b Thomas Thrainer
                       instance.name, disk.size, size)
681 7352d33b Thomas Thrainer
          disk.size = size
682 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
683 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", size))
684 1c3231aa Thomas Thrainer
        if es_flags[node_uuid]:
685 40d93e3b Bernardo Dal Seno
          if spindles is None:
686 40d93e3b Bernardo Dal Seno
            self.LogWarning("Disk %d of instance %s did not return valid"
687 40d93e3b Bernardo Dal Seno
                            " spindles information, ignoring", idx,
688 40d93e3b Bernardo Dal Seno
                            instance.name)
689 40d93e3b Bernardo Dal Seno
          elif disk.spindles is None or disk.spindles != spindles:
690 40d93e3b Bernardo Dal Seno
            self.LogInfo("Disk %d of instance %s has mismatched spindles,"
691 40d93e3b Bernardo Dal Seno
                         " correcting: recorded %s, actual %s",
692 40d93e3b Bernardo Dal Seno
                         idx, instance.name, disk.spindles, spindles)
693 40d93e3b Bernardo Dal Seno
            disk.spindles = spindles
694 40d93e3b Bernardo Dal Seno
            self.cfg.Update(instance, feedback_fn)
695 40d93e3b Bernardo Dal Seno
            changed.append((instance.name, idx, "spindles", disk.spindles))
696 7352d33b Thomas Thrainer
        if self._EnsureChildSizes(disk):
697 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
698 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", disk.size))
699 7352d33b Thomas Thrainer
    return changed
700 7352d33b Thomas Thrainer
701 7352d33b Thomas Thrainer
702 7352d33b Thomas Thrainer
def _ValidateNetmask(cfg, netmask):
703 7352d33b Thomas Thrainer
  """Checks if a netmask is valid.
704 7352d33b Thomas Thrainer

705 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
706 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
707 7352d33b Thomas Thrainer
  @type netmask: int
708 7352d33b Thomas Thrainer
  @param netmask: the netmask to be verified
709 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the validation fails
710 7352d33b Thomas Thrainer

711 7352d33b Thomas Thrainer
  """
712 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
713 7352d33b Thomas Thrainer
  try:
714 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
715 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
716 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
717 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
718 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
719 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
720 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
721 7352d33b Thomas Thrainer
722 7352d33b Thomas Thrainer
723 e8b5640e Helga Velroyen
def CheckFileBasedStoragePathVsEnabledDiskTemplates(
724 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates,
725 e8b5640e Helga Velroyen
    file_disk_template):
726 e8b5640e Helga Velroyen
  """Checks whether the given file-based storage directory is acceptable.
727 e8b5640e Helga Velroyen

728 e8b5640e Helga Velroyen
  Note: This function is public, because it is also used in bootstrap.py.
729 3039e2dc Helga Velroyen

730 3039e2dc Helga Velroyen
  @type logging_warn_fn: function
731 3039e2dc Helga Velroyen
  @param logging_warn_fn: function which accepts a string and logs it
732 3039e2dc Helga Velroyen
  @type file_storage_dir: string
733 3039e2dc Helga Velroyen
  @param file_storage_dir: the directory to be used for file-based instances
734 3039e2dc Helga Velroyen
  @type enabled_disk_templates: list of string
735 3039e2dc Helga Velroyen
  @param enabled_disk_templates: the list of enabled disk templates
736 e8b5640e Helga Velroyen
  @type file_disk_template: string
737 e8b5640e Helga Velroyen
  @param file_disk_template: the file-based disk template for which the
738 e8b5640e Helga Velroyen
      path should be checked
739 3039e2dc Helga Velroyen

740 3039e2dc Helga Velroyen
  """
741 5a904197 Santi Raffa
  assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
742 5a904197 Santi Raffa
            constants.ST_FILE, constants.ST_SHARED_FILE
743 5a904197 Santi Raffa
         ))
744 e8b5640e Helga Velroyen
  file_storage_enabled = file_disk_template in enabled_disk_templates
745 3039e2dc Helga Velroyen
  if file_storage_dir is not None:
746 3039e2dc Helga Velroyen
    if file_storage_dir == "":
747 3039e2dc Helga Velroyen
      if file_storage_enabled:
748 e8b5640e Helga Velroyen
        raise errors.OpPrereqError(
749 e8b5640e Helga Velroyen
            "Unsetting the '%s' storage directory while having '%s' storage"
750 e8b5640e Helga Velroyen
            " enabled is not permitted." %
751 e8b5640e Helga Velroyen
            (file_disk_template, file_disk_template))
752 3039e2dc Helga Velroyen
    else:
753 3039e2dc Helga Velroyen
      if not file_storage_enabled:
754 e8b5640e Helga Velroyen
        logging_warn_fn(
755 e8b5640e Helga Velroyen
            "Specified a %s storage directory, although %s storage is not"
756 e8b5640e Helga Velroyen
            " enabled." % (file_disk_template, file_disk_template))
757 3039e2dc Helga Velroyen
  else:
758 e8b5640e Helga Velroyen
    raise errors.ProgrammerError("Received %s storage dir with value"
759 e8b5640e Helga Velroyen
                                 " 'None'." % file_disk_template)
760 e8b5640e Helga Velroyen
761 e8b5640e Helga Velroyen
762 e8b5640e Helga Velroyen
def CheckFileStoragePathVsEnabledDiskTemplates(
763 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
764 e8b5640e Helga Velroyen
  """Checks whether the given file storage directory is acceptable.
765 e8b5640e Helga Velroyen

766 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
767 e8b5640e Helga Velroyen

768 e8b5640e Helga Velroyen
  """
769 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
770 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
771 e8b5640e Helga Velroyen
      constants.DT_FILE)
772 e8b5640e Helga Velroyen
773 e8b5640e Helga Velroyen
774 e8b5640e Helga Velroyen
def CheckSharedFileStoragePathVsEnabledDiskTemplates(
775 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
776 e8b5640e Helga Velroyen
  """Checks whether the given shared file storage directory is acceptable.
777 e8b5640e Helga Velroyen

778 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
779 e8b5640e Helga Velroyen

780 e8b5640e Helga Velroyen
  """
781 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
782 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
783 e8b5640e Helga Velroyen
      constants.DT_SHARED_FILE)
784 3039e2dc Helga Velroyen
785 3039e2dc Helga Velroyen
786 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
787 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
788 7352d33b Thomas Thrainer

789 7352d33b Thomas Thrainer
  """
790 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
791 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
792 7352d33b Thomas Thrainer
  REQ_BGL = False
793 7352d33b Thomas Thrainer
794 7352d33b Thomas Thrainer
  def CheckArguments(self):
795 7352d33b Thomas Thrainer
    """Check parameters
796 7352d33b Thomas Thrainer

797 7352d33b Thomas Thrainer
    """
798 7352d33b Thomas Thrainer
    if self.op.uid_pool:
799 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.uid_pool)
800 7352d33b Thomas Thrainer
801 7352d33b Thomas Thrainer
    if self.op.add_uids:
802 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.add_uids)
803 7352d33b Thomas Thrainer
804 7352d33b Thomas Thrainer
    if self.op.remove_uids:
805 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.remove_uids)
806 7352d33b Thomas Thrainer
807 7352d33b Thomas Thrainer
    if self.op.master_netmask is not None:
808 7352d33b Thomas Thrainer
      _ValidateNetmask(self.cfg, self.op.master_netmask)
809 7352d33b Thomas Thrainer
810 7352d33b Thomas Thrainer
    if self.op.diskparams:
811 7352d33b Thomas Thrainer
      for dt_params in self.op.diskparams.values():
812 7352d33b Thomas Thrainer
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
813 7352d33b Thomas Thrainer
      try:
814 7352d33b Thomas Thrainer
        utils.VerifyDictOptions(self.op.diskparams, constants.DISK_DT_DEFAULTS)
815 294254b1 Raffa Santi
        CheckDiskAccessModeValidity(self.op.diskparams)
816 7352d33b Thomas Thrainer
      except errors.OpPrereqError, err:
817 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
818 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
819 7352d33b Thomas Thrainer
820 7352d33b Thomas Thrainer
  def ExpandNames(self):
821 7352d33b Thomas Thrainer
    # FIXME: in the future maybe other cluster params won't require checking on
822 7352d33b Thomas Thrainer
    # all nodes to be modified.
823 7352d33b Thomas Thrainer
    # FIXME: This opcode changes cluster-wide settings. Is acquiring all
824 7352d33b Thomas Thrainer
    # resource locks the right thing, shouldn't it be the BGL instead?
825 7352d33b Thomas Thrainer
    self.needed_locks = {
826 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
827 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: locking.ALL_SET,
828 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
829 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
830 7352d33b Thomas Thrainer
    }
831 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
832 7352d33b Thomas Thrainer
833 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
834 7352d33b Thomas Thrainer
    """Build hooks env.
835 7352d33b Thomas Thrainer

836 7352d33b Thomas Thrainer
    """
837 7352d33b Thomas Thrainer
    return {
838 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
839 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
840 7352d33b Thomas Thrainer
      }
841 7352d33b Thomas Thrainer
842 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
843 7352d33b Thomas Thrainer
    """Build hooks nodes.
844 7352d33b Thomas Thrainer

845 7352d33b Thomas Thrainer
    """
846 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
847 7352d33b Thomas Thrainer
    return ([mn], [mn])
848 7352d33b Thomas Thrainer
849 1c3231aa Thomas Thrainer
  def _CheckVgName(self, node_uuids, enabled_disk_templates,
850 1bb99a33 Bernardo Dal Seno
                   new_enabled_disk_templates):
851 1bb99a33 Bernardo Dal Seno
    """Check the consistency of the vg name on all nodes and in case it gets
852 1bb99a33 Bernardo Dal Seno
       unset whether there are instances still using it.
853 7352d33b Thomas Thrainer

854 7352d33b Thomas Thrainer
    """
855 c89eb67d Helga Velroyen
    lvm_is_enabled = utils.IsLvmEnabled(enabled_disk_templates)
856 c89eb67d Helga Velroyen
    lvm_gets_enabled = utils.LvmGetsEnabled(enabled_disk_templates,
857 c89eb67d Helga Velroyen
                                            new_enabled_disk_templates)
858 c89eb67d Helga Velroyen
    current_vg_name = self.cfg.GetVGName()
859 c89eb67d Helga Velroyen
860 c89eb67d Helga Velroyen
    if self.op.vg_name == '':
861 c89eb67d Helga Velroyen
      if lvm_is_enabled:
862 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
863 c89eb67d Helga Velroyen
                                   " disk templates are or get enabled.")
864 c89eb67d Helga Velroyen
865 c89eb67d Helga Velroyen
    if self.op.vg_name is None:
866 c89eb67d Helga Velroyen
      if current_vg_name is None and lvm_is_enabled:
867 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Please specify a volume group when"
868 c89eb67d Helga Velroyen
                                   " enabling lvm-based disk-templates.")
869 c89eb67d Helga Velroyen
870 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
871 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_PLAIN):
872 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
873 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
874 7352d33b Thomas Thrainer
875 c89eb67d Helga Velroyen
    if (self.op.vg_name is not None and lvm_is_enabled) or \
876 c89eb67d Helga Velroyen
        (self.cfg.GetVGName() is not None and lvm_gets_enabled):
877 1c3231aa Thomas Thrainer
      self._CheckVgNameOnNodes(node_uuids)
878 1bb99a33 Bernardo Dal Seno
879 1c3231aa Thomas Thrainer
  def _CheckVgNameOnNodes(self, node_uuids):
880 1bb99a33 Bernardo Dal Seno
    """Check the status of the volume group on each node.
881 1bb99a33 Bernardo Dal Seno

882 1bb99a33 Bernardo Dal Seno
    """
883 1c3231aa Thomas Thrainer
    vglist = self.rpc.call_vg_list(node_uuids)
884 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
885 1c3231aa Thomas Thrainer
      msg = vglist[node_uuid].fail_msg
886 1bb99a33 Bernardo Dal Seno
      if msg:
887 1bb99a33 Bernardo Dal Seno
        # ignoring down node
888 1bb99a33 Bernardo Dal Seno
        self.LogWarning("Error while gathering data on node %s"
889 1c3231aa Thomas Thrainer
                        " (ignoring node): %s",
890 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
891 1bb99a33 Bernardo Dal Seno
        continue
892 1c3231aa Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist[node_uuid].payload,
893 1bb99a33 Bernardo Dal Seno
                                            self.op.vg_name,
894 1bb99a33 Bernardo Dal Seno
                                            constants.MIN_VG_SIZE)
895 1bb99a33 Bernardo Dal Seno
      if vgstatus:
896 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Error on node '%s': %s" %
897 1c3231aa Thomas Thrainer
                                   (self.cfg.GetNodeName(node_uuid), vgstatus),
898 1c3231aa Thomas Thrainer
                                   errors.ECODE_ENVIRON)
899 1bb99a33 Bernardo Dal Seno
900 c89eb67d Helga Velroyen
  @staticmethod
901 87e23f2d Helga Velroyen
  def _GetDiskTemplateSetsInner(op_enabled_disk_templates,
902 87e23f2d Helga Velroyen
                                old_enabled_disk_templates):
903 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
904 6e513917 Helga Velroyen

905 6e513917 Helga Velroyen
    @see: C{_GetDiskTemplateSets} for more details.
906 1bb99a33 Bernardo Dal Seno

907 1bb99a33 Bernardo Dal Seno
    """
908 1bb99a33 Bernardo Dal Seno
    enabled_disk_templates = None
909 1bb99a33 Bernardo Dal Seno
    new_enabled_disk_templates = []
910 6e513917 Helga Velroyen
    disabled_disk_templates = []
911 c89eb67d Helga Velroyen
    if op_enabled_disk_templates:
912 c89eb67d Helga Velroyen
      enabled_disk_templates = op_enabled_disk_templates
913 1bb99a33 Bernardo Dal Seno
      new_enabled_disk_templates = \
914 5808df30 Helga Velroyen
        list(set(enabled_disk_templates)
915 5808df30 Helga Velroyen
             - set(old_enabled_disk_templates))
916 6e513917 Helga Velroyen
      disabled_disk_templates = \
917 5808df30 Helga Velroyen
        list(set(old_enabled_disk_templates)
918 5808df30 Helga Velroyen
             - set(enabled_disk_templates))
919 1bb99a33 Bernardo Dal Seno
    else:
920 c89eb67d Helga Velroyen
      enabled_disk_templates = old_enabled_disk_templates
921 6e513917 Helga Velroyen
    return (enabled_disk_templates, new_enabled_disk_templates,
922 6e513917 Helga Velroyen
            disabled_disk_templates)
923 1bb99a33 Bernardo Dal Seno
924 87e23f2d Helga Velroyen
  def _GetDiskTemplateSets(self, cluster):
925 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
926 6e513917 Helga Velroyen

927 6e513917 Helga Velroyen
    The three sets are:
928 6e513917 Helga Velroyen
      - disk templates that will be enabled after this operation (no matter if
929 6e513917 Helga Velroyen
        they were enabled before or not)
930 6e513917 Helga Velroyen
      - disk templates that get enabled by this operation (thus haven't been
931 6e513917 Helga Velroyen
        enabled before.)
932 6e513917 Helga Velroyen
      - disk templates that get disabled by this operation
933 c89eb67d Helga Velroyen

934 c89eb67d Helga Velroyen
    """
935 87e23f2d Helga Velroyen
    return self._GetDiskTemplateSetsInner(self.op.enabled_disk_templates,
936 87e23f2d Helga Velroyen
                                          cluster.enabled_disk_templates)
937 c89eb67d Helga Velroyen
938 33a6464e Helga Velroyen
  def _CheckIpolicy(self, cluster, enabled_disk_templates):
939 1532b078 Helga Velroyen
    """Checks the ipolicy.
940 1532b078 Helga Velroyen

941 1532b078 Helga Velroyen
    @type cluster: C{objects.Cluster}
942 1532b078 Helga Velroyen
    @param cluster: the cluster's configuration
943 33a6464e Helga Velroyen
    @type enabled_disk_templates: list of string
944 33a6464e Helga Velroyen
    @param enabled_disk_templates: list of (possibly newly) enabled disk
945 33a6464e Helga Velroyen
      templates
946 1532b078 Helga Velroyen

947 1532b078 Helga Velroyen
    """
948 33a6464e Helga Velroyen
    # FIXME: write unit tests for this
949 1532b078 Helga Velroyen
    if self.op.ipolicy:
950 1532b078 Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
951 1532b078 Helga Velroyen
                                           group_policy=False)
952 1532b078 Helga Velroyen
953 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(self.new_ipolicy,
954 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
955 33a6464e Helga Velroyen
956 1532b078 Helga Velroyen
      all_instances = self.cfg.GetAllInstancesInfo().values()
957 1532b078 Helga Velroyen
      violations = set()
958 1532b078 Helga Velroyen
      for group in self.cfg.GetAllNodeGroupsInfo().values():
959 1532b078 Helga Velroyen
        instances = frozenset([inst for inst in all_instances
960 1532b078 Helga Velroyen
                               if compat.any(nuuid in group.members
961 1532b078 Helga Velroyen
                                             for nuuid in inst.all_nodes)])
962 1532b078 Helga Velroyen
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
963 1532b078 Helga Velroyen
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
964 1532b078 Helga Velroyen
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
965 1532b078 Helga Velroyen
                                           self.cfg)
966 1532b078 Helga Velroyen
        if new:
967 1532b078 Helga Velroyen
          violations.update(new)
968 1532b078 Helga Velroyen
969 1532b078 Helga Velroyen
      if violations:
970 1532b078 Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
971 1532b078 Helga Velroyen
                        " violate them: %s",
972 1532b078 Helga Velroyen
                        utils.CommaJoin(utils.NiceSort(violations)))
973 33a6464e Helga Velroyen
    else:
974 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(cluster.ipolicy,
975 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
976 1532b078 Helga Velroyen
977 31ccfc0e Helga Velroyen
  def _CheckDrbdHelperOnNodes(self, drbd_helper, node_uuids):
978 31ccfc0e Helga Velroyen
    """Checks whether the set DRBD helper actually exists on the nodes.
979 31ccfc0e Helga Velroyen

980 31ccfc0e Helga Velroyen
    @type drbd_helper: string
981 31ccfc0e Helga Velroyen
    @param drbd_helper: path of the drbd usermode helper binary
982 31ccfc0e Helga Velroyen
    @type node_uuids: list of strings
983 31ccfc0e Helga Velroyen
    @param node_uuids: list of node UUIDs to check for the helper
984 31ccfc0e Helga Velroyen

985 31ccfc0e Helga Velroyen
    """
986 31ccfc0e Helga Velroyen
    # checks given drbd helper on all nodes
987 31ccfc0e Helga Velroyen
    helpers = self.rpc.call_drbd_helper(node_uuids)
988 31ccfc0e Helga Velroyen
    for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
989 31ccfc0e Helga Velroyen
      if ninfo.offline:
990 31ccfc0e Helga Velroyen
        self.LogInfo("Not checking drbd helper on offline node %s",
991 31ccfc0e Helga Velroyen
                     ninfo.name)
992 31ccfc0e Helga Velroyen
        continue
993 31ccfc0e Helga Velroyen
      msg = helpers[ninfo.uuid].fail_msg
994 31ccfc0e Helga Velroyen
      if msg:
995 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error checking drbd helper on node"
996 31ccfc0e Helga Velroyen
                                   " '%s': %s" % (ninfo.name, msg),
997 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
998 31ccfc0e Helga Velroyen
      node_helper = helpers[ninfo.uuid].payload
999 31ccfc0e Helga Velroyen
      if node_helper != drbd_helper:
1000 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
1001 31ccfc0e Helga Velroyen
                                   (ninfo.name, node_helper),
1002 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
1003 31ccfc0e Helga Velroyen
1004 31ccfc0e Helga Velroyen
  def _CheckDrbdHelper(self, node_uuids, drbd_enabled, drbd_gets_enabled):
1005 7c577910 Helga Velroyen
    """Check the DRBD usermode helper.
1006 1bb99a33 Bernardo Dal Seno

1007 7c577910 Helga Velroyen
    @type node_uuids: list of strings
1008 7c577910 Helga Velroyen
    @param node_uuids: a list of nodes' UUIDs
1009 31ccfc0e Helga Velroyen
    @type drbd_enabled: boolean
1010 31ccfc0e Helga Velroyen
    @param drbd_enabled: whether DRBD will be enabled after this operation
1011 31ccfc0e Helga Velroyen
      (no matter if it was disabled before or not)
1012 31ccfc0e Helga Velroyen
    @type drbd_gets_enabled: boolen
1013 31ccfc0e Helga Velroyen
    @param drbd_gets_enabled: true if DRBD was disabled before this
1014 31ccfc0e Helga Velroyen
      operation, but will be enabled afterwards
1015 1bb99a33 Bernardo Dal Seno

1016 1bb99a33 Bernardo Dal Seno
    """
1017 31ccfc0e Helga Velroyen
    if self.op.drbd_helper == '':
1018 31ccfc0e Helga Velroyen
      if drbd_enabled:
1019 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Cannot disable drbd helper while"
1020 31ccfc0e Helga Velroyen
                                   " DRBD is enabled.")
1021 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_DRBD8):
1022 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
1023 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
1024 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1025 7352d33b Thomas Thrainer
1026 31ccfc0e Helga Velroyen
    else:
1027 31ccfc0e Helga Velroyen
      if self.op.drbd_helper is not None and drbd_enabled:
1028 31ccfc0e Helga Velroyen
        self._CheckDrbdHelperOnNodes(self.op.drbd_helper, node_uuids)
1029 31ccfc0e Helga Velroyen
      else:
1030 31ccfc0e Helga Velroyen
        if drbd_gets_enabled:
1031 31ccfc0e Helga Velroyen
          current_drbd_helper = self.cfg.GetClusterInfo().drbd_usermode_helper
1032 31ccfc0e Helga Velroyen
          if current_drbd_helper is not None:
1033 31ccfc0e Helga Velroyen
            self._CheckDrbdHelperOnNodes(current_drbd_helper, node_uuids)
1034 31ccfc0e Helga Velroyen
          else:
1035 31ccfc0e Helga Velroyen
            raise errors.OpPrereqError("Cannot enable DRBD without a"
1036 31ccfc0e Helga Velroyen
                                       " DRBD usermode helper set.")
1037 7c577910 Helga Velroyen
1038 c2e984e2 Helga Velroyen
  def _CheckInstancesOfDisabledDiskTemplates(
1039 c2e984e2 Helga Velroyen
      self, disabled_disk_templates):
1040 5808df30 Helga Velroyen
    """Check whether we try to disable a disk template that is in use.
1041 c2e984e2 Helga Velroyen

1042 c2e984e2 Helga Velroyen
    @type disabled_disk_templates: list of string
1043 c2e984e2 Helga Velroyen
    @param disabled_disk_templates: list of disk templates that are going to
1044 c2e984e2 Helga Velroyen
      be disabled by this operation
1045 c2e984e2 Helga Velroyen

1046 c2e984e2 Helga Velroyen
    """
1047 c2e984e2 Helga Velroyen
    for disk_template in disabled_disk_templates:
1048 c2e984e2 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(disk_template):
1049 c2e984e2 Helga Velroyen
        raise errors.OpPrereqError(
1050 c2e984e2 Helga Velroyen
            "Cannot disable disk template '%s', because there is at least one"
1051 c2e984e2 Helga Velroyen
            " instance using it." % disk_template)
1052 c2e984e2 Helga Velroyen
1053 7c577910 Helga Velroyen
  def CheckPrereq(self):
1054 7c577910 Helga Velroyen
    """Check prerequisites.
1055 7c577910 Helga Velroyen

1056 7c577910 Helga Velroyen
    This checks whether the given params don't conflict and
1057 7c577910 Helga Velroyen
    if the given volume group is valid.
1058 7c577910 Helga Velroyen

1059 7c577910 Helga Velroyen
    """
1060 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
1061 1bb99a33 Bernardo Dal Seno
    self.cluster = cluster = self.cfg.GetClusterInfo()
1062 7352d33b Thomas Thrainer
1063 1c3231aa Thomas Thrainer
    vm_capable_node_uuids = [node.uuid
1064 1c3231aa Thomas Thrainer
                             for node in self.cfg.GetAllNodesInfo().values()
1065 1c3231aa Thomas Thrainer
                             if node.uuid in node_uuids and node.vm_capable]
1066 7352d33b Thomas Thrainer
1067 6e513917 Helga Velroyen
    (enabled_disk_templates, new_enabled_disk_templates,
1068 5808df30 Helga Velroyen
      disabled_disk_templates) = self._GetDiskTemplateSets(cluster)
1069 6e513917 Helga Velroyen
    self._CheckInstancesOfDisabledDiskTemplates(disabled_disk_templates)
1070 1bb99a33 Bernardo Dal Seno
1071 1c3231aa Thomas Thrainer
    self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
1072 1bb99a33 Bernardo Dal Seno
                      new_enabled_disk_templates)
1073 7352d33b Thomas Thrainer
1074 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1075 3039e2dc Helga Velroyen
      CheckFileStoragePathVsEnabledDiskTemplates(
1076 3039e2dc Helga Velroyen
          self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
1077 3039e2dc Helga Velroyen
1078 4e6cfd11 Helga Velroyen
    if self.op.shared_file_storage_dir is not None:
1079 4e6cfd11 Helga Velroyen
      CheckSharedFileStoragePathVsEnabledDiskTemplates(
1080 4e6cfd11 Helga Velroyen
          self.LogWarning, self.op.shared_file_storage_dir,
1081 4e6cfd11 Helga Velroyen
          enabled_disk_templates)
1082 4e6cfd11 Helga Velroyen
1083 31ccfc0e Helga Velroyen
    drbd_enabled = constants.DT_DRBD8 in enabled_disk_templates
1084 31ccfc0e Helga Velroyen
    drbd_gets_enabled = constants.DT_DRBD8 in new_enabled_disk_templates
1085 31ccfc0e Helga Velroyen
    self._CheckDrbdHelper(node_uuids, drbd_enabled, drbd_gets_enabled)
1086 7352d33b Thomas Thrainer
1087 7352d33b Thomas Thrainer
    # validate params changes
1088 7352d33b Thomas Thrainer
    if self.op.beparams:
1089 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
1090 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
1091 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
1092 7352d33b Thomas Thrainer
1093 7352d33b Thomas Thrainer
    if self.op.ndparams:
1094 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
1095 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
1096 7352d33b Thomas Thrainer
1097 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
1098 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
1099 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
1100 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
1101 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
1102 7352d33b Thomas Thrainer
1103 7352d33b Thomas Thrainer
    if self.op.hv_state:
1104 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
1105 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
1106 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
1107 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
1108 7352d33b Thomas Thrainer
1109 7352d33b Thomas Thrainer
    if self.op.disk_state:
1110 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
1111 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
1112 7352d33b Thomas Thrainer
      self.new_disk_state = \
1113 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
1114 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
1115 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
1116 7352d33b Thomas Thrainer
1117 33a6464e Helga Velroyen
    self._CheckIpolicy(cluster, enabled_disk_templates)
1118 7352d33b Thomas Thrainer
1119 7352d33b Thomas Thrainer
    if self.op.nicparams:
1120 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
1121 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
1122 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
1123 7352d33b Thomas Thrainer
      nic_errors = []
1124 7352d33b Thomas Thrainer
1125 7352d33b Thomas Thrainer
      # check all instances for consistency
1126 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
1127 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
1128 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
1129 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
1130 7352d33b Thomas Thrainer
1131 7352d33b Thomas Thrainer
          # check parameter syntax
1132 7352d33b Thomas Thrainer
          try:
1133 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
1134 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
1135 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
1136 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
1137 7352d33b Thomas Thrainer
1138 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
1139 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
1140 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
1141 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
1142 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
1143 7352d33b Thomas Thrainer
      if nic_errors:
1144 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
1145 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
1146 7352d33b Thomas Thrainer
1147 7352d33b Thomas Thrainer
    # hypervisor list/parameters
1148 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
1149 7352d33b Thomas Thrainer
    if self.op.hvparams:
1150 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
1151 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
1152 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
1153 7352d33b Thomas Thrainer
        else:
1154 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
1155 7352d33b Thomas Thrainer
1156 7352d33b Thomas Thrainer
    # disk template parameters
1157 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
1158 7352d33b Thomas Thrainer
    if self.op.diskparams:
1159 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
1160 f06af3ca Thomas Thrainer
        if dt_name not in self.new_diskparams:
1161 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
1162 7352d33b Thomas Thrainer
        else:
1163 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
1164 294254b1 Raffa Santi
      CheckDiskAccessModeConsistency(self.op.diskparams, self.cfg)
1165 7352d33b Thomas Thrainer
1166 7352d33b Thomas Thrainer
    # os hypervisor parameters
1167 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
1168 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1169 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
1170 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
1171 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
1172 7352d33b Thomas Thrainer
        else:
1173 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
1174 7352d33b Thomas Thrainer
            if hv_dict is None:
1175 7352d33b Thomas Thrainer
              # Delete if it exists
1176 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
1177 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
1178 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
1179 7352d33b Thomas Thrainer
            else:
1180 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
1181 7352d33b Thomas Thrainer
1182 7352d33b Thomas Thrainer
    # os parameters
1183 7352d33b Thomas Thrainer
    self.new_osp = objects.FillDict(cluster.osparams, {})
1184 7352d33b Thomas Thrainer
    if self.op.osparams:
1185 7352d33b Thomas Thrainer
      for os_name, osp in self.op.osparams.items():
1186 7352d33b Thomas Thrainer
        if os_name not in self.new_osp:
1187 7352d33b Thomas Thrainer
          self.new_osp[os_name] = {}
1188 7352d33b Thomas Thrainer
1189 5eacbcae Thomas Thrainer
        self.new_osp[os_name] = GetUpdatedParams(self.new_osp[os_name], osp,
1190 5eacbcae Thomas Thrainer
                                                 use_none=True)
1191 7352d33b Thomas Thrainer
1192 7352d33b Thomas Thrainer
        if not self.new_osp[os_name]:
1193 7352d33b Thomas Thrainer
          # we removed all parameters
1194 7352d33b Thomas Thrainer
          del self.new_osp[os_name]
1195 7352d33b Thomas Thrainer
        else:
1196 7352d33b Thomas Thrainer
          # check the parameter validity (remote check)
1197 5eacbcae Thomas Thrainer
          CheckOSParams(self, False, [self.cfg.GetMasterNode()],
1198 5eacbcae Thomas Thrainer
                        os_name, self.new_osp[os_name])
1199 7352d33b Thomas Thrainer
1200 7352d33b Thomas Thrainer
    # changes to the hypervisor list
1201 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1202 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
1203 7352d33b Thomas Thrainer
      for hv in self.hv_list:
1204 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
1205 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
1206 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
1207 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
1208 7352d33b Thomas Thrainer
        # enabled before
1209 7352d33b Thomas Thrainer
        if hv not in new_hvp:
1210 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
1211 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
1212 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
1213 7352d33b Thomas Thrainer
    else:
1214 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
1215 7352d33b Thomas Thrainer
1216 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
1217 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
1218 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
1219 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
1220 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
1221 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
1222 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
1223 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1224 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1225 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
1226 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, hv_params)
1227 7352d33b Thomas Thrainer
1228 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
1229 7352d33b Thomas Thrainer
1230 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1231 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
1232 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
1233 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
1234 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
1235 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1236 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
1237 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
1238 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
1239 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1240 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
1241 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, new_osp)
1242 7352d33b Thomas Thrainer
1243 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
1244 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
1245 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
1246 7352d33b Thomas Thrainer
                                    os.path.isfile)
1247 7352d33b Thomas Thrainer
      if alloc_script is None:
1248 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
1249 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
1250 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1251 7352d33b Thomas Thrainer
1252 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
1253 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
1254 7352d33b Thomas Thrainer
       are still in use by some instances.
1255 7352d33b Thomas Thrainer

1256 7352d33b Thomas Thrainer
    """
1257 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
1258 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1259 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
1260 7352d33b Thomas Thrainer
1261 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
1262 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
1263 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
1264 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
1265 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
1266 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
1267 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
1268 7352d33b Thomas Thrainer
1269 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
1270 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
1271 7352d33b Thomas Thrainer

1272 7352d33b Thomas Thrainer
    """
1273 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
1274 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
1275 7352d33b Thomas Thrainer
      if not new_volume:
1276 7352d33b Thomas Thrainer
        new_volume = None
1277 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
1278 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
1279 7352d33b Thomas Thrainer
      else:
1280 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
1281 7352d33b Thomas Thrainer
                    " state, not changing")
1282 1bb99a33 Bernardo Dal Seno
1283 3039e2dc Helga Velroyen
  def _SetFileStorageDir(self, feedback_fn):
1284 3039e2dc Helga Velroyen
    """Set the file storage directory.
1285 3039e2dc Helga Velroyen

1286 3039e2dc Helga Velroyen
    """
1287 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1288 3039e2dc Helga Velroyen
      if self.cluster.file_storage_dir == self.op.file_storage_dir:
1289 3039e2dc Helga Velroyen
        feedback_fn("Global file storage dir already set to value '%s'"
1290 3039e2dc Helga Velroyen
                    % self.cluster.file_storage_dir)
1291 3039e2dc Helga Velroyen
      else:
1292 3039e2dc Helga Velroyen
        self.cluster.file_storage_dir = self.op.file_storage_dir
1293 3039e2dc Helga Velroyen
1294 7c577910 Helga Velroyen
  def _SetDrbdHelper(self, feedback_fn):
1295 7c577910 Helga Velroyen
    """Set the DRBD usermode helper.
1296 1bb99a33 Bernardo Dal Seno

1297 1bb99a33 Bernardo Dal Seno
    """
1298 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1299 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1300 a794b8d7 Thomas Thrainer
        feedback_fn("Note that you specified a drbd user helper, but did not"
1301 a794b8d7 Thomas Thrainer
                    " enable the drbd disk template.")
1302 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1303 7352d33b Thomas Thrainer
      if not new_helper:
1304 7352d33b Thomas Thrainer
        new_helper = None
1305 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1306 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1307 7352d33b Thomas Thrainer
      else:
1308 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1309 7352d33b Thomas Thrainer
                    " not changing")
1310 7c577910 Helga Velroyen
1311 7c577910 Helga Velroyen
  def Exec(self, feedback_fn):
1312 7c577910 Helga Velroyen
    """Change the parameters of the cluster.
1313 7c577910 Helga Velroyen

1314 7c577910 Helga Velroyen
    """
1315 7c577910 Helga Velroyen
    if self.op.enabled_disk_templates:
1316 7c577910 Helga Velroyen
      self.cluster.enabled_disk_templates = \
1317 8b95dfdc Helga Velroyen
        list(self.op.enabled_disk_templates)
1318 7c577910 Helga Velroyen
1319 7c577910 Helga Velroyen
    self._SetVgName(feedback_fn)
1320 7c577910 Helga Velroyen
    self._SetFileStorageDir(feedback_fn)
1321 7c577910 Helga Velroyen
    self._SetDrbdHelper(feedback_fn)
1322 7c577910 Helga Velroyen
1323 7352d33b Thomas Thrainer
    if self.op.hvparams:
1324 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1325 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1326 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1327 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1328 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1329 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1330 7352d33b Thomas Thrainer
    if self.op.beparams:
1331 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1332 7352d33b Thomas Thrainer
    if self.op.nicparams:
1333 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1334 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1335 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1336 7352d33b Thomas Thrainer
    if self.op.osparams:
1337 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1338 7352d33b Thomas Thrainer
    if self.op.ndparams:
1339 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1340 7352d33b Thomas Thrainer
    if self.op.diskparams:
1341 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1342 7352d33b Thomas Thrainer
    if self.op.hv_state:
1343 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1344 7352d33b Thomas Thrainer
    if self.op.disk_state:
1345 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1346 7352d33b Thomas Thrainer
1347 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1348 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1349 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1350 c1410048 Helga Velroyen
      AdjustCandidatePool(self, [], feedback_fn)
1351 7352d33b Thomas Thrainer
1352 ad756c77 Klaus Aehlig
    if self.op.max_running_jobs is not None:
1353 ad756c77 Klaus Aehlig
      self.cluster.max_running_jobs = self.op.max_running_jobs
1354 ad756c77 Klaus Aehlig
1355 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1356 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1357 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1358 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1359 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1360 7352d33b Thomas Thrainer
1361 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1362 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1363 75f2ff7d Michele Tartara
1364 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1365 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1366 7352d33b Thomas Thrainer
1367 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1368 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1369 7352d33b Thomas Thrainer
1370 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1371 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1372 7352d33b Thomas Thrainer
1373 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1374 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1375 7352d33b Thomas Thrainer
1376 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1377 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1378 7352d33b Thomas Thrainer
1379 0359e5d0 Spyros Trigazis
    if self.op.default_iallocator_params is not None:
1380 0359e5d0 Spyros Trigazis
      self.cluster.default_iallocator_params = self.op.default_iallocator_params
1381 0359e5d0 Spyros Trigazis
1382 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1383 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1384 7352d33b Thomas Thrainer
1385 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1386 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1387 7352d33b Thomas Thrainer
1388 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1389 7352d33b Thomas Thrainer
      desc += " OS list"
1390 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1391 7352d33b Thomas Thrainer
      for key, val in mods:
1392 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1393 7352d33b Thomas Thrainer
          if val in lst:
1394 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1395 7352d33b Thomas Thrainer
          else:
1396 7352d33b Thomas Thrainer
            lst.append(val)
1397 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1398 7352d33b Thomas Thrainer
          if val in lst:
1399 7352d33b Thomas Thrainer
            lst.remove(val)
1400 7352d33b Thomas Thrainer
          else:
1401 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1402 7352d33b Thomas Thrainer
        else:
1403 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1404 7352d33b Thomas Thrainer
1405 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1406 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1407 7352d33b Thomas Thrainer
1408 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1409 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1410 7352d33b Thomas Thrainer
1411 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1412 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1413 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1414 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1415 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1416 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1417 7352d33b Thomas Thrainer
                                                       master_params, ems)
1418 e5c92cfb Klaus Aehlig
      if not self.op.force:
1419 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1420 e5c92cfb Klaus Aehlig
      else:
1421 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1422 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1423 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1424 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1425 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1426 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1427 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1428 7352d33b Thomas Thrainer
1429 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1430 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1431 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1432 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1433 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1434 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1435 1c3231aa Thomas Thrainer
                 master_params.netdev)
1436 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1437 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1438 7352d33b Thomas Thrainer
1439 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1440 7352d33b Thomas Thrainer
1441 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1442 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1443 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1444 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1445 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1446 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1447 7352d33b Thomas Thrainer
                                                     master_params, ems)
1448 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1449 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1450 7352d33b Thomas Thrainer
1451 7352d33b Thomas Thrainer
1452 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1453 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1454 7352d33b Thomas Thrainer

1455 7352d33b Thomas Thrainer
  """
1456 7352d33b Thomas Thrainer
  REQ_BGL = False
1457 7352d33b Thomas Thrainer
1458 7352d33b Thomas Thrainer
  def ExpandNames(self):
1459 7352d33b Thomas Thrainer
    self.needed_locks = {}
1460 7352d33b Thomas Thrainer
1461 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1462 7352d33b Thomas Thrainer
    jobs = []
1463 7352d33b Thomas Thrainer
1464 7352d33b Thomas Thrainer
    if self.op.group_name:
1465 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1466 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1467 7352d33b Thomas Thrainer
    else:
1468 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1469 7352d33b Thomas Thrainer
1470 7352d33b Thomas Thrainer
      # Verify global configuration
1471 7352d33b Thomas Thrainer
      jobs.append([
1472 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1473 7352d33b Thomas Thrainer
        ])
1474 7352d33b Thomas Thrainer
1475 7352d33b Thomas Thrainer
      # Always depend on global verification
1476 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1477 7352d33b Thomas Thrainer
1478 7352d33b Thomas Thrainer
    jobs.extend(
1479 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1480 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1481 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1482 7352d33b Thomas Thrainer
      for group in groups)
1483 7352d33b Thomas Thrainer
1484 7352d33b Thomas Thrainer
    # Fix up all parameters
1485 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1486 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1487 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1488 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1489 7352d33b Thomas Thrainer
      try:
1490 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1491 7352d33b Thomas Thrainer
      except AttributeError:
1492 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1493 7352d33b Thomas Thrainer
1494 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1495 7352d33b Thomas Thrainer
1496 7352d33b Thomas Thrainer
1497 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1498 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1499 7352d33b Thomas Thrainer

1500 7352d33b Thomas Thrainer
  It provides _Error and _ErrorIf, and updates the self.bad boolean. (Expects
1501 7352d33b Thomas Thrainer
  self.op and self._feedback_fn to be available.)
1502 7352d33b Thomas Thrainer

1503 7352d33b Thomas Thrainer
  """
1504 7352d33b Thomas Thrainer
1505 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1506 a6c43c02 Helga Velroyen
  ETYPE_ERROR = constants.CV_ERROR
1507 a6c43c02 Helga Velroyen
  ETYPE_WARNING = constants.CV_WARNING
1508 7352d33b Thomas Thrainer
1509 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1510 7352d33b Thomas Thrainer
    """Format an error message.
1511 7352d33b Thomas Thrainer

1512 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1513 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1514 7352d33b Thomas Thrainer

1515 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1516 7352d33b Thomas Thrainer

1517 7352d33b Thomas Thrainer
    """
1518 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1519 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1520 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1521 7352d33b Thomas Thrainer
    # warning
1522 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1523 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1524 7352d33b Thomas Thrainer
    # first complete the msg
1525 7352d33b Thomas Thrainer
    if args:
1526 7352d33b Thomas Thrainer
      msg = msg % args
1527 7352d33b Thomas Thrainer
    # then format the whole message
1528 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1529 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1530 7352d33b Thomas Thrainer
    else:
1531 7352d33b Thomas Thrainer
      if item:
1532 7352d33b Thomas Thrainer
        item = " " + item
1533 7352d33b Thomas Thrainer
      else:
1534 7352d33b Thomas Thrainer
        item = ""
1535 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1536 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1537 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1538 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1539 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1540 7352d33b Thomas Thrainer
      self.bad = True
1541 7352d33b Thomas Thrainer
1542 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1543 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1544 7352d33b Thomas Thrainer

1545 7352d33b Thomas Thrainer
    """
1546 7352d33b Thomas Thrainer
    if (bool(cond)
1547 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1548 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1549 7352d33b Thomas Thrainer
1550 7352d33b Thomas Thrainer
1551 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1552 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1553 7352d33b Thomas Thrainer

1554 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1555 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1556 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1557 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1558 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1559 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1560 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1561 7352d33b Thomas Thrainer

1562 7352d33b Thomas Thrainer
  """
1563 7352d33b Thomas Thrainer
  hvp_data = []
1564 7352d33b Thomas Thrainer
1565 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1566 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1567 7352d33b Thomas Thrainer
1568 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1569 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1570 7352d33b Thomas Thrainer
      if hv_params:
1571 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1572 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1573 7352d33b Thomas Thrainer
1574 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1575 7352d33b Thomas Thrainer
  for instance in instances:
1576 7352d33b Thomas Thrainer
    if instance.hvparams:
1577 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1578 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1579 7352d33b Thomas Thrainer
1580 7352d33b Thomas Thrainer
  return hvp_data
1581 7352d33b Thomas Thrainer
1582 7352d33b Thomas Thrainer
1583 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1584 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1585 7352d33b Thomas Thrainer

1586 7352d33b Thomas Thrainer
  """
1587 7352d33b Thomas Thrainer
  REQ_BGL = False
1588 7352d33b Thomas Thrainer
1589 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1590 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1591 7352d33b Thomas Thrainer

1592 7352d33b Thomas Thrainer
    """
1593 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1594 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1595 7352d33b Thomas Thrainer
             (item, hv_name))
1596 7352d33b Thomas Thrainer
      try:
1597 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1598 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1599 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1600 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1601 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1602 7352d33b Thomas Thrainer
1603 7352d33b Thomas Thrainer
  def ExpandNames(self):
1604 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1605 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1606 7352d33b Thomas Thrainer
1607 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1608 7352d33b Thomas Thrainer
    """Check prerequisites.
1609 7352d33b Thomas Thrainer

1610 7352d33b Thomas Thrainer
    """
1611 7352d33b Thomas Thrainer
    # Retrieve all information
1612 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1613 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1614 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1615 7352d33b Thomas Thrainer
1616 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1617 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1618 7352d33b Thomas Thrainer

1619 7352d33b Thomas Thrainer
    """
1620 7352d33b Thomas Thrainer
    self.bad = False
1621 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1622 7352d33b Thomas Thrainer
1623 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1624 7352d33b Thomas Thrainer
1625 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1626 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1627 7352d33b Thomas Thrainer
1628 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1629 7352d33b Thomas Thrainer
1630 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1631 a6c43c02 Helga Velroyen
      (errcode, msg) = utils.VerifyCertificate(cert_filename)
1632 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1633 7352d33b Thomas Thrainer
1634 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1635 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1636 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1637 69ac3b74 Michele Tartara
                  None,
1638 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1639 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1640 69ac3b74 Michele Tartara
1641 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1642 7352d33b Thomas Thrainer
1643 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1644 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1645 7352d33b Thomas Thrainer
1646 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1647 7352d33b Thomas Thrainer
1648 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1649 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1650 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1651 7352d33b Thomas Thrainer
1652 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1653 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1654 7352d33b Thomas Thrainer
1655 7352d33b Thomas Thrainer
    dangling_instances = {}
1656 7352d33b Thomas Thrainer
    no_node_instances = []
1657 7352d33b Thomas Thrainer
1658 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1659 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1660 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1661 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1662 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1663 7352d33b Thomas Thrainer
1664 7352d33b Thomas Thrainer
    pretty_dangling = [
1665 7352d33b Thomas Thrainer
        "%s (%s)" %
1666 7352d33b Thomas Thrainer
        (node.name,
1667 24e96ef6 Thomas Thrainer
         utils.CommaJoin(inst.name for
1668 24e96ef6 Thomas Thrainer
                         inst in dangling_instances.get(node.uuid, [])))
1669 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1670 7352d33b Thomas Thrainer
1671 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1672 7352d33b Thomas Thrainer
                  None,
1673 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1674 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1675 7352d33b Thomas Thrainer
1676 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1677 7352d33b Thomas Thrainer
                  None,
1678 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1679 24e96ef6 Thomas Thrainer
                  " %s", utils.CommaJoin(inst.name for
1680 24e96ef6 Thomas Thrainer
                                         inst in no_node_instances))
1681 7352d33b Thomas Thrainer
1682 7352d33b Thomas Thrainer
    return not self.bad
1683 7352d33b Thomas Thrainer
1684 7352d33b Thomas Thrainer
1685 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1686 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1687 7352d33b Thomas Thrainer

1688 7352d33b Thomas Thrainer
  """
1689 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1690 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1691 7352d33b Thomas Thrainer
  REQ_BGL = False
1692 7352d33b Thomas Thrainer
1693 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1694 7352d33b Thomas Thrainer
1695 7352d33b Thomas Thrainer
  class NodeImage(object):
1696 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1697 7352d33b Thomas Thrainer

1698 1c3231aa Thomas Thrainer
    @type uuid: string
1699 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1700 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1701 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1702 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1703 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1704 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1705 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1706 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1707 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1708 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1709 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1710 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1711 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1712 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1713 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1714 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1715 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1716 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1717 7352d33b Thomas Thrainer
    @type ghost: boolean
1718 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1719 7352d33b Thomas Thrainer
    @type os_fail: boolean
1720 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1721 7352d33b Thomas Thrainer
    @type oslist: list
1722 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1723 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1724 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1725 7352d33b Thomas Thrainer
    @type pv_min: float
1726 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1727 7352d33b Thomas Thrainer
    @type pv_max: float
1728 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1729 7352d33b Thomas Thrainer

1730 7352d33b Thomas Thrainer
    """
1731 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1732 1c3231aa Thomas Thrainer
      self.uuid = uuid
1733 7352d33b Thomas Thrainer
      self.volumes = {}
1734 7352d33b Thomas Thrainer
      self.instances = []
1735 7352d33b Thomas Thrainer
      self.pinst = []
1736 7352d33b Thomas Thrainer
      self.sinst = []
1737 7352d33b Thomas Thrainer
      self.sbp = {}
1738 7352d33b Thomas Thrainer
      self.mfree = 0
1739 7352d33b Thomas Thrainer
      self.dfree = 0
1740 7352d33b Thomas Thrainer
      self.offline = offline
1741 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1742 7352d33b Thomas Thrainer
      self.rpc_fail = False
1743 7352d33b Thomas Thrainer
      self.lvm_fail = False
1744 7352d33b Thomas Thrainer
      self.hyp_fail = False
1745 7352d33b Thomas Thrainer
      self.ghost = False
1746 7352d33b Thomas Thrainer
      self.os_fail = False
1747 7352d33b Thomas Thrainer
      self.oslist = {}
1748 7352d33b Thomas Thrainer
      self.pv_min = None
1749 7352d33b Thomas Thrainer
      self.pv_max = None
1750 7352d33b Thomas Thrainer
1751 7352d33b Thomas Thrainer
  def ExpandNames(self):
1752 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1753 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1754 7352d33b Thomas Thrainer
1755 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1756 da4a52a3 Thomas Thrainer
    inst_uuids = \
1757 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1758 7352d33b Thomas Thrainer
1759 7352d33b Thomas Thrainer
    self.needed_locks = {
1760 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1761 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1762 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1763 7352d33b Thomas Thrainer
1764 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1765 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1766 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1767 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1768 7352d33b Thomas Thrainer
      }
1769 7352d33b Thomas Thrainer
1770 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1771 7352d33b Thomas Thrainer
1772 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1773 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1774 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1775 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1776 7352d33b Thomas Thrainer
1777 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1778 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1779 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1780 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1781 7352d33b Thomas Thrainer
      # be locked.
1782 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1783 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1784 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1785 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1786 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1787 7352d33b Thomas Thrainer
1788 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1789 7352d33b Thomas Thrainer
1790 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1791 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1792 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1793 7352d33b Thomas Thrainer
1794 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1795 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1796 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1797 7352d33b Thomas Thrainer
1798 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1799 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1800 7352d33b Thomas Thrainer
1801 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
1802 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
1803 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
1804 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
1805 7352d33b Thomas Thrainer
1806 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
1807 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
1808 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
1809 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
1810 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
1811 7352d33b Thomas Thrainer
1812 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
1813 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
1814 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
1815 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
1816 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
1817 7352d33b Thomas Thrainer
1818 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1819 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1820 7352d33b Thomas Thrainer
1821 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
1822 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
1823 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
1824 7352d33b Thomas Thrainer
1825 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
1826 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
1827 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
1828 7352d33b Thomas Thrainer
1829 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1830 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1831 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1832 7352d33b Thomas Thrainer
1833 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1834 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1835 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
1836 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
1837 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
1838 7352d33b Thomas Thrainer
1839 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1840 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1841 7352d33b Thomas Thrainer
1842 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1843 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1844 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1845 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1846 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1847 7352d33b Thomas Thrainer
1848 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1849 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1850 7352d33b Thomas Thrainer

1851 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1852 7352d33b Thomas Thrainer
        mandatory fields
1853 7352d33b Thomas Thrainer
      - check ganeti version
1854 7352d33b Thomas Thrainer

1855 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1856 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1857 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1858 7352d33b Thomas Thrainer
    @rtype: boolean
1859 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1860 7352d33b Thomas Thrainer
         reasonable values in the respose)
1861 7352d33b Thomas Thrainer

1862 7352d33b Thomas Thrainer
    """
1863 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1864 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1865 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1866 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1867 7352d33b Thomas Thrainer
    if test:
1868 7352d33b Thomas Thrainer
      return False
1869 7352d33b Thomas Thrainer
1870 7352d33b Thomas Thrainer
    # compares ganeti version
1871 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1872 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1873 7352d33b Thomas Thrainer
    test = not (remote_version and
1874 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1875 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1876 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1877 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
1878 7352d33b Thomas Thrainer
    if test:
1879 7352d33b Thomas Thrainer
      return False
1880 7352d33b Thomas Thrainer
1881 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1882 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
1883 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
1884 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
1885 7352d33b Thomas Thrainer
    if test:
1886 7352d33b Thomas Thrainer
      return False
1887 7352d33b Thomas Thrainer
1888 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1889 7352d33b Thomas Thrainer
1890 7352d33b Thomas Thrainer
    # full package version
1891 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1892 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
1893 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1894 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1895 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1896 7352d33b Thomas Thrainer
1897 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1898 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1899 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1900 7352d33b Thomas Thrainer
        test = hv_result is not None
1901 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
1902 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1903 7352d33b Thomas Thrainer
1904 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1905 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1906 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1907 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
1908 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
1909 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
1910 7352d33b Thomas Thrainer
1911 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1912 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1913 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
1914 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
1915 7352d33b Thomas Thrainer
1916 7352d33b Thomas Thrainer
    return True
1917 7352d33b Thomas Thrainer
1918 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1919 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1920 7352d33b Thomas Thrainer
    """Check the node time.
1921 7352d33b Thomas Thrainer

1922 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1923 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1924 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1925 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1926 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1927 7352d33b Thomas Thrainer

1928 7352d33b Thomas Thrainer
    """
1929 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1930 7352d33b Thomas Thrainer
    try:
1931 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1932 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1933 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
1934 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
1935 7352d33b Thomas Thrainer
      return
1936 7352d33b Thomas Thrainer
1937 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1938 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1939 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1940 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1941 7352d33b Thomas Thrainer
    else:
1942 7352d33b Thomas Thrainer
      ntime_diff = None
1943 7352d33b Thomas Thrainer
1944 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
1945 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
1946 d0d7d7cf Thomas Thrainer
                  ntime_diff)
1947 7352d33b Thomas Thrainer
1948 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1949 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1950 7352d33b Thomas Thrainer

1951 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1952 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1953 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1954 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1955 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1956 7352d33b Thomas Thrainer
    @param nimg: node image
1957 7352d33b Thomas Thrainer

1958 7352d33b Thomas Thrainer
    """
1959 7352d33b Thomas Thrainer
    if vg_name is None:
1960 7352d33b Thomas Thrainer
      return
1961 7352d33b Thomas Thrainer
1962 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1963 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1964 7352d33b Thomas Thrainer
    test = not vglist
1965 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
1966 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
1967 7352d33b Thomas Thrainer
    if not test:
1968 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1969 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1970 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
1971 7352d33b Thomas Thrainer
1972 7352d33b Thomas Thrainer
    # Check PVs
1973 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
1974 7352d33b Thomas Thrainer
    for em in errmsgs:
1975 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
1976 7352d33b Thomas Thrainer
    if pvminmax is not None:
1977 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1978 7352d33b Thomas Thrainer
1979 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
1980 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
1981 1bb99a33 Bernardo Dal Seno

1982 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
1983 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
1984 1bb99a33 Bernardo Dal Seno
      node_verify call.
1985 1bb99a33 Bernardo Dal Seno

1986 1bb99a33 Bernardo Dal Seno
    """
1987 1bb99a33 Bernardo Dal Seno
    node_versions = {}
1988 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
1989 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
1990 fb62843c Klaus Aehlig
      if nresult:
1991 fb62843c Klaus Aehlig
        version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
1992 fb62843c Klaus Aehlig
        node_versions[node_uuid] = version
1993 1bb99a33 Bernardo Dal Seno
1994 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
1995 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
1996 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
1997 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
1998 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
1999 1bb99a33 Bernardo Dal Seno
2000 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
2001 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
2002 7352d33b Thomas Thrainer

2003 7352d33b Thomas Thrainer
    @type node_image: dict
2004 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
2005 7352d33b Thomas Thrainer
      L{NodeImage} objects
2006 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2007 7352d33b Thomas Thrainer

2008 7352d33b Thomas Thrainer
    """
2009 7352d33b Thomas Thrainer
    if vg_name is None:
2010 7352d33b Thomas Thrainer
      return
2011 7352d33b Thomas Thrainer
2012 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
2013 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
2014 7352d33b Thomas Thrainer
      return
2015 7352d33b Thomas Thrainer
2016 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
2017 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
2018 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
2019 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
2020 7352d33b Thomas Thrainer
    if not vals:
2021 7352d33b Thomas Thrainer
      return
2022 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
2023 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
2024 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
2025 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
2026 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
2027 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
2028 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
2029 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
2030 7352d33b Thomas Thrainer
2031 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
2032 7352d33b Thomas Thrainer
    """Check the node bridges.
2033 7352d33b Thomas Thrainer

2034 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2035 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2036 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2037 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
2038 7352d33b Thomas Thrainer

2039 7352d33b Thomas Thrainer
    """
2040 7352d33b Thomas Thrainer
    if not bridges:
2041 7352d33b Thomas Thrainer
      return
2042 7352d33b Thomas Thrainer
2043 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
2044 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
2045 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2046 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
2047 7352d33b Thomas Thrainer
    if not test:
2048 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
2049 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
2050 7352d33b Thomas Thrainer
2051 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
2052 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
2053 7352d33b Thomas Thrainer

2054 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2055 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2056 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2057 7352d33b Thomas Thrainer

2058 7352d33b Thomas Thrainer
    """
2059 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
2060 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2061 7352d33b Thomas Thrainer
                  "did not return user scripts information")
2062 7352d33b Thomas Thrainer
2063 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
2064 7352d33b Thomas Thrainer
    if not test:
2065 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2066 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
2067 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
2068 7352d33b Thomas Thrainer
2069 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
2070 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
2071 7352d33b Thomas Thrainer

2072 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2073 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2074 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2075 7352d33b Thomas Thrainer

2076 7352d33b Thomas Thrainer
    """
2077 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
2078 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
2079 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
2080 7352d33b Thomas Thrainer
    if not test:
2081 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
2082 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
2083 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
2084 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
2085 7352d33b Thomas Thrainer
2086 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
2087 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2088 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
2089 7352d33b Thomas Thrainer
    if not test:
2090 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
2091 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
2092 7352d33b Thomas Thrainer
        for anode in nlist:
2093 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
2094 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
2095 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
2096 7352d33b Thomas Thrainer
2097 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
2098 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2099 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
2100 7352d33b Thomas Thrainer
    if not test:
2101 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
2102 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
2103 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
2104 7352d33b Thomas Thrainer
        else:
2105 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
2106 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
2107 7352d33b Thomas Thrainer
2108 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
2109 7352d33b Thomas Thrainer
    """Verify an instance.
2110 7352d33b Thomas Thrainer

2111 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
2112 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
2113 7352d33b Thomas Thrainer
    state.
2114 7352d33b Thomas Thrainer

2115 7352d33b Thomas Thrainer
    """
2116 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
2117 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
2118 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
2119 7352d33b Thomas Thrainer
2120 7352d33b Thomas Thrainer
    node_vol_should = {}
2121 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
2122 7352d33b Thomas Thrainer
2123 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2124 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
2125 7352d33b Thomas Thrainer
                                                            self.group_info)
2126 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
2127 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
2128 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
2129 7352d33b Thomas Thrainer
2130 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
2131 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
2132 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
2133 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
2134 7352d33b Thomas Thrainer
        continue
2135 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
2136 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
2137 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
2138 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
2139 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
2140 7352d33b Thomas Thrainer
2141 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
2142 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
2143 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
2144 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
2145 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
2146 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
2147 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
2148 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
2149 7352d33b Thomas Thrainer
2150 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
2151 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
2152 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
2153 7352d33b Thomas Thrainer
2154 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
2155 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
2156 7352d33b Thomas Thrainer
      # node here
2157 7352d33b Thomas Thrainer
      snode = node_image[nname]
2158 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
2159 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
2160 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
2161 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
2162 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
2163 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
2164 9b0e86e2 Thomas Thrainer
2165 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
2166 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
2167 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
2168 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
2169 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
2170 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
2171 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
2172 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
2173 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
2174 9b0e86e2 Thomas Thrainer
2175 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
2176 d0d7d7cf Thomas Thrainer
2177 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
2178 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
2179 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
2180 da4a52a3 Thomas Thrainer
                  instance.name)
2181 da4a52a3 Thomas Thrainer
2182 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
2183 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
2184 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
2185 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
2186 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
2187 7352d33b Thomas Thrainer
2188 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
2189 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
2190 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
2191 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
2192 c69b147d Bernardo Dal Seno
        # node should have the flag set
2193 c69b147d Bernardo Dal Seno
        es_nodes = [n
2194 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
2195 c69b147d Bernardo Dal Seno
                    if es]
2196 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
2197 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
2198 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
2199 da4a52a3 Thomas Thrainer
                    instance.disk_template,
2200 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
2201 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
2202 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
2203 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
2204 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
2205 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
2206 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
2207 7352d33b Thomas Thrainer
2208 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2209 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
2210 7352d33b Thomas Thrainer
      instance_groups = {}
2211 7352d33b Thomas Thrainer
2212 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
2213 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
2214 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
2215 7352d33b Thomas Thrainer
2216 7352d33b Thomas Thrainer
      pretty_list = [
2217 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
2218 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
2219 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
2220 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
2221 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
2222 7352d33b Thomas Thrainer
                                   reverse=True)]
2223 7352d33b Thomas Thrainer
2224 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
2225 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
2226 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
2227 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
2228 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
2229 7352d33b Thomas Thrainer
2230 7352d33b Thomas Thrainer
    inst_nodes_offline = []
2231 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
2232 7352d33b Thomas Thrainer
      s_img = node_image[snode]
2233 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
2234 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
2235 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
2236 da4a52a3 Thomas Thrainer
                    instance.name)
2237 7352d33b Thomas Thrainer
2238 7352d33b Thomas Thrainer
      if s_img.offline:
2239 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
2240 7352d33b Thomas Thrainer
2241 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
2242 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
2243 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
2244 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
2245 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
2246 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
2247 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
2248 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
2249 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2250 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
2251 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
2252 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
2253 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2254 7352d33b Thomas Thrainer
2255 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
2256 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
2257 7352d33b Thomas Thrainer

2258 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
2259 7352d33b Thomas Thrainer
    reported as unknown.
2260 7352d33b Thomas Thrainer

2261 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
2262 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
2263 7352d33b Thomas Thrainer

2264 7352d33b Thomas Thrainer
    """
2265 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2266 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
2267 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
2268 7352d33b Thomas Thrainer
        # skip non-healthy nodes
2269 7352d33b Thomas Thrainer
        continue
2270 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
2271 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
2272 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2273 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2274 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2275 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2276 b0e8ed3f Santi Raffa
                      "volume %s is unknown", volume,
2277 b0e8ed3f Santi Raffa
                      code=_VerifyErrors.ETYPE_WARNING)
2278 7352d33b Thomas Thrainer
2279 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2280 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2281 7352d33b Thomas Thrainer

2282 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2283 7352d33b Thomas Thrainer
    instances it was primary for.
2284 7352d33b Thomas Thrainer

2285 7352d33b Thomas Thrainer
    """
2286 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2287 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2288 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2289 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2290 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2291 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2292 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2293 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2294 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2295 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2296 1c3231aa Thomas Thrainer
      if n_img.offline or \
2297 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2298 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2299 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2300 9fdb10be Thomas Thrainer
        # information from them; we already list instances living on such
2301 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2302 7352d33b Thomas Thrainer
        continue
2303 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2304 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2305 7352d33b Thomas Thrainer
        needed_mem = 0
2306 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2307 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2308 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2309 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2310 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2311 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2312 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2313 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2314 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2315 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2316 7352d33b Thomas Thrainer
2317 a6c43c02 Helga Velroyen
  def _VerifyClientCertificates(self, nodes, all_nvinfo):
2318 a6c43c02 Helga Velroyen
    """Verifies the consistency of the client certificates.
2319 a6c43c02 Helga Velroyen

2320 a6c43c02 Helga Velroyen
    This includes several aspects:
2321 a6c43c02 Helga Velroyen
      - the individual validation of all nodes' certificates
2322 a6c43c02 Helga Velroyen
      - the consistency of the master candidate certificate map
2323 a6c43c02 Helga Velroyen
      - the consistency of the master candidate certificate map with the
2324 a6c43c02 Helga Velroyen
        certificates that the master candidates are actually using.
2325 a6c43c02 Helga Velroyen

2326 a6c43c02 Helga Velroyen
    @param nodes: the list of nodes to consider in this verification
2327 a6c43c02 Helga Velroyen
    @param all_nvinfo: the map of results of the verify_node call to
2328 a6c43c02 Helga Velroyen
      all nodes
2329 a6c43c02 Helga Velroyen

2330 a6c43c02 Helga Velroyen
    """
2331 a6c43c02 Helga Velroyen
    candidate_certs = self.cfg.GetClusterInfo().candidate_certs
2332 a6c43c02 Helga Velroyen
    if candidate_certs is None or len(candidate_certs) == 0:
2333 a6c43c02 Helga Velroyen
      self._ErrorIf(
2334 a6c43c02 Helga Velroyen
        True, constants.CV_ECLUSTERCLIENTCERT, None,
2335 a6c43c02 Helga Velroyen
        "The cluster's list of master candidate certificates is empty."
2336 a6c43c02 Helga Velroyen
        "If you just updated the cluster, please run"
2337 a6c43c02 Helga Velroyen
        " 'gnt-cluster renew-crypto --new-node-certificates'.")
2338 a6c43c02 Helga Velroyen
      return
2339 a6c43c02 Helga Velroyen
2340 a6c43c02 Helga Velroyen
    self._ErrorIf(
2341 a6c43c02 Helga Velroyen
      len(candidate_certs) != len(set(candidate_certs.values())),
2342 a6c43c02 Helga Velroyen
      constants.CV_ECLUSTERCLIENTCERT, None,
2343 a6c43c02 Helga Velroyen
      "There are at least two master candidates configured to use the same"
2344 a6c43c02 Helga Velroyen
      " certificate.")
2345 a6c43c02 Helga Velroyen
2346 a6c43c02 Helga Velroyen
    # collect the client certificate
2347 a6c43c02 Helga Velroyen
    for node in nodes:
2348 a6c43c02 Helga Velroyen
      if node.offline:
2349 a6c43c02 Helga Velroyen
        continue
2350 a6c43c02 Helga Velroyen
2351 a6c43c02 Helga Velroyen
      nresult = all_nvinfo[node.uuid]
2352 a6c43c02 Helga Velroyen
      if nresult.fail_msg or not nresult.payload:
2353 a6c43c02 Helga Velroyen
        continue
2354 a6c43c02 Helga Velroyen
2355 a6c43c02 Helga Velroyen
      (errcode, msg) = nresult.payload.get(constants.NV_CLIENT_CERT, None)
2356 a6c43c02 Helga Velroyen
2357 a6c43c02 Helga Velroyen
      self._ErrorIf(
2358 a6c43c02 Helga Velroyen
        errcode is not None, constants.CV_ECLUSTERCLIENTCERT, None,
2359 a6c43c02 Helga Velroyen
        "Client certificate of node '%s' failed validation: %s (code '%s')",
2360 a6c43c02 Helga Velroyen
        node.uuid, msg, errcode)
2361 a6c43c02 Helga Velroyen
2362 a6c43c02 Helga Velroyen
      if not errcode:
2363 a6c43c02 Helga Velroyen
        digest = msg
2364 a6c43c02 Helga Velroyen
        if node.master_candidate:
2365 a6c43c02 Helga Velroyen
          if node.uuid in candidate_certs:
2366 a6c43c02 Helga Velroyen
            self._ErrorIf(
2367 a6c43c02 Helga Velroyen
              digest != candidate_certs[node.uuid],
2368 a6c43c02 Helga Velroyen
              constants.CV_ECLUSTERCLIENTCERT, None,
2369 a6c43c02 Helga Velroyen
              "Client certificate digest of master candidate '%s' does not"
2370 a6c43c02 Helga Velroyen
              " match its entry in the cluster's map of master candidate"
2371 a6c43c02 Helga Velroyen
              " certificates. Expected: %s Got: %s", node.uuid,
2372 a6c43c02 Helga Velroyen
              digest, candidate_certs[node.uuid])
2373 a6c43c02 Helga Velroyen
          else:
2374 a6c43c02 Helga Velroyen
            self._ErrorIf(
2375 a6c43c02 Helga Velroyen
              True, constants.CV_ECLUSTERCLIENTCERT, None,
2376 a6c43c02 Helga Velroyen
              "The master candidate '%s' does not have an entry in the"
2377 a6c43c02 Helga Velroyen
              " map of candidate certificates.", node.uuid)
2378 a6c43c02 Helga Velroyen
            self._ErrorIf(
2379 a6c43c02 Helga Velroyen
              digest in candidate_certs.values(),
2380 a6c43c02 Helga Velroyen
              constants.CV_ECLUSTERCLIENTCERT, None,
2381 a6c43c02 Helga Velroyen
              "Master candidate '%s' is using a certificate of another node.",
2382 a6c43c02 Helga Velroyen
              node.uuid)
2383 a6c43c02 Helga Velroyen
        else:
2384 a6c43c02 Helga Velroyen
          self._ErrorIf(
2385 a6c43c02 Helga Velroyen
            node.uuid in candidate_certs,
2386 a6c43c02 Helga Velroyen
            constants.CV_ECLUSTERCLIENTCERT, None,
2387 a6c43c02 Helga Velroyen
            "Node '%s' is not a master candidate, but still listed in the"
2388 a6c43c02 Helga Velroyen
            " map of master candidate certificates.", node.uuid)
2389 a6c43c02 Helga Velroyen
          self._ErrorIf(
2390 a6c43c02 Helga Velroyen
            (node.uuid not in candidate_certs) and
2391 a6c43c02 Helga Velroyen
              (digest in candidate_certs.values()),
2392 a6c43c02 Helga Velroyen
            constants.CV_ECLUSTERCLIENTCERT, None,
2393 a6c43c02 Helga Velroyen
            "Node '%s' is not a master candidate and is incorrectly using a"
2394 a6c43c02 Helga Velroyen
            " certificate of another node which is master candidate.",
2395 a6c43c02 Helga Velroyen
            node.uuid)
2396 a6c43c02 Helga Velroyen
2397 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2398 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2399 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2400 7352d33b Thomas Thrainer

2401 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2402 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2403 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2404 7352d33b Thomas Thrainer

2405 7352d33b Thomas Thrainer
    """
2406 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2407 7352d33b Thomas Thrainer
    files2nodefn = [
2408 7352d33b Thomas Thrainer
      (files_all, None),
2409 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2410 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2411 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2412 7352d33b Thomas Thrainer
      ]
2413 7352d33b Thomas Thrainer
2414 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2415 7352d33b Thomas Thrainer
    nodefiles = {}
2416 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2417 7352d33b Thomas Thrainer
      if fn is None:
2418 1c3231aa Thomas Thrainer
        filenodes = nodes
2419 7352d33b Thomas Thrainer
      else:
2420 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2421 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2422 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2423 7352d33b Thomas Thrainer
                       for filename in files)
2424 7352d33b Thomas Thrainer
2425 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2426 7352d33b Thomas Thrainer
2427 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2428 7352d33b Thomas Thrainer
    ignore_nodes = set()
2429 7352d33b Thomas Thrainer
2430 1c3231aa Thomas Thrainer
    for node in nodes:
2431 7352d33b Thomas Thrainer
      if node.offline:
2432 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2433 7352d33b Thomas Thrainer
        continue
2434 7352d33b Thomas Thrainer
2435 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2436 7352d33b Thomas Thrainer
2437 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2438 7352d33b Thomas Thrainer
        node_files = None
2439 7352d33b Thomas Thrainer
      else:
2440 a6c43c02 Helga Velroyen
        fingerprints = nresult.payload.get(constants.NV_FILELIST, {})
2441 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2442 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2443 7352d33b Thomas Thrainer
        del fingerprints
2444 7352d33b Thomas Thrainer
2445 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2446 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2447 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2448 7352d33b Thomas Thrainer
      if test:
2449 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2450 7352d33b Thomas Thrainer
        continue
2451 7352d33b Thomas Thrainer
2452 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2453 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2454 7352d33b Thomas Thrainer
        assert filename in nodefiles
2455 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2456 7352d33b Thomas Thrainer
2457 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2458 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2459 7352d33b Thomas Thrainer
2460 7352d33b Thomas Thrainer
      # Nodes having the file
2461 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2462 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2463 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2464 7352d33b Thomas Thrainer
2465 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2466 7352d33b Thomas Thrainer
2467 7352d33b Thomas Thrainer
      # Nodes missing file
2468 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2469 7352d33b Thomas Thrainer
2470 7352d33b Thomas Thrainer
      if filename in files_opt:
2471 7352d33b Thomas Thrainer
        # All or no nodes
2472 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2473 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2474 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2475 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2476 1c3231aa Thomas Thrainer
                      filename,
2477 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2478 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2479 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2480 7352d33b Thomas Thrainer
      else:
2481 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2482 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2483 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2484 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2485 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2486 7352d33b Thomas Thrainer
2487 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2488 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2489 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2490 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2491 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2492 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2493 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2494 7352d33b Thomas Thrainer
2495 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2496 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2497 7352d33b Thomas Thrainer
      if test:
2498 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2499 1c3231aa Thomas Thrainer
                    (idx + 1,
2500 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2501 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2502 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2503 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2504 7352d33b Thomas Thrainer
      else:
2505 7352d33b Thomas Thrainer
        variants = []
2506 7352d33b Thomas Thrainer
2507 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2508 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2509 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2510 7352d33b Thomas Thrainer
2511 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbdHelper(self, ninfo, nresult, drbd_helper):
2512 9af7ece3 Helga Velroyen
    """Verify the drbd helper.
2513 7352d33b Thomas Thrainer

2514 7352d33b Thomas Thrainer
    """
2515 7352d33b Thomas Thrainer
    if drbd_helper:
2516 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2517 7352d33b Thomas Thrainer
      test = (helper_result is None)
2518 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2519 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2520 7352d33b Thomas Thrainer
      if helper_result:
2521 7352d33b Thomas Thrainer
        status, payload = helper_result
2522 7352d33b Thomas Thrainer
        test = not status
2523 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2524 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2525 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2526 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2527 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2528 7352d33b Thomas Thrainer
2529 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2530 9af7ece3 Helga Velroyen
                      drbd_map):
2531 9af7ece3 Helga Velroyen
    """Verifies and the node DRBD status.
2532 9af7ece3 Helga Velroyen

2533 9af7ece3 Helga Velroyen
    @type ninfo: L{objects.Node}
2534 9af7ece3 Helga Velroyen
    @param ninfo: the node to check
2535 9af7ece3 Helga Velroyen
    @param nresult: the remote results for the node
2536 9af7ece3 Helga Velroyen
    @param instanceinfo: the dict of instances
2537 9af7ece3 Helga Velroyen
    @param drbd_helper: the configured DRBD usermode helper
2538 9af7ece3 Helga Velroyen
    @param drbd_map: the DRBD map as returned by
2539 9af7ece3 Helga Velroyen
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2540 9af7ece3 Helga Velroyen

2541 9af7ece3 Helga Velroyen
    """
2542 9af7ece3 Helga Velroyen
    self._VerifyNodeDrbdHelper(ninfo, nresult, drbd_helper)
2543 9af7ece3 Helga Velroyen
2544 7352d33b Thomas Thrainer
    # compute the DRBD minors
2545 7352d33b Thomas Thrainer
    node_drbd = {}
2546 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2547 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2548 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2549 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2550 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2551 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2552 7352d33b Thomas Thrainer
        # unallocated minor in use)
2553 7352d33b Thomas Thrainer
      if test:
2554 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2555 7352d33b Thomas Thrainer
      else:
2556 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2557 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2558 7352d33b Thomas Thrainer
2559 7352d33b Thomas Thrainer
    # and now check them
2560 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2561 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2562 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2563 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2564 7352d33b Thomas Thrainer
    if test:
2565 7352d33b Thomas Thrainer
      # we cannot check drbd status
2566 7352d33b Thomas Thrainer
      return
2567 7352d33b Thomas Thrainer
2568 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2569 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2570 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2571 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2572 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2573 7352d33b Thomas Thrainer
    for minor in used_minors:
2574 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2575 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2576 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2577 7352d33b Thomas Thrainer
2578 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2579 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2580 7352d33b Thomas Thrainer

2581 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2582 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2583 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2584 7352d33b Thomas Thrainer
    @param nimg: the node image object
2585 7352d33b Thomas Thrainer

2586 7352d33b Thomas Thrainer
    """
2587 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2588 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2589 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2590 7352d33b Thomas Thrainer
                           for v in remote_os))
2591 7352d33b Thomas Thrainer
2592 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2593 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2594 7352d33b Thomas Thrainer
2595 7352d33b Thomas Thrainer
    nimg.os_fail = test
2596 7352d33b Thomas Thrainer
2597 7352d33b Thomas Thrainer
    if test:
2598 7352d33b Thomas Thrainer
      return
2599 7352d33b Thomas Thrainer
2600 7352d33b Thomas Thrainer
    os_dict = {}
2601 7352d33b Thomas Thrainer
2602 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2603 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2604 7352d33b Thomas Thrainer
2605 7352d33b Thomas Thrainer
      if name not in os_dict:
2606 7352d33b Thomas Thrainer
        os_dict[name] = []
2607 7352d33b Thomas Thrainer
2608 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2609 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2610 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2611 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2612 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2613 7352d33b Thomas Thrainer
2614 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2615 7352d33b Thomas Thrainer
2616 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2617 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2618 7352d33b Thomas Thrainer

2619 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2620 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2621 7352d33b Thomas Thrainer
    @param nimg: the node image object
2622 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2623 7352d33b Thomas Thrainer

2624 7352d33b Thomas Thrainer
    """
2625 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2626 7352d33b Thomas Thrainer
2627 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2628 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2629 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2630 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2631 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2632 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2633 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2634 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2635 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2636 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2637 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2638 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2639 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2640 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2641 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2642 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2643 7352d33b Thomas Thrainer
      if test:
2644 7352d33b Thomas Thrainer
        continue
2645 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2646 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2647 7352d33b Thomas Thrainer
      if not b_status:
2648 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2649 7352d33b Thomas Thrainer
        continue
2650 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2651 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2652 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2653 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2654 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2655 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2656 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2657 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2658 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2659 7352d33b Thomas Thrainer
2660 7352d33b Thomas Thrainer
    # check any missing OSes
2661 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2662 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2663 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2664 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2665 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2666 7352d33b Thomas Thrainer
2667 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2668 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2669 7352d33b Thomas Thrainer

2670 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2671 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2672 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2673 7352d33b Thomas Thrainer
    @type is_master: bool
2674 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2675 7352d33b Thomas Thrainer

2676 7352d33b Thomas Thrainer
    """
2677 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2678 7352d33b Thomas Thrainer
    if (is_master and
2679 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2680 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2681 7352d33b Thomas Thrainer
      try:
2682 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2683 7352d33b Thomas Thrainer
      except KeyError:
2684 7352d33b Thomas Thrainer
        # This should never happen
2685 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2686 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2687 7352d33b Thomas Thrainer
      else:
2688 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2689 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2690 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2691 7352d33b Thomas Thrainer
    else:
2692 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2693 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2694 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2695 7352d33b Thomas Thrainer
                    " paths")
2696 7352d33b Thomas Thrainer
2697 4b322a76 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult, file_disk_template,
2698 4b322a76 Helga Velroyen
                          verify_key, error_key):
2699 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2700 9c1c3c19 Helga Velroyen

2701 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2702 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2703 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2704 4b322a76 Helga Velroyen
    @type file_disk_template: string
2705 4b322a76 Helga Velroyen
    @param file_disk_template: file-based disk template, whose directory
2706 4b322a76 Helga Velroyen
        is supposed to be verified
2707 4b322a76 Helga Velroyen
    @type verify_key: string
2708 4b322a76 Helga Velroyen
    @param verify_key: key for the verification map of this file
2709 4b322a76 Helga Velroyen
        verification step
2710 4b322a76 Helga Velroyen
    @param error_key: error key to be added to the verification results
2711 4b322a76 Helga Velroyen
        in case something goes wrong in this verification step
2712 9c1c3c19 Helga Velroyen

2713 9c1c3c19 Helga Velroyen
    """
2714 5a904197 Santi Raffa
    assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
2715 5a904197 Santi Raffa
              constants.ST_FILE, constants.ST_SHARED_FILE
2716 5a904197 Santi Raffa
           ))
2717 5a904197 Santi Raffa
2718 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2719 4b322a76 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(file_disk_template):
2720 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2721 4b322a76 Helga Velroyen
          verify_key in nresult,
2722 4b322a76 Helga Velroyen
          error_key, ninfo.name,
2723 4b322a76 Helga Velroyen
          "The configured %s storage path is unusable: %s" %
2724 4b322a76 Helga Velroyen
          (file_disk_template, nresult.get(verify_key)))
2725 4b322a76 Helga Velroyen
2726 4b322a76 Helga Velroyen
  def _VerifyFileStoragePaths(self, ninfo, nresult):
2727 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2728 4b322a76 Helga Velroyen

2729 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2730 4b322a76 Helga Velroyen

2731 4b322a76 Helga Velroyen
    """
2732 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2733 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_FILE,
2734 4b322a76 Helga Velroyen
        constants.NV_FILE_STORAGE_PATH,
2735 4b322a76 Helga Velroyen
        constants.CV_ENODEFILESTORAGEPATHUNUSABLE)
2736 4b322a76 Helga Velroyen
2737 4b322a76 Helga Velroyen
  def _VerifySharedFileStoragePaths(self, ninfo, nresult):
2738 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2739 4b322a76 Helga Velroyen

2740 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2741 4b322a76 Helga Velroyen

2742 4b322a76 Helga Velroyen
    """
2743 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2744 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_SHARED_FILE,
2745 4b322a76 Helga Velroyen
        constants.NV_SHARED_FILE_STORAGE_PATH,
2746 4b322a76 Helga Velroyen
        constants.CV_ENODESHAREDFILESTORAGEPATHUNUSABLE)
2747 9c1c3c19 Helga Velroyen
2748 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2749 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2750 7352d33b Thomas Thrainer

2751 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2752 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2753 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2754 7352d33b Thomas Thrainer

2755 7352d33b Thomas Thrainer
    """
2756 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2757 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2758 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2759 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2760 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2761 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2762 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2763 7352d33b Thomas Thrainer
2764 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2765 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2766 7352d33b Thomas Thrainer

2767 7352d33b Thomas Thrainer
    This function will update a L{NodeImage}'s internal structures
2768 7352d33b Thomas Thrainer
    with data from the remote call.
2769 7352d33b Thomas Thrainer

2770 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2771 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2772 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2773 7352d33b Thomas Thrainer
    @param nimg: the node image object
2774 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2775 7352d33b Thomas Thrainer

2776 7352d33b Thomas Thrainer
    """
2777 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2778 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2779 7352d33b Thomas Thrainer
    if vg_name is None:
2780 7352d33b Thomas Thrainer
      pass
2781 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2782 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2783 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2784 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2785 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2786 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2787 7352d33b Thomas Thrainer
    else:
2788 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2789 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2790 7352d33b Thomas Thrainer
2791 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2792 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2793 7352d33b Thomas Thrainer

2794 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2795 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2796 7352d33b Thomas Thrainer
    list key.
2797 7352d33b Thomas Thrainer

2798 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2799 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2800 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2801 7352d33b Thomas Thrainer
    @param nimg: the node image object
2802 7352d33b Thomas Thrainer

2803 7352d33b Thomas Thrainer
    """
2804 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2805 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2806 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2807 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2808 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2809 7352d33b Thomas Thrainer
    if test:
2810 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2811 7352d33b Thomas Thrainer
    else:
2812 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
2813 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
2814 7352d33b Thomas Thrainer
2815 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2816 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2817 7352d33b Thomas Thrainer

2818 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2819 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2820 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2821 7352d33b Thomas Thrainer
    @param nimg: the node image object
2822 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2823 7352d33b Thomas Thrainer

2824 7352d33b Thomas Thrainer
    """
2825 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2826 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2827 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2828 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2829 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
2830 7352d33b Thomas Thrainer
    if not test:
2831 7352d33b Thomas Thrainer
      try:
2832 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2833 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2834 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2835 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
2836 7352d33b Thomas Thrainer
2837 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2838 7352d33b Thomas Thrainer
    if vg_name is not None:
2839 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2840 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2841 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2842 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
2843 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
2844 7352d33b Thomas Thrainer
      if not test:
2845 7352d33b Thomas Thrainer
        try:
2846 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2847 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2848 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2849 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
2850 7352d33b Thomas Thrainer
2851 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
2852 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2853 7352d33b Thomas Thrainer

2854 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
2855 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
2856 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
2857 7352d33b Thomas Thrainer
    @param node_image: Node objects
2858 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
2859 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2860 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2861 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2862 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2863 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2864 7352d33b Thomas Thrainer

2865 7352d33b Thomas Thrainer
    """
2866 7352d33b Thomas Thrainer
    node_disks = {}
2867 0c3d9c7c Thomas Thrainer
    node_disks_dev_inst_only = {}
2868 7352d33b Thomas Thrainer
    diskless_instances = set()
2869 099ed3b2 Klaus Aehlig
    nodisk_instances = set()
2870 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2871 7352d33b Thomas Thrainer
2872 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
2873 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
2874 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
2875 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
2876 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
2877 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
2878 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
2879 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
2880 7352d33b Thomas Thrainer
2881 7352d33b Thomas Thrainer
      if not disks:
2882 099ed3b2 Klaus Aehlig
        nodisk_instances.update(uuid for uuid in node_inst_uuids
2883 099ed3b2 Klaus Aehlig
                                if instanceinfo[uuid].disk_template != diskless)
2884 7352d33b Thomas Thrainer
        # No need to collect data
2885 7352d33b Thomas Thrainer
        continue
2886 7352d33b Thomas Thrainer
2887 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
2888 7352d33b Thomas Thrainer
2889 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2890 0c3d9c7c Thomas Thrainer
      dev_inst_only = []
2891 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
2892 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
2893 da4a52a3 Thomas Thrainer
                                          self.cfg)
2894 0c3d9c7c Thomas Thrainer
        dev_inst_only.append((anno_disk, instanceinfo[inst_uuid]))
2895 7352d33b Thomas Thrainer
2896 0c3d9c7c Thomas Thrainer
      node_disks_dev_inst_only[nuuid] = dev_inst_only
2897 7352d33b Thomas Thrainer
2898 0c3d9c7c Thomas Thrainer
    assert len(node_disks) == len(node_disks_dev_inst_only)
2899 7352d33b Thomas Thrainer
2900 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2901 0c3d9c7c Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(
2902 0c3d9c7c Thomas Thrainer
               node_disks.keys(), node_disks_dev_inst_only)
2903 7352d33b Thomas Thrainer
2904 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2905 7352d33b Thomas Thrainer
2906 7352d33b Thomas Thrainer
    instdisk = {}
2907 7352d33b Thomas Thrainer
2908 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
2909 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
2910 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
2911 7352d33b Thomas Thrainer
2912 7352d33b Thomas Thrainer
      if nres.offline:
2913 7352d33b Thomas Thrainer
        # No data from this node
2914 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2915 7352d33b Thomas Thrainer
      else:
2916 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2917 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
2918 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
2919 7352d33b Thomas Thrainer
        if msg:
2920 7352d33b Thomas Thrainer
          # No data from this node
2921 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2922 7352d33b Thomas Thrainer
        else:
2923 7352d33b Thomas Thrainer
          data = []
2924 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2925 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2926 7352d33b Thomas Thrainer
              data.append(i)
2927 7352d33b Thomas Thrainer
            else:
2928 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2929 1c3231aa Thomas Thrainer
                              node.name, idx, i)
2930 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2931 7352d33b Thomas Thrainer
2932 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
2933 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
2934 da4a52a3 Thomas Thrainer
          .append(status)
2935 7352d33b Thomas Thrainer
2936 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2937 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
2938 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
2939 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
2940 099ed3b2 Klaus Aehlig
    # ...and disk-full instances that happen to have no disks
2941 099ed3b2 Klaus Aehlig
    for inst_uuid in nodisk_instances:
2942 099ed3b2 Klaus Aehlig
      assert inst_uuid not in instdisk
2943 099ed3b2 Klaus Aehlig
      instdisk[inst_uuid] = {}
2944 7352d33b Thomas Thrainer
2945 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
2946 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
2947 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
2948 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
2949 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
2950 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
2951 7352d33b Thomas Thrainer
    if __debug__:
2952 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
2953 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
2954 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
2955 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
2956 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
2957 7352d33b Thomas Thrainer
2958 7352d33b Thomas Thrainer
    return instdisk
2959 7352d33b Thomas Thrainer
2960 7352d33b Thomas Thrainer
  @staticmethod
2961 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
2962 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
2963 7352d33b Thomas Thrainer

2964 7352d33b Thomas Thrainer
    """
2965 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
2966 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
2967 7352d33b Thomas Thrainer
                 not node.offline)]
2968 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
2969 7352d33b Thomas Thrainer
2970 7352d33b Thomas Thrainer
    return map(itertools.cycle,
2971 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
2972 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2973 7352d33b Thomas Thrainer
                                                  keyfunc)])
2974 7352d33b Thomas Thrainer
2975 7352d33b Thomas Thrainer
  @classmethod
2976 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2977 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
2978 7352d33b Thomas Thrainer

2979 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
2980 7352d33b Thomas Thrainer
    every other group.
2981 7352d33b Thomas Thrainer

2982 7352d33b Thomas Thrainer
    @warning: This algorithm has a known issue if one node group is much
2983 7352d33b Thomas Thrainer
      smaller than others (e.g. just one node). In such a case all other
2984 7352d33b Thomas Thrainer
      nodes will talk to the single node.
2985 7352d33b Thomas Thrainer

2986 7352d33b Thomas Thrainer
    """
2987 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2988 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2989 7352d33b Thomas Thrainer
2990 7352d33b Thomas Thrainer
    return (online_nodes,
2991 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
2992 7352d33b Thomas Thrainer
                 for name in online_nodes))
2993 7352d33b Thomas Thrainer
2994 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
2995 7352d33b Thomas Thrainer
    """Build hooks env.
2996 7352d33b Thomas Thrainer

2997 7352d33b Thomas Thrainer
    Cluster-Verify hooks just ran in the post phase and their failure makes
2998 7352d33b Thomas Thrainer
    the output be logged in the verify output and the verification to fail.
2999 7352d33b Thomas Thrainer

3000 7352d33b Thomas Thrainer
    """
3001 7352d33b Thomas Thrainer
    env = {
3002 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
3003 7352d33b Thomas Thrainer
      }
3004 7352d33b Thomas Thrainer
3005 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
3006 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
3007 7352d33b Thomas Thrainer
3008 7352d33b Thomas Thrainer
    return env
3009 7352d33b Thomas Thrainer
3010 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
3011 7352d33b Thomas Thrainer
    """Build hooks nodes.
3012 7352d33b Thomas Thrainer

3013 7352d33b Thomas Thrainer
    """
3014 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
3015 7352d33b Thomas Thrainer
3016 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3017 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
3018 7352d33b Thomas Thrainer

3019 7352d33b Thomas Thrainer
    """
3020 7352d33b Thomas Thrainer
    # This method has too many local variables. pylint: disable=R0914
3021 7352d33b Thomas Thrainer
    feedback_fn("* Verifying group '%s'" % self.group_info.name)
3022 7352d33b Thomas Thrainer
3023 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3024 7352d33b Thomas Thrainer
      # empty node group
3025 7352d33b Thomas Thrainer
      feedback_fn("* Empty node group, skipping verification")
3026 7352d33b Thomas Thrainer
      return True
3027 7352d33b Thomas Thrainer
3028 7352d33b Thomas Thrainer
    self.bad = False
3029 7352d33b Thomas Thrainer
    verbose = self.op.verbose
3030 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
3031 7352d33b Thomas Thrainer
3032 7352d33b Thomas Thrainer
    vg_name = self.cfg.GetVGName()
3033 7352d33b Thomas Thrainer
    drbd_helper = self.cfg.GetDRBDHelper()
3034 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
3035 7352d33b Thomas Thrainer
    hypervisors = cluster.enabled_hypervisors
3036 1c3231aa Thomas Thrainer
    node_data_list = self.my_node_info.values()
3037 7352d33b Thomas Thrainer
3038 7352d33b Thomas Thrainer
    i_non_redundant = [] # Non redundant instances
3039 7352d33b Thomas Thrainer
    i_non_a_balanced = [] # Non auto-balanced instances
3040 7352d33b Thomas Thrainer
    i_offline = 0 # Count of offline instances
3041 7352d33b Thomas Thrainer
    n_offline = 0 # Count of offline nodes
3042 7352d33b Thomas Thrainer
    n_drained = 0 # Count of nodes being drained
3043 7352d33b Thomas Thrainer
    node_vol_should = {}
3044 7352d33b Thomas Thrainer
3045 7352d33b Thomas Thrainer
    # FIXME: verify OS list
3046 7352d33b Thomas Thrainer
3047 7352d33b Thomas Thrainer
    # File verification
3048 5eacbcae Thomas Thrainer
    filemap = ComputeAncillaryFiles(cluster, False)
3049 7352d33b Thomas Thrainer
3050 7352d33b Thomas Thrainer
    # do local checksums
3051 1c3231aa Thomas Thrainer
    master_node_uuid = self.master_node = self.cfg.GetMasterNode()
3052 7352d33b Thomas Thrainer
    master_ip = self.cfg.GetMasterIP()
3053 7352d33b Thomas Thrainer
3054 1c3231aa Thomas Thrainer
    feedback_fn("* Gathering data (%d nodes)" % len(self.my_node_uuids))
3055 7352d33b Thomas Thrainer
3056 7352d33b Thomas Thrainer
    user_scripts = []
3057 7352d33b Thomas Thrainer
    if self.cfg.GetUseExternalMipScript():
3058 7352d33b Thomas Thrainer
      user_scripts.append(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
3059 7352d33b Thomas Thrainer
3060 7352d33b Thomas Thrainer
    node_verify_param = {
3061 7352d33b Thomas Thrainer
      constants.NV_FILELIST:
3062 7352d33b Thomas Thrainer
        map(vcluster.MakeVirtualPath,
3063 7352d33b Thomas Thrainer
            utils.UniqueSequence(filename
3064 7352d33b Thomas Thrainer
                                 for files in filemap
3065 7352d33b Thomas Thrainer
                                 for filename in files)),
3066 7352d33b Thomas Thrainer
      constants.NV_NODELIST:
3067 7352d33b Thomas Thrainer
        self._SelectSshCheckNodes(node_data_list, self.group_uuid,
3068 7352d33b Thomas Thrainer
                                  self.all_node_info.values()),
3069 7352d33b Thomas Thrainer
      constants.NV_HYPERVISOR: hypervisors,
3070 7352d33b Thomas Thrainer
      constants.NV_HVPARAMS:
3071 7352d33b Thomas Thrainer
        _GetAllHypervisorParameters(cluster, self.all_inst_info.values()),
3072 7352d33b Thomas Thrainer
      constants.NV_NODENETTEST: [(node.name, node.primary_ip, node.secondary_ip)
3073 7352d33b Thomas Thrainer
                                 for node in node_data_list
3074 7352d33b Thomas Thrainer
                                 if not node.offline],
3075 7352d33b Thomas Thrainer
      constants.NV_INSTANCELIST: hypervisors,
3076 7352d33b Thomas Thrainer
      constants.NV_VERSION: None,
3077 7352d33b Thomas Thrainer
      constants.NV_HVINFO: self.cfg.GetHypervisorType(),
3078 7352d33b Thomas Thrainer
      constants.NV_NODESETUP: None,
3079 7352d33b Thomas Thrainer
      constants.NV_TIME: None,
3080 1c3231aa Thomas Thrainer
      constants.NV_MASTERIP: (self.cfg.GetMasterNodeName(), master_ip),
3081 7352d33b Thomas Thrainer
      constants.NV_OSLIST: None,
3082 7352d33b Thomas Thrainer
      constants.NV_VMNODES: self.cfg.GetNonVmCapableNodeList(),
3083 7352d33b Thomas Thrainer
      constants.NV_USERSCRIPTS: user_scripts,
3084 a6c43c02 Helga Velroyen
      constants.NV_CLIENT_CERT: None,
3085 7352d33b Thomas Thrainer
      }
3086 7352d33b Thomas Thrainer
3087 7352d33b Thomas Thrainer
    if vg_name is not None:
3088 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_VGLIST] = None
3089 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_LVLIST] = vg_name
3090 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_PVLIST] = [vg_name]
3091 7352d33b Thomas Thrainer
3092 9af7ece3 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(constants.DT_DRBD8):
3093 9af7ece3 Helga Velroyen
      if drbd_helper:
3094 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDVERSION] = None
3095 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDLIST] = None
3096 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDHELPER] = drbd_helper
3097 7352d33b Thomas Thrainer
3098 850c53f1 Helga Velroyen
    if cluster.IsFileStorageEnabled() or \
3099 850c53f1 Helga Velroyen
        cluster.IsSharedFileStorageEnabled():
3100 7352d33b Thomas Thrainer
      # Load file storage paths only from master node
3101 13a6c760 Helga Velroyen
      node_verify_param[constants.NV_ACCEPTED_STORAGE_PATHS] = \
3102 1c3231aa Thomas Thrainer
        self.cfg.GetMasterNodeName()
3103 13a6c760 Helga Velroyen
      if cluster.IsFileStorageEnabled():
3104 13a6c760 Helga Velroyen
        node_verify_param[constants.NV_FILE_STORAGE_PATH] = \
3105 13a6c760 Helga Velroyen
          cluster.file_storage_dir
3106 7352d33b Thomas Thrainer
3107 7352d33b Thomas Thrainer
    # bridge checks
3108 7352d33b Thomas Thrainer
    # FIXME: this needs to be changed per node-group, not cluster-wide
3109 7352d33b Thomas Thrainer
    bridges = set()
3110 7352d33b Thomas Thrainer
    default_nicpp = cluster.nicparams[constants.PP_DEFAULT]
3111 7352d33b Thomas Thrainer
    if default_nicpp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
3112 7352d33b Thomas Thrainer
      bridges.add(default_nicpp[constants.NIC_LINK])
3113 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_info.values():
3114 da4a52a3 Thomas Thrainer
      for nic in inst_uuid.nics:
3115 7352d33b Thomas Thrainer
        full_nic = cluster.SimpleFillNIC(nic.nicparams)
3116 7352d33b Thomas Thrainer
        if full_nic[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
3117 7352d33b Thomas Thrainer
          bridges.add(full_nic[constants.NIC_LINK])
3118 7352d33b Thomas Thrainer
3119 7352d33b Thomas Thrainer
    if bridges:
3120 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_BRIDGES] = list(bridges)
3121 7352d33b Thomas Thrainer
3122 7352d33b Thomas Thrainer
    # Build our expected cluster state
3123 1c3231aa Thomas Thrainer
    node_image = dict((node.uuid, self.NodeImage(offline=node.offline,
3124 1c3231aa Thomas Thrainer
                                                 uuid=node.uuid,
3125 7352d33b Thomas Thrainer
                                                 vm_capable=node.vm_capable))
3126 7352d33b Thomas Thrainer
                      for node in node_data_list)
3127 7352d33b Thomas Thrainer
3128 7352d33b Thomas Thrainer
    # Gather OOB paths
3129 7352d33b Thomas Thrainer
    oob_paths = []
3130 7352d33b Thomas Thrainer
    for node in self.all_node_info.values():
3131 5eacbcae Thomas Thrainer
      path = SupportsOob(self.cfg, node)
3132 7352d33b Thomas Thrainer
      if path and path not in oob_paths:
3133 7352d33b Thomas Thrainer
        oob_paths.append(path)
3134 7352d33b Thomas Thrainer
3135 7352d33b Thomas Thrainer
    if oob_paths:
3136 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_OOB_PATHS] = oob_paths
3137 7352d33b Thomas Thrainer
3138 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
3139 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
3140 da4a52a3 Thomas Thrainer
      if instance.admin_state == constants.ADMINST_OFFLINE:
3141 7352d33b Thomas Thrainer
        i_offline += 1
3142 7352d33b Thomas Thrainer
3143 da4a52a3 Thomas Thrainer
      for nuuid in instance.all_nodes:
3144 1c3231aa Thomas Thrainer
        if nuuid not in node_image:
3145 1c3231aa Thomas Thrainer
          gnode = self.NodeImage(uuid=nuuid)
3146 1c3231aa Thomas Thrainer
          gnode.ghost = (nuuid not in self.all_node_info)
3147 1c3231aa Thomas Thrainer
          node_image[nuuid] = gnode
3148 7352d33b Thomas Thrainer
3149 da4a52a3 Thomas Thrainer
      instance.MapLVsByNode(node_vol_should)
3150 7352d33b Thomas Thrainer
3151 da4a52a3 Thomas Thrainer
      pnode = instance.primary_node
3152 da4a52a3 Thomas Thrainer
      node_image[pnode].pinst.append(instance.uuid)
3153 7352d33b Thomas Thrainer
3154 da4a52a3 Thomas Thrainer
      for snode in instance.secondary_nodes:
3155 7352d33b Thomas Thrainer
        nimg = node_image[snode]
3156 da4a52a3 Thomas Thrainer
        nimg.sinst.append(instance.uuid)
3157 7352d33b Thomas Thrainer
        if pnode not in nimg.sbp:
3158 7352d33b Thomas Thrainer
          nimg.sbp[pnode] = []
3159 da4a52a3 Thomas Thrainer
        nimg.sbp[pnode].append(instance.uuid)
3160 7352d33b Thomas Thrainer
3161 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
3162 1c3231aa Thomas Thrainer
                                               self.my_node_info.keys())
3163 7352d33b Thomas Thrainer
    # The value of exclusive_storage should be the same across the group, so if
3164 7352d33b Thomas Thrainer
    # it's True for at least a node, we act as if it were set for all the nodes
3165 7352d33b Thomas Thrainer
    self._exclusive_storage = compat.any(es_flags.values())
3166 7352d33b Thomas Thrainer
    if self._exclusive_storage:
3167 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_EXCLUSIVEPVS] = True
3168 7352d33b Thomas Thrainer
3169 a9f33339 Petr Pudlak
    node_group_uuids = dict(map(lambda n: (n.name, n.group),
3170 a9f33339 Petr Pudlak
                                self.cfg.GetAllNodesInfo().values()))
3171 a9f33339 Petr Pudlak
    groups_config = self.cfg.GetAllNodeGroupsInfoDict()
3172 a9f33339 Petr Pudlak
3173 7352d33b Thomas Thrainer
    # At this point, we have the in-memory data structures complete,
3174 7352d33b Thomas Thrainer
    # except for the runtime information, which we'll gather next
3175 7352d33b Thomas Thrainer
3176 7352d33b Thomas Thrainer
    # Due to the way our RPC system works, exact response times cannot be
3177 7352d33b Thomas Thrainer
    # guaranteed (e.g. a broken node could run into a timeout). By keeping the
3178 7352d33b Thomas Thrainer
    # time before and after executing the request, we can at least have a time
3179 7352d33b Thomas Thrainer
    # window.
3180 7352d33b Thomas Thrainer
    nvinfo_starttime = time.time()
3181 1c3231aa Thomas Thrainer
    all_nvinfo = self.rpc.call_node_verify(self.my_node_uuids,
3182 7352d33b Thomas Thrainer
                                           node_verify_param,
3183 5b0dfcef Helga Velroyen
                                           self.cfg.GetClusterName(),
3184 a9f33339 Petr Pudlak
                                           self.cfg.GetClusterInfo().hvparams,
3185 a9f33339 Petr Pudlak
                                           node_group_uuids,
3186 a9f33339 Petr Pudlak
                                           groups_config)
3187 7352d33b Thomas Thrainer
    nvinfo_endtime = time.time()
3188 7352d33b Thomas Thrainer
3189 7352d33b Thomas Thrainer
    if self.extra_lv_nodes and vg_name is not None:
3190 7352d33b Thomas Thrainer
      extra_lv_nvinfo = \
3191 7352d33b Thomas Thrainer
          self.rpc.call_node_verify(self.extra_lv_nodes,
3192 7352d33b Thomas Thrainer
                                    {constants.NV_LVLIST: vg_name},
3193 5b0dfcef Helga Velroyen
                                    self.cfg.GetClusterName(),
3194 a9f33339 Petr Pudlak
                                    self.cfg.GetClusterInfo().hvparams,
3195 a9f33339 Petr Pudlak
                                    node_group_uuids,
3196 a9f33339 Petr Pudlak
                                    groups_config)
3197 7352d33b Thomas Thrainer
    else:
3198 7352d33b Thomas Thrainer
      extra_lv_nvinfo = {}
3199 7352d33b Thomas Thrainer
3200 7352d33b Thomas Thrainer
    all_drbd_map = self.cfg.ComputeDRBDMap()
3201 7352d33b Thomas Thrainer
3202 7352d33b Thomas Thrainer
    feedback_fn("* Gathering disk information (%s nodes)" %
3203 1c3231aa Thomas Thrainer
                len(self.my_node_uuids))
3204 1c3231aa Thomas Thrainer
    instdisk = self._CollectDiskInfo(self.my_node_info.keys(), node_image,
3205 7352d33b Thomas Thrainer
                                     self.my_inst_info)
3206 7352d33b Thomas Thrainer
3207 7352d33b Thomas Thrainer
    feedback_fn("* Verifying configuration file consistency")
3208 7352d33b Thomas Thrainer
3209 d5104ca4 Helga Velroyen
    self._VerifyClientCertificates(self.my_node_info.values(), all_nvinfo)
3210 7352d33b Thomas Thrainer
    # If not all nodes are being checked, we need to make sure the master node
3211 7352d33b Thomas Thrainer
    # and a non-checked vm_capable node are in the list.
3212 1c3231aa Thomas Thrainer
    absent_node_uuids = set(self.all_node_info).difference(self.my_node_info)
3213 1c3231aa Thomas Thrainer
    if absent_node_uuids:
3214 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo.copy()
3215 7352d33b Thomas Thrainer
      vf_node_info = list(self.my_node_info.values())
3216 1c3231aa Thomas Thrainer
      additional_node_uuids = []
3217 1c3231aa Thomas Thrainer
      if master_node_uuid not in self.my_node_info:
3218 1c3231aa Thomas Thrainer
        additional_node_uuids.append(master_node_uuid)
3219 1c3231aa Thomas Thrainer
        vf_node_info.append(self.all_node_info[master_node_uuid])
3220 7352d33b Thomas Thrainer
      # Add the first vm_capable node we find which is not included,
3221 7352d33b Thomas Thrainer
      # excluding the master node (which we already have)
3222 1c3231aa Thomas Thrainer
      for node_uuid in absent_node_uuids:
3223 1c3231aa Thomas Thrainer
        nodeinfo = self.all_node_info[node_uuid]
3224 7352d33b Thomas Thrainer
        if (nodeinfo.vm_capable and not nodeinfo.offline and
3225 1c3231aa Thomas Thrainer
            node_uuid != master_node_uuid):
3226 1c3231aa Thomas Thrainer
          additional_node_uuids.append(node_uuid)
3227 1c3231aa Thomas Thrainer
          vf_node_info.append(self.all_node_info[node_uuid])
3228 7352d33b Thomas Thrainer
          break
3229 7352d33b Thomas Thrainer
      key = constants.NV_FILELIST
3230 5b0dfcef Helga Velroyen
      vf_nvinfo.update(self.rpc.call_node_verify(
3231 1c3231aa Thomas Thrainer
         additional_node_uuids, {key: node_verify_param[key]},
3232 a9f33339 Petr Pudlak
         self.cfg.GetClusterName(), self.cfg.GetClusterInfo().hvparams,
3233 a9f33339 Petr Pudlak
         node_group_uuids,
3234 a9f33339 Petr Pudlak
         groups_config))
3235 7352d33b Thomas Thrainer
    else:
3236 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo
3237 7352d33b Thomas Thrainer
      vf_node_info = self.my_node_info.values()
3238 7352d33b Thomas Thrainer
3239 1c3231aa Thomas Thrainer
    self._VerifyFiles(vf_node_info, master_node_uuid, vf_nvinfo, filemap)
3240 7352d33b Thomas Thrainer
3241 7352d33b Thomas Thrainer
    feedback_fn("* Verifying node status")
3242 7352d33b Thomas Thrainer
3243 7352d33b Thomas Thrainer
    refos_img = None
3244 7352d33b Thomas Thrainer
3245 7352d33b Thomas Thrainer
    for node_i in node_data_list:
3246 1c3231aa Thomas Thrainer
      nimg = node_image[node_i.uuid]
3247 7352d33b Thomas Thrainer
3248 7352d33b Thomas Thrainer
      if node_i.offline:
3249 7352d33b Thomas Thrainer
        if verbose:
3250 1c3231aa Thomas Thrainer
          feedback_fn("* Skipping offline node %s" % (node_i.name,))
3251 7352d33b Thomas Thrainer
        n_offline += 1
3252 7352d33b Thomas Thrainer
        continue
3253 7352d33b Thomas Thrainer
3254 1c3231aa Thomas Thrainer
      if node_i.uuid == master_node_uuid:
3255 7352d33b Thomas Thrainer
        ntype = "master"
3256 7352d33b Thomas Thrainer
      elif node_i.master_candidate:
3257 7352d33b Thomas Thrainer
        ntype = "master candidate"
3258 7352d33b Thomas Thrainer
      elif node_i.drained:
3259 7352d33b Thomas Thrainer
        ntype = "drained"
3260 7352d33b Thomas Thrainer
        n_drained += 1
3261 7352d33b Thomas Thrainer
      else:
3262 7352d33b Thomas Thrainer
        ntype = "regular"
3263 7352d33b Thomas Thrainer
      if verbose:
3264 1c3231aa Thomas Thrainer
        feedback_fn("* Verifying node %s (%s)" % (node_i.name, ntype))
3265 7352d33b Thomas Thrainer
3266 1c3231aa Thomas Thrainer
      msg = all_nvinfo[node_i.uuid].fail_msg
3267 d0d7d7cf Thomas Thrainer
      self._ErrorIf(msg, constants.CV_ENODERPC, node_i.name,
3268 d0d7d7cf Thomas Thrainer
                    "while contacting node: %s", msg)
3269 7352d33b Thomas Thrainer
      if msg:
3270 7352d33b Thomas Thrainer
        nimg.rpc_fail = True
3271 7352d33b Thomas Thrainer
        continue
3272 7352d33b Thomas Thrainer
3273 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node_i.uuid].payload
3274 7352d33b Thomas Thrainer
3275 7352d33b Thomas Thrainer
      nimg.call_ok = self._VerifyNode(node_i, nresult)
3276 7352d33b Thomas Thrainer
      self._VerifyNodeTime(node_i, nresult, nvinfo_starttime, nvinfo_endtime)
3277 7352d33b Thomas Thrainer
      self._VerifyNodeNetwork(node_i, nresult)
3278 7352d33b Thomas Thrainer
      self._VerifyNodeUserScripts(node_i, nresult)
3279 7352d33b Thomas Thrainer
      self._VerifyOob(node_i, nresult)
3280 13a6c760 Helga Velroyen
      self._VerifyAcceptedFileStoragePaths(node_i, nresult,
3281 13a6c760 Helga Velroyen
                                           node_i.uuid == master_node_uuid)
3282 4b322a76 Helga Velroyen
      self._VerifyFileStoragePaths(node_i, nresult)
3283 4b322a76 Helga Velroyen
      self._VerifySharedFileStoragePaths(node_i, nresult)
3284 7352d33b Thomas Thrainer
3285 7352d33b Thomas Thrainer
      if nimg.vm_capable:
3286 7352d33b Thomas Thrainer
        self._UpdateVerifyNodeLVM(node_i, nresult, vg_name, nimg)
3287 7352d33b Thomas Thrainer
        self._VerifyNodeDrbd(node_i, nresult, self.all_inst_info, drbd_helper,
3288 7352d33b Thomas Thrainer
                             all_drbd_map)
3289 7352d33b Thomas Thrainer
3290 7352d33b Thomas Thrainer
        self._UpdateNodeVolumes(node_i, nresult, nimg, vg_name)
3291 7352d33b Thomas Thrainer
        self._UpdateNodeInstances(node_i, nresult, nimg)
3292 7352d33b Thomas Thrainer
        self._UpdateNodeInfo(node_i, nresult, nimg, vg_name)
3293 7352d33b Thomas Thrainer
        self._UpdateNodeOS(node_i, nresult, nimg)
3294 7352d33b Thomas Thrainer
3295 7352d33b Thomas Thrainer
        if not nimg.os_fail:
3296 7352d33b Thomas Thrainer
          if refos_img is None:
3297 7352d33b Thomas Thrainer
            refos_img = nimg
3298 7352d33b Thomas Thrainer
          self._VerifyNodeOS(node_i, nimg, refos_img)
3299 7352d33b Thomas Thrainer
        self._VerifyNodeBridges(node_i, nresult, bridges)
3300 7352d33b Thomas Thrainer
3301 da4a52a3 Thomas Thrainer
        # Check whether all running instances are primary for the node. (This
3302 7352d33b Thomas Thrainer
        # can no longer be done from _VerifyInstance below, since some of the
3303 7352d33b Thomas Thrainer
        # wrong instances could be from other node groups.)
3304 da4a52a3 Thomas Thrainer
        non_primary_inst_uuids = set(nimg.instances).difference(nimg.pinst)
3305 7352d33b Thomas Thrainer
3306 da4a52a3 Thomas Thrainer
        for inst_uuid in non_primary_inst_uuids:
3307 da4a52a3 Thomas Thrainer
          test = inst_uuid in self.all_inst_info
3308 da4a52a3 Thomas Thrainer
          self._ErrorIf(test, constants.CV_EINSTANCEWRONGNODE,
3309 da4a52a3 Thomas Thrainer
                        self.cfg.GetInstanceName(inst_uuid),
3310 d0d7d7cf Thomas Thrainer
                        "instance should not run on node %s", node_i.name)
3311 d0d7d7cf Thomas Thrainer
          self._ErrorIf(not test, constants.CV_ENODEORPHANINSTANCE, node_i.name,
3312 da4a52a3 Thomas Thrainer
                        "node is running unknown instance %s", inst_uuid)
3313 7352d33b Thomas Thrainer
3314 1bb99a33 Bernardo Dal Seno
    self._VerifyGroupDRBDVersion(all_nvinfo)
3315 7352d33b Thomas Thrainer
    self._VerifyGroupLVM(node_image, vg_name)
3316 7352d33b Thomas Thrainer
3317 1c3231aa Thomas Thrainer
    for node_uuid, result in extra_lv_nvinfo.items():
3318 1c3231aa Thomas Thrainer
      self._UpdateNodeVolumes(self.all_node_info[node_uuid], result.payload,
3319 1c3231aa Thomas Thrainer
                              node_image[node_uuid], vg_name)
3320 7352d33b Thomas Thrainer
3321 7352d33b Thomas Thrainer
    feedback_fn("* Verifying instance status")
3322 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
3323 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
3324 7352d33b Thomas Thrainer
      if verbose:
3325 da4a52a3 Thomas Thrainer
        feedback_fn("* Verifying instance %s" % instance.name)
3326 da4a52a3 Thomas Thrainer
      self._VerifyInstance(instance, node_image, instdisk[inst_uuid])
3327 7352d33b Thomas Thrainer
3328 7352d33b Thomas Thrainer
      # If the instance is non-redundant we cannot survive losing its primary
3329 7352d33b Thomas Thrainer
      # node, so we are not N+1 compliant.
3330 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_MIRRORED:
3331 7352d33b Thomas Thrainer
        i_non_redundant.append(instance)
3332 7352d33b Thomas Thrainer
3333 da4a52a3 Thomas Thrainer
      if not cluster.FillBE(instance)[constants.BE_AUTO_BALANCE]:
3334 7352d33b Thomas Thrainer
        i_non_a_balanced.append(instance)
3335 7352d33b Thomas Thrainer
3336 7352d33b Thomas Thrainer
    feedback_fn("* Verifying orphan volumes")
3337 7352d33b Thomas Thrainer
    reserved = utils.FieldSet(*cluster.reserved_lvs)
3338 7352d33b Thomas Thrainer
3339 7352d33b Thomas Thrainer
    # We will get spurious "unknown volume" warnings if any node of this group
3340 7352d33b Thomas Thrainer
    # is secondary for an instance whose primary is in another group. To avoid
3341 7352d33b Thomas Thrainer
    # them, we find these instances and add their volumes to node_vol_should.
3342 da4a52a3 Thomas Thrainer
    for instance in self.all_inst_info.values():
3343 da4a52a3 Thomas Thrainer
      for secondary in instance.secondary_nodes:
3344 7352d33b Thomas Thrainer
        if (secondary in self.my_node_info
3345 da4a52a3 Thomas Thrainer
            and instance.name not in self.my_inst_info):
3346 da4a52a3 Thomas Thrainer
          instance.MapLVsByNode(node_vol_should)
3347 7352d33b Thomas Thrainer
          break
3348 7352d33b Thomas Thrainer
3349 7352d33b Thomas Thrainer
    self._VerifyOrphanVolumes(node_vol_should, node_image, reserved)
3350 7352d33b Thomas Thrainer
3351 7352d33b Thomas Thrainer
    if constants.VERIFY_NPLUSONE_MEM not in self.op.skip_checks:
3352 7352d33b Thomas Thrainer
      feedback_fn("* Verifying N+1 Memory redundancy")
3353 7352d33b Thomas Thrainer
      self._VerifyNPlusOneMemory(node_image, self.my_inst_info)
3354 7352d33b Thomas Thrainer
3355 7352d33b Thomas Thrainer
    feedback_fn("* Other Notes")
3356 7352d33b Thomas Thrainer
    if i_non_redundant:
3357 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-redundant instance(s) found."
3358 7352d33b Thomas Thrainer
                  % len(i_non_redundant))
3359 7352d33b Thomas Thrainer
3360 7352d33b Thomas Thrainer
    if i_non_a_balanced:
3361 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-auto-balanced instance(s) found."
3362 7352d33b Thomas Thrainer
                  % len(i_non_a_balanced))
3363 7352d33b Thomas Thrainer
3364 7352d33b Thomas Thrainer
    if i_offline:
3365 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline instance(s) found." % i_offline)
3366 7352d33b Thomas Thrainer
3367 7352d33b Thomas Thrainer
    if n_offline:
3368 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline node(s) found." % n_offline)
3369 7352d33b Thomas Thrainer
3370 7352d33b Thomas Thrainer
    if n_drained:
3371 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d drained node(s) found." % n_drained)
3372 7352d33b Thomas Thrainer
3373 7352d33b Thomas Thrainer
    return not self.bad
3374 7352d33b Thomas Thrainer
3375 7352d33b Thomas Thrainer
  def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
3376 7352d33b Thomas Thrainer
    """Analyze the post-hooks' result
3377 7352d33b Thomas Thrainer

3378 7352d33b Thomas Thrainer
    This method analyses the hook result, handles it, and sends some
3379 7352d33b Thomas Thrainer
    nicely-formatted feedback back to the user.
3380 7352d33b Thomas Thrainer

3381 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
3382 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
3383 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
3384 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
3385 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
3386 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
3387 7352d33b Thomas Thrainer
        and hook results
3388 7352d33b Thomas Thrainer

3389 7352d33b Thomas Thrainer
    """
3390 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
3391 7352d33b Thomas Thrainer
    # and are only interested in their results
3392 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3393 7352d33b Thomas Thrainer
      # empty node group
3394 7352d33b Thomas Thrainer
      pass
3395 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
3396 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
3397 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
3398 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
3399 7352d33b Thomas Thrainer
3400 7352d33b Thomas Thrainer
      for node_name in hooks_results:
3401 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
3402 7352d33b Thomas Thrainer
        msg = res.fail_msg
3403 7352d33b Thomas Thrainer
        test = msg and not res.offline
3404 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3405 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
3406 7352d33b Thomas Thrainer
        if res.offline or msg:
3407 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
3408 7352d33b Thomas Thrainer
          # an error.
3409 7352d33b Thomas Thrainer
          continue
3410 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
3411 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
3412 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3413 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
3414 7352d33b Thomas Thrainer
          if test:
3415 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3416 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3417 7352d33b Thomas Thrainer
            lu_result = False
3418 7352d33b Thomas Thrainer
3419 7352d33b Thomas Thrainer
    return lu_result
3420 7352d33b Thomas Thrainer
3421 7352d33b Thomas Thrainer
3422 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3423 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3424 7352d33b Thomas Thrainer

3425 7352d33b Thomas Thrainer
  """
3426 7352d33b Thomas Thrainer
  REQ_BGL = False
3427 7352d33b Thomas Thrainer
3428 7352d33b Thomas Thrainer
  def ExpandNames(self):
3429 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3430 7352d33b Thomas Thrainer
    self.needed_locks = {
3431 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3432 7352d33b Thomas Thrainer
      }
3433 7352d33b Thomas Thrainer
3434 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3435 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3436 7352d33b Thomas Thrainer
3437 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3438 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3439 7352d33b Thomas Thrainer
                           for group in group_names])