Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ 11eeb1b9

History | View | Annotate | Download (131 kB)

1 7352d33b Thomas Thrainer
#
2 7352d33b Thomas Thrainer
#
3 7352d33b Thomas Thrainer
4 8a5d326f Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 b3cc1646 Helga Velroyen
    new_master_digest = _UpdateMasterClientCert(self, master_uuid, cluster,
115 b3cc1646 Helga Velroyen
                                                feedback_fn)
116 b3cc1646 Helga Velroyen
117 b3cc1646 Helga Velroyen
    cluster.candidate_certs = {master_uuid: new_master_digest}
118 b3cc1646 Helga Velroyen
    nodes = self.cfg.GetAllNodesInfo()
119 b3cc1646 Helga Velroyen
    for (node_uuid, node_info) in nodes.items():
120 b3cc1646 Helga Velroyen
      if node_uuid != master_uuid:
121 b3cc1646 Helga Velroyen
        new_digest = CreateNewClientCert(self, node_uuid)
122 b3cc1646 Helga Velroyen
        if node_info.master_candidate:
123 b3cc1646 Helga Velroyen
          cluster.candidate_certs[node_uuid] = new_digest
124 b3cc1646 Helga Velroyen
    # Trigger another update of the config now with the new master cert
125 b3cc1646 Helga Velroyen
    self.cfg.Update(cluster, feedback_fn)
126 b3cc1646 Helga Velroyen
127 b3cc1646 Helga Velroyen
128 7352d33b Thomas Thrainer
class LUClusterActivateMasterIp(NoHooksLU):
129 7352d33b Thomas Thrainer
  """Activate the master IP on the master node.
130 7352d33b Thomas Thrainer

131 7352d33b Thomas Thrainer
  """
132 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
133 7352d33b Thomas Thrainer
    """Activate the master IP.
134 7352d33b Thomas Thrainer

135 7352d33b Thomas Thrainer
    """
136 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
137 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
138 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_activate_master_ip(master_params.uuid,
139 7352d33b Thomas Thrainer
                                                   master_params, ems)
140 7352d33b Thomas Thrainer
    result.Raise("Could not activate the master IP")
141 7352d33b Thomas Thrainer
142 7352d33b Thomas Thrainer
143 7352d33b Thomas Thrainer
class LUClusterDeactivateMasterIp(NoHooksLU):
144 7352d33b Thomas Thrainer
  """Deactivate the master IP on the master node.
145 7352d33b Thomas Thrainer

146 7352d33b Thomas Thrainer
  """
147 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
148 7352d33b Thomas Thrainer
    """Deactivate the master IP.
149 7352d33b Thomas Thrainer

150 7352d33b Thomas Thrainer
    """
151 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
152 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
153 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
154 7352d33b Thomas Thrainer
                                                     master_params, ems)
155 7352d33b Thomas Thrainer
    result.Raise("Could not deactivate the master IP")
156 7352d33b Thomas Thrainer
157 7352d33b Thomas Thrainer
158 7352d33b Thomas Thrainer
class LUClusterConfigQuery(NoHooksLU):
159 7352d33b Thomas Thrainer
  """Return configuration values.
160 7352d33b Thomas Thrainer

161 7352d33b Thomas Thrainer
  """
162 7352d33b Thomas Thrainer
  REQ_BGL = False
163 7352d33b Thomas Thrainer
164 7352d33b Thomas Thrainer
  def CheckArguments(self):
165 5eacbcae Thomas Thrainer
    self.cq = ClusterQuery(None, self.op.output_fields, False)
166 7352d33b Thomas Thrainer
167 7352d33b Thomas Thrainer
  def ExpandNames(self):
168 7352d33b Thomas Thrainer
    self.cq.ExpandNames(self)
169 7352d33b Thomas Thrainer
170 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
171 7352d33b Thomas Thrainer
    self.cq.DeclareLocks(self, level)
172 7352d33b Thomas Thrainer
173 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
174 7352d33b Thomas Thrainer
    result = self.cq.OldStyleQuery(self)
175 7352d33b Thomas Thrainer
176 7352d33b Thomas Thrainer
    assert len(result) == 1
177 7352d33b Thomas Thrainer
178 7352d33b Thomas Thrainer
    return result[0]
179 7352d33b Thomas Thrainer
180 7352d33b Thomas Thrainer
181 7352d33b Thomas Thrainer
class LUClusterDestroy(LogicalUnit):
182 7352d33b Thomas Thrainer
  """Logical unit for destroying the cluster.
183 7352d33b Thomas Thrainer

184 7352d33b Thomas Thrainer
  """
185 7352d33b Thomas Thrainer
  HPATH = "cluster-destroy"
186 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
187 7352d33b Thomas Thrainer
188 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
189 7352d33b Thomas Thrainer
    """Build hooks env.
190 7352d33b Thomas Thrainer

191 7352d33b Thomas Thrainer
    """
192 7352d33b Thomas Thrainer
    return {
193 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
194 7352d33b Thomas Thrainer
      }
195 7352d33b Thomas Thrainer
196 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
197 7352d33b Thomas Thrainer
    """Build hooks nodes.
198 7352d33b Thomas Thrainer

199 7352d33b Thomas Thrainer
    """
200 7352d33b Thomas Thrainer
    return ([], [])
201 7352d33b Thomas Thrainer
202 7352d33b Thomas Thrainer
  def CheckPrereq(self):
203 7352d33b Thomas Thrainer
    """Check prerequisites.
204 7352d33b Thomas Thrainer

205 7352d33b Thomas Thrainer
    This checks whether the cluster is empty.
206 7352d33b Thomas Thrainer

207 7352d33b Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
208 7352d33b Thomas Thrainer

209 7352d33b Thomas Thrainer
    """
210 7352d33b Thomas Thrainer
    master = self.cfg.GetMasterNode()
211 7352d33b Thomas Thrainer
212 7352d33b Thomas Thrainer
    nodelist = self.cfg.GetNodeList()
213 7352d33b Thomas Thrainer
    if len(nodelist) != 1 or nodelist[0] != master:
214 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d node(s) in"
215 7352d33b Thomas Thrainer
                                 " this cluster." % (len(nodelist) - 1),
216 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
217 7352d33b Thomas Thrainer
    instancelist = self.cfg.GetInstanceList()
218 7352d33b Thomas Thrainer
    if instancelist:
219 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d instance(s) in"
220 7352d33b Thomas Thrainer
                                 " this cluster." % len(instancelist),
221 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
222 7352d33b Thomas Thrainer
223 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
224 7352d33b Thomas Thrainer
    """Destroys the cluster.
225 7352d33b Thomas Thrainer

226 7352d33b Thomas Thrainer
    """
227 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
228 7352d33b Thomas Thrainer
229 7352d33b Thomas Thrainer
    # Run post hooks on master node before it's removed
230 1c3231aa Thomas Thrainer
    RunPostHook(self, self.cfg.GetNodeName(master_params.uuid))
231 7352d33b Thomas Thrainer
232 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
233 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
234 7352d33b Thomas Thrainer
                                                     master_params, ems)
235 c7dd65be Klaus Aehlig
    result.Warn("Error disabling the master IP address", self.LogWarning)
236 1c3231aa Thomas Thrainer
    return master_params.uuid
237 7352d33b Thomas Thrainer
238 7352d33b Thomas Thrainer
239 7352d33b Thomas Thrainer
class LUClusterPostInit(LogicalUnit):
240 7352d33b Thomas Thrainer
  """Logical unit for running hooks after cluster initialization.
241 7352d33b Thomas Thrainer

242 7352d33b Thomas Thrainer
  """
243 7352d33b Thomas Thrainer
  HPATH = "cluster-init"
244 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
245 7352d33b Thomas Thrainer
246 25ea5b98 Sebastian Gebhard
  def CheckArguments(self):
247 25ea5b98 Sebastian Gebhard
    self.master_uuid = self.cfg.GetMasterNode()
248 25ea5b98 Sebastian Gebhard
    self.master_ndparams = self.cfg.GetNdParams(self.cfg.GetMasterNodeInfo())
249 25ea5b98 Sebastian Gebhard
250 25ea5b98 Sebastian Gebhard
    # TODO: When Issue 584 is solved, and None is properly parsed when used
251 25ea5b98 Sebastian Gebhard
    # as a default value, ndparams.get(.., None) can be changed to
252 25ea5b98 Sebastian Gebhard
    # ndparams[..] to access the values directly
253 25ea5b98 Sebastian Gebhard
254 25ea5b98 Sebastian Gebhard
    # OpenvSwitch: Warn user if link is missing
255 25ea5b98 Sebastian Gebhard
    if (self.master_ndparams[constants.ND_OVS] and not
256 25ea5b98 Sebastian Gebhard
        self.master_ndparams.get(constants.ND_OVS_LINK, None)):
257 25ea5b98 Sebastian Gebhard
      self.LogInfo("No physical interface for OpenvSwitch was given."
258 25ea5b98 Sebastian Gebhard
                   " OpenvSwitch will not have an outside connection. This"
259 25ea5b98 Sebastian Gebhard
                   " might not be what you want.")
260 25ea5b98 Sebastian Gebhard
261 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
262 7352d33b Thomas Thrainer
    """Build hooks env.
263 7352d33b Thomas Thrainer

264 7352d33b Thomas Thrainer
    """
265 7352d33b Thomas Thrainer
    return {
266 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
267 7352d33b Thomas Thrainer
      }
268 7352d33b Thomas Thrainer
269 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
270 7352d33b Thomas Thrainer
    """Build hooks nodes.
271 7352d33b Thomas Thrainer

272 7352d33b Thomas Thrainer
    """
273 7352d33b Thomas Thrainer
    return ([], [self.cfg.GetMasterNode()])
274 7352d33b Thomas Thrainer
275 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
276 25ea5b98 Sebastian Gebhard
    """Create and configure Open vSwitch
277 7352d33b Thomas Thrainer

278 7352d33b Thomas Thrainer
    """
279 25ea5b98 Sebastian Gebhard
    if self.master_ndparams[constants.ND_OVS]:
280 25ea5b98 Sebastian Gebhard
      result = self.rpc.call_node_configure_ovs(
281 25ea5b98 Sebastian Gebhard
                 self.master_uuid,
282 25ea5b98 Sebastian Gebhard
                 self.master_ndparams[constants.ND_OVS_NAME],
283 25ea5b98 Sebastian Gebhard
                 self.master_ndparams.get(constants.ND_OVS_LINK, None))
284 25ea5b98 Sebastian Gebhard
      result.Raise("Could not successully configure Open vSwitch")
285 5b6f9e35 Helga Velroyen
286 b3cc1646 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
287 b3cc1646 Helga Velroyen
    _UpdateMasterClientCert(self, self.master_uuid, cluster, feedback_fn)
288 5b6f9e35 Helga Velroyen
289 7352d33b Thomas Thrainer
    return True
290 7352d33b Thomas Thrainer
291 7352d33b Thomas Thrainer
292 5eacbcae Thomas Thrainer
class ClusterQuery(QueryBase):
293 7352d33b Thomas Thrainer
  FIELDS = query.CLUSTER_FIELDS
294 7352d33b Thomas Thrainer
295 7352d33b Thomas Thrainer
  #: Do not sort (there is only one item)
296 7352d33b Thomas Thrainer
  SORT_FIELD = None
297 7352d33b Thomas Thrainer
298 7352d33b Thomas Thrainer
  def ExpandNames(self, lu):
299 7352d33b Thomas Thrainer
    lu.needed_locks = {}
300 7352d33b Thomas Thrainer
301 7352d33b Thomas Thrainer
    # The following variables interact with _QueryBase._GetNames
302 7352d33b Thomas Thrainer
    self.wanted = locking.ALL_SET
303 7352d33b Thomas Thrainer
    self.do_locking = self.use_locking
304 7352d33b Thomas Thrainer
305 7352d33b Thomas Thrainer
    if self.do_locking:
306 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Can not use locking for cluster queries",
307 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
308 7352d33b Thomas Thrainer
309 7352d33b Thomas Thrainer
  def DeclareLocks(self, lu, level):
310 7352d33b Thomas Thrainer
    pass
311 7352d33b Thomas Thrainer
312 7352d33b Thomas Thrainer
  def _GetQueryData(self, lu):
313 7352d33b Thomas Thrainer
    """Computes the list of nodes and their attributes.
314 7352d33b Thomas Thrainer

315 7352d33b Thomas Thrainer
    """
316 7352d33b Thomas Thrainer
    # Locking is not used
317 7352d33b Thomas Thrainer
    assert not (compat.any(lu.glm.is_owned(level)
318 7352d33b Thomas Thrainer
                           for level in locking.LEVELS
319 7352d33b Thomas Thrainer
                           if level != locking.LEVEL_CLUSTER) or
320 7352d33b Thomas Thrainer
                self.do_locking or self.use_locking)
321 7352d33b Thomas Thrainer
322 7352d33b Thomas Thrainer
    if query.CQ_CONFIG in self.requested_data:
323 7352d33b Thomas Thrainer
      cluster = lu.cfg.GetClusterInfo()
324 1c3231aa Thomas Thrainer
      nodes = lu.cfg.GetAllNodesInfo()
325 7352d33b Thomas Thrainer
    else:
326 7352d33b Thomas Thrainer
      cluster = NotImplemented
327 1c3231aa Thomas Thrainer
      nodes = NotImplemented
328 7352d33b Thomas Thrainer
329 7352d33b Thomas Thrainer
    if query.CQ_QUEUE_DRAINED in self.requested_data:
330 7352d33b Thomas Thrainer
      drain_flag = os.path.exists(pathutils.JOB_QUEUE_DRAIN_FILE)
331 7352d33b Thomas Thrainer
    else:
332 7352d33b Thomas Thrainer
      drain_flag = NotImplemented
333 7352d33b Thomas Thrainer
334 7352d33b Thomas Thrainer
    if query.CQ_WATCHER_PAUSE in self.requested_data:
335 1c3231aa Thomas Thrainer
      master_node_uuid = lu.cfg.GetMasterNode()
336 7352d33b Thomas Thrainer
337 1c3231aa Thomas Thrainer
      result = lu.rpc.call_get_watcher_pause(master_node_uuid)
338 7352d33b Thomas Thrainer
      result.Raise("Can't retrieve watcher pause from master node '%s'" %
339 1c3231aa Thomas Thrainer
                   lu.cfg.GetMasterNodeName())
340 7352d33b Thomas Thrainer
341 7352d33b Thomas Thrainer
      watcher_pause = result.payload
342 7352d33b Thomas Thrainer
    else:
343 7352d33b Thomas Thrainer
      watcher_pause = NotImplemented
344 7352d33b Thomas Thrainer
345 1c3231aa Thomas Thrainer
    return query.ClusterQueryData(cluster, nodes, drain_flag, watcher_pause)
346 7352d33b Thomas Thrainer
347 7352d33b Thomas Thrainer
348 7352d33b Thomas Thrainer
class LUClusterQuery(NoHooksLU):
349 7352d33b Thomas Thrainer
  """Query cluster configuration.
350 7352d33b Thomas Thrainer

351 7352d33b Thomas Thrainer
  """
352 7352d33b Thomas Thrainer
  REQ_BGL = False
353 7352d33b Thomas Thrainer
354 7352d33b Thomas Thrainer
  def ExpandNames(self):
355 7352d33b Thomas Thrainer
    self.needed_locks = {}
356 7352d33b Thomas Thrainer
357 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
358 7352d33b Thomas Thrainer
    """Return cluster config.
359 7352d33b Thomas Thrainer

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

429 7352d33b Thomas Thrainer
  This is a very simple LU.
430 7352d33b Thomas Thrainer

431 7352d33b Thomas Thrainer
  """
432 7352d33b Thomas Thrainer
  REQ_BGL = False
433 7352d33b Thomas Thrainer
434 7352d33b Thomas Thrainer
  def ExpandNames(self):
435 7352d33b Thomas Thrainer
    self.needed_locks = {
436 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
437 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
438 7352d33b Thomas Thrainer
    }
439 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
440 7352d33b Thomas Thrainer
441 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
442 7352d33b Thomas Thrainer
    """Redistribute the configuration.
443 7352d33b Thomas Thrainer

444 7352d33b Thomas Thrainer
    """
445 7352d33b Thomas Thrainer
    self.cfg.Update(self.cfg.GetClusterInfo(), feedback_fn)
446 5eacbcae Thomas Thrainer
    RedistributeAncillaryFiles(self)
447 7352d33b Thomas Thrainer
448 7352d33b Thomas Thrainer
449 7352d33b Thomas Thrainer
class LUClusterRename(LogicalUnit):
450 7352d33b Thomas Thrainer
  """Rename the cluster.
451 7352d33b Thomas Thrainer

452 7352d33b Thomas Thrainer
  """
453 7352d33b Thomas Thrainer
  HPATH = "cluster-rename"
454 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
455 7352d33b Thomas Thrainer
456 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
457 7352d33b Thomas Thrainer
    """Build hooks env.
458 7352d33b Thomas Thrainer

459 7352d33b Thomas Thrainer
    """
460 7352d33b Thomas Thrainer
    return {
461 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
462 7352d33b Thomas Thrainer
      "NEW_NAME": self.op.name,
463 7352d33b Thomas Thrainer
      }
464 7352d33b Thomas Thrainer
465 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
466 7352d33b Thomas Thrainer
    """Build hooks nodes.
467 7352d33b Thomas Thrainer

468 7352d33b Thomas Thrainer
    """
469 7352d33b Thomas Thrainer
    return ([self.cfg.GetMasterNode()], self.cfg.GetNodeList())
470 7352d33b Thomas Thrainer
471 7352d33b Thomas Thrainer
  def CheckPrereq(self):
472 7352d33b Thomas Thrainer
    """Verify that the passed name is a valid one.
473 7352d33b Thomas Thrainer

474 7352d33b Thomas Thrainer
    """
475 7352d33b Thomas Thrainer
    hostname = netutils.GetHostname(name=self.op.name,
476 7352d33b Thomas Thrainer
                                    family=self.cfg.GetPrimaryIPFamily())
477 7352d33b Thomas Thrainer
478 7352d33b Thomas Thrainer
    new_name = hostname.name
479 7352d33b Thomas Thrainer
    self.ip = new_ip = hostname.ip
480 7352d33b Thomas Thrainer
    old_name = self.cfg.GetClusterName()
481 7352d33b Thomas Thrainer
    old_ip = self.cfg.GetMasterIP()
482 7352d33b Thomas Thrainer
    if new_name == old_name and new_ip == old_ip:
483 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Neither the name nor the IP address of the"
484 7352d33b Thomas Thrainer
                                 " cluster has changed",
485 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
486 7352d33b Thomas Thrainer
    if new_ip != old_ip:
487 7352d33b Thomas Thrainer
      if netutils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT):
488 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("The given cluster IP address (%s) is"
489 7352d33b Thomas Thrainer
                                   " reachable on the network" %
490 7352d33b Thomas Thrainer
                                   new_ip, errors.ECODE_NOTUNIQUE)
491 7352d33b Thomas Thrainer
492 7352d33b Thomas Thrainer
    self.op.name = new_name
493 7352d33b Thomas Thrainer
494 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
495 7352d33b Thomas Thrainer
    """Rename the cluster.
496 7352d33b Thomas Thrainer

497 7352d33b Thomas Thrainer
    """
498 7352d33b Thomas Thrainer
    clustername = self.op.name
499 7352d33b Thomas Thrainer
    new_ip = self.ip
500 7352d33b Thomas Thrainer
501 7352d33b Thomas Thrainer
    # shutdown the master IP
502 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
503 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
504 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
505 7352d33b Thomas Thrainer
                                                     master_params, ems)
506 7352d33b Thomas Thrainer
    result.Raise("Could not disable the master role")
507 7352d33b Thomas Thrainer
508 7352d33b Thomas Thrainer
    try:
509 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
510 7352d33b Thomas Thrainer
      cluster.cluster_name = clustername
511 7352d33b Thomas Thrainer
      cluster.master_ip = new_ip
512 7352d33b Thomas Thrainer
      self.cfg.Update(cluster, feedback_fn)
513 7352d33b Thomas Thrainer
514 7352d33b Thomas Thrainer
      # update the known hosts file
515 7352d33b Thomas Thrainer
      ssh.WriteKnownHostsFile(self.cfg, pathutils.SSH_KNOWN_HOSTS_FILE)
516 7352d33b Thomas Thrainer
      node_list = self.cfg.GetOnlineNodeList()
517 7352d33b Thomas Thrainer
      try:
518 1c3231aa Thomas Thrainer
        node_list.remove(master_params.uuid)
519 7352d33b Thomas Thrainer
      except ValueError:
520 7352d33b Thomas Thrainer
        pass
521 5eacbcae Thomas Thrainer
      UploadHelper(self, node_list, pathutils.SSH_KNOWN_HOSTS_FILE)
522 7352d33b Thomas Thrainer
    finally:
523 7352d33b Thomas Thrainer
      master_params.ip = new_ip
524 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
525 7352d33b Thomas Thrainer
                                                     master_params, ems)
526 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master role on the master,"
527 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
528 7352d33b Thomas Thrainer
529 7352d33b Thomas Thrainer
    return clustername
530 7352d33b Thomas Thrainer
531 7352d33b Thomas Thrainer
532 7352d33b Thomas Thrainer
class LUClusterRepairDiskSizes(NoHooksLU):
533 7352d33b Thomas Thrainer
  """Verifies the cluster disks sizes.
534 7352d33b Thomas Thrainer

535 7352d33b Thomas Thrainer
  """
536 7352d33b Thomas Thrainer
  REQ_BGL = False
537 7352d33b Thomas Thrainer
538 7352d33b Thomas Thrainer
  def ExpandNames(self):
539 7352d33b Thomas Thrainer
    if self.op.instances:
540 da4a52a3 Thomas Thrainer
      (_, self.wanted_names) = GetWantedInstances(self, self.op.instances)
541 7352d33b Thomas Thrainer
      # Not getting the node allocation lock as only a specific set of
542 7352d33b Thomas Thrainer
      # instances (and their nodes) is going to be acquired
543 7352d33b Thomas Thrainer
      self.needed_locks = {
544 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: [],
545 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: self.wanted_names,
546 7352d33b Thomas Thrainer
        }
547 7352d33b Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
548 7352d33b Thomas Thrainer
    else:
549 7352d33b Thomas Thrainer
      self.wanted_names = None
550 7352d33b Thomas Thrainer
      self.needed_locks = {
551 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: locking.ALL_SET,
552 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: locking.ALL_SET,
553 7352d33b Thomas Thrainer
554 7352d33b Thomas Thrainer
        # This opcode is acquires the node locks for all instances
555 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
556 7352d33b Thomas Thrainer
        }
557 7352d33b Thomas Thrainer
558 7352d33b Thomas Thrainer
    self.share_locks = {
559 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_RES: 1,
560 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: 0,
561 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: 1,
562 7352d33b Thomas Thrainer
      }
563 7352d33b Thomas Thrainer
564 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
565 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and self.wanted_names is not None:
566 7352d33b Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=level)
567 7352d33b Thomas Thrainer
568 7352d33b Thomas Thrainer
  def CheckPrereq(self):
569 7352d33b Thomas Thrainer
    """Check prerequisites.
570 7352d33b Thomas Thrainer

571 7352d33b Thomas Thrainer
    This only checks the optional instance list against the existing names.
572 7352d33b Thomas Thrainer

573 7352d33b Thomas Thrainer
    """
574 7352d33b Thomas Thrainer
    if self.wanted_names is None:
575 7352d33b Thomas Thrainer
      self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE)
576 7352d33b Thomas Thrainer
577 7352d33b Thomas Thrainer
    self.wanted_instances = \
578 da4a52a3 Thomas Thrainer
        map(compat.snd, self.cfg.GetMultiInstanceInfoByName(self.wanted_names))
579 7352d33b Thomas Thrainer
580 7352d33b Thomas Thrainer
  def _EnsureChildSizes(self, disk):
581 7352d33b Thomas Thrainer
    """Ensure children of the disk have the needed disk size.
582 7352d33b Thomas Thrainer

583 7352d33b Thomas Thrainer
    This is valid mainly for DRBD8 and fixes an issue where the
584 7352d33b Thomas Thrainer
    children have smaller disk size.
585 7352d33b Thomas Thrainer

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

588 7352d33b Thomas Thrainer
    """
589 cd3b4ff4 Helga Velroyen
    if disk.dev_type == constants.DT_DRBD8:
590 7352d33b Thomas Thrainer
      assert disk.children, "Empty children for DRBD8?"
591 7352d33b Thomas Thrainer
      fchild = disk.children[0]
592 7352d33b Thomas Thrainer
      mismatch = fchild.size < disk.size
593 7352d33b Thomas Thrainer
      if mismatch:
594 7352d33b Thomas Thrainer
        self.LogInfo("Child disk has size %d, parent %d, fixing",
595 7352d33b Thomas Thrainer
                     fchild.size, disk.size)
596 7352d33b Thomas Thrainer
        fchild.size = disk.size
597 7352d33b Thomas Thrainer
598 7352d33b Thomas Thrainer
      # and we recurse on this child only, not on the metadev
599 7352d33b Thomas Thrainer
      return self._EnsureChildSizes(fchild) or mismatch
600 7352d33b Thomas Thrainer
    else:
601 7352d33b Thomas Thrainer
      return False
602 7352d33b Thomas Thrainer
603 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
604 7352d33b Thomas Thrainer
    """Verify the size of cluster disks.
605 7352d33b Thomas Thrainer

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

689 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
690 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
691 7352d33b Thomas Thrainer
  @type netmask: int
692 7352d33b Thomas Thrainer
  @param netmask: the netmask to be verified
693 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the validation fails
694 7352d33b Thomas Thrainer

695 7352d33b Thomas Thrainer
  """
696 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
697 7352d33b Thomas Thrainer
  try:
698 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
699 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
700 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
701 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
702 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
703 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
704 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
705 7352d33b Thomas Thrainer
706 7352d33b Thomas Thrainer
707 e8b5640e Helga Velroyen
def CheckFileBasedStoragePathVsEnabledDiskTemplates(
708 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates,
709 e8b5640e Helga Velroyen
    file_disk_template):
710 e8b5640e Helga Velroyen
  """Checks whether the given file-based storage directory is acceptable.
711 e8b5640e Helga Velroyen

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

714 3039e2dc Helga Velroyen
  @type logging_warn_fn: function
715 3039e2dc Helga Velroyen
  @param logging_warn_fn: function which accepts a string and logs it
716 3039e2dc Helga Velroyen
  @type file_storage_dir: string
717 3039e2dc Helga Velroyen
  @param file_storage_dir: the directory to be used for file-based instances
718 3039e2dc Helga Velroyen
  @type enabled_disk_templates: list of string
719 3039e2dc Helga Velroyen
  @param enabled_disk_templates: the list of enabled disk templates
720 e8b5640e Helga Velroyen
  @type file_disk_template: string
721 e8b5640e Helga Velroyen
  @param file_disk_template: the file-based disk template for which the
722 e8b5640e Helga Velroyen
      path should be checked
723 3039e2dc Helga Velroyen

724 3039e2dc Helga Velroyen
  """
725 5a904197 Santi Raffa
  assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
726 5a904197 Santi Raffa
            constants.ST_FILE, constants.ST_SHARED_FILE
727 5a904197 Santi Raffa
         ))
728 e8b5640e Helga Velroyen
  file_storage_enabled = file_disk_template in enabled_disk_templates
729 3039e2dc Helga Velroyen
  if file_storage_dir is not None:
730 3039e2dc Helga Velroyen
    if file_storage_dir == "":
731 3039e2dc Helga Velroyen
      if file_storage_enabled:
732 e8b5640e Helga Velroyen
        raise errors.OpPrereqError(
733 e8b5640e Helga Velroyen
            "Unsetting the '%s' storage directory while having '%s' storage"
734 e8b5640e Helga Velroyen
            " enabled is not permitted." %
735 e8b5640e Helga Velroyen
            (file_disk_template, file_disk_template))
736 3039e2dc Helga Velroyen
    else:
737 3039e2dc Helga Velroyen
      if not file_storage_enabled:
738 e8b5640e Helga Velroyen
        logging_warn_fn(
739 e8b5640e Helga Velroyen
            "Specified a %s storage directory, although %s storage is not"
740 e8b5640e Helga Velroyen
            " enabled." % (file_disk_template, file_disk_template))
741 3039e2dc Helga Velroyen
  else:
742 e8b5640e Helga Velroyen
    raise errors.ProgrammerError("Received %s storage dir with value"
743 e8b5640e Helga Velroyen
                                 " 'None'." % file_disk_template)
744 e8b5640e Helga Velroyen
745 e8b5640e Helga Velroyen
746 e8b5640e Helga Velroyen
def CheckFileStoragePathVsEnabledDiskTemplates(
747 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
748 e8b5640e Helga Velroyen
  """Checks whether the given file storage directory is acceptable.
749 e8b5640e Helga Velroyen

750 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
751 e8b5640e Helga Velroyen

752 e8b5640e Helga Velroyen
  """
753 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
754 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
755 e8b5640e Helga Velroyen
      constants.DT_FILE)
756 e8b5640e Helga Velroyen
757 e8b5640e Helga Velroyen
758 e8b5640e Helga Velroyen
def CheckSharedFileStoragePathVsEnabledDiskTemplates(
759 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
760 e8b5640e Helga Velroyen
  """Checks whether the given shared file storage directory is acceptable.
761 e8b5640e Helga Velroyen

762 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
763 e8b5640e Helga Velroyen

764 e8b5640e Helga Velroyen
  """
765 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
766 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
767 e8b5640e Helga Velroyen
      constants.DT_SHARED_FILE)
768 3039e2dc Helga Velroyen
769 3039e2dc Helga Velroyen
770 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
771 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
772 7352d33b Thomas Thrainer

773 7352d33b Thomas Thrainer
  """
774 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
775 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
776 7352d33b Thomas Thrainer
  REQ_BGL = False
777 7352d33b Thomas Thrainer
778 7352d33b Thomas Thrainer
  def CheckArguments(self):
779 7352d33b Thomas Thrainer
    """Check parameters
780 7352d33b Thomas Thrainer

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

820 7352d33b Thomas Thrainer
    """
821 7352d33b Thomas Thrainer
    return {
822 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
823 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
824 7352d33b Thomas Thrainer
      }
825 7352d33b Thomas Thrainer
826 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
827 7352d33b Thomas Thrainer
    """Build hooks nodes.
828 7352d33b Thomas Thrainer

829 7352d33b Thomas Thrainer
    """
830 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
831 7352d33b Thomas Thrainer
    return ([mn], [mn])
832 7352d33b Thomas Thrainer
833 1c3231aa Thomas Thrainer
  def _CheckVgName(self, node_uuids, enabled_disk_templates,
834 1bb99a33 Bernardo Dal Seno
                   new_enabled_disk_templates):
835 1bb99a33 Bernardo Dal Seno
    """Check the consistency of the vg name on all nodes and in case it gets
836 1bb99a33 Bernardo Dal Seno
       unset whether there are instances still using it.
837 7352d33b Thomas Thrainer

838 7352d33b Thomas Thrainer
    """
839 c89eb67d Helga Velroyen
    lvm_is_enabled = utils.IsLvmEnabled(enabled_disk_templates)
840 c89eb67d Helga Velroyen
    lvm_gets_enabled = utils.LvmGetsEnabled(enabled_disk_templates,
841 c89eb67d Helga Velroyen
                                            new_enabled_disk_templates)
842 c89eb67d Helga Velroyen
    current_vg_name = self.cfg.GetVGName()
843 c89eb67d Helga Velroyen
844 c89eb67d Helga Velroyen
    if self.op.vg_name == '':
845 c89eb67d Helga Velroyen
      if lvm_is_enabled:
846 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
847 c89eb67d Helga Velroyen
                                   " disk templates are or get enabled.")
848 c89eb67d Helga Velroyen
849 c89eb67d Helga Velroyen
    if self.op.vg_name is None:
850 c89eb67d Helga Velroyen
      if current_vg_name is None and lvm_is_enabled:
851 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Please specify a volume group when"
852 c89eb67d Helga Velroyen
                                   " enabling lvm-based disk-templates.")
853 c89eb67d Helga Velroyen
854 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
855 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_PLAIN):
856 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
857 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
858 7352d33b Thomas Thrainer
859 c89eb67d Helga Velroyen
    if (self.op.vg_name is not None and lvm_is_enabled) or \
860 c89eb67d Helga Velroyen
        (self.cfg.GetVGName() is not None and lvm_gets_enabled):
861 1c3231aa Thomas Thrainer
      self._CheckVgNameOnNodes(node_uuids)
862 1bb99a33 Bernardo Dal Seno
863 1c3231aa Thomas Thrainer
  def _CheckVgNameOnNodes(self, node_uuids):
864 1bb99a33 Bernardo Dal Seno
    """Check the status of the volume group on each node.
865 1bb99a33 Bernardo Dal Seno

866 1bb99a33 Bernardo Dal Seno
    """
867 1c3231aa Thomas Thrainer
    vglist = self.rpc.call_vg_list(node_uuids)
868 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
869 1c3231aa Thomas Thrainer
      msg = vglist[node_uuid].fail_msg
870 1bb99a33 Bernardo Dal Seno
      if msg:
871 1bb99a33 Bernardo Dal Seno
        # ignoring down node
872 1bb99a33 Bernardo Dal Seno
        self.LogWarning("Error while gathering data on node %s"
873 1c3231aa Thomas Thrainer
                        " (ignoring node): %s",
874 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
875 1bb99a33 Bernardo Dal Seno
        continue
876 1c3231aa Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist[node_uuid].payload,
877 1bb99a33 Bernardo Dal Seno
                                            self.op.vg_name,
878 1bb99a33 Bernardo Dal Seno
                                            constants.MIN_VG_SIZE)
879 1bb99a33 Bernardo Dal Seno
      if vgstatus:
880 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Error on node '%s': %s" %
881 1c3231aa Thomas Thrainer
                                   (self.cfg.GetNodeName(node_uuid), vgstatus),
882 1c3231aa Thomas Thrainer
                                   errors.ECODE_ENVIRON)
883 1bb99a33 Bernardo Dal Seno
884 c89eb67d Helga Velroyen
  @staticmethod
885 87e23f2d Helga Velroyen
  def _GetDiskTemplateSetsInner(op_enabled_disk_templates,
886 87e23f2d Helga Velroyen
                                old_enabled_disk_templates):
887 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
888 6e513917 Helga Velroyen

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

891 1bb99a33 Bernardo Dal Seno
    """
892 1bb99a33 Bernardo Dal Seno
    enabled_disk_templates = None
893 1bb99a33 Bernardo Dal Seno
    new_enabled_disk_templates = []
894 6e513917 Helga Velroyen
    disabled_disk_templates = []
895 c89eb67d Helga Velroyen
    if op_enabled_disk_templates:
896 c89eb67d Helga Velroyen
      enabled_disk_templates = op_enabled_disk_templates
897 1bb99a33 Bernardo Dal Seno
      new_enabled_disk_templates = \
898 5808df30 Helga Velroyen
        list(set(enabled_disk_templates)
899 5808df30 Helga Velroyen
             - set(old_enabled_disk_templates))
900 6e513917 Helga Velroyen
      disabled_disk_templates = \
901 5808df30 Helga Velroyen
        list(set(old_enabled_disk_templates)
902 5808df30 Helga Velroyen
             - set(enabled_disk_templates))
903 1bb99a33 Bernardo Dal Seno
    else:
904 c89eb67d Helga Velroyen
      enabled_disk_templates = old_enabled_disk_templates
905 6e513917 Helga Velroyen
    return (enabled_disk_templates, new_enabled_disk_templates,
906 6e513917 Helga Velroyen
            disabled_disk_templates)
907 1bb99a33 Bernardo Dal Seno
908 87e23f2d Helga Velroyen
  def _GetDiskTemplateSets(self, cluster):
909 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
910 6e513917 Helga Velroyen

911 6e513917 Helga Velroyen
    The three sets are:
912 6e513917 Helga Velroyen
      - disk templates that will be enabled after this operation (no matter if
913 6e513917 Helga Velroyen
        they were enabled before or not)
914 6e513917 Helga Velroyen
      - disk templates that get enabled by this operation (thus haven't been
915 6e513917 Helga Velroyen
        enabled before.)
916 6e513917 Helga Velroyen
      - disk templates that get disabled by this operation
917 c89eb67d Helga Velroyen

918 c89eb67d Helga Velroyen
    """
919 87e23f2d Helga Velroyen
    return self._GetDiskTemplateSetsInner(self.op.enabled_disk_templates,
920 87e23f2d Helga Velroyen
                                          cluster.enabled_disk_templates)
921 c89eb67d Helga Velroyen
922 33a6464e Helga Velroyen
  def _CheckIpolicy(self, cluster, enabled_disk_templates):
923 1532b078 Helga Velroyen
    """Checks the ipolicy.
924 1532b078 Helga Velroyen

925 1532b078 Helga Velroyen
    @type cluster: C{objects.Cluster}
926 1532b078 Helga Velroyen
    @param cluster: the cluster's configuration
927 33a6464e Helga Velroyen
    @type enabled_disk_templates: list of string
928 33a6464e Helga Velroyen
    @param enabled_disk_templates: list of (possibly newly) enabled disk
929 33a6464e Helga Velroyen
      templates
930 1532b078 Helga Velroyen

931 1532b078 Helga Velroyen
    """
932 33a6464e Helga Velroyen
    # FIXME: write unit tests for this
933 1532b078 Helga Velroyen
    if self.op.ipolicy:
934 1532b078 Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
935 1532b078 Helga Velroyen
                                           group_policy=False)
936 1532b078 Helga Velroyen
937 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(self.new_ipolicy,
938 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
939 33a6464e Helga Velroyen
940 1532b078 Helga Velroyen
      all_instances = self.cfg.GetAllInstancesInfo().values()
941 1532b078 Helga Velroyen
      violations = set()
942 1532b078 Helga Velroyen
      for group in self.cfg.GetAllNodeGroupsInfo().values():
943 1532b078 Helga Velroyen
        instances = frozenset([inst for inst in all_instances
944 1532b078 Helga Velroyen
                               if compat.any(nuuid in group.members
945 1532b078 Helga Velroyen
                                             for nuuid in inst.all_nodes)])
946 1532b078 Helga Velroyen
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
947 1532b078 Helga Velroyen
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
948 1532b078 Helga Velroyen
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
949 1532b078 Helga Velroyen
                                           self.cfg)
950 1532b078 Helga Velroyen
        if new:
951 1532b078 Helga Velroyen
          violations.update(new)
952 1532b078 Helga Velroyen
953 1532b078 Helga Velroyen
      if violations:
954 1532b078 Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
955 1532b078 Helga Velroyen
                        " violate them: %s",
956 1532b078 Helga Velroyen
                        utils.CommaJoin(utils.NiceSort(violations)))
957 33a6464e Helga Velroyen
    else:
958 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(cluster.ipolicy,
959 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
960 1532b078 Helga Velroyen
961 31ccfc0e Helga Velroyen
  def _CheckDrbdHelperOnNodes(self, drbd_helper, node_uuids):
962 31ccfc0e Helga Velroyen
    """Checks whether the set DRBD helper actually exists on the nodes.
963 31ccfc0e Helga Velroyen

964 31ccfc0e Helga Velroyen
    @type drbd_helper: string
965 31ccfc0e Helga Velroyen
    @param drbd_helper: path of the drbd usermode helper binary
966 31ccfc0e Helga Velroyen
    @type node_uuids: list of strings
967 31ccfc0e Helga Velroyen
    @param node_uuids: list of node UUIDs to check for the helper
968 31ccfc0e Helga Velroyen

969 31ccfc0e Helga Velroyen
    """
970 31ccfc0e Helga Velroyen
    # checks given drbd helper on all nodes
971 31ccfc0e Helga Velroyen
    helpers = self.rpc.call_drbd_helper(node_uuids)
972 31ccfc0e Helga Velroyen
    for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
973 31ccfc0e Helga Velroyen
      if ninfo.offline:
974 31ccfc0e Helga Velroyen
        self.LogInfo("Not checking drbd helper on offline node %s",
975 31ccfc0e Helga Velroyen
                     ninfo.name)
976 31ccfc0e Helga Velroyen
        continue
977 31ccfc0e Helga Velroyen
      msg = helpers[ninfo.uuid].fail_msg
978 31ccfc0e Helga Velroyen
      if msg:
979 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error checking drbd helper on node"
980 31ccfc0e Helga Velroyen
                                   " '%s': %s" % (ninfo.name, msg),
981 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
982 31ccfc0e Helga Velroyen
      node_helper = helpers[ninfo.uuid].payload
983 31ccfc0e Helga Velroyen
      if node_helper != drbd_helper:
984 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
985 31ccfc0e Helga Velroyen
                                   (ninfo.name, node_helper),
986 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
987 31ccfc0e Helga Velroyen
988 31ccfc0e Helga Velroyen
  def _CheckDrbdHelper(self, node_uuids, drbd_enabled, drbd_gets_enabled):
989 7c577910 Helga Velroyen
    """Check the DRBD usermode helper.
990 1bb99a33 Bernardo Dal Seno

991 7c577910 Helga Velroyen
    @type node_uuids: list of strings
992 7c577910 Helga Velroyen
    @param node_uuids: a list of nodes' UUIDs
993 31ccfc0e Helga Velroyen
    @type drbd_enabled: boolean
994 31ccfc0e Helga Velroyen
    @param drbd_enabled: whether DRBD will be enabled after this operation
995 31ccfc0e Helga Velroyen
      (no matter if it was disabled before or not)
996 31ccfc0e Helga Velroyen
    @type drbd_gets_enabled: boolen
997 31ccfc0e Helga Velroyen
    @param drbd_gets_enabled: true if DRBD was disabled before this
998 31ccfc0e Helga Velroyen
      operation, but will be enabled afterwards
999 1bb99a33 Bernardo Dal Seno

1000 1bb99a33 Bernardo Dal Seno
    """
1001 31ccfc0e Helga Velroyen
    if self.op.drbd_helper == '':
1002 31ccfc0e Helga Velroyen
      if drbd_enabled:
1003 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Cannot disable drbd helper while"
1004 31ccfc0e Helga Velroyen
                                   " DRBD is enabled.")
1005 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_DRBD8):
1006 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
1007 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
1008 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1009 7352d33b Thomas Thrainer
1010 31ccfc0e Helga Velroyen
    else:
1011 31ccfc0e Helga Velroyen
      if self.op.drbd_helper is not None and drbd_enabled:
1012 31ccfc0e Helga Velroyen
        self._CheckDrbdHelperOnNodes(self.op.drbd_helper, node_uuids)
1013 31ccfc0e Helga Velroyen
      else:
1014 31ccfc0e Helga Velroyen
        if drbd_gets_enabled:
1015 31ccfc0e Helga Velroyen
          current_drbd_helper = self.cfg.GetClusterInfo().drbd_usermode_helper
1016 31ccfc0e Helga Velroyen
          if current_drbd_helper is not None:
1017 31ccfc0e Helga Velroyen
            self._CheckDrbdHelperOnNodes(current_drbd_helper, node_uuids)
1018 31ccfc0e Helga Velroyen
          else:
1019 31ccfc0e Helga Velroyen
            raise errors.OpPrereqError("Cannot enable DRBD without a"
1020 31ccfc0e Helga Velroyen
                                       " DRBD usermode helper set.")
1021 7c577910 Helga Velroyen
1022 c2e984e2 Helga Velroyen
  def _CheckInstancesOfDisabledDiskTemplates(
1023 c2e984e2 Helga Velroyen
      self, disabled_disk_templates):
1024 5808df30 Helga Velroyen
    """Check whether we try to disable a disk template that is in use.
1025 c2e984e2 Helga Velroyen

1026 c2e984e2 Helga Velroyen
    @type disabled_disk_templates: list of string
1027 c2e984e2 Helga Velroyen
    @param disabled_disk_templates: list of disk templates that are going to
1028 c2e984e2 Helga Velroyen
      be disabled by this operation
1029 c2e984e2 Helga Velroyen

1030 c2e984e2 Helga Velroyen
    """
1031 c2e984e2 Helga Velroyen
    for disk_template in disabled_disk_templates:
1032 c2e984e2 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(disk_template):
1033 c2e984e2 Helga Velroyen
        raise errors.OpPrereqError(
1034 c2e984e2 Helga Velroyen
            "Cannot disable disk template '%s', because there is at least one"
1035 c2e984e2 Helga Velroyen
            " instance using it." % disk_template)
1036 c2e984e2 Helga Velroyen
1037 11eeb1b9 Jose A. Lopes
  @staticmethod
1038 11eeb1b9 Jose A. Lopes
  def _CheckInstanceCommunicationNetwork(network, warning_fn):
1039 11eeb1b9 Jose A. Lopes
    """Check whether an existing network is configured for instance
1040 11eeb1b9 Jose A. Lopes
    communication.
1041 11eeb1b9 Jose A. Lopes

1042 11eeb1b9 Jose A. Lopes
    Checks whether an existing network is configured with the
1043 11eeb1b9 Jose A. Lopes
    parameters that are advisable for instance communication, and
1044 11eeb1b9 Jose A. Lopes
    otherwise issue security warnings.
1045 11eeb1b9 Jose A. Lopes

1046 11eeb1b9 Jose A. Lopes
    @type network: L{ganeti.objects.Network}
1047 11eeb1b9 Jose A. Lopes
    @param network: L{ganeti.objects.Network} object whose
1048 11eeb1b9 Jose A. Lopes
                    configuration is being checked
1049 11eeb1b9 Jose A. Lopes
    @type warning_fn: function
1050 11eeb1b9 Jose A. Lopes
    @param warning_fn: function used to print warnings
1051 11eeb1b9 Jose A. Lopes
    @rtype: None
1052 11eeb1b9 Jose A. Lopes
    @return: None
1053 11eeb1b9 Jose A. Lopes

1054 11eeb1b9 Jose A. Lopes
    """
1055 11eeb1b9 Jose A. Lopes
    def _MaybeWarn(err, val, default):
1056 11eeb1b9 Jose A. Lopes
      if val != default:
1057 11eeb1b9 Jose A. Lopes
        warning_fn("Supplied instance communication network '%s' %s '%s',"
1058 11eeb1b9 Jose A. Lopes
                   " this might pose a security risk (default is '%s').",
1059 11eeb1b9 Jose A. Lopes
                   network.name, err, val, default)
1060 11eeb1b9 Jose A. Lopes
1061 11eeb1b9 Jose A. Lopes
    if network.network is None:
1062 11eeb1b9 Jose A. Lopes
      raise errors.OpPrereqError("Supplied instance communication network '%s'"
1063 11eeb1b9 Jose A. Lopes
                                 " must have an IPv4 network address.",
1064 11eeb1b9 Jose A. Lopes
                                 network.name)
1065 11eeb1b9 Jose A. Lopes
1066 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has an IPv4 gateway", network.gateway, None)
1067 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has a non-standard IPv4 network address", network.network,
1068 11eeb1b9 Jose A. Lopes
               constants.INSTANCE_COMMUNICATION_NETWORK4)
1069 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has an IPv6 gateway", network.gateway6, None)
1070 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has a non-standard IPv6 network address", network.network6,
1071 11eeb1b9 Jose A. Lopes
               constants.INSTANCE_COMMUNICATION_NETWORK6)
1072 11eeb1b9 Jose A. Lopes
    _MaybeWarn("has a non-standard MAC prefix", network.mac_prefix,
1073 11eeb1b9 Jose A. Lopes
               constants.INSTANCE_COMMUNICATION_MAC_PREFIX)
1074 11eeb1b9 Jose A. Lopes
1075 7c577910 Helga Velroyen
  def CheckPrereq(self):
1076 7c577910 Helga Velroyen
    """Check prerequisites.
1077 7c577910 Helga Velroyen

1078 7c577910 Helga Velroyen
    This checks whether the given params don't conflict and
1079 7c577910 Helga Velroyen
    if the given volume group is valid.
1080 7c577910 Helga Velroyen

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

1308 7352d33b Thomas Thrainer
    """
1309 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
1310 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1311 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
1312 7352d33b Thomas Thrainer
1313 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
1314 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
1315 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
1316 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
1317 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
1318 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
1319 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
1320 7352d33b Thomas Thrainer
1321 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
1322 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
1323 7352d33b Thomas Thrainer

1324 7352d33b Thomas Thrainer
    """
1325 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
1326 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
1327 7352d33b Thomas Thrainer
      if not new_volume:
1328 7352d33b Thomas Thrainer
        new_volume = None
1329 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
1330 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
1331 7352d33b Thomas Thrainer
      else:
1332 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
1333 7352d33b Thomas Thrainer
                    " state, not changing")
1334 1bb99a33 Bernardo Dal Seno
1335 3039e2dc Helga Velroyen
  def _SetFileStorageDir(self, feedback_fn):
1336 3039e2dc Helga Velroyen
    """Set the file storage directory.
1337 3039e2dc Helga Velroyen

1338 3039e2dc Helga Velroyen
    """
1339 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1340 3039e2dc Helga Velroyen
      if self.cluster.file_storage_dir == self.op.file_storage_dir:
1341 3039e2dc Helga Velroyen
        feedback_fn("Global file storage dir already set to value '%s'"
1342 3039e2dc Helga Velroyen
                    % self.cluster.file_storage_dir)
1343 3039e2dc Helga Velroyen
      else:
1344 3039e2dc Helga Velroyen
        self.cluster.file_storage_dir = self.op.file_storage_dir
1345 3039e2dc Helga Velroyen
1346 7c577910 Helga Velroyen
  def _SetDrbdHelper(self, feedback_fn):
1347 7c577910 Helga Velroyen
    """Set the DRBD usermode helper.
1348 1bb99a33 Bernardo Dal Seno

1349 1bb99a33 Bernardo Dal Seno
    """
1350 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1351 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1352 a794b8d7 Thomas Thrainer
        feedback_fn("Note that you specified a drbd user helper, but did not"
1353 a794b8d7 Thomas Thrainer
                    " enable the drbd disk template.")
1354 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1355 7352d33b Thomas Thrainer
      if not new_helper:
1356 7352d33b Thomas Thrainer
        new_helper = None
1357 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1358 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1359 7352d33b Thomas Thrainer
      else:
1360 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1361 7352d33b Thomas Thrainer
                    " not changing")
1362 7c577910 Helga Velroyen
1363 7c577910 Helga Velroyen
  def Exec(self, feedback_fn):
1364 7c577910 Helga Velroyen
    """Change the parameters of the cluster.
1365 7c577910 Helga Velroyen

1366 7c577910 Helga Velroyen
    """
1367 7c577910 Helga Velroyen
    if self.op.enabled_disk_templates:
1368 7c577910 Helga Velroyen
      self.cluster.enabled_disk_templates = \
1369 8b95dfdc Helga Velroyen
        list(self.op.enabled_disk_templates)
1370 7c577910 Helga Velroyen
1371 7c577910 Helga Velroyen
    self._SetVgName(feedback_fn)
1372 7c577910 Helga Velroyen
    self._SetFileStorageDir(feedback_fn)
1373 7c577910 Helga Velroyen
    self._SetDrbdHelper(feedback_fn)
1374 7c577910 Helga Velroyen
1375 7352d33b Thomas Thrainer
    if self.op.hvparams:
1376 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1377 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1378 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1379 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1380 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1381 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1382 7352d33b Thomas Thrainer
    if self.op.beparams:
1383 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1384 7352d33b Thomas Thrainer
    if self.op.nicparams:
1385 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1386 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1387 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1388 7352d33b Thomas Thrainer
    if self.op.osparams:
1389 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1390 07e3c124 Santi Raffa
    if self.op.osparams_private_cluster:
1391 07e3c124 Santi Raffa
      self.cluster.osparams_private_cluster = self.new_osp_private
1392 7352d33b Thomas Thrainer
    if self.op.ndparams:
1393 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1394 7352d33b Thomas Thrainer
    if self.op.diskparams:
1395 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1396 7352d33b Thomas Thrainer
    if self.op.hv_state:
1397 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1398 7352d33b Thomas Thrainer
    if self.op.disk_state:
1399 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1400 7352d33b Thomas Thrainer
1401 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1402 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1403 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1404 c1410048 Helga Velroyen
      AdjustCandidatePool(self, [], feedback_fn)
1405 7352d33b Thomas Thrainer
1406 ad756c77 Klaus Aehlig
    if self.op.max_running_jobs is not None:
1407 ad756c77 Klaus Aehlig
      self.cluster.max_running_jobs = self.op.max_running_jobs
1408 ad756c77 Klaus Aehlig
1409 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1410 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1411 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1412 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1413 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1414 7352d33b Thomas Thrainer
1415 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1416 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1417 75f2ff7d Michele Tartara
1418 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1419 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1420 7352d33b Thomas Thrainer
1421 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1422 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1423 7352d33b Thomas Thrainer
1424 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1425 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1426 7352d33b Thomas Thrainer
1427 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1428 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1429 7352d33b Thomas Thrainer
1430 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1431 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1432 7352d33b Thomas Thrainer
1433 0359e5d0 Spyros Trigazis
    if self.op.default_iallocator_params is not None:
1434 0359e5d0 Spyros Trigazis
      self.cluster.default_iallocator_params = self.op.default_iallocator_params
1435 0359e5d0 Spyros Trigazis
1436 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1437 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1438 7352d33b Thomas Thrainer
1439 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1440 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1441 7352d33b Thomas Thrainer
1442 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1443 7352d33b Thomas Thrainer
      desc += " OS list"
1444 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1445 7352d33b Thomas Thrainer
      for key, val in mods:
1446 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1447 7352d33b Thomas Thrainer
          if val in lst:
1448 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1449 7352d33b Thomas Thrainer
          else:
1450 7352d33b Thomas Thrainer
            lst.append(val)
1451 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1452 7352d33b Thomas Thrainer
          if val in lst:
1453 7352d33b Thomas Thrainer
            lst.remove(val)
1454 7352d33b Thomas Thrainer
          else:
1455 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1456 7352d33b Thomas Thrainer
        else:
1457 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1458 7352d33b Thomas Thrainer
1459 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1460 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1461 7352d33b Thomas Thrainer
1462 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1463 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1464 7352d33b Thomas Thrainer
1465 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1466 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1467 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1468 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1469 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1470 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1471 7352d33b Thomas Thrainer
                                                       master_params, ems)
1472 e5c92cfb Klaus Aehlig
      if not self.op.force:
1473 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1474 e5c92cfb Klaus Aehlig
      else:
1475 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1476 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1477 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1478 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1479 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1480 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1481 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1482 7352d33b Thomas Thrainer
1483 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1484 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1485 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1486 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1487 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1488 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1489 1c3231aa Thomas Thrainer
                 master_params.netdev)
1490 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1491 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1492 7352d33b Thomas Thrainer
1493 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1494 7352d33b Thomas Thrainer
1495 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1496 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1497 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1498 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1499 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1500 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1501 7352d33b Thomas Thrainer
                                                     master_params, ems)
1502 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1503 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1504 7352d33b Thomas Thrainer
1505 7352d33b Thomas Thrainer
1506 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1507 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1508 7352d33b Thomas Thrainer

1509 7352d33b Thomas Thrainer
  """
1510 7352d33b Thomas Thrainer
  REQ_BGL = False
1511 7352d33b Thomas Thrainer
1512 7352d33b Thomas Thrainer
  def ExpandNames(self):
1513 7352d33b Thomas Thrainer
    self.needed_locks = {}
1514 7352d33b Thomas Thrainer
1515 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1516 7352d33b Thomas Thrainer
    jobs = []
1517 7352d33b Thomas Thrainer
1518 7352d33b Thomas Thrainer
    if self.op.group_name:
1519 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1520 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1521 7352d33b Thomas Thrainer
    else:
1522 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1523 7352d33b Thomas Thrainer
1524 7352d33b Thomas Thrainer
      # Verify global configuration
1525 7352d33b Thomas Thrainer
      jobs.append([
1526 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1527 7352d33b Thomas Thrainer
        ])
1528 7352d33b Thomas Thrainer
1529 7352d33b Thomas Thrainer
      # Always depend on global verification
1530 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1531 7352d33b Thomas Thrainer
1532 7352d33b Thomas Thrainer
    jobs.extend(
1533 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1534 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1535 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1536 7352d33b Thomas Thrainer
      for group in groups)
1537 7352d33b Thomas Thrainer
1538 7352d33b Thomas Thrainer
    # Fix up all parameters
1539 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1540 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1541 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1542 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1543 7352d33b Thomas Thrainer
      try:
1544 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1545 7352d33b Thomas Thrainer
      except AttributeError:
1546 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1547 7352d33b Thomas Thrainer
1548 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1549 7352d33b Thomas Thrainer
1550 7352d33b Thomas Thrainer
1551 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1552 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1553 7352d33b Thomas Thrainer

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

1557 7352d33b Thomas Thrainer
  """
1558 7352d33b Thomas Thrainer
1559 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1560 a6c43c02 Helga Velroyen
  ETYPE_ERROR = constants.CV_ERROR
1561 a6c43c02 Helga Velroyen
  ETYPE_WARNING = constants.CV_WARNING
1562 7352d33b Thomas Thrainer
1563 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1564 7352d33b Thomas Thrainer
    """Format an error message.
1565 7352d33b Thomas Thrainer

1566 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1567 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1568 7352d33b Thomas Thrainer

1569 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1570 7352d33b Thomas Thrainer

1571 7352d33b Thomas Thrainer
    """
1572 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1573 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1574 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1575 7352d33b Thomas Thrainer
    # warning
1576 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1577 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1578 7352d33b Thomas Thrainer
    # first complete the msg
1579 7352d33b Thomas Thrainer
    if args:
1580 7352d33b Thomas Thrainer
      msg = msg % args
1581 7352d33b Thomas Thrainer
    # then format the whole message
1582 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1583 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1584 7352d33b Thomas Thrainer
    else:
1585 7352d33b Thomas Thrainer
      if item:
1586 7352d33b Thomas Thrainer
        item = " " + item
1587 7352d33b Thomas Thrainer
      else:
1588 7352d33b Thomas Thrainer
        item = ""
1589 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1590 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1591 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1592 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1593 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1594 7352d33b Thomas Thrainer
      self.bad = True
1595 7352d33b Thomas Thrainer
1596 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1597 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1598 7352d33b Thomas Thrainer

1599 7352d33b Thomas Thrainer
    """
1600 7352d33b Thomas Thrainer
    if (bool(cond)
1601 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1602 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1603 7352d33b Thomas Thrainer
1604 7352d33b Thomas Thrainer
1605 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1606 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1607 7352d33b Thomas Thrainer

1608 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1609 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1610 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1611 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1612 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1613 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1614 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1615 7352d33b Thomas Thrainer

1616 7352d33b Thomas Thrainer
  """
1617 7352d33b Thomas Thrainer
  hvp_data = []
1618 7352d33b Thomas Thrainer
1619 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1620 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1621 7352d33b Thomas Thrainer
1622 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1623 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1624 7352d33b Thomas Thrainer
      if hv_params:
1625 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1626 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1627 7352d33b Thomas Thrainer
1628 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1629 7352d33b Thomas Thrainer
  for instance in instances:
1630 7352d33b Thomas Thrainer
    if instance.hvparams:
1631 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1632 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1633 7352d33b Thomas Thrainer
1634 7352d33b Thomas Thrainer
  return hvp_data
1635 7352d33b Thomas Thrainer
1636 7352d33b Thomas Thrainer
1637 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1638 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1639 7352d33b Thomas Thrainer

1640 7352d33b Thomas Thrainer
  """
1641 7352d33b Thomas Thrainer
  REQ_BGL = False
1642 7352d33b Thomas Thrainer
1643 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1644 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1645 7352d33b Thomas Thrainer

1646 7352d33b Thomas Thrainer
    """
1647 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1648 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1649 7352d33b Thomas Thrainer
             (item, hv_name))
1650 7352d33b Thomas Thrainer
      try:
1651 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1652 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1653 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1654 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1655 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1656 7352d33b Thomas Thrainer
1657 7352d33b Thomas Thrainer
  def ExpandNames(self):
1658 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1659 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1660 7352d33b Thomas Thrainer
1661 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1662 7352d33b Thomas Thrainer
    """Check prerequisites.
1663 7352d33b Thomas Thrainer

1664 7352d33b Thomas Thrainer
    """
1665 7352d33b Thomas Thrainer
    # Retrieve all information
1666 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1667 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1668 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1669 7352d33b Thomas Thrainer
1670 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1671 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1672 7352d33b Thomas Thrainer

1673 7352d33b Thomas Thrainer
    """
1674 7352d33b Thomas Thrainer
    self.bad = False
1675 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1676 7352d33b Thomas Thrainer
1677 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1678 7352d33b Thomas Thrainer
1679 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1680 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1681 7352d33b Thomas Thrainer
1682 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1683 7352d33b Thomas Thrainer
1684 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1685 a6c43c02 Helga Velroyen
      (errcode, msg) = utils.VerifyCertificate(cert_filename)
1686 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1687 7352d33b Thomas Thrainer
1688 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1689 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1690 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1691 69ac3b74 Michele Tartara
                  None,
1692 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1693 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1694 69ac3b74 Michele Tartara
1695 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1696 7352d33b Thomas Thrainer
1697 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1698 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1699 7352d33b Thomas Thrainer
1700 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1701 7352d33b Thomas Thrainer
1702 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1703 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1704 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1705 7352d33b Thomas Thrainer
1706 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1707 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1708 7352d33b Thomas Thrainer
1709 7352d33b Thomas Thrainer
    dangling_instances = {}
1710 7352d33b Thomas Thrainer
    no_node_instances = []
1711 7352d33b Thomas Thrainer
1712 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1713 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1714 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1715 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1716 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1717 7352d33b Thomas Thrainer
1718 7352d33b Thomas Thrainer
    pretty_dangling = [
1719 7352d33b Thomas Thrainer
        "%s (%s)" %
1720 7352d33b Thomas Thrainer
        (node.name,
1721 24e96ef6 Thomas Thrainer
         utils.CommaJoin(inst.name for
1722 24e96ef6 Thomas Thrainer
                         inst in dangling_instances.get(node.uuid, [])))
1723 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1724 7352d33b Thomas Thrainer
1725 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1726 7352d33b Thomas Thrainer
                  None,
1727 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1728 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1729 7352d33b Thomas Thrainer
1730 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1731 7352d33b Thomas Thrainer
                  None,
1732 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1733 24e96ef6 Thomas Thrainer
                  " %s", utils.CommaJoin(inst.name for
1734 24e96ef6 Thomas Thrainer
                                         inst in no_node_instances))
1735 7352d33b Thomas Thrainer
1736 7352d33b Thomas Thrainer
    return not self.bad
1737 7352d33b Thomas Thrainer
1738 7352d33b Thomas Thrainer
1739 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1740 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1741 7352d33b Thomas Thrainer

1742 7352d33b Thomas Thrainer
  """
1743 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1744 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1745 7352d33b Thomas Thrainer
  REQ_BGL = False
1746 7352d33b Thomas Thrainer
1747 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1748 7352d33b Thomas Thrainer
1749 7352d33b Thomas Thrainer
  class NodeImage(object):
1750 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1751 7352d33b Thomas Thrainer

1752 1c3231aa Thomas Thrainer
    @type uuid: string
1753 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1754 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1755 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1756 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1757 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1758 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1759 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1760 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1761 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1762 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1763 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1764 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1765 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1766 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1767 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1768 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1769 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1770 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1771 7352d33b Thomas Thrainer
    @type ghost: boolean
1772 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1773 7352d33b Thomas Thrainer
    @type os_fail: boolean
1774 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1775 7352d33b Thomas Thrainer
    @type oslist: list
1776 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1777 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1778 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1779 7352d33b Thomas Thrainer
    @type pv_min: float
1780 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1781 7352d33b Thomas Thrainer
    @type pv_max: float
1782 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1783 7352d33b Thomas Thrainer

1784 7352d33b Thomas Thrainer
    """
1785 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1786 1c3231aa Thomas Thrainer
      self.uuid = uuid
1787 7352d33b Thomas Thrainer
      self.volumes = {}
1788 7352d33b Thomas Thrainer
      self.instances = []
1789 7352d33b Thomas Thrainer
      self.pinst = []
1790 7352d33b Thomas Thrainer
      self.sinst = []
1791 7352d33b Thomas Thrainer
      self.sbp = {}
1792 7352d33b Thomas Thrainer
      self.mfree = 0
1793 7352d33b Thomas Thrainer
      self.dfree = 0
1794 7352d33b Thomas Thrainer
      self.offline = offline
1795 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1796 7352d33b Thomas Thrainer
      self.rpc_fail = False
1797 7352d33b Thomas Thrainer
      self.lvm_fail = False
1798 7352d33b Thomas Thrainer
      self.hyp_fail = False
1799 7352d33b Thomas Thrainer
      self.ghost = False
1800 7352d33b Thomas Thrainer
      self.os_fail = False
1801 7352d33b Thomas Thrainer
      self.oslist = {}
1802 7352d33b Thomas Thrainer
      self.pv_min = None
1803 7352d33b Thomas Thrainer
      self.pv_max = None
1804 7352d33b Thomas Thrainer
1805 7352d33b Thomas Thrainer
  def ExpandNames(self):
1806 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1807 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1808 7352d33b Thomas Thrainer
1809 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1810 da4a52a3 Thomas Thrainer
    inst_uuids = \
1811 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1812 7352d33b Thomas Thrainer
1813 7352d33b Thomas Thrainer
    self.needed_locks = {
1814 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1815 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1816 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1817 7352d33b Thomas Thrainer
1818 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1819 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1820 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1821 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1822 7352d33b Thomas Thrainer
      }
1823 7352d33b Thomas Thrainer
1824 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1825 7352d33b Thomas Thrainer
1826 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1827 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1828 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1829 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1830 7352d33b Thomas Thrainer
1831 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1832 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1833 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1834 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1835 7352d33b Thomas Thrainer
      # be locked.
1836 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1837 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1838 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1839 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1840 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1841 7352d33b Thomas Thrainer
1842 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1843 7352d33b Thomas Thrainer
1844 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1845 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1846 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1847 7352d33b Thomas Thrainer
1848 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1849 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1850 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1851 7352d33b Thomas Thrainer
1852 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1853 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1854 7352d33b Thomas Thrainer
1855 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
1856 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
1857 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
1858 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
1859 7352d33b Thomas Thrainer
1860 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
1861 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
1862 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
1863 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
1864 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
1865 7352d33b Thomas Thrainer
1866 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
1867 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
1868 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
1869 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
1870 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
1871 7352d33b Thomas Thrainer
1872 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1873 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1874 7352d33b Thomas Thrainer
1875 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
1876 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
1877 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
1878 7352d33b Thomas Thrainer
1879 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
1880 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
1881 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
1882 7352d33b Thomas Thrainer
1883 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1884 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1885 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1886 7352d33b Thomas Thrainer
1887 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1888 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1889 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
1890 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
1891 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
1892 7352d33b Thomas Thrainer
1893 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1894 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1895 7352d33b Thomas Thrainer
1896 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1897 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1898 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1899 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1900 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1901 7352d33b Thomas Thrainer
1902 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1903 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1904 7352d33b Thomas Thrainer

1905 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1906 7352d33b Thomas Thrainer
        mandatory fields
1907 7352d33b Thomas Thrainer
      - check ganeti version
1908 7352d33b Thomas Thrainer

1909 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1910 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1911 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1912 7352d33b Thomas Thrainer
    @rtype: boolean
1913 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1914 7352d33b Thomas Thrainer
         reasonable values in the respose)
1915 7352d33b Thomas Thrainer

1916 7352d33b Thomas Thrainer
    """
1917 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1918 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1919 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1920 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1921 7352d33b Thomas Thrainer
    if test:
1922 7352d33b Thomas Thrainer
      return False
1923 7352d33b Thomas Thrainer
1924 7352d33b Thomas Thrainer
    # compares ganeti version
1925 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1926 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1927 7352d33b Thomas Thrainer
    test = not (remote_version and
1928 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1929 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1930 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1931 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
1932 7352d33b Thomas Thrainer
    if test:
1933 7352d33b Thomas Thrainer
      return False
1934 7352d33b Thomas Thrainer
1935 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1936 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
1937 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
1938 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
1939 7352d33b Thomas Thrainer
    if test:
1940 7352d33b Thomas Thrainer
      return False
1941 7352d33b Thomas Thrainer
1942 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1943 7352d33b Thomas Thrainer
1944 7352d33b Thomas Thrainer
    # full package version
1945 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1946 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
1947 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1948 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1949 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1950 7352d33b Thomas Thrainer
1951 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1952 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1953 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1954 7352d33b Thomas Thrainer
        test = hv_result is not None
1955 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
1956 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1957 7352d33b Thomas Thrainer
1958 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1959 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1960 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1961 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
1962 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
1963 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
1964 7352d33b Thomas Thrainer
1965 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1966 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1967 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
1968 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
1969 7352d33b Thomas Thrainer
1970 7352d33b Thomas Thrainer
    return True
1971 7352d33b Thomas Thrainer
1972 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1973 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1974 7352d33b Thomas Thrainer
    """Check the node time.
1975 7352d33b Thomas Thrainer

1976 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1977 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1978 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1979 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1980 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1981 7352d33b Thomas Thrainer

1982 7352d33b Thomas Thrainer
    """
1983 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1984 7352d33b Thomas Thrainer
    try:
1985 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1986 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1987 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
1988 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
1989 7352d33b Thomas Thrainer
      return
1990 7352d33b Thomas Thrainer
1991 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1992 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1993 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1994 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1995 7352d33b Thomas Thrainer
    else:
1996 7352d33b Thomas Thrainer
      ntime_diff = None
1997 7352d33b Thomas Thrainer
1998 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
1999 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
2000 d0d7d7cf Thomas Thrainer
                  ntime_diff)
2001 7352d33b Thomas Thrainer
2002 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
2003 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
2004 7352d33b Thomas Thrainer

2005 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2006 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2007 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2008 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2009 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
2010 7352d33b Thomas Thrainer
    @param nimg: node image
2011 7352d33b Thomas Thrainer

2012 7352d33b Thomas Thrainer
    """
2013 7352d33b Thomas Thrainer
    if vg_name is None:
2014 7352d33b Thomas Thrainer
      return
2015 7352d33b Thomas Thrainer
2016 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
2017 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
2018 7352d33b Thomas Thrainer
    test = not vglist
2019 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2020 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
2021 7352d33b Thomas Thrainer
    if not test:
2022 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
2023 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
2024 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
2025 7352d33b Thomas Thrainer
2026 7352d33b Thomas Thrainer
    # Check PVs
2027 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
2028 7352d33b Thomas Thrainer
    for em in errmsgs:
2029 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
2030 7352d33b Thomas Thrainer
    if pvminmax is not None:
2031 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
2032 7352d33b Thomas Thrainer
2033 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
2034 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
2035 1bb99a33 Bernardo Dal Seno

2036 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
2037 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
2038 1bb99a33 Bernardo Dal Seno
      node_verify call.
2039 1bb99a33 Bernardo Dal Seno

2040 1bb99a33 Bernardo Dal Seno
    """
2041 1bb99a33 Bernardo Dal Seno
    node_versions = {}
2042 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
2043 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
2044 fb62843c Klaus Aehlig
      if nresult:
2045 fb62843c Klaus Aehlig
        version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
2046 fb62843c Klaus Aehlig
        node_versions[node_uuid] = version
2047 1bb99a33 Bernardo Dal Seno
2048 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
2049 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
2050 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
2051 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
2052 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
2053 1bb99a33 Bernardo Dal Seno
2054 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
2055 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
2056 7352d33b Thomas Thrainer

2057 7352d33b Thomas Thrainer
    @type node_image: dict
2058 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
2059 7352d33b Thomas Thrainer
      L{NodeImage} objects
2060 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2061 7352d33b Thomas Thrainer

2062 7352d33b Thomas Thrainer
    """
2063 7352d33b Thomas Thrainer
    if vg_name is None:
2064 7352d33b Thomas Thrainer
      return
2065 7352d33b Thomas Thrainer
2066 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
2067 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
2068 7352d33b Thomas Thrainer
      return
2069 7352d33b Thomas Thrainer
2070 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
2071 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
2072 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
2073 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
2074 7352d33b Thomas Thrainer
    if not vals:
2075 7352d33b Thomas Thrainer
      return
2076 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
2077 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
2078 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
2079 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
2080 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
2081 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
2082 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
2083 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
2084 7352d33b Thomas Thrainer
2085 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
2086 7352d33b Thomas Thrainer
    """Check the node bridges.
2087 7352d33b Thomas Thrainer

2088 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2089 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2090 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2091 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
2092 7352d33b Thomas Thrainer

2093 7352d33b Thomas Thrainer
    """
2094 7352d33b Thomas Thrainer
    if not bridges:
2095 7352d33b Thomas Thrainer
      return
2096 7352d33b Thomas Thrainer
2097 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
2098 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
2099 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2100 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
2101 7352d33b Thomas Thrainer
    if not test:
2102 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
2103 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
2104 7352d33b Thomas Thrainer
2105 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
2106 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
2107 7352d33b Thomas Thrainer

2108 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2109 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2110 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2111 7352d33b Thomas Thrainer

2112 7352d33b Thomas Thrainer
    """
2113 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
2114 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2115 7352d33b Thomas Thrainer
                  "did not return user scripts information")
2116 7352d33b Thomas Thrainer
2117 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
2118 7352d33b Thomas Thrainer
    if not test:
2119 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2120 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
2121 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
2122 7352d33b Thomas Thrainer
2123 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
2124 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
2125 7352d33b Thomas Thrainer

2126 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2127 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2128 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2129 7352d33b Thomas Thrainer

2130 7352d33b Thomas Thrainer
    """
2131 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
2132 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
2133 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
2134 7352d33b Thomas Thrainer
    if not test:
2135 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
2136 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
2137 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
2138 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
2139 7352d33b Thomas Thrainer
2140 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
2141 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2142 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
2143 7352d33b Thomas Thrainer
    if not test:
2144 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
2145 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
2146 7352d33b Thomas Thrainer
        for anode in nlist:
2147 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
2148 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
2149 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
2150 7352d33b Thomas Thrainer
2151 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
2152 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2153 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
2154 7352d33b Thomas Thrainer
    if not test:
2155 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
2156 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
2157 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
2158 7352d33b Thomas Thrainer
        else:
2159 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
2160 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
2161 7352d33b Thomas Thrainer
2162 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
2163 7352d33b Thomas Thrainer
    """Verify an instance.
2164 7352d33b Thomas Thrainer

2165 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
2166 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
2167 7352d33b Thomas Thrainer
    state.
2168 7352d33b Thomas Thrainer

2169 7352d33b Thomas Thrainer
    """
2170 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
2171 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
2172 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
2173 7352d33b Thomas Thrainer
2174 7352d33b Thomas Thrainer
    node_vol_should = {}
2175 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
2176 7352d33b Thomas Thrainer
2177 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2178 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
2179 7352d33b Thomas Thrainer
                                                            self.group_info)
2180 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
2181 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
2182 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
2183 7352d33b Thomas Thrainer
2184 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
2185 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
2186 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
2187 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
2188 7352d33b Thomas Thrainer
        continue
2189 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
2190 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
2191 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
2192 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
2193 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
2194 7352d33b Thomas Thrainer
2195 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
2196 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
2197 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
2198 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
2199 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
2200 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
2201 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
2202 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
2203 7352d33b Thomas Thrainer
2204 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
2205 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
2206 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
2207 7352d33b Thomas Thrainer
2208 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
2209 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
2210 7352d33b Thomas Thrainer
      # node here
2211 7352d33b Thomas Thrainer
      snode = node_image[nname]
2212 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
2213 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
2214 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
2215 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
2216 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
2217 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
2218 9b0e86e2 Thomas Thrainer
2219 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
2220 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
2221 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
2222 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
2223 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
2224 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
2225 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
2226 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
2227 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
2228 9b0e86e2 Thomas Thrainer
2229 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
2230 d0d7d7cf Thomas Thrainer
2231 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
2232 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
2233 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
2234 da4a52a3 Thomas Thrainer
                  instance.name)
2235 da4a52a3 Thomas Thrainer
2236 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
2237 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
2238 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
2239 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
2240 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
2241 7352d33b Thomas Thrainer
2242 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
2243 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
2244 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
2245 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
2246 c69b147d Bernardo Dal Seno
        # node should have the flag set
2247 c69b147d Bernardo Dal Seno
        es_nodes = [n
2248 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
2249 c69b147d Bernardo Dal Seno
                    if es]
2250 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
2251 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
2252 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
2253 da4a52a3 Thomas Thrainer
                    instance.disk_template,
2254 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
2255 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
2256 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
2257 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
2258 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
2259 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
2260 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
2261 7352d33b Thomas Thrainer
2262 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2263 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
2264 7352d33b Thomas Thrainer
      instance_groups = {}
2265 7352d33b Thomas Thrainer
2266 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
2267 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
2268 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
2269 7352d33b Thomas Thrainer
2270 7352d33b Thomas Thrainer
      pretty_list = [
2271 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
2272 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
2273 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
2274 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
2275 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
2276 7352d33b Thomas Thrainer
                                   reverse=True)]
2277 7352d33b Thomas Thrainer
2278 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
2279 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
2280 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
2281 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
2282 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
2283 7352d33b Thomas Thrainer
2284 7352d33b Thomas Thrainer
    inst_nodes_offline = []
2285 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
2286 7352d33b Thomas Thrainer
      s_img = node_image[snode]
2287 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
2288 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
2289 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
2290 da4a52a3 Thomas Thrainer
                    instance.name)
2291 7352d33b Thomas Thrainer
2292 7352d33b Thomas Thrainer
      if s_img.offline:
2293 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
2294 7352d33b Thomas Thrainer
2295 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
2296 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
2297 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
2298 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
2299 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
2300 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
2301 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
2302 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
2303 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2304 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
2305 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
2306 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
2307 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2308 7352d33b Thomas Thrainer
2309 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
2310 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
2311 7352d33b Thomas Thrainer

2312 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
2313 7352d33b Thomas Thrainer
    reported as unknown.
2314 7352d33b Thomas Thrainer

2315 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
2316 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
2317 7352d33b Thomas Thrainer

2318 7352d33b Thomas Thrainer
    """
2319 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2320 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
2321 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
2322 7352d33b Thomas Thrainer
        # skip non-healthy nodes
2323 7352d33b Thomas Thrainer
        continue
2324 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
2325 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
2326 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2327 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2328 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2329 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2330 b0e8ed3f Santi Raffa
                      "volume %s is unknown", volume,
2331 b0e8ed3f Santi Raffa
                      code=_VerifyErrors.ETYPE_WARNING)
2332 7352d33b Thomas Thrainer
2333 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2334 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2335 7352d33b Thomas Thrainer

2336 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2337 7352d33b Thomas Thrainer
    instances it was primary for.
2338 7352d33b Thomas Thrainer

2339 7352d33b Thomas Thrainer
    """
2340 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2341 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2342 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2343 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2344 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2345 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2346 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2347 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2348 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2349 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2350 1c3231aa Thomas Thrainer
      if n_img.offline or \
2351 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2352 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2353 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2354 9fdb10be Thomas Thrainer
        # information from them; we already list instances living on such
2355 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2356 7352d33b Thomas Thrainer
        continue
2357 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2358 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2359 7352d33b Thomas Thrainer
        needed_mem = 0
2360 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2361 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2362 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2363 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2364 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2365 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2366 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2367 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2368 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2369 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2370 7352d33b Thomas Thrainer
2371 a6c43c02 Helga Velroyen
  def _VerifyClientCertificates(self, nodes, all_nvinfo):
2372 a6c43c02 Helga Velroyen
    """Verifies the consistency of the client certificates.
2373 a6c43c02 Helga Velroyen

2374 a6c43c02 Helga Velroyen
    This includes several aspects:
2375 a6c43c02 Helga Velroyen
      - the individual validation of all nodes' certificates
2376 a6c43c02 Helga Velroyen
      - the consistency of the master candidate certificate map
2377 a6c43c02 Helga Velroyen
      - the consistency of the master candidate certificate map with the
2378 a6c43c02 Helga Velroyen
        certificates that the master candidates are actually using.
2379 a6c43c02 Helga Velroyen

2380 a6c43c02 Helga Velroyen
    @param nodes: the list of nodes to consider in this verification
2381 a6c43c02 Helga Velroyen
    @param all_nvinfo: the map of results of the verify_node call to
2382 a6c43c02 Helga Velroyen
      all nodes
2383 a6c43c02 Helga Velroyen

2384 a6c43c02 Helga Velroyen
    """
2385 a6c43c02 Helga Velroyen
    candidate_certs = self.cfg.GetClusterInfo().candidate_certs
2386 a6c43c02 Helga Velroyen
    if candidate_certs is None or len(candidate_certs) == 0:
2387 a6c43c02 Helga Velroyen
      self._ErrorIf(
2388 a6c43c02 Helga Velroyen
        True, constants.CV_ECLUSTERCLIENTCERT, None,
2389 a6c43c02 Helga Velroyen
        "The cluster's list of master candidate certificates is empty."
2390 a6c43c02 Helga Velroyen
        "If you just updated the cluster, please run"
2391 a6c43c02 Helga Velroyen
        " 'gnt-cluster renew-crypto --new-node-certificates'.")
2392 a6c43c02 Helga Velroyen
      return
2393 a6c43c02 Helga Velroyen
2394 a6c43c02 Helga Velroyen
    self._ErrorIf(
2395 a6c43c02 Helga Velroyen
      len(candidate_certs) != len(set(candidate_certs.values())),
2396 a6c43c02 Helga Velroyen
      constants.CV_ECLUSTERCLIENTCERT, None,
2397 a6c43c02 Helga Velroyen
      "There are at least two master candidates configured to use the same"
2398 a6c43c02 Helga Velroyen
      " certificate.")
2399 a6c43c02 Helga Velroyen
2400 a6c43c02 Helga Velroyen
    # collect the client certificate
2401 a6c43c02 Helga Velroyen
    for node in nodes:
2402 a6c43c02 Helga Velroyen
      if node.offline:
2403 a6c43c02 Helga Velroyen
        continue
2404 a6c43c02 Helga Velroyen
2405 a6c43c02 Helga Velroyen
      nresult = all_nvinfo[node.uuid]
2406 a6c43c02 Helga Velroyen
      if nresult.fail_msg or not nresult.payload:
2407 a6c43c02 Helga Velroyen
        continue
2408 a6c43c02 Helga Velroyen
2409 a6c43c02 Helga Velroyen
      (errcode, msg) = nresult.payload.get(constants.NV_CLIENT_CERT, None)
2410 a6c43c02 Helga Velroyen
2411 a6c43c02 Helga Velroyen
      self._ErrorIf(
2412 a6c43c02 Helga Velroyen
        errcode is not None, constants.CV_ECLUSTERCLIENTCERT, None,
2413 a6c43c02 Helga Velroyen
        "Client certificate of node '%s' failed validation: %s (code '%s')",
2414 a6c43c02 Helga Velroyen
        node.uuid, msg, errcode)
2415 a6c43c02 Helga Velroyen
2416 a6c43c02 Helga Velroyen
      if not errcode:
2417 a6c43c02 Helga Velroyen
        digest = msg
2418 a6c43c02 Helga Velroyen
        if node.master_candidate:
2419 a6c43c02 Helga Velroyen
          if node.uuid in candidate_certs:
2420 a6c43c02 Helga Velroyen
            self._ErrorIf(
2421 a6c43c02 Helga Velroyen
              digest != candidate_certs[node.uuid],
2422 a6c43c02 Helga Velroyen
              constants.CV_ECLUSTERCLIENTCERT, None,
2423 a6c43c02 Helga Velroyen
              "Client certificate digest of master candidate '%s' does not"
2424 a6c43c02 Helga Velroyen
              " match its entry in the cluster's map of master candidate"
2425 a6c43c02 Helga Velroyen
              " certificates. Expected: %s Got: %s", node.uuid,
2426 a6c43c02 Helga Velroyen
              digest, candidate_certs[node.uuid])
2427 a6c43c02 Helga Velroyen
          else:
2428 a6c43c02 Helga Velroyen
            self._ErrorIf(
2429 a6c43c02 Helga Velroyen
              True, constants.CV_ECLUSTERCLIENTCERT, None,
2430 a6c43c02 Helga Velroyen
              "The master candidate '%s' does not have an entry in the"
2431 a6c43c02 Helga Velroyen
              " map of candidate certificates.", node.uuid)
2432 a6c43c02 Helga Velroyen
            self._ErrorIf(
2433 a6c43c02 Helga Velroyen
              digest in candidate_certs.values(),
2434 a6c43c02 Helga Velroyen
              constants.CV_ECLUSTERCLIENTCERT, None,
2435 a6c43c02 Helga Velroyen
              "Master candidate '%s' is using a certificate of another node.",
2436 a6c43c02 Helga Velroyen
              node.uuid)
2437 a6c43c02 Helga Velroyen
        else:
2438 a6c43c02 Helga Velroyen
          self._ErrorIf(
2439 a6c43c02 Helga Velroyen
            node.uuid in candidate_certs,
2440 a6c43c02 Helga Velroyen
            constants.CV_ECLUSTERCLIENTCERT, None,
2441 a6c43c02 Helga Velroyen
            "Node '%s' is not a master candidate, but still listed in the"
2442 a6c43c02 Helga Velroyen
            " map of master candidate certificates.", node.uuid)
2443 a6c43c02 Helga Velroyen
          self._ErrorIf(
2444 a6c43c02 Helga Velroyen
            (node.uuid not in candidate_certs) and
2445 a6c43c02 Helga Velroyen
              (digest in candidate_certs.values()),
2446 a6c43c02 Helga Velroyen
            constants.CV_ECLUSTERCLIENTCERT, None,
2447 a6c43c02 Helga Velroyen
            "Node '%s' is not a master candidate and is incorrectly using a"
2448 a6c43c02 Helga Velroyen
            " certificate of another node which is master candidate.",
2449 a6c43c02 Helga Velroyen
            node.uuid)
2450 a6c43c02 Helga Velroyen
2451 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2452 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2453 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2454 7352d33b Thomas Thrainer

2455 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2456 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2457 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2458 7352d33b Thomas Thrainer

2459 7352d33b Thomas Thrainer
    """
2460 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2461 7352d33b Thomas Thrainer
    files2nodefn = [
2462 7352d33b Thomas Thrainer
      (files_all, None),
2463 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2464 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2465 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2466 7352d33b Thomas Thrainer
      ]
2467 7352d33b Thomas Thrainer
2468 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2469 7352d33b Thomas Thrainer
    nodefiles = {}
2470 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2471 7352d33b Thomas Thrainer
      if fn is None:
2472 1c3231aa Thomas Thrainer
        filenodes = nodes
2473 7352d33b Thomas Thrainer
      else:
2474 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2475 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2476 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2477 7352d33b Thomas Thrainer
                       for filename in files)
2478 7352d33b Thomas Thrainer
2479 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2480 7352d33b Thomas Thrainer
2481 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2482 7352d33b Thomas Thrainer
    ignore_nodes = set()
2483 7352d33b Thomas Thrainer
2484 1c3231aa Thomas Thrainer
    for node in nodes:
2485 7352d33b Thomas Thrainer
      if node.offline:
2486 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2487 7352d33b Thomas Thrainer
        continue
2488 7352d33b Thomas Thrainer
2489 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2490 7352d33b Thomas Thrainer
2491 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2492 7352d33b Thomas Thrainer
        node_files = None
2493 7352d33b Thomas Thrainer
      else:
2494 a6c43c02 Helga Velroyen
        fingerprints = nresult.payload.get(constants.NV_FILELIST, {})
2495 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2496 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2497 7352d33b Thomas Thrainer
        del fingerprints
2498 7352d33b Thomas Thrainer
2499 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2500 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2501 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2502 7352d33b Thomas Thrainer
      if test:
2503 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2504 7352d33b Thomas Thrainer
        continue
2505 7352d33b Thomas Thrainer
2506 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2507 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2508 7352d33b Thomas Thrainer
        assert filename in nodefiles
2509 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2510 7352d33b Thomas Thrainer
2511 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2512 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2513 7352d33b Thomas Thrainer
2514 7352d33b Thomas Thrainer
      # Nodes having the file
2515 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2516 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2517 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2518 7352d33b Thomas Thrainer
2519 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2520 7352d33b Thomas Thrainer
2521 7352d33b Thomas Thrainer
      # Nodes missing file
2522 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2523 7352d33b Thomas Thrainer
2524 7352d33b Thomas Thrainer
      if filename in files_opt:
2525 7352d33b Thomas Thrainer
        # All or no nodes
2526 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2527 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2528 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2529 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2530 1c3231aa Thomas Thrainer
                      filename,
2531 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2532 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2533 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2534 7352d33b Thomas Thrainer
      else:
2535 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2536 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2537 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2538 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2539 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2540 7352d33b Thomas Thrainer
2541 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2542 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2543 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2544 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2545 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2546 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2547 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2548 7352d33b Thomas Thrainer
2549 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2550 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2551 7352d33b Thomas Thrainer
      if test:
2552 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2553 1c3231aa Thomas Thrainer
                    (idx + 1,
2554 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2555 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2556 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2557 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2558 7352d33b Thomas Thrainer
      else:
2559 7352d33b Thomas Thrainer
        variants = []
2560 7352d33b Thomas Thrainer
2561 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2562 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2563 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2564 7352d33b Thomas Thrainer
2565 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbdHelper(self, ninfo, nresult, drbd_helper):
2566 9af7ece3 Helga Velroyen
    """Verify the drbd helper.
2567 7352d33b Thomas Thrainer

2568 7352d33b Thomas Thrainer
    """
2569 7352d33b Thomas Thrainer
    if drbd_helper:
2570 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2571 7352d33b Thomas Thrainer
      test = (helper_result is None)
2572 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2573 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2574 7352d33b Thomas Thrainer
      if helper_result:
2575 7352d33b Thomas Thrainer
        status, payload = helper_result
2576 7352d33b Thomas Thrainer
        test = not status
2577 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2578 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2579 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2580 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2581 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2582 7352d33b Thomas Thrainer
2583 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2584 9af7ece3 Helga Velroyen
                      drbd_map):
2585 9af7ece3 Helga Velroyen
    """Verifies and the node DRBD status.
2586 9af7ece3 Helga Velroyen

2587 9af7ece3 Helga Velroyen
    @type ninfo: L{objects.Node}
2588 9af7ece3 Helga Velroyen
    @param ninfo: the node to check
2589 9af7ece3 Helga Velroyen
    @param nresult: the remote results for the node
2590 9af7ece3 Helga Velroyen
    @param instanceinfo: the dict of instances
2591 9af7ece3 Helga Velroyen
    @param drbd_helper: the configured DRBD usermode helper
2592 9af7ece3 Helga Velroyen
    @param drbd_map: the DRBD map as returned by
2593 9af7ece3 Helga Velroyen
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2594 9af7ece3 Helga Velroyen

2595 9af7ece3 Helga Velroyen
    """
2596 9af7ece3 Helga Velroyen
    self._VerifyNodeDrbdHelper(ninfo, nresult, drbd_helper)
2597 9af7ece3 Helga Velroyen
2598 7352d33b Thomas Thrainer
    # compute the DRBD minors
2599 7352d33b Thomas Thrainer
    node_drbd = {}
2600 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2601 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2602 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2603 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2604 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2605 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2606 7352d33b Thomas Thrainer
        # unallocated minor in use)
2607 7352d33b Thomas Thrainer
      if test:
2608 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2609 7352d33b Thomas Thrainer
      else:
2610 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2611 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2612 7352d33b Thomas Thrainer
2613 7352d33b Thomas Thrainer
    # and now check them
2614 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2615 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2616 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2617 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2618 7352d33b Thomas Thrainer
    if test:
2619 7352d33b Thomas Thrainer
      # we cannot check drbd status
2620 7352d33b Thomas Thrainer
      return
2621 7352d33b Thomas Thrainer
2622 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2623 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2624 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2625 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2626 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2627 7352d33b Thomas Thrainer
    for minor in used_minors:
2628 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2629 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2630 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2631 7352d33b Thomas Thrainer
2632 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2633 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2634 7352d33b Thomas Thrainer

2635 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2636 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2637 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2638 7352d33b Thomas Thrainer
    @param nimg: the node image object
2639 7352d33b Thomas Thrainer

2640 7352d33b Thomas Thrainer
    """
2641 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2642 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2643 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2644 7352d33b Thomas Thrainer
                           for v in remote_os))
2645 7352d33b Thomas Thrainer
2646 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2647 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2648 7352d33b Thomas Thrainer
2649 7352d33b Thomas Thrainer
    nimg.os_fail = test
2650 7352d33b Thomas Thrainer
2651 7352d33b Thomas Thrainer
    if test:
2652 7352d33b Thomas Thrainer
      return
2653 7352d33b Thomas Thrainer
2654 7352d33b Thomas Thrainer
    os_dict = {}
2655 7352d33b Thomas Thrainer
2656 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2657 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2658 7352d33b Thomas Thrainer
2659 7352d33b Thomas Thrainer
      if name not in os_dict:
2660 7352d33b Thomas Thrainer
        os_dict[name] = []
2661 7352d33b Thomas Thrainer
2662 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2663 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2664 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2665 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2666 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2667 7352d33b Thomas Thrainer
2668 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2669 7352d33b Thomas Thrainer
2670 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2671 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2672 7352d33b Thomas Thrainer

2673 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2674 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2675 7352d33b Thomas Thrainer
    @param nimg: the node image object
2676 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2677 7352d33b Thomas Thrainer

2678 7352d33b Thomas Thrainer
    """
2679 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2680 7352d33b Thomas Thrainer
2681 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2682 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2683 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2684 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2685 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2686 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2687 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2688 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2689 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2690 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2691 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2692 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2693 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2694 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2695 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2696 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2697 7352d33b Thomas Thrainer
      if test:
2698 7352d33b Thomas Thrainer
        continue
2699 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2700 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2701 7352d33b Thomas Thrainer
      if not b_status:
2702 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2703 7352d33b Thomas Thrainer
        continue
2704 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2705 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2706 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2707 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2708 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2709 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2710 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2711 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2712 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2713 7352d33b Thomas Thrainer
2714 7352d33b Thomas Thrainer
    # check any missing OSes
2715 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2716 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2717 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2718 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2719 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2720 7352d33b Thomas Thrainer
2721 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2722 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2723 7352d33b Thomas Thrainer

2724 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2725 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2726 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2727 7352d33b Thomas Thrainer
    @type is_master: bool
2728 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2729 7352d33b Thomas Thrainer

2730 7352d33b Thomas Thrainer
    """
2731 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2732 7352d33b Thomas Thrainer
    if (is_master and
2733 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2734 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2735 7352d33b Thomas Thrainer
      try:
2736 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2737 7352d33b Thomas Thrainer
      except KeyError:
2738 7352d33b Thomas Thrainer
        # This should never happen
2739 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2740 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2741 7352d33b Thomas Thrainer
      else:
2742 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2743 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2744 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2745 7352d33b Thomas Thrainer
    else:
2746 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2747 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2748 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2749 7352d33b Thomas Thrainer
                    " paths")
2750 7352d33b Thomas Thrainer
2751 4b322a76 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult, file_disk_template,
2752 4b322a76 Helga Velroyen
                          verify_key, error_key):
2753 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2754 9c1c3c19 Helga Velroyen

2755 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2756 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2757 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2758 4b322a76 Helga Velroyen
    @type file_disk_template: string
2759 4b322a76 Helga Velroyen
    @param file_disk_template: file-based disk template, whose directory
2760 4b322a76 Helga Velroyen
        is supposed to be verified
2761 4b322a76 Helga Velroyen
    @type verify_key: string
2762 4b322a76 Helga Velroyen
    @param verify_key: key for the verification map of this file
2763 4b322a76 Helga Velroyen
        verification step
2764 4b322a76 Helga Velroyen
    @param error_key: error key to be added to the verification results
2765 4b322a76 Helga Velroyen
        in case something goes wrong in this verification step
2766 9c1c3c19 Helga Velroyen

2767 9c1c3c19 Helga Velroyen
    """
2768 5a904197 Santi Raffa
    assert (file_disk_template in utils.storage.GetDiskTemplatesOfStorageTypes(
2769 5a904197 Santi Raffa
              constants.ST_FILE, constants.ST_SHARED_FILE
2770 5a904197 Santi Raffa
           ))
2771 5a904197 Santi Raffa
2772 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2773 4b322a76 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(file_disk_template):
2774 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2775 4b322a76 Helga Velroyen
          verify_key in nresult,
2776 4b322a76 Helga Velroyen
          error_key, ninfo.name,
2777 4b322a76 Helga Velroyen
          "The configured %s storage path is unusable: %s" %
2778 4b322a76 Helga Velroyen
          (file_disk_template, nresult.get(verify_key)))
2779 4b322a76 Helga Velroyen
2780 4b322a76 Helga Velroyen
  def _VerifyFileStoragePaths(self, ninfo, nresult):
2781 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2782 4b322a76 Helga Velroyen

2783 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2784 4b322a76 Helga Velroyen

2785 4b322a76 Helga Velroyen
    """
2786 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2787 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_FILE,
2788 4b322a76 Helga Velroyen
        constants.NV_FILE_STORAGE_PATH,
2789 4b322a76 Helga Velroyen
        constants.CV_ENODEFILESTORAGEPATHUNUSABLE)
2790 4b322a76 Helga Velroyen
2791 4b322a76 Helga Velroyen
  def _VerifySharedFileStoragePaths(self, ninfo, nresult):
2792 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2793 4b322a76 Helga Velroyen

2794 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2795 4b322a76 Helga Velroyen

2796 4b322a76 Helga Velroyen
    """
2797 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2798 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_SHARED_FILE,
2799 4b322a76 Helga Velroyen
        constants.NV_SHARED_FILE_STORAGE_PATH,
2800 4b322a76 Helga Velroyen
        constants.CV_ENODESHAREDFILESTORAGEPATHUNUSABLE)
2801 9c1c3c19 Helga Velroyen
2802 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2803 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2804 7352d33b Thomas Thrainer

2805 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2806 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2807 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2808 7352d33b Thomas Thrainer

2809 7352d33b Thomas Thrainer
    """
2810 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2811 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2812 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2813 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2814 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2815 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2816 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2817 7352d33b Thomas Thrainer
2818 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2819 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2820 7352d33b Thomas Thrainer

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

2824 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2825 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2826 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2827 7352d33b Thomas Thrainer
    @param nimg: the node image object
2828 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2829 7352d33b Thomas Thrainer

2830 7352d33b Thomas Thrainer
    """
2831 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2832 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2833 7352d33b Thomas Thrainer
    if vg_name is None:
2834 7352d33b Thomas Thrainer
      pass
2835 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2836 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2837 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2838 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2839 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2840 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2841 7352d33b Thomas Thrainer
    else:
2842 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2843 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2844 7352d33b Thomas Thrainer
2845 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2846 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2847 7352d33b Thomas Thrainer

2848 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2849 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2850 7352d33b Thomas Thrainer
    list key.
2851 7352d33b Thomas Thrainer

2852 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2853 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2854 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2855 7352d33b Thomas Thrainer
    @param nimg: the node image object
2856 7352d33b Thomas Thrainer

2857 7352d33b Thomas Thrainer
    """
2858 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2859 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2860 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2861 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2862 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2863 7352d33b Thomas Thrainer
    if test:
2864 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2865 7352d33b Thomas Thrainer
    else:
2866 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
2867 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
2868 7352d33b Thomas Thrainer
2869 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2870 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2871 7352d33b Thomas Thrainer

2872 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2873 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2874 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2875 7352d33b Thomas Thrainer
    @param nimg: the node image object
2876 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2877 7352d33b Thomas Thrainer

2878 7352d33b Thomas Thrainer
    """
2879 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2880 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2881 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2882 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2883 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
2884 7352d33b Thomas Thrainer
    if not test:
2885 7352d33b Thomas Thrainer
      try:
2886 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2887 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2888 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2889 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
2890 7352d33b Thomas Thrainer
2891 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2892 7352d33b Thomas Thrainer
    if vg_name is not None:
2893 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2894 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2895 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2896 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
2897 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
2898 7352d33b Thomas Thrainer
      if not test:
2899 7352d33b Thomas Thrainer
        try:
2900 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2901 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2902 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2903 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
2904 7352d33b Thomas Thrainer
2905 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
2906 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2907 7352d33b Thomas Thrainer

2908 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
2909 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
2910 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
2911 7352d33b Thomas Thrainer
    @param node_image: Node objects
2912 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
2913 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2914 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2915 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2916 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2917 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2918 7352d33b Thomas Thrainer

2919 7352d33b Thomas Thrainer
    """
2920 7352d33b Thomas Thrainer
    node_disks = {}
2921 0c3d9c7c Thomas Thrainer
    node_disks_dev_inst_only = {}
2922 7352d33b Thomas Thrainer
    diskless_instances = set()
2923 099ed3b2 Klaus Aehlig
    nodisk_instances = set()
2924 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2925 7352d33b Thomas Thrainer
2926 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
2927 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
2928 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
2929 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
2930 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
2931 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
2932 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
2933 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
2934 7352d33b Thomas Thrainer
2935 7352d33b Thomas Thrainer
      if not disks:
2936 099ed3b2 Klaus Aehlig
        nodisk_instances.update(uuid for uuid in node_inst_uuids
2937 099ed3b2 Klaus Aehlig
                                if instanceinfo[uuid].disk_template != diskless)
2938 7352d33b Thomas Thrainer
        # No need to collect data
2939 7352d33b Thomas Thrainer
        continue
2940 7352d33b Thomas Thrainer
2941 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
2942 7352d33b Thomas Thrainer
2943 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2944 0c3d9c7c Thomas Thrainer
      dev_inst_only = []
2945 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
2946 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
2947 da4a52a3 Thomas Thrainer
                                          self.cfg)
2948 0c3d9c7c Thomas Thrainer
        dev_inst_only.append((anno_disk, instanceinfo[inst_uuid]))
2949 7352d33b Thomas Thrainer
2950 0c3d9c7c Thomas Thrainer
      node_disks_dev_inst_only[nuuid] = dev_inst_only
2951 7352d33b Thomas Thrainer
2952 0c3d9c7c Thomas Thrainer
    assert len(node_disks) == len(node_disks_dev_inst_only)
2953 7352d33b Thomas Thrainer
2954 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2955 0c3d9c7c Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(
2956 0c3d9c7c Thomas Thrainer
               node_disks.keys(), node_disks_dev_inst_only)
2957 7352d33b Thomas Thrainer
2958 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2959 7352d33b Thomas Thrainer
2960 7352d33b Thomas Thrainer
    instdisk = {}
2961 7352d33b Thomas Thrainer
2962 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
2963 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
2964 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
2965 7352d33b Thomas Thrainer
2966 7352d33b Thomas Thrainer
      if nres.offline:
2967 7352d33b Thomas Thrainer
        # No data from this node
2968 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2969 7352d33b Thomas Thrainer
      else:
2970 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2971 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
2972 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
2973 7352d33b Thomas Thrainer
        if msg:
2974 7352d33b Thomas Thrainer
          # No data from this node
2975 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2976 7352d33b Thomas Thrainer
        else:
2977 7352d33b Thomas Thrainer
          data = []
2978 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2979 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2980 7352d33b Thomas Thrainer
              data.append(i)
2981 7352d33b Thomas Thrainer
            else:
2982 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2983 1c3231aa Thomas Thrainer
                              node.name, idx, i)
2984 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2985 7352d33b Thomas Thrainer
2986 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
2987 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
2988 da4a52a3 Thomas Thrainer
          .append(status)
2989 7352d33b Thomas Thrainer
2990 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2991 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
2992 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
2993 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
2994 099ed3b2 Klaus Aehlig
    # ...and disk-full instances that happen to have no disks
2995 099ed3b2 Klaus Aehlig
    for inst_uuid in nodisk_instances:
2996 099ed3b2 Klaus Aehlig
      assert inst_uuid not in instdisk
2997 099ed3b2 Klaus Aehlig
      instdisk[inst_uuid] = {}
2998 7352d33b Thomas Thrainer
2999 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
3000 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
3001 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
3002 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
3003 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
3004 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
3005 7352d33b Thomas Thrainer
    if __debug__:
3006 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
3007 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
3008 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
3009 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
3010 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
3011 7352d33b Thomas Thrainer
3012 7352d33b Thomas Thrainer
    return instdisk
3013 7352d33b Thomas Thrainer
3014 7352d33b Thomas Thrainer
  @staticmethod
3015 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
3016 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
3017 7352d33b Thomas Thrainer

3018 7352d33b Thomas Thrainer
    """
3019 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
3020 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
3021 7352d33b Thomas Thrainer
                 not node.offline)]
3022 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
3023 7352d33b Thomas Thrainer
3024 7352d33b Thomas Thrainer
    return map(itertools.cycle,
3025 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
3026 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
3027 7352d33b Thomas Thrainer
                                                  keyfunc)])
3028 7352d33b Thomas Thrainer
3029 7352d33b Thomas Thrainer
  @classmethod
3030 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
3031 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
3032 7352d33b Thomas Thrainer

3033 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
3034 7352d33b Thomas Thrainer
    every other group.
3035 7352d33b Thomas Thrainer

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

3040 7352d33b Thomas Thrainer
    """
3041 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
3042 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
3043 7352d33b Thomas Thrainer
3044 7352d33b Thomas Thrainer
    return (online_nodes,
3045 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
3046 7352d33b Thomas Thrainer
                 for name in online_nodes))
3047 7352d33b Thomas Thrainer
3048 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
3049 7352d33b Thomas Thrainer
    """Build hooks env.
3050 7352d33b Thomas Thrainer

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

3054 7352d33b Thomas Thrainer
    """
3055 7352d33b Thomas Thrainer
    env = {
3056 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
3057 7352d33b Thomas Thrainer
      }
3058 7352d33b Thomas Thrainer
3059 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
3060 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
3061 7352d33b Thomas Thrainer
3062 7352d33b Thomas Thrainer
    return env
3063 7352d33b Thomas Thrainer
3064 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
3065 7352d33b Thomas Thrainer
    """Build hooks nodes.
3066 7352d33b Thomas Thrainer

3067 7352d33b Thomas Thrainer
    """
3068 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
3069 7352d33b Thomas Thrainer
3070 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3071 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
3072 7352d33b Thomas Thrainer

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

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

3434 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
3435 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
3436 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
3437 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
3438 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
3439 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
3440 7352d33b Thomas Thrainer
        and hook results
3441 7352d33b Thomas Thrainer

3442 7352d33b Thomas Thrainer
    """
3443 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
3444 7352d33b Thomas Thrainer
    # and are only interested in their results
3445 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3446 7352d33b Thomas Thrainer
      # empty node group
3447 7352d33b Thomas Thrainer
      pass
3448 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
3449 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
3450 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
3451 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
3452 7352d33b Thomas Thrainer
3453 7352d33b Thomas Thrainer
      for node_name in hooks_results:
3454 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
3455 7352d33b Thomas Thrainer
        msg = res.fail_msg
3456 7352d33b Thomas Thrainer
        test = msg and not res.offline
3457 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3458 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
3459 7352d33b Thomas Thrainer
        if res.offline or msg:
3460 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
3461 7352d33b Thomas Thrainer
          # an error.
3462 7352d33b Thomas Thrainer
          continue
3463 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
3464 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
3465 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3466 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
3467 7352d33b Thomas Thrainer
          if test:
3468 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3469 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3470 7352d33b Thomas Thrainer
            lu_result = False
3471 7352d33b Thomas Thrainer
3472 7352d33b Thomas Thrainer
    return lu_result
3473 7352d33b Thomas Thrainer
3474 7352d33b Thomas Thrainer
3475 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3476 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3477 7352d33b Thomas Thrainer

3478 7352d33b Thomas Thrainer
  """
3479 7352d33b Thomas Thrainer
  REQ_BGL = False
3480 7352d33b Thomas Thrainer
3481 7352d33b Thomas Thrainer
  def ExpandNames(self):
3482 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3483 7352d33b Thomas Thrainer
    self.needed_locks = {
3484 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3485 7352d33b Thomas Thrainer
      }
3486 7352d33b Thomas Thrainer
3487 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3488 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3489 7352d33b Thomas Thrainer
3490 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3491 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3492 7352d33b Thomas Thrainer
                           for group in group_names])