Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ 06c2fb4a

History | View | Annotate | Download (104.7 kB)

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

67 7352d33b Thomas Thrainer
  """
68 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
69 7352d33b Thomas Thrainer
    """Activate the master IP.
70 7352d33b Thomas Thrainer

71 7352d33b Thomas Thrainer
    """
72 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
73 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
74 7352d33b Thomas Thrainer
    result = self.rpc.call_node_activate_master_ip(master_params.name,
75 7352d33b Thomas Thrainer
                                                   master_params, ems)
76 7352d33b Thomas Thrainer
    result.Raise("Could not activate the master IP")
77 7352d33b Thomas Thrainer
78 7352d33b Thomas Thrainer
79 7352d33b Thomas Thrainer
class LUClusterDeactivateMasterIp(NoHooksLU):
80 7352d33b Thomas Thrainer
  """Deactivate the master IP on the master node.
81 7352d33b Thomas Thrainer

82 7352d33b Thomas Thrainer
  """
83 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
84 7352d33b Thomas Thrainer
    """Deactivate the master IP.
85 7352d33b Thomas Thrainer

86 7352d33b Thomas Thrainer
    """
87 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
88 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
89 7352d33b Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.name,
90 7352d33b Thomas Thrainer
                                                     master_params, ems)
91 7352d33b Thomas Thrainer
    result.Raise("Could not deactivate the master IP")
92 7352d33b Thomas Thrainer
93 7352d33b Thomas Thrainer
94 7352d33b Thomas Thrainer
class LUClusterConfigQuery(NoHooksLU):
95 7352d33b Thomas Thrainer
  """Return configuration values.
96 7352d33b Thomas Thrainer

97 7352d33b Thomas Thrainer
  """
98 7352d33b Thomas Thrainer
  REQ_BGL = False
99 7352d33b Thomas Thrainer
100 7352d33b Thomas Thrainer
  def CheckArguments(self):
101 5eacbcae Thomas Thrainer
    self.cq = ClusterQuery(None, self.op.output_fields, False)
102 7352d33b Thomas Thrainer
103 7352d33b Thomas Thrainer
  def ExpandNames(self):
104 7352d33b Thomas Thrainer
    self.cq.ExpandNames(self)
105 7352d33b Thomas Thrainer
106 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
107 7352d33b Thomas Thrainer
    self.cq.DeclareLocks(self, level)
108 7352d33b Thomas Thrainer
109 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
110 7352d33b Thomas Thrainer
    result = self.cq.OldStyleQuery(self)
111 7352d33b Thomas Thrainer
112 7352d33b Thomas Thrainer
    assert len(result) == 1
113 7352d33b Thomas Thrainer
114 7352d33b Thomas Thrainer
    return result[0]
115 7352d33b Thomas Thrainer
116 7352d33b Thomas Thrainer
117 7352d33b Thomas Thrainer
class LUClusterDestroy(LogicalUnit):
118 7352d33b Thomas Thrainer
  """Logical unit for destroying the cluster.
119 7352d33b Thomas Thrainer

120 7352d33b Thomas Thrainer
  """
121 7352d33b Thomas Thrainer
  HPATH = "cluster-destroy"
122 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
123 7352d33b Thomas Thrainer
124 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
125 7352d33b Thomas Thrainer
    """Build hooks env.
126 7352d33b Thomas Thrainer

127 7352d33b Thomas Thrainer
    """
128 7352d33b Thomas Thrainer
    return {
129 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
130 7352d33b Thomas Thrainer
      }
131 7352d33b Thomas Thrainer
132 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
133 7352d33b Thomas Thrainer
    """Build hooks nodes.
134 7352d33b Thomas Thrainer

135 7352d33b Thomas Thrainer
    """
136 7352d33b Thomas Thrainer
    return ([], [])
137 7352d33b Thomas Thrainer
138 7352d33b Thomas Thrainer
  def CheckPrereq(self):
139 7352d33b Thomas Thrainer
    """Check prerequisites.
140 7352d33b Thomas Thrainer

141 7352d33b Thomas Thrainer
    This checks whether the cluster is empty.
142 7352d33b Thomas Thrainer

143 7352d33b Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
144 7352d33b Thomas Thrainer

145 7352d33b Thomas Thrainer
    """
146 7352d33b Thomas Thrainer
    master = self.cfg.GetMasterNode()
147 7352d33b Thomas Thrainer
148 7352d33b Thomas Thrainer
    nodelist = self.cfg.GetNodeList()
149 7352d33b Thomas Thrainer
    if len(nodelist) != 1 or nodelist[0] != master:
150 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d node(s) in"
151 7352d33b Thomas Thrainer
                                 " this cluster." % (len(nodelist) - 1),
152 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
153 7352d33b Thomas Thrainer
    instancelist = self.cfg.GetInstanceList()
154 7352d33b Thomas Thrainer
    if instancelist:
155 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("There are still %d instance(s) in"
156 7352d33b Thomas Thrainer
                                 " this cluster." % len(instancelist),
157 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
158 7352d33b Thomas Thrainer
159 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
160 7352d33b Thomas Thrainer
    """Destroys the cluster.
161 7352d33b Thomas Thrainer

162 7352d33b Thomas Thrainer
    """
163 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
164 7352d33b Thomas Thrainer
165 7352d33b Thomas Thrainer
    # Run post hooks on master node before it's removed
166 5eacbcae Thomas Thrainer
    RunPostHook(self, master_params.name)
167 7352d33b Thomas Thrainer
168 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
169 7352d33b Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.name,
170 7352d33b Thomas Thrainer
                                                     master_params, ems)
171 7352d33b Thomas Thrainer
    if result.fail_msg:
172 7352d33b Thomas Thrainer
      self.LogWarning("Error disabling the master IP address: %s",
173 7352d33b Thomas Thrainer
                      result.fail_msg)
174 7352d33b Thomas Thrainer
175 7352d33b Thomas Thrainer
    return master_params.name
176 7352d33b Thomas Thrainer
177 7352d33b Thomas Thrainer
178 7352d33b Thomas Thrainer
class LUClusterPostInit(LogicalUnit):
179 7352d33b Thomas Thrainer
  """Logical unit for running hooks after cluster initialization.
180 7352d33b Thomas Thrainer

181 7352d33b Thomas Thrainer
  """
182 7352d33b Thomas Thrainer
  HPATH = "cluster-init"
183 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
184 7352d33b Thomas Thrainer
185 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
186 7352d33b Thomas Thrainer
    """Build hooks env.
187 7352d33b Thomas Thrainer

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

196 7352d33b Thomas Thrainer
    """
197 7352d33b Thomas Thrainer
    return ([], [self.cfg.GetMasterNode()])
198 7352d33b Thomas Thrainer
199 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
200 7352d33b Thomas Thrainer
    """Nothing to do.
201 7352d33b Thomas Thrainer

202 7352d33b Thomas Thrainer
    """
203 7352d33b Thomas Thrainer
    return True
204 7352d33b Thomas Thrainer
205 7352d33b Thomas Thrainer
206 5eacbcae Thomas Thrainer
class ClusterQuery(QueryBase):
207 7352d33b Thomas Thrainer
  FIELDS = query.CLUSTER_FIELDS
208 7352d33b Thomas Thrainer
209 7352d33b Thomas Thrainer
  #: Do not sort (there is only one item)
210 7352d33b Thomas Thrainer
  SORT_FIELD = None
211 7352d33b Thomas Thrainer
212 7352d33b Thomas Thrainer
  def ExpandNames(self, lu):
213 7352d33b Thomas Thrainer
    lu.needed_locks = {}
214 7352d33b Thomas Thrainer
215 7352d33b Thomas Thrainer
    # The following variables interact with _QueryBase._GetNames
216 7352d33b Thomas Thrainer
    self.wanted = locking.ALL_SET
217 7352d33b Thomas Thrainer
    self.do_locking = self.use_locking
218 7352d33b Thomas Thrainer
219 7352d33b Thomas Thrainer
    if self.do_locking:
220 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Can not use locking for cluster queries",
221 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
222 7352d33b Thomas Thrainer
223 7352d33b Thomas Thrainer
  def DeclareLocks(self, lu, level):
224 7352d33b Thomas Thrainer
    pass
225 7352d33b Thomas Thrainer
226 7352d33b Thomas Thrainer
  def _GetQueryData(self, lu):
227 7352d33b Thomas Thrainer
    """Computes the list of nodes and their attributes.
228 7352d33b Thomas Thrainer

229 7352d33b Thomas Thrainer
    """
230 7352d33b Thomas Thrainer
    # Locking is not used
231 7352d33b Thomas Thrainer
    assert not (compat.any(lu.glm.is_owned(level)
232 7352d33b Thomas Thrainer
                           for level in locking.LEVELS
233 7352d33b Thomas Thrainer
                           if level != locking.LEVEL_CLUSTER) or
234 7352d33b Thomas Thrainer
                self.do_locking or self.use_locking)
235 7352d33b Thomas Thrainer
236 7352d33b Thomas Thrainer
    if query.CQ_CONFIG in self.requested_data:
237 7352d33b Thomas Thrainer
      cluster = lu.cfg.GetClusterInfo()
238 7352d33b Thomas Thrainer
    else:
239 7352d33b Thomas Thrainer
      cluster = NotImplemented
240 7352d33b Thomas Thrainer
241 7352d33b Thomas Thrainer
    if query.CQ_QUEUE_DRAINED in self.requested_data:
242 7352d33b Thomas Thrainer
      drain_flag = os.path.exists(pathutils.JOB_QUEUE_DRAIN_FILE)
243 7352d33b Thomas Thrainer
    else:
244 7352d33b Thomas Thrainer
      drain_flag = NotImplemented
245 7352d33b Thomas Thrainer
246 7352d33b Thomas Thrainer
    if query.CQ_WATCHER_PAUSE in self.requested_data:
247 7352d33b Thomas Thrainer
      master_name = lu.cfg.GetMasterNode()
248 7352d33b Thomas Thrainer
249 7352d33b Thomas Thrainer
      result = lu.rpc.call_get_watcher_pause(master_name)
250 7352d33b Thomas Thrainer
      result.Raise("Can't retrieve watcher pause from master node '%s'" %
251 7352d33b Thomas Thrainer
                   master_name)
252 7352d33b Thomas Thrainer
253 7352d33b Thomas Thrainer
      watcher_pause = result.payload
254 7352d33b Thomas Thrainer
    else:
255 7352d33b Thomas Thrainer
      watcher_pause = NotImplemented
256 7352d33b Thomas Thrainer
257 7352d33b Thomas Thrainer
    return query.ClusterQueryData(cluster, drain_flag, watcher_pause)
258 7352d33b Thomas Thrainer
259 7352d33b Thomas Thrainer
260 7352d33b Thomas Thrainer
class LUClusterQuery(NoHooksLU):
261 7352d33b Thomas Thrainer
  """Query cluster configuration.
262 7352d33b Thomas Thrainer

263 7352d33b Thomas Thrainer
  """
264 7352d33b Thomas Thrainer
  REQ_BGL = False
265 7352d33b Thomas Thrainer
266 7352d33b Thomas Thrainer
  def ExpandNames(self):
267 7352d33b Thomas Thrainer
    self.needed_locks = {}
268 7352d33b Thomas Thrainer
269 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
270 7352d33b Thomas Thrainer
    """Return cluster config.
271 7352d33b Thomas Thrainer

272 7352d33b Thomas Thrainer
    """
273 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
274 7352d33b Thomas Thrainer
    os_hvp = {}
275 7352d33b Thomas Thrainer
276 7352d33b Thomas Thrainer
    # Filter just for enabled hypervisors
277 7352d33b Thomas Thrainer
    for os_name, hv_dict in cluster.os_hvp.items():
278 7352d33b Thomas Thrainer
      os_hvp[os_name] = {}
279 7352d33b Thomas Thrainer
      for hv_name, hv_params in hv_dict.items():
280 7352d33b Thomas Thrainer
        if hv_name in cluster.enabled_hypervisors:
281 7352d33b Thomas Thrainer
          os_hvp[os_name][hv_name] = hv_params
282 7352d33b Thomas Thrainer
283 7352d33b Thomas Thrainer
    # Convert ip_family to ip_version
284 7352d33b Thomas Thrainer
    primary_ip_version = constants.IP4_VERSION
285 7352d33b Thomas Thrainer
    if cluster.primary_ip_family == netutils.IP6Address.family:
286 7352d33b Thomas Thrainer
      primary_ip_version = constants.IP6_VERSION
287 7352d33b Thomas Thrainer
288 7352d33b Thomas Thrainer
    result = {
289 7352d33b Thomas Thrainer
      "software_version": constants.RELEASE_VERSION,
290 7352d33b Thomas Thrainer
      "protocol_version": constants.PROTOCOL_VERSION,
291 7352d33b Thomas Thrainer
      "config_version": constants.CONFIG_VERSION,
292 7352d33b Thomas Thrainer
      "os_api_version": max(constants.OS_API_VERSIONS),
293 7352d33b Thomas Thrainer
      "export_version": constants.EXPORT_VERSION,
294 026f444f Thomas Thrainer
      "vcs_version": constants.VCS_VERSION,
295 7352d33b Thomas Thrainer
      "architecture": runtime.GetArchInfo(),
296 7352d33b Thomas Thrainer
      "name": cluster.cluster_name,
297 7352d33b Thomas Thrainer
      "master": cluster.master_node,
298 7352d33b Thomas Thrainer
      "default_hypervisor": cluster.primary_hypervisor,
299 7352d33b Thomas Thrainer
      "enabled_hypervisors": cluster.enabled_hypervisors,
300 7352d33b Thomas Thrainer
      "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
301 7352d33b Thomas Thrainer
                        for hypervisor_name in cluster.enabled_hypervisors]),
302 7352d33b Thomas Thrainer
      "os_hvp": os_hvp,
303 7352d33b Thomas Thrainer
      "beparams": cluster.beparams,
304 7352d33b Thomas Thrainer
      "osparams": cluster.osparams,
305 7352d33b Thomas Thrainer
      "ipolicy": cluster.ipolicy,
306 7352d33b Thomas Thrainer
      "nicparams": cluster.nicparams,
307 7352d33b Thomas Thrainer
      "ndparams": cluster.ndparams,
308 7352d33b Thomas Thrainer
      "diskparams": cluster.diskparams,
309 7352d33b Thomas Thrainer
      "candidate_pool_size": cluster.candidate_pool_size,
310 7352d33b Thomas Thrainer
      "master_netdev": cluster.master_netdev,
311 7352d33b Thomas Thrainer
      "master_netmask": cluster.master_netmask,
312 7352d33b Thomas Thrainer
      "use_external_mip_script": cluster.use_external_mip_script,
313 7352d33b Thomas Thrainer
      "volume_group_name": cluster.volume_group_name,
314 7352d33b Thomas Thrainer
      "drbd_usermode_helper": cluster.drbd_usermode_helper,
315 7352d33b Thomas Thrainer
      "file_storage_dir": cluster.file_storage_dir,
316 7352d33b Thomas Thrainer
      "shared_file_storage_dir": cluster.shared_file_storage_dir,
317 7352d33b Thomas Thrainer
      "maintain_node_health": cluster.maintain_node_health,
318 7352d33b Thomas Thrainer
      "ctime": cluster.ctime,
319 7352d33b Thomas Thrainer
      "mtime": cluster.mtime,
320 7352d33b Thomas Thrainer
      "uuid": cluster.uuid,
321 7352d33b Thomas Thrainer
      "tags": list(cluster.GetTags()),
322 7352d33b Thomas Thrainer
      "uid_pool": cluster.uid_pool,
323 7352d33b Thomas Thrainer
      "default_iallocator": cluster.default_iallocator,
324 7352d33b Thomas Thrainer
      "reserved_lvs": cluster.reserved_lvs,
325 7352d33b Thomas Thrainer
      "primary_ip_version": primary_ip_version,
326 7352d33b Thomas Thrainer
      "prealloc_wipe_disks": cluster.prealloc_wipe_disks,
327 7352d33b Thomas Thrainer
      "hidden_os": cluster.hidden_os,
328 7352d33b Thomas Thrainer
      "blacklisted_os": cluster.blacklisted_os,
329 fe782deb Helga Velroyen
      "enabled_disk_templates": cluster.enabled_disk_templates,
330 7352d33b Thomas Thrainer
      }
331 7352d33b Thomas Thrainer
332 7352d33b Thomas Thrainer
    return result
333 7352d33b Thomas Thrainer
334 7352d33b Thomas Thrainer
335 7352d33b Thomas Thrainer
class LUClusterRedistConf(NoHooksLU):
336 7352d33b Thomas Thrainer
  """Force the redistribution of cluster configuration.
337 7352d33b Thomas Thrainer

338 7352d33b Thomas Thrainer
  This is a very simple LU.
339 7352d33b Thomas Thrainer

340 7352d33b Thomas Thrainer
  """
341 7352d33b Thomas Thrainer
  REQ_BGL = False
342 7352d33b Thomas Thrainer
343 7352d33b Thomas Thrainer
  def ExpandNames(self):
344 7352d33b Thomas Thrainer
    self.needed_locks = {
345 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
346 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
347 7352d33b Thomas Thrainer
    }
348 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
349 7352d33b Thomas Thrainer
350 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
351 7352d33b Thomas Thrainer
    """Redistribute the configuration.
352 7352d33b Thomas Thrainer

353 7352d33b Thomas Thrainer
    """
354 7352d33b Thomas Thrainer
    self.cfg.Update(self.cfg.GetClusterInfo(), feedback_fn)
355 5eacbcae Thomas Thrainer
    RedistributeAncillaryFiles(self)
356 7352d33b Thomas Thrainer
357 7352d33b Thomas Thrainer
358 7352d33b Thomas Thrainer
class LUClusterRename(LogicalUnit):
359 7352d33b Thomas Thrainer
  """Rename the cluster.
360 7352d33b Thomas Thrainer

361 7352d33b Thomas Thrainer
  """
362 7352d33b Thomas Thrainer
  HPATH = "cluster-rename"
363 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
364 7352d33b Thomas Thrainer
365 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
366 7352d33b Thomas Thrainer
    """Build hooks env.
367 7352d33b Thomas Thrainer

368 7352d33b Thomas Thrainer
    """
369 7352d33b Thomas Thrainer
    return {
370 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
371 7352d33b Thomas Thrainer
      "NEW_NAME": self.op.name,
372 7352d33b Thomas Thrainer
      }
373 7352d33b Thomas Thrainer
374 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
375 7352d33b Thomas Thrainer
    """Build hooks nodes.
376 7352d33b Thomas Thrainer

377 7352d33b Thomas Thrainer
    """
378 7352d33b Thomas Thrainer
    return ([self.cfg.GetMasterNode()], self.cfg.GetNodeList())
379 7352d33b Thomas Thrainer
380 7352d33b Thomas Thrainer
  def CheckPrereq(self):
381 7352d33b Thomas Thrainer
    """Verify that the passed name is a valid one.
382 7352d33b Thomas Thrainer

383 7352d33b Thomas Thrainer
    """
384 7352d33b Thomas Thrainer
    hostname = netutils.GetHostname(name=self.op.name,
385 7352d33b Thomas Thrainer
                                    family=self.cfg.GetPrimaryIPFamily())
386 7352d33b Thomas Thrainer
387 7352d33b Thomas Thrainer
    new_name = hostname.name
388 7352d33b Thomas Thrainer
    self.ip = new_ip = hostname.ip
389 7352d33b Thomas Thrainer
    old_name = self.cfg.GetClusterName()
390 7352d33b Thomas Thrainer
    old_ip = self.cfg.GetMasterIP()
391 7352d33b Thomas Thrainer
    if new_name == old_name and new_ip == old_ip:
392 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Neither the name nor the IP address of the"
393 7352d33b Thomas Thrainer
                                 " cluster has changed",
394 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
395 7352d33b Thomas Thrainer
    if new_ip != old_ip:
396 7352d33b Thomas Thrainer
      if netutils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT):
397 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("The given cluster IP address (%s) is"
398 7352d33b Thomas Thrainer
                                   " reachable on the network" %
399 7352d33b Thomas Thrainer
                                   new_ip, errors.ECODE_NOTUNIQUE)
400 7352d33b Thomas Thrainer
401 7352d33b Thomas Thrainer
    self.op.name = new_name
402 7352d33b Thomas Thrainer
403 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
404 7352d33b Thomas Thrainer
    """Rename the cluster.
405 7352d33b Thomas Thrainer

406 7352d33b Thomas Thrainer
    """
407 7352d33b Thomas Thrainer
    clustername = self.op.name
408 7352d33b Thomas Thrainer
    new_ip = self.ip
409 7352d33b Thomas Thrainer
410 7352d33b Thomas Thrainer
    # shutdown the master IP
411 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
412 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
413 7352d33b Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.name,
414 7352d33b Thomas Thrainer
                                                     master_params, ems)
415 7352d33b Thomas Thrainer
    result.Raise("Could not disable the master role")
416 7352d33b Thomas Thrainer
417 7352d33b Thomas Thrainer
    try:
418 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
419 7352d33b Thomas Thrainer
      cluster.cluster_name = clustername
420 7352d33b Thomas Thrainer
      cluster.master_ip = new_ip
421 7352d33b Thomas Thrainer
      self.cfg.Update(cluster, feedback_fn)
422 7352d33b Thomas Thrainer
423 7352d33b Thomas Thrainer
      # update the known hosts file
424 7352d33b Thomas Thrainer
      ssh.WriteKnownHostsFile(self.cfg, pathutils.SSH_KNOWN_HOSTS_FILE)
425 7352d33b Thomas Thrainer
      node_list = self.cfg.GetOnlineNodeList()
426 7352d33b Thomas Thrainer
      try:
427 7352d33b Thomas Thrainer
        node_list.remove(master_params.name)
428 7352d33b Thomas Thrainer
      except ValueError:
429 7352d33b Thomas Thrainer
        pass
430 5eacbcae Thomas Thrainer
      UploadHelper(self, node_list, pathutils.SSH_KNOWN_HOSTS_FILE)
431 7352d33b Thomas Thrainer
    finally:
432 7352d33b Thomas Thrainer
      master_params.ip = new_ip
433 7352d33b Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.name,
434 7352d33b Thomas Thrainer
                                                     master_params, ems)
435 7352d33b Thomas Thrainer
      msg = result.fail_msg
436 7352d33b Thomas Thrainer
      if msg:
437 7352d33b Thomas Thrainer
        self.LogWarning("Could not re-enable the master role on"
438 7352d33b Thomas Thrainer
                        " the master, please restart manually: %s", msg)
439 7352d33b Thomas Thrainer
440 7352d33b Thomas Thrainer
    return clustername
441 7352d33b Thomas Thrainer
442 7352d33b Thomas Thrainer
443 7352d33b Thomas Thrainer
class LUClusterRepairDiskSizes(NoHooksLU):
444 7352d33b Thomas Thrainer
  """Verifies the cluster disks sizes.
445 7352d33b Thomas Thrainer

446 7352d33b Thomas Thrainer
  """
447 7352d33b Thomas Thrainer
  REQ_BGL = False
448 7352d33b Thomas Thrainer
449 7352d33b Thomas Thrainer
  def ExpandNames(self):
450 7352d33b Thomas Thrainer
    if self.op.instances:
451 5eacbcae Thomas Thrainer
      self.wanted_names = GetWantedInstances(self, self.op.instances)
452 7352d33b Thomas Thrainer
      # Not getting the node allocation lock as only a specific set of
453 7352d33b Thomas Thrainer
      # instances (and their nodes) is going to be acquired
454 7352d33b Thomas Thrainer
      self.needed_locks = {
455 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: [],
456 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: self.wanted_names,
457 7352d33b Thomas Thrainer
        }
458 7352d33b Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
459 7352d33b Thomas Thrainer
    else:
460 7352d33b Thomas Thrainer
      self.wanted_names = None
461 7352d33b Thomas Thrainer
      self.needed_locks = {
462 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: locking.ALL_SET,
463 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: locking.ALL_SET,
464 7352d33b Thomas Thrainer
465 7352d33b Thomas Thrainer
        # This opcode is acquires the node locks for all instances
466 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
467 7352d33b Thomas Thrainer
        }
468 7352d33b Thomas Thrainer
469 7352d33b Thomas Thrainer
    self.share_locks = {
470 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_RES: 1,
471 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: 0,
472 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: 1,
473 7352d33b Thomas Thrainer
      }
474 7352d33b Thomas Thrainer
475 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
476 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and self.wanted_names is not None:
477 7352d33b Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=level)
478 7352d33b Thomas Thrainer
479 7352d33b Thomas Thrainer
  def CheckPrereq(self):
480 7352d33b Thomas Thrainer
    """Check prerequisites.
481 7352d33b Thomas Thrainer

482 7352d33b Thomas Thrainer
    This only checks the optional instance list against the existing names.
483 7352d33b Thomas Thrainer

484 7352d33b Thomas Thrainer
    """
485 7352d33b Thomas Thrainer
    if self.wanted_names is None:
486 7352d33b Thomas Thrainer
      self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE)
487 7352d33b Thomas Thrainer
488 7352d33b Thomas Thrainer
    self.wanted_instances = \
489 7352d33b Thomas Thrainer
        map(compat.snd, self.cfg.GetMultiInstanceInfo(self.wanted_names))
490 7352d33b Thomas Thrainer
491 7352d33b Thomas Thrainer
  def _EnsureChildSizes(self, disk):
492 7352d33b Thomas Thrainer
    """Ensure children of the disk have the needed disk size.
493 7352d33b Thomas Thrainer

494 7352d33b Thomas Thrainer
    This is valid mainly for DRBD8 and fixes an issue where the
495 7352d33b Thomas Thrainer
    children have smaller disk size.
496 7352d33b Thomas Thrainer

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

499 7352d33b Thomas Thrainer
    """
500 7352d33b Thomas Thrainer
    if disk.dev_type == constants.LD_DRBD8:
501 7352d33b Thomas Thrainer
      assert disk.children, "Empty children for DRBD8?"
502 7352d33b Thomas Thrainer
      fchild = disk.children[0]
503 7352d33b Thomas Thrainer
      mismatch = fchild.size < disk.size
504 7352d33b Thomas Thrainer
      if mismatch:
505 7352d33b Thomas Thrainer
        self.LogInfo("Child disk has size %d, parent %d, fixing",
506 7352d33b Thomas Thrainer
                     fchild.size, disk.size)
507 7352d33b Thomas Thrainer
        fchild.size = disk.size
508 7352d33b Thomas Thrainer
509 7352d33b Thomas Thrainer
      # and we recurse on this child only, not on the metadev
510 7352d33b Thomas Thrainer
      return self._EnsureChildSizes(fchild) or mismatch
511 7352d33b Thomas Thrainer
    else:
512 7352d33b Thomas Thrainer
      return False
513 7352d33b Thomas Thrainer
514 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
515 7352d33b Thomas Thrainer
    """Verify the size of cluster disks.
516 7352d33b Thomas Thrainer

517 7352d33b Thomas Thrainer
    """
518 7352d33b Thomas Thrainer
    # TODO: check child disks too
519 7352d33b Thomas Thrainer
    # TODO: check differences in size between primary/secondary nodes
520 7352d33b Thomas Thrainer
    per_node_disks = {}
521 7352d33b Thomas Thrainer
    for instance in self.wanted_instances:
522 7352d33b Thomas Thrainer
      pnode = instance.primary_node
523 7352d33b Thomas Thrainer
      if pnode not in per_node_disks:
524 7352d33b Thomas Thrainer
        per_node_disks[pnode] = []
525 7352d33b Thomas Thrainer
      for idx, disk in enumerate(instance.disks):
526 7352d33b Thomas Thrainer
        per_node_disks[pnode].append((instance, idx, disk))
527 7352d33b Thomas Thrainer
528 7352d33b Thomas Thrainer
    assert not (frozenset(per_node_disks.keys()) -
529 7352d33b Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE_RES)), \
530 7352d33b Thomas Thrainer
      "Not owning correct locks"
531 7352d33b Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE)
532 7352d33b Thomas Thrainer
533 7352d33b Thomas Thrainer
    changed = []
534 7352d33b Thomas Thrainer
    for node, dskl in per_node_disks.items():
535 7352d33b Thomas Thrainer
      newl = [v[2].Copy() for v in dskl]
536 7352d33b Thomas Thrainer
      for dsk in newl:
537 7352d33b Thomas Thrainer
        self.cfg.SetDiskID(dsk, node)
538 7352d33b Thomas Thrainer
      result = self.rpc.call_blockdev_getsize(node, newl)
539 7352d33b Thomas Thrainer
      if result.fail_msg:
540 7352d33b Thomas Thrainer
        self.LogWarning("Failure in blockdev_getsize call to node"
541 7352d33b Thomas Thrainer
                        " %s, ignoring", node)
542 7352d33b Thomas Thrainer
        continue
543 7352d33b Thomas Thrainer
      if len(result.payload) != len(dskl):
544 7352d33b Thomas Thrainer
        logging.warning("Invalid result from node %s: len(dksl)=%d,"
545 7352d33b Thomas Thrainer
                        " result.payload=%s", node, len(dskl), result.payload)
546 7352d33b Thomas Thrainer
        self.LogWarning("Invalid result from node %s, ignoring node results",
547 7352d33b Thomas Thrainer
                        node)
548 7352d33b Thomas Thrainer
        continue
549 7352d33b Thomas Thrainer
      for ((instance, idx, disk), size) in zip(dskl, result.payload):
550 7352d33b Thomas Thrainer
        if size is None:
551 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return size"
552 7352d33b Thomas Thrainer
                          " information, ignoring", idx, instance.name)
553 7352d33b Thomas Thrainer
          continue
554 7352d33b Thomas Thrainer
        if not isinstance(size, (int, long)):
555 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return valid"
556 7352d33b Thomas Thrainer
                          " size information, ignoring", idx, instance.name)
557 7352d33b Thomas Thrainer
          continue
558 7352d33b Thomas Thrainer
        size = size >> 20
559 7352d33b Thomas Thrainer
        if size != disk.size:
560 7352d33b Thomas Thrainer
          self.LogInfo("Disk %d of instance %s has mismatched size,"
561 7352d33b Thomas Thrainer
                       " correcting: recorded %d, actual %d", idx,
562 7352d33b Thomas Thrainer
                       instance.name, disk.size, size)
563 7352d33b Thomas Thrainer
          disk.size = size
564 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
565 7352d33b Thomas Thrainer
          changed.append((instance.name, idx, size))
566 7352d33b Thomas Thrainer
        if self._EnsureChildSizes(disk):
567 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
568 7352d33b Thomas Thrainer
          changed.append((instance.name, idx, disk.size))
569 7352d33b Thomas Thrainer
    return changed
570 7352d33b Thomas Thrainer
571 7352d33b Thomas Thrainer
572 7352d33b Thomas Thrainer
def _ValidateNetmask(cfg, netmask):
573 7352d33b Thomas Thrainer
  """Checks if a netmask is valid.
574 7352d33b Thomas Thrainer

575 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
576 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
577 7352d33b Thomas Thrainer
  @type netmask: int
578 7352d33b Thomas Thrainer
  @param netmask: the netmask to be verified
579 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the validation fails
580 7352d33b Thomas Thrainer

581 7352d33b Thomas Thrainer
  """
582 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
583 7352d33b Thomas Thrainer
  try:
584 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
585 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
586 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
587 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
588 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
589 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
590 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
591 7352d33b Thomas Thrainer
592 7352d33b Thomas Thrainer
593 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
594 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
595 7352d33b Thomas Thrainer

596 7352d33b Thomas Thrainer
  """
597 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
598 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
599 7352d33b Thomas Thrainer
  REQ_BGL = False
600 7352d33b Thomas Thrainer
601 7352d33b Thomas Thrainer
  def CheckArguments(self):
602 7352d33b Thomas Thrainer
    """Check parameters
603 7352d33b Thomas Thrainer

604 7352d33b Thomas Thrainer
    """
605 7352d33b Thomas Thrainer
    if self.op.uid_pool:
606 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.uid_pool)
607 7352d33b Thomas Thrainer
608 7352d33b Thomas Thrainer
    if self.op.add_uids:
609 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.add_uids)
610 7352d33b Thomas Thrainer
611 7352d33b Thomas Thrainer
    if self.op.remove_uids:
612 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.remove_uids)
613 7352d33b Thomas Thrainer
614 7352d33b Thomas Thrainer
    if self.op.master_netmask is not None:
615 7352d33b Thomas Thrainer
      _ValidateNetmask(self.cfg, self.op.master_netmask)
616 7352d33b Thomas Thrainer
617 7352d33b Thomas Thrainer
    if self.op.diskparams:
618 7352d33b Thomas Thrainer
      for dt_params in self.op.diskparams.values():
619 7352d33b Thomas Thrainer
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
620 7352d33b Thomas Thrainer
      try:
621 7352d33b Thomas Thrainer
        utils.VerifyDictOptions(self.op.diskparams, constants.DISK_DT_DEFAULTS)
622 7352d33b Thomas Thrainer
      except errors.OpPrereqError, err:
623 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
624 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
625 7352d33b Thomas Thrainer
626 7352d33b Thomas Thrainer
  def ExpandNames(self):
627 7352d33b Thomas Thrainer
    # FIXME: in the future maybe other cluster params won't require checking on
628 7352d33b Thomas Thrainer
    # all nodes to be modified.
629 7352d33b Thomas Thrainer
    # FIXME: This opcode changes cluster-wide settings. Is acquiring all
630 7352d33b Thomas Thrainer
    # resource locks the right thing, shouldn't it be the BGL instead?
631 7352d33b Thomas Thrainer
    self.needed_locks = {
632 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
633 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: locking.ALL_SET,
634 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
635 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
636 7352d33b Thomas Thrainer
    }
637 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
638 7352d33b Thomas Thrainer
639 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
640 7352d33b Thomas Thrainer
    """Build hooks env.
641 7352d33b Thomas Thrainer

642 7352d33b Thomas Thrainer
    """
643 7352d33b Thomas Thrainer
    return {
644 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
645 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
646 7352d33b Thomas Thrainer
      }
647 7352d33b Thomas Thrainer
648 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
649 7352d33b Thomas Thrainer
    """Build hooks nodes.
650 7352d33b Thomas Thrainer

651 7352d33b Thomas Thrainer
    """
652 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
653 7352d33b Thomas Thrainer
    return ([mn], [mn])
654 7352d33b Thomas Thrainer
655 7352d33b Thomas Thrainer
  def CheckPrereq(self):
656 7352d33b Thomas Thrainer
    """Check prerequisites.
657 7352d33b Thomas Thrainer

658 7352d33b Thomas Thrainer
    This checks whether the given params don't conflict and
659 7352d33b Thomas Thrainer
    if the given volume group is valid.
660 7352d33b Thomas Thrainer

661 7352d33b Thomas Thrainer
    """
662 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
663 7352d33b Thomas Thrainer
      if self.cfg.HasAnyDiskOfType(constants.LD_LV):
664 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
665 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
666 7352d33b Thomas Thrainer
667 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None and not self.op.drbd_helper:
668 7352d33b Thomas Thrainer
      if self.cfg.HasAnyDiskOfType(constants.LD_DRBD8):
669 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
670 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
671 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
672 7352d33b Thomas Thrainer
673 7352d33b Thomas Thrainer
    node_list = self.owned_locks(locking.LEVEL_NODE)
674 7352d33b Thomas Thrainer
675 7352d33b Thomas Thrainer
    vm_capable_nodes = [node.name
676 7352d33b Thomas Thrainer
                        for node in self.cfg.GetAllNodesInfo().values()
677 7352d33b Thomas Thrainer
                        if node.name in node_list and node.vm_capable]
678 7352d33b Thomas Thrainer
679 7352d33b Thomas Thrainer
    # if vg_name not None, checks given volume group on all nodes
680 7352d33b Thomas Thrainer
    if self.op.vg_name:
681 7352d33b Thomas Thrainer
      vglist = self.rpc.call_vg_list(vm_capable_nodes)
682 7352d33b Thomas Thrainer
      for node in vm_capable_nodes:
683 7352d33b Thomas Thrainer
        msg = vglist[node].fail_msg
684 7352d33b Thomas Thrainer
        if msg:
685 7352d33b Thomas Thrainer
          # ignoring down node
686 7352d33b Thomas Thrainer
          self.LogWarning("Error while gathering data on node %s"
687 7352d33b Thomas Thrainer
                          " (ignoring node): %s", node, msg)
688 7352d33b Thomas Thrainer
          continue
689 7352d33b Thomas Thrainer
        vgstatus = utils.CheckVolumeGroupSize(vglist[node].payload,
690 7352d33b Thomas Thrainer
                                              self.op.vg_name,
691 7352d33b Thomas Thrainer
                                              constants.MIN_VG_SIZE)
692 7352d33b Thomas Thrainer
        if vgstatus:
693 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error on node '%s': %s" %
694 7352d33b Thomas Thrainer
                                     (node, vgstatus), errors.ECODE_ENVIRON)
695 7352d33b Thomas Thrainer
696 7352d33b Thomas Thrainer
    if self.op.drbd_helper:
697 7352d33b Thomas Thrainer
      # checks given drbd helper on all nodes
698 7352d33b Thomas Thrainer
      helpers = self.rpc.call_drbd_helper(node_list)
699 7352d33b Thomas Thrainer
      for (node, ninfo) in self.cfg.GetMultiNodeInfo(node_list):
700 7352d33b Thomas Thrainer
        if ninfo.offline:
701 7352d33b Thomas Thrainer
          self.LogInfo("Not checking drbd helper on offline node %s", node)
702 7352d33b Thomas Thrainer
          continue
703 7352d33b Thomas Thrainer
        msg = helpers[node].fail_msg
704 7352d33b Thomas Thrainer
        if msg:
705 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error checking drbd helper on node"
706 7352d33b Thomas Thrainer
                                     " '%s': %s" % (node, msg),
707 7352d33b Thomas Thrainer
                                     errors.ECODE_ENVIRON)
708 7352d33b Thomas Thrainer
        node_helper = helpers[node].payload
709 7352d33b Thomas Thrainer
        if node_helper != self.op.drbd_helper:
710 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
711 7352d33b Thomas Thrainer
                                     (node, node_helper), errors.ECODE_ENVIRON)
712 7352d33b Thomas Thrainer
713 7352d33b Thomas Thrainer
    self.cluster = cluster = self.cfg.GetClusterInfo()
714 7352d33b Thomas Thrainer
    # validate params changes
715 7352d33b Thomas Thrainer
    if self.op.beparams:
716 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
717 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
718 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
719 7352d33b Thomas Thrainer
720 7352d33b Thomas Thrainer
    if self.op.ndparams:
721 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
722 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
723 7352d33b Thomas Thrainer
724 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
725 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
726 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
727 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
728 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
729 7352d33b Thomas Thrainer
730 7352d33b Thomas Thrainer
    if self.op.hv_state:
731 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
732 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
733 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
734 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
735 7352d33b Thomas Thrainer
736 7352d33b Thomas Thrainer
    if self.op.disk_state:
737 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
738 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
739 7352d33b Thomas Thrainer
      self.new_disk_state = \
740 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
741 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
742 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
743 7352d33b Thomas Thrainer
744 7352d33b Thomas Thrainer
    if self.op.ipolicy:
745 5eacbcae Thomas Thrainer
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
746 5eacbcae Thomas Thrainer
                                           group_policy=False)
747 7352d33b Thomas Thrainer
748 7352d33b Thomas Thrainer
      all_instances = self.cfg.GetAllInstancesInfo().values()
749 7352d33b Thomas Thrainer
      violations = set()
750 7352d33b Thomas Thrainer
      for group in self.cfg.GetAllNodeGroupsInfo().values():
751 7352d33b Thomas Thrainer
        instances = frozenset([inst for inst in all_instances
752 7352d33b Thomas Thrainer
                               if compat.any(node in group.members
753 7352d33b Thomas Thrainer
                                             for node in inst.all_nodes)])
754 7352d33b Thomas Thrainer
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
755 7352d33b Thomas Thrainer
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
756 5eacbcae Thomas Thrainer
        new = ComputeNewInstanceViolations(ipol,
757 5eacbcae Thomas Thrainer
                                           new_ipolicy, instances, self.cfg)
758 7352d33b Thomas Thrainer
        if new:
759 7352d33b Thomas Thrainer
          violations.update(new)
760 7352d33b Thomas Thrainer
761 7352d33b Thomas Thrainer
      if violations:
762 7352d33b Thomas Thrainer
        self.LogWarning("After the ipolicy change the following instances"
763 7352d33b Thomas Thrainer
                        " violate them: %s",
764 7352d33b Thomas Thrainer
                        utils.CommaJoin(utils.NiceSort(violations)))
765 7352d33b Thomas Thrainer
766 7352d33b Thomas Thrainer
    if self.op.nicparams:
767 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
768 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
769 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
770 7352d33b Thomas Thrainer
      nic_errors = []
771 7352d33b Thomas Thrainer
772 7352d33b Thomas Thrainer
      # check all instances for consistency
773 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
774 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
775 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
776 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
777 7352d33b Thomas Thrainer
778 7352d33b Thomas Thrainer
          # check parameter syntax
779 7352d33b Thomas Thrainer
          try:
780 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
781 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
782 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
783 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
784 7352d33b Thomas Thrainer
785 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
786 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
787 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
788 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
789 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
790 7352d33b Thomas Thrainer
      if nic_errors:
791 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
792 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
793 7352d33b Thomas Thrainer
794 7352d33b Thomas Thrainer
    # hypervisor list/parameters
795 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
796 7352d33b Thomas Thrainer
    if self.op.hvparams:
797 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
798 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
799 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
800 7352d33b Thomas Thrainer
        else:
801 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
802 7352d33b Thomas Thrainer
803 7352d33b Thomas Thrainer
    # disk template parameters
804 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
805 7352d33b Thomas Thrainer
    if self.op.diskparams:
806 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
807 f06af3ca Thomas Thrainer
        if dt_name not in self.new_diskparams:
808 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
809 7352d33b Thomas Thrainer
        else:
810 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
811 7352d33b Thomas Thrainer
812 7352d33b Thomas Thrainer
    # os hypervisor parameters
813 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
814 7352d33b Thomas Thrainer
    if self.op.os_hvp:
815 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
816 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
817 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
818 7352d33b Thomas Thrainer
        else:
819 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
820 7352d33b Thomas Thrainer
            if hv_dict is None:
821 7352d33b Thomas Thrainer
              # Delete if it exists
822 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
823 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
824 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
825 7352d33b Thomas Thrainer
            else:
826 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
827 7352d33b Thomas Thrainer
828 7352d33b Thomas Thrainer
    # os parameters
829 7352d33b Thomas Thrainer
    self.new_osp = objects.FillDict(cluster.osparams, {})
830 7352d33b Thomas Thrainer
    if self.op.osparams:
831 7352d33b Thomas Thrainer
      for os_name, osp in self.op.osparams.items():
832 7352d33b Thomas Thrainer
        if os_name not in self.new_osp:
833 7352d33b Thomas Thrainer
          self.new_osp[os_name] = {}
834 7352d33b Thomas Thrainer
835 5eacbcae Thomas Thrainer
        self.new_osp[os_name] = GetUpdatedParams(self.new_osp[os_name], osp,
836 5eacbcae Thomas Thrainer
                                                 use_none=True)
837 7352d33b Thomas Thrainer
838 7352d33b Thomas Thrainer
        if not self.new_osp[os_name]:
839 7352d33b Thomas Thrainer
          # we removed all parameters
840 7352d33b Thomas Thrainer
          del self.new_osp[os_name]
841 7352d33b Thomas Thrainer
        else:
842 7352d33b Thomas Thrainer
          # check the parameter validity (remote check)
843 5eacbcae Thomas Thrainer
          CheckOSParams(self, False, [self.cfg.GetMasterNode()],
844 5eacbcae Thomas Thrainer
                        os_name, self.new_osp[os_name])
845 7352d33b Thomas Thrainer
846 7352d33b Thomas Thrainer
    # changes to the hypervisor list
847 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
848 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
849 7352d33b Thomas Thrainer
      for hv in self.hv_list:
850 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
851 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
852 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
853 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
854 7352d33b Thomas Thrainer
        # enabled before
855 7352d33b Thomas Thrainer
        if hv not in new_hvp:
856 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
857 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
858 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
859 7352d33b Thomas Thrainer
    else:
860 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
861 7352d33b Thomas Thrainer
862 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
863 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
864 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
865 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
866 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
867 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
868 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
869 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
870 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
871 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
872 5eacbcae Thomas Thrainer
          CheckHVParams(self, node_list, hv_name, hv_params)
873 7352d33b Thomas Thrainer
874 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
875 7352d33b Thomas Thrainer
876 7352d33b Thomas Thrainer
    if self.op.os_hvp:
877 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
878 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
879 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
880 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
881 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
882 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
883 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
884 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
885 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
886 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
887 5eacbcae Thomas Thrainer
          CheckHVParams(self, node_list, hv_name, new_osp)
888 7352d33b Thomas Thrainer
889 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
890 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
891 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
892 7352d33b Thomas Thrainer
                                    os.path.isfile)
893 7352d33b Thomas Thrainer
      if alloc_script is None:
894 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
895 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
896 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
897 7352d33b Thomas Thrainer
898 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
899 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
900 7352d33b Thomas Thrainer
       are still in use by some instances.
901 7352d33b Thomas Thrainer

902 7352d33b Thomas Thrainer
    """
903 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
904 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
905 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
906 7352d33b Thomas Thrainer
907 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
908 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
909 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
910 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
911 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
912 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
913 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
914 7352d33b Thomas Thrainer
915 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
916 7352d33b Thomas Thrainer
    """Change the parameters of the cluster.
917 7352d33b Thomas Thrainer

918 7352d33b Thomas Thrainer
    """
919 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
920 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
921 7352d33b Thomas Thrainer
      if not new_volume:
922 7352d33b Thomas Thrainer
        new_volume = None
923 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
924 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
925 7352d33b Thomas Thrainer
      else:
926 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
927 7352d33b Thomas Thrainer
                    " state, not changing")
928 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
929 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
930 7352d33b Thomas Thrainer
      if not new_helper:
931 7352d33b Thomas Thrainer
        new_helper = None
932 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
933 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
934 7352d33b Thomas Thrainer
      else:
935 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
936 7352d33b Thomas Thrainer
                    " not changing")
937 7352d33b Thomas Thrainer
    if self.op.hvparams:
938 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
939 7352d33b Thomas Thrainer
    if self.op.os_hvp:
940 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
941 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
942 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
943 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
944 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
945 7352d33b Thomas Thrainer
      self.cluster.enabled_disk_templates = \
946 7352d33b Thomas Thrainer
        list(set(self.op.enabled_disk_templates))
947 7352d33b Thomas Thrainer
    if self.op.beparams:
948 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
949 7352d33b Thomas Thrainer
    if self.op.nicparams:
950 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
951 7352d33b Thomas Thrainer
    if self.op.ipolicy:
952 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
953 7352d33b Thomas Thrainer
    if self.op.osparams:
954 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
955 7352d33b Thomas Thrainer
    if self.op.ndparams:
956 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
957 7352d33b Thomas Thrainer
    if self.op.diskparams:
958 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
959 7352d33b Thomas Thrainer
    if self.op.hv_state:
960 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
961 7352d33b Thomas Thrainer
    if self.op.disk_state:
962 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
963 7352d33b Thomas Thrainer
964 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
965 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
966 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
967 5eacbcae Thomas Thrainer
      AdjustCandidatePool(self, [])
968 7352d33b Thomas Thrainer
969 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
970 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
971 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
972 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
973 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
974 7352d33b Thomas Thrainer
975 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
976 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
977 75f2ff7d Michele Tartara
978 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
979 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
980 7352d33b Thomas Thrainer
981 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
982 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
983 7352d33b Thomas Thrainer
984 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
985 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
986 7352d33b Thomas Thrainer
987 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
988 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
989 7352d33b Thomas Thrainer
990 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
991 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
992 7352d33b Thomas Thrainer
993 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
994 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
995 7352d33b Thomas Thrainer
996 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
997 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
998 7352d33b Thomas Thrainer
999 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1000 7352d33b Thomas Thrainer
      desc += " OS list"
1001 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1002 7352d33b Thomas Thrainer
      for key, val in mods:
1003 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1004 7352d33b Thomas Thrainer
          if val in lst:
1005 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1006 7352d33b Thomas Thrainer
          else:
1007 7352d33b Thomas Thrainer
            lst.append(val)
1008 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1009 7352d33b Thomas Thrainer
          if val in lst:
1010 7352d33b Thomas Thrainer
            lst.remove(val)
1011 7352d33b Thomas Thrainer
          else:
1012 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1013 7352d33b Thomas Thrainer
        else:
1014 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1015 7352d33b Thomas Thrainer
1016 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1017 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1018 7352d33b Thomas Thrainer
1019 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1020 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1021 7352d33b Thomas Thrainer
1022 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1023 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1024 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1025 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1026 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1027 7352d33b Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.name,
1028 7352d33b Thomas Thrainer
                                                       master_params, ems)
1029 e5c92cfb Klaus Aehlig
      if not self.op.force:
1030 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1031 e5c92cfb Klaus Aehlig
      else:
1032 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1033 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1034 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1035 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1036 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1037 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1038 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1039 7352d33b Thomas Thrainer
1040 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1041 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1042 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1043 7352d33b Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(master_params.name,
1044 7352d33b Thomas Thrainer
                                                        master_params.netmask,
1045 7352d33b Thomas Thrainer
                                                        self.op.master_netmask,
1046 7352d33b Thomas Thrainer
                                                        master_params.ip,
1047 7352d33b Thomas Thrainer
                                                        master_params.netdev)
1048 7352d33b Thomas Thrainer
      if result.fail_msg:
1049 7352d33b Thomas Thrainer
        msg = "Could not change the master IP netmask: %s" % result.fail_msg
1050 7352d33b Thomas Thrainer
        feedback_fn(msg)
1051 7352d33b Thomas Thrainer
1052 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1053 7352d33b Thomas Thrainer
1054 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1055 7352d33b Thomas Thrainer
1056 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1057 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1058 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1059 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1060 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1061 7352d33b Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.name,
1062 7352d33b Thomas Thrainer
                                                     master_params, ems)
1063 7352d33b Thomas Thrainer
      if result.fail_msg:
1064 7352d33b Thomas Thrainer
        self.LogWarning("Could not re-enable the master ip on"
1065 7352d33b Thomas Thrainer
                        " the master, please restart manually: %s",
1066 7352d33b Thomas Thrainer
                        result.fail_msg)
1067 7352d33b Thomas Thrainer
1068 7352d33b Thomas Thrainer
1069 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1070 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1071 7352d33b Thomas Thrainer

1072 7352d33b Thomas Thrainer
  """
1073 7352d33b Thomas Thrainer
  REQ_BGL = False
1074 7352d33b Thomas Thrainer
1075 7352d33b Thomas Thrainer
  def ExpandNames(self):
1076 7352d33b Thomas Thrainer
    self.needed_locks = {}
1077 7352d33b Thomas Thrainer
1078 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1079 7352d33b Thomas Thrainer
    jobs = []
1080 7352d33b Thomas Thrainer
1081 7352d33b Thomas Thrainer
    if self.op.group_name:
1082 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1083 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1084 7352d33b Thomas Thrainer
    else:
1085 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1086 7352d33b Thomas Thrainer
1087 7352d33b Thomas Thrainer
      # Verify global configuration
1088 7352d33b Thomas Thrainer
      jobs.append([
1089 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1090 7352d33b Thomas Thrainer
        ])
1091 7352d33b Thomas Thrainer
1092 7352d33b Thomas Thrainer
      # Always depend on global verification
1093 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1094 7352d33b Thomas Thrainer
1095 7352d33b Thomas Thrainer
    jobs.extend(
1096 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1097 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1098 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1099 7352d33b Thomas Thrainer
      for group in groups)
1100 7352d33b Thomas Thrainer
1101 7352d33b Thomas Thrainer
    # Fix up all parameters
1102 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1103 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1104 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1105 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1106 7352d33b Thomas Thrainer
      try:
1107 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1108 7352d33b Thomas Thrainer
      except AttributeError:
1109 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1110 7352d33b Thomas Thrainer
1111 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1112 7352d33b Thomas Thrainer
1113 7352d33b Thomas Thrainer
1114 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1115 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1116 7352d33b Thomas Thrainer

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

1120 7352d33b Thomas Thrainer
  """
1121 7352d33b Thomas Thrainer
1122 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1123 7352d33b Thomas Thrainer
  ETYPE_ERROR = "ERROR"
1124 7352d33b Thomas Thrainer
  ETYPE_WARNING = "WARNING"
1125 7352d33b Thomas Thrainer
1126 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1127 7352d33b Thomas Thrainer
    """Format an error message.
1128 7352d33b Thomas Thrainer

1129 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1130 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1131 7352d33b Thomas Thrainer

1132 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1133 7352d33b Thomas Thrainer

1134 7352d33b Thomas Thrainer
    """
1135 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1136 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1137 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1138 7352d33b Thomas Thrainer
    # warning
1139 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1140 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1141 7352d33b Thomas Thrainer
    # first complete the msg
1142 7352d33b Thomas Thrainer
    if args:
1143 7352d33b Thomas Thrainer
      msg = msg % args
1144 7352d33b Thomas Thrainer
    # then format the whole message
1145 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1146 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1147 7352d33b Thomas Thrainer
    else:
1148 7352d33b Thomas Thrainer
      if item:
1149 7352d33b Thomas Thrainer
        item = " " + item
1150 7352d33b Thomas Thrainer
      else:
1151 7352d33b Thomas Thrainer
        item = ""
1152 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1153 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1154 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1155 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1156 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1157 7352d33b Thomas Thrainer
      self.bad = True
1158 7352d33b Thomas Thrainer
1159 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1160 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1161 7352d33b Thomas Thrainer

1162 7352d33b Thomas Thrainer
    """
1163 7352d33b Thomas Thrainer
    if (bool(cond)
1164 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1165 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1166 7352d33b Thomas Thrainer
1167 7352d33b Thomas Thrainer
1168 7352d33b Thomas Thrainer
def _VerifyCertificate(filename):
1169 7352d33b Thomas Thrainer
  """Verifies a certificate for L{LUClusterVerifyConfig}.
1170 7352d33b Thomas Thrainer

1171 7352d33b Thomas Thrainer
  @type filename: string
1172 7352d33b Thomas Thrainer
  @param filename: Path to PEM file
1173 7352d33b Thomas Thrainer

1174 7352d33b Thomas Thrainer
  """
1175 7352d33b Thomas Thrainer
  try:
1176 7352d33b Thomas Thrainer
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
1177 7352d33b Thomas Thrainer
                                           utils.ReadFile(filename))
1178 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
1179 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR,
1180 7352d33b Thomas Thrainer
            "Failed to load X509 certificate %s: %s" % (filename, err))
1181 7352d33b Thomas Thrainer
1182 7352d33b Thomas Thrainer
  (errcode, msg) = \
1183 7352d33b Thomas Thrainer
    utils.VerifyX509Certificate(cert, constants.SSL_CERT_EXPIRATION_WARN,
1184 7352d33b Thomas Thrainer
                                constants.SSL_CERT_EXPIRATION_ERROR)
1185 7352d33b Thomas Thrainer
1186 7352d33b Thomas Thrainer
  if msg:
1187 7352d33b Thomas Thrainer
    fnamemsg = "While verifying %s: %s" % (filename, msg)
1188 7352d33b Thomas Thrainer
  else:
1189 7352d33b Thomas Thrainer
    fnamemsg = None
1190 7352d33b Thomas Thrainer
1191 7352d33b Thomas Thrainer
  if errcode is None:
1192 7352d33b Thomas Thrainer
    return (None, fnamemsg)
1193 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_WARNING:
1194 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_WARNING, fnamemsg)
1195 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_ERROR:
1196 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR, fnamemsg)
1197 7352d33b Thomas Thrainer
1198 7352d33b Thomas Thrainer
  raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode)
1199 7352d33b Thomas Thrainer
1200 7352d33b Thomas Thrainer
1201 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1202 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1203 7352d33b Thomas Thrainer

1204 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1205 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1206 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1207 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1208 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1209 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1210 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1211 7352d33b Thomas Thrainer

1212 7352d33b Thomas Thrainer
  """
1213 7352d33b Thomas Thrainer
  hvp_data = []
1214 7352d33b Thomas Thrainer
1215 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1216 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1217 7352d33b Thomas Thrainer
1218 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1219 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1220 7352d33b Thomas Thrainer
      if hv_params:
1221 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1222 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1223 7352d33b Thomas Thrainer
1224 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1225 7352d33b Thomas Thrainer
  for instance in instances:
1226 7352d33b Thomas Thrainer
    if instance.hvparams:
1227 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1228 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1229 7352d33b Thomas Thrainer
1230 7352d33b Thomas Thrainer
  return hvp_data
1231 7352d33b Thomas Thrainer
1232 7352d33b Thomas Thrainer
1233 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1234 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1235 7352d33b Thomas Thrainer

1236 7352d33b Thomas Thrainer
  """
1237 7352d33b Thomas Thrainer
  REQ_BGL = False
1238 7352d33b Thomas Thrainer
1239 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1240 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1241 7352d33b Thomas Thrainer

1242 7352d33b Thomas Thrainer
    """
1243 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1244 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1245 7352d33b Thomas Thrainer
             (item, hv_name))
1246 7352d33b Thomas Thrainer
      try:
1247 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1248 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1249 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1250 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1251 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1252 7352d33b Thomas Thrainer
1253 7352d33b Thomas Thrainer
  def ExpandNames(self):
1254 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1255 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1256 7352d33b Thomas Thrainer
1257 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1258 7352d33b Thomas Thrainer
    """Check prerequisites.
1259 7352d33b Thomas Thrainer

1260 7352d33b Thomas Thrainer
    """
1261 7352d33b Thomas Thrainer
    # Retrieve all information
1262 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1263 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1264 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1265 7352d33b Thomas Thrainer
1266 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1267 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1268 7352d33b Thomas Thrainer

1269 7352d33b Thomas Thrainer
    """
1270 7352d33b Thomas Thrainer
    self.bad = False
1271 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1272 7352d33b Thomas Thrainer
1273 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1274 7352d33b Thomas Thrainer
1275 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1276 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1277 7352d33b Thomas Thrainer
1278 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1279 7352d33b Thomas Thrainer
1280 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1281 7352d33b Thomas Thrainer
      (errcode, msg) = _VerifyCertificate(cert_filename)
1282 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1283 7352d33b Thomas Thrainer
1284 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1285 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1286 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1287 69ac3b74 Michele Tartara
                  None,
1288 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1289 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1290 69ac3b74 Michele Tartara
1291 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1292 7352d33b Thomas Thrainer
1293 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1294 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1295 7352d33b Thomas Thrainer
1296 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1297 7352d33b Thomas Thrainer
1298 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1299 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1300 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1301 7352d33b Thomas Thrainer
1302 7352d33b Thomas Thrainer
    dangling_nodes = set(node.name for node in self.all_node_info.values()
1303 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1304 7352d33b Thomas Thrainer
1305 7352d33b Thomas Thrainer
    dangling_instances = {}
1306 7352d33b Thomas Thrainer
    no_node_instances = []
1307 7352d33b Thomas Thrainer
1308 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1309 7352d33b Thomas Thrainer
      if inst.primary_node in dangling_nodes:
1310 7352d33b Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst.name)
1311 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1312 7352d33b Thomas Thrainer
        no_node_instances.append(inst.name)
1313 7352d33b Thomas Thrainer
1314 7352d33b Thomas Thrainer
    pretty_dangling = [
1315 7352d33b Thomas Thrainer
        "%s (%s)" %
1316 7352d33b Thomas Thrainer
        (node.name,
1317 7352d33b Thomas Thrainer
         utils.CommaJoin(dangling_instances.get(node.name,
1318 7352d33b Thomas Thrainer
                                                ["no instances"])))
1319 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1320 7352d33b Thomas Thrainer
1321 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1322 7352d33b Thomas Thrainer
                  None,
1323 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1324 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1325 7352d33b Thomas Thrainer
1326 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1327 7352d33b Thomas Thrainer
                  None,
1328 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1329 7352d33b Thomas Thrainer
                  " %s", utils.CommaJoin(no_node_instances))
1330 7352d33b Thomas Thrainer
1331 7352d33b Thomas Thrainer
    return not self.bad
1332 7352d33b Thomas Thrainer
1333 7352d33b Thomas Thrainer
1334 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1335 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1336 7352d33b Thomas Thrainer

1337 7352d33b Thomas Thrainer
  """
1338 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1339 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1340 7352d33b Thomas Thrainer
  REQ_BGL = False
1341 7352d33b Thomas Thrainer
1342 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1343 7352d33b Thomas Thrainer
1344 7352d33b Thomas Thrainer
  class NodeImage(object):
1345 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1346 7352d33b Thomas Thrainer

1347 7352d33b Thomas Thrainer
    @type name: string
1348 7352d33b Thomas Thrainer
    @ivar name: the node name to which this object refers
1349 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1350 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1351 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1352 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1353 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1354 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1355 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1356 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1357 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1358 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1359 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1360 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1361 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1362 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1363 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1364 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1365 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1366 7352d33b Thomas Thrainer
    @type ghost: boolean
1367 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1368 7352d33b Thomas Thrainer
    @type os_fail: boolean
1369 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1370 7352d33b Thomas Thrainer
    @type oslist: list
1371 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1372 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1373 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1374 7352d33b Thomas Thrainer
    @type pv_min: float
1375 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1376 7352d33b Thomas Thrainer
    @type pv_max: float
1377 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1378 7352d33b Thomas Thrainer

1379 7352d33b Thomas Thrainer
    """
1380 7352d33b Thomas Thrainer
    def __init__(self, offline=False, name=None, vm_capable=True):
1381 7352d33b Thomas Thrainer
      self.name = name
1382 7352d33b Thomas Thrainer
      self.volumes = {}
1383 7352d33b Thomas Thrainer
      self.instances = []
1384 7352d33b Thomas Thrainer
      self.pinst = []
1385 7352d33b Thomas Thrainer
      self.sinst = []
1386 7352d33b Thomas Thrainer
      self.sbp = {}
1387 7352d33b Thomas Thrainer
      self.mfree = 0
1388 7352d33b Thomas Thrainer
      self.dfree = 0
1389 7352d33b Thomas Thrainer
      self.offline = offline
1390 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1391 7352d33b Thomas Thrainer
      self.rpc_fail = False
1392 7352d33b Thomas Thrainer
      self.lvm_fail = False
1393 7352d33b Thomas Thrainer
      self.hyp_fail = False
1394 7352d33b Thomas Thrainer
      self.ghost = False
1395 7352d33b Thomas Thrainer
      self.os_fail = False
1396 7352d33b Thomas Thrainer
      self.oslist = {}
1397 7352d33b Thomas Thrainer
      self.pv_min = None
1398 7352d33b Thomas Thrainer
      self.pv_max = None
1399 7352d33b Thomas Thrainer
1400 7352d33b Thomas Thrainer
  def ExpandNames(self):
1401 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1402 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1403 7352d33b Thomas Thrainer
1404 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1405 7352d33b Thomas Thrainer
    inst_names = \
1406 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1407 7352d33b Thomas Thrainer
1408 7352d33b Thomas Thrainer
    self.needed_locks = {
1409 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: inst_names,
1410 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1411 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1412 7352d33b Thomas Thrainer
1413 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1414 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1415 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1416 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1417 7352d33b Thomas Thrainer
      }
1418 7352d33b Thomas Thrainer
1419 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1420 7352d33b Thomas Thrainer
1421 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1422 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1423 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1424 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1425 7352d33b Thomas Thrainer
1426 7352d33b Thomas Thrainer
      all_inst_info = self.cfg.GetAllInstancesInfo()
1427 7352d33b Thomas Thrainer
1428 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1429 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1430 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1431 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1432 7352d33b Thomas Thrainer
      # be locked.
1433 7352d33b Thomas Thrainer
      for inst in self.owned_locks(locking.LEVEL_INSTANCE):
1434 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1435 7352d33b Thomas Thrainer
        if all_inst_info[inst].disk_template in constants.DTS_INT_MIRROR:
1436 7352d33b Thomas Thrainer
          nodes.update(all_inst_info[inst].secondary_nodes)
1437 7352d33b Thomas Thrainer
1438 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1439 7352d33b Thomas Thrainer
1440 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1441 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1442 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1443 7352d33b Thomas Thrainer
1444 7352d33b Thomas Thrainer
    group_nodes = set(self.group_info.members)
1445 7352d33b Thomas Thrainer
    group_instances = \
1446 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1447 7352d33b Thomas Thrainer
1448 7352d33b Thomas Thrainer
    unlocked_nodes = \
1449 7352d33b Thomas Thrainer
        group_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1450 7352d33b Thomas Thrainer
1451 7352d33b Thomas Thrainer
    unlocked_instances = \
1452 7352d33b Thomas Thrainer
        group_instances.difference(self.owned_locks(locking.LEVEL_INSTANCE))
1453 7352d33b Thomas Thrainer
1454 7352d33b Thomas Thrainer
    if unlocked_nodes:
1455 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing lock for nodes: %s" %
1456 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_nodes),
1457 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1458 7352d33b Thomas Thrainer
1459 7352d33b Thomas Thrainer
    if unlocked_instances:
1460 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing lock for instances: %s" %
1461 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_instances),
1462 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1463 7352d33b Thomas Thrainer
1464 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1465 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1466 7352d33b Thomas Thrainer
1467 7352d33b Thomas Thrainer
    self.my_node_names = utils.NiceSort(group_nodes)
1468 7352d33b Thomas Thrainer
    self.my_inst_names = utils.NiceSort(group_instances)
1469 7352d33b Thomas Thrainer
1470 7352d33b Thomas Thrainer
    self.my_node_info = dict((name, self.all_node_info[name])
1471 7352d33b Thomas Thrainer
                             for name in self.my_node_names)
1472 7352d33b Thomas Thrainer
1473 7352d33b Thomas Thrainer
    self.my_inst_info = dict((name, self.all_inst_info[name])
1474 7352d33b Thomas Thrainer
                             for name in self.my_inst_names)
1475 7352d33b Thomas Thrainer
1476 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1477 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1478 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1479 7352d33b Thomas Thrainer
1480 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1481 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1482 7352d33b Thomas Thrainer
        for nname in inst.all_nodes:
1483 7352d33b Thomas Thrainer
          if self.all_node_info[nname].group != self.group_uuid:
1484 7352d33b Thomas Thrainer
            extra_lv_nodes.add(nname)
1485 7352d33b Thomas Thrainer
1486 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1487 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1488 7352d33b Thomas Thrainer
1489 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1490 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1491 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1492 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1493 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1494 7352d33b Thomas Thrainer
1495 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1496 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1497 7352d33b Thomas Thrainer

1498 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1499 7352d33b Thomas Thrainer
        mandatory fields
1500 7352d33b Thomas Thrainer
      - check ganeti version
1501 7352d33b Thomas Thrainer

1502 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1503 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1504 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1505 7352d33b Thomas Thrainer
    @rtype: boolean
1506 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1507 7352d33b Thomas Thrainer
         reasonable values in the respose)
1508 7352d33b Thomas Thrainer

1509 7352d33b Thomas Thrainer
    """
1510 7352d33b Thomas Thrainer
    node = ninfo.name
1511 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1512 7352d33b Thomas Thrainer
1513 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1514 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1515 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODERPC, node,
1516 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1517 7352d33b Thomas Thrainer
    if test:
1518 7352d33b Thomas Thrainer
      return False
1519 7352d33b Thomas Thrainer
1520 7352d33b Thomas Thrainer
    # compares ganeti version
1521 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1522 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1523 7352d33b Thomas Thrainer
    test = not (remote_version and
1524 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1525 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1526 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODERPC, node,
1527 7352d33b Thomas Thrainer
             "connection to node returned invalid data")
1528 7352d33b Thomas Thrainer
    if test:
1529 7352d33b Thomas Thrainer
      return False
1530 7352d33b Thomas Thrainer
1531 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1532 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODEVERSION, node,
1533 7352d33b Thomas Thrainer
             "incompatible protocol versions: master %s,"
1534 7352d33b Thomas Thrainer
             " node %s", local_version, remote_version[0])
1535 7352d33b Thomas Thrainer
    if test:
1536 7352d33b Thomas Thrainer
      return False
1537 7352d33b Thomas Thrainer
1538 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1539 7352d33b Thomas Thrainer
1540 7352d33b Thomas Thrainer
    # full package version
1541 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1542 7352d33b Thomas Thrainer
                  constants.CV_ENODEVERSION, node,
1543 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1544 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1545 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1546 7352d33b Thomas Thrainer
1547 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1548 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1549 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1550 7352d33b Thomas Thrainer
        test = hv_result is not None
1551 7352d33b Thomas Thrainer
        _ErrorIf(test, constants.CV_ENODEHV, node,
1552 7352d33b Thomas Thrainer
                 "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1553 7352d33b Thomas Thrainer
1554 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1555 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1556 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1557 7352d33b Thomas Thrainer
        _ErrorIf(True, constants.CV_ENODEHV, node,
1558 7352d33b Thomas Thrainer
                 "hypervisor %s parameter verify failure (source %s): %s",
1559 7352d33b Thomas Thrainer
                 hv_name, item, hv_result)
1560 7352d33b Thomas Thrainer
1561 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1562 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1563 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODESETUP, node, "node setup error: %s",
1564 7352d33b Thomas Thrainer
             "; ".join(test))
1565 7352d33b Thomas Thrainer
1566 7352d33b Thomas Thrainer
    return True
1567 7352d33b Thomas Thrainer
1568 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1569 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1570 7352d33b Thomas Thrainer
    """Check the node time.
1571 7352d33b Thomas Thrainer

1572 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1573 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1574 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1575 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1576 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1577 7352d33b Thomas Thrainer

1578 7352d33b Thomas Thrainer
    """
1579 7352d33b Thomas Thrainer
    node = ninfo.name
1580 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1581 7352d33b Thomas Thrainer
1582 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1583 7352d33b Thomas Thrainer
    try:
1584 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1585 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1586 7352d33b Thomas Thrainer
      _ErrorIf(True, constants.CV_ENODETIME, node, "Node returned invalid time")
1587 7352d33b Thomas Thrainer
      return
1588 7352d33b Thomas Thrainer
1589 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1590 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1591 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1592 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1593 7352d33b Thomas Thrainer
    else:
1594 7352d33b Thomas Thrainer
      ntime_diff = None
1595 7352d33b Thomas Thrainer
1596 7352d33b Thomas Thrainer
    _ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, node,
1597 7352d33b Thomas Thrainer
             "Node time diverges by at least %s from master node time",
1598 7352d33b Thomas Thrainer
             ntime_diff)
1599 7352d33b Thomas Thrainer
1600 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1601 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1602 7352d33b Thomas Thrainer

1603 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1604 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1605 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1606 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1607 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1608 7352d33b Thomas Thrainer
    @param nimg: node image
1609 7352d33b Thomas Thrainer

1610 7352d33b Thomas Thrainer
    """
1611 7352d33b Thomas Thrainer
    if vg_name is None:
1612 7352d33b Thomas Thrainer
      return
1613 7352d33b Thomas Thrainer
1614 7352d33b Thomas Thrainer
    node = ninfo.name
1615 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1616 7352d33b Thomas Thrainer
1617 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1618 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1619 7352d33b Thomas Thrainer
    test = not vglist
1620 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODELVM, node, "unable to check volume groups")
1621 7352d33b Thomas Thrainer
    if not test:
1622 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1623 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1624 7352d33b Thomas Thrainer
      _ErrorIf(vgstatus, constants.CV_ENODELVM, node, vgstatus)
1625 7352d33b Thomas Thrainer
1626 7352d33b Thomas Thrainer
    # Check PVs
1627 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
1628 7352d33b Thomas Thrainer
    for em in errmsgs:
1629 7352d33b Thomas Thrainer
      self._Error(constants.CV_ENODELVM, node, em)
1630 7352d33b Thomas Thrainer
    if pvminmax is not None:
1631 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1632 7352d33b Thomas Thrainer
1633 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
1634 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
1635 7352d33b Thomas Thrainer

1636 7352d33b Thomas Thrainer
    @type node_image: dict
1637 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
1638 7352d33b Thomas Thrainer
      L{NodeImage} objects
1639 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1640 7352d33b Thomas Thrainer

1641 7352d33b Thomas Thrainer
    """
1642 7352d33b Thomas Thrainer
    if vg_name is None:
1643 7352d33b Thomas Thrainer
      return
1644 7352d33b Thomas Thrainer
1645 7352d33b Thomas Thrainer
    # Only exlcusive storage needs this kind of checks
1646 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
1647 7352d33b Thomas Thrainer
      return
1648 7352d33b Thomas Thrainer
1649 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
1650 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
1651 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
1652 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
1653 7352d33b Thomas Thrainer
    if not vals:
1654 7352d33b Thomas Thrainer
      return
1655 7352d33b Thomas Thrainer
    (pvmin, minnode) = min((ni.pv_min, ni.name) for ni in vals)
1656 7352d33b Thomas Thrainer
    (pvmax, maxnode) = max((ni.pv_max, ni.name) for ni in vals)
1657 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
1658 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
1659 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
1660 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
1661 7352d33b Thomas Thrainer
                  pvmin, minnode, pvmax, maxnode)
1662 7352d33b Thomas Thrainer
1663 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
1664 7352d33b Thomas Thrainer
    """Check the node bridges.
1665 7352d33b Thomas Thrainer

1666 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1667 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1668 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1669 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
1670 7352d33b Thomas Thrainer

1671 7352d33b Thomas Thrainer
    """
1672 7352d33b Thomas Thrainer
    if not bridges:
1673 7352d33b Thomas Thrainer
      return
1674 7352d33b Thomas Thrainer
1675 7352d33b Thomas Thrainer
    node = ninfo.name
1676 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1677 7352d33b Thomas Thrainer
1678 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
1679 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
1680 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODENET, node,
1681 7352d33b Thomas Thrainer
             "did not return valid bridge information")
1682 7352d33b Thomas Thrainer
    if not test:
1683 7352d33b Thomas Thrainer
      _ErrorIf(bool(missing), constants.CV_ENODENET, node,
1684 7352d33b Thomas Thrainer
               "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
1685 7352d33b Thomas Thrainer
1686 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
1687 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
1688 7352d33b Thomas Thrainer

1689 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1690 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1691 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1692 7352d33b Thomas Thrainer

1693 7352d33b Thomas Thrainer
    """
1694 7352d33b Thomas Thrainer
    node = ninfo.name
1695 7352d33b Thomas Thrainer
1696 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
1697 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, node,
1698 7352d33b Thomas Thrainer
                  "did not return user scripts information")
1699 7352d33b Thomas Thrainer
1700 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
1701 7352d33b Thomas Thrainer
    if not test:
1702 7352d33b Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, node,
1703 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
1704 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
1705 7352d33b Thomas Thrainer
1706 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
1707 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
1708 7352d33b Thomas Thrainer

1709 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1710 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1711 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1712 7352d33b Thomas Thrainer

1713 7352d33b Thomas Thrainer
    """
1714 7352d33b Thomas Thrainer
    node = ninfo.name
1715 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1716 7352d33b Thomas Thrainer
1717 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
1718 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODESSH, node,
1719 7352d33b Thomas Thrainer
             "node hasn't returned node ssh connectivity data")
1720 7352d33b Thomas Thrainer
    if not test:
1721 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
1722 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
1723 7352d33b Thomas Thrainer
          _ErrorIf(True, constants.CV_ENODESSH, node,
1724 7352d33b Thomas Thrainer
                   "ssh communication with node '%s': %s", a_node, a_msg)
1725 7352d33b Thomas Thrainer
1726 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
1727 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODENET, node,
1728 7352d33b Thomas Thrainer
             "node hasn't returned node tcp connectivity data")
1729 7352d33b Thomas Thrainer
    if not test:
1730 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
1731 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
1732 7352d33b Thomas Thrainer
        for anode in nlist:
1733 7352d33b Thomas Thrainer
          _ErrorIf(True, constants.CV_ENODENET, node,
1734 7352d33b Thomas Thrainer
                   "tcp communication with node '%s': %s",
1735 7352d33b Thomas Thrainer
                   anode, nresult[constants.NV_NODENETTEST][anode])
1736 7352d33b Thomas Thrainer
1737 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
1738 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODENET, node,
1739 7352d33b Thomas Thrainer
             "node hasn't returned node master IP reachability data")
1740 7352d33b Thomas Thrainer
    if not test:
1741 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
1742 7352d33b Thomas Thrainer
        if node == self.master_node:
1743 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
1744 7352d33b Thomas Thrainer
        else:
1745 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
1746 7352d33b Thomas Thrainer
        _ErrorIf(True, constants.CV_ENODENET, node, msg)
1747 7352d33b Thomas Thrainer
1748 7352d33b Thomas Thrainer
  def _VerifyInstance(self, instance, inst_config, node_image,
1749 7352d33b Thomas Thrainer
                      diskstatus):
1750 7352d33b Thomas Thrainer
    """Verify an instance.
1751 7352d33b Thomas Thrainer

1752 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
1753 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
1754 7352d33b Thomas Thrainer
    state.
1755 7352d33b Thomas Thrainer

1756 7352d33b Thomas Thrainer
    """
1757 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1758 7352d33b Thomas Thrainer
    pnode = inst_config.primary_node
1759 7352d33b Thomas Thrainer
    pnode_img = node_image[pnode]
1760 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
1761 7352d33b Thomas Thrainer
1762 7352d33b Thomas Thrainer
    node_vol_should = {}
1763 7352d33b Thomas Thrainer
    inst_config.MapLVsByNode(node_vol_should)
1764 7352d33b Thomas Thrainer
1765 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1766 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
1767 7352d33b Thomas Thrainer
                                                            self.group_info)
1768 5eacbcae Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, inst_config, self.cfg)
1769 7352d33b Thomas Thrainer
    _ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance, utils.CommaJoin(err),
1770 7352d33b Thomas Thrainer
             code=self.ETYPE_WARNING)
1771 7352d33b Thomas Thrainer
1772 7352d33b Thomas Thrainer
    for node in node_vol_should:
1773 7352d33b Thomas Thrainer
      n_img = node_image[node]
1774 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
1775 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
1776 7352d33b Thomas Thrainer
        continue
1777 7352d33b Thomas Thrainer
      for volume in node_vol_should[node]:
1778 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
1779 7352d33b Thomas Thrainer
        _ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance,
1780 7352d33b Thomas Thrainer
                 "volume %s missing on node %s", volume, node)
1781 7352d33b Thomas Thrainer
1782 7352d33b Thomas Thrainer
    if inst_config.admin_state == constants.ADMINST_UP:
1783 7352d33b Thomas Thrainer
      test = instance not in pnode_img.instances and not pnode_img.offline
1784 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_EINSTANCEDOWN, instance,
1785 7352d33b Thomas Thrainer
               "instance not running on its primary node %s",
1786 7352d33b Thomas Thrainer
               pnode)
1787 7352d33b Thomas Thrainer
      _ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE, instance,
1788 7352d33b Thomas Thrainer
               "instance is marked as running and lives on offline node %s",
1789 7352d33b Thomas Thrainer
               pnode)
1790 7352d33b Thomas Thrainer
1791 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
1792 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
1793 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
1794 7352d33b Thomas Thrainer
1795 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
1796 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
1797 7352d33b Thomas Thrainer
      # node here
1798 7352d33b Thomas Thrainer
      snode = node_image[nname]
1799 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
1800 1d4a4b26 Thomas Thrainer
      _ErrorIf(inst_config.disks_active and
1801 7352d33b Thomas Thrainer
               not success and not bad_snode,
1802 7352d33b Thomas Thrainer
               constants.CV_EINSTANCEFAULTYDISK, instance,
1803 7352d33b Thomas Thrainer
               "couldn't retrieve status for disk/%s on %s: %s",
1804 7352d33b Thomas Thrainer
               idx, nname, bdev_status)
1805 1d4a4b26 Thomas Thrainer
      _ErrorIf((inst_config.disks_active and
1806 7352d33b Thomas Thrainer
                success and bdev_status.ldisk_status == constants.LDS_FAULTY),
1807 7352d33b Thomas Thrainer
               constants.CV_EINSTANCEFAULTYDISK, instance,
1808 7352d33b Thomas Thrainer
               "disk/%s on %s is faulty", idx, nname)
1809 7352d33b Thomas Thrainer
1810 7352d33b Thomas Thrainer
    _ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
1811 7352d33b Thomas Thrainer
             constants.CV_ENODERPC, pnode, "instance %s, connection to"
1812 7352d33b Thomas Thrainer
             " primary node failed", instance)
1813 7352d33b Thomas Thrainer
1814 7352d33b Thomas Thrainer
    _ErrorIf(len(inst_config.secondary_nodes) > 1,
1815 7352d33b Thomas Thrainer
             constants.CV_EINSTANCELAYOUT,
1816 7352d33b Thomas Thrainer
             instance, "instance has multiple secondary nodes: %s",
1817 7352d33b Thomas Thrainer
             utils.CommaJoin(inst_config.secondary_nodes),
1818 7352d33b Thomas Thrainer
             code=self.ETYPE_WARNING)
1819 7352d33b Thomas Thrainer
1820 7352d33b Thomas Thrainer
    if inst_config.disk_template not in constants.DTS_EXCL_STORAGE:
1821 7352d33b Thomas Thrainer
      # Disk template not compatible with exclusive_storage: no instance
1822 7352d33b Thomas Thrainer
      # node should have the flag set
1823 7352d33b Thomas Thrainer
      es_flags = rpc.GetExclusiveStorageForNodeNames(self.cfg,
1824 7352d33b Thomas Thrainer
                                                     inst_config.all_nodes)
1825 7352d33b Thomas Thrainer
      es_nodes = [n for (n, es) in es_flags.items()
1826 7352d33b Thomas Thrainer
                  if es]
1827 7352d33b Thomas Thrainer
      _ErrorIf(es_nodes, constants.CV_EINSTANCEUNSUITABLENODE, instance,
1828 7352d33b Thomas Thrainer
               "instance has template %s, which is not supported on nodes"
1829 7352d33b Thomas Thrainer
               " that have exclusive storage set: %s",
1830 7352d33b Thomas Thrainer
               inst_config.disk_template, utils.CommaJoin(es_nodes))
1831 7352d33b Thomas Thrainer
1832 7352d33b Thomas Thrainer
    if inst_config.disk_template in constants.DTS_INT_MIRROR:
1833 7352d33b Thomas Thrainer
      instance_nodes = utils.NiceSort(inst_config.all_nodes)
1834 7352d33b Thomas Thrainer
      instance_groups = {}
1835 7352d33b Thomas Thrainer
1836 7352d33b Thomas Thrainer
      for node in instance_nodes:
1837 7352d33b Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node].group,
1838 7352d33b Thomas Thrainer
                                   []).append(node)
1839 7352d33b Thomas Thrainer
1840 7352d33b Thomas Thrainer
      pretty_list = [
1841 7352d33b Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(nodes), groupinfo[group].name)
1842 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
1843 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
1844 7352d33b Thomas Thrainer
                                   key=lambda (_, nodes): pnode in nodes,
1845 7352d33b Thomas Thrainer
                                   reverse=True)]
1846 7352d33b Thomas Thrainer
1847 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
1848 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
1849 7352d33b Thomas Thrainer
                    instance, "instance has primary and secondary nodes in"
1850 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
1851 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
1852 7352d33b Thomas Thrainer
1853 7352d33b Thomas Thrainer
    inst_nodes_offline = []
1854 7352d33b Thomas Thrainer
    for snode in inst_config.secondary_nodes:
1855 7352d33b Thomas Thrainer
      s_img = node_image[snode]
1856 7352d33b Thomas Thrainer
      _ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
1857 7352d33b Thomas Thrainer
               snode, "instance %s, connection to secondary node failed",
1858 7352d33b Thomas Thrainer
               instance)
1859 7352d33b Thomas Thrainer
1860 7352d33b Thomas Thrainer
      if s_img.offline:
1861 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
1862 7352d33b Thomas Thrainer
1863 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
1864 7352d33b Thomas Thrainer
    _ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE, instance,
1865 7352d33b Thomas Thrainer
             "instance has offline secondary node(s) %s",
1866 7352d33b Thomas Thrainer
             utils.CommaJoin(inst_nodes_offline))
1867 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
1868 7352d33b Thomas Thrainer
    for node in inst_config.all_nodes:
1869 7352d33b Thomas Thrainer
      _ErrorIf(node_image[node].ghost, constants.CV_EINSTANCEBADNODE,
1870 7352d33b Thomas Thrainer
               instance, "instance lives on ghost node %s", node)
1871 7352d33b Thomas Thrainer
      _ErrorIf(not node_image[node].vm_capable, constants.CV_EINSTANCEBADNODE,
1872 7352d33b Thomas Thrainer
               instance, "instance lives on non-vm_capable node %s", node)
1873 7352d33b Thomas Thrainer
1874 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
1875 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
1876 7352d33b Thomas Thrainer

1877 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
1878 7352d33b Thomas Thrainer
    reported as unknown.
1879 7352d33b Thomas Thrainer

1880 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
1881 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
1882 7352d33b Thomas Thrainer

1883 7352d33b Thomas Thrainer
    """
1884 7352d33b Thomas Thrainer
    for node, n_img in node_image.items():
1885 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
1886 7352d33b Thomas Thrainer
          self.all_node_info[node].group != self.group_uuid):
1887 7352d33b Thomas Thrainer
        # skip non-healthy nodes
1888 7352d33b Thomas Thrainer
        continue
1889 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
1890 7352d33b Thomas Thrainer
        test = ((node not in node_vol_should or
1891 7352d33b Thomas Thrainer
                volume not in node_vol_should[node]) and
1892 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
1893 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV, node,
1894 7352d33b Thomas Thrainer
                      "volume %s is unknown", volume)
1895 7352d33b Thomas Thrainer
1896 7352d33b Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, instance_cfg):
1897 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
1898 7352d33b Thomas Thrainer

1899 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
1900 7352d33b Thomas Thrainer
    instances it was primary for.
1901 7352d33b Thomas Thrainer

1902 7352d33b Thomas Thrainer
    """
1903 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
1904 7352d33b Thomas Thrainer
    for node, n_img in node_image.items():
1905 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
1906 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
1907 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
1908 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
1909 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
1910 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
1911 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
1912 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
1913 7352d33b Thomas Thrainer
      if n_img.offline or self.all_node_info[node].group != self.group_uuid:
1914 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
1915 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
1916 7352d33b Thomas Thrainer
        # infromation from them; we already list instances living on such
1917 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
1918 7352d33b Thomas Thrainer
        continue
1919 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
1920 7352d33b Thomas Thrainer
      for prinode, instances in n_img.sbp.items():
1921 7352d33b Thomas Thrainer
        needed_mem = 0
1922 7352d33b Thomas Thrainer
        for instance in instances:
1923 7352d33b Thomas Thrainer
          bep = cluster_info.FillBE(instance_cfg[instance])
1924 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
1925 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
1926 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
1927 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1, node,
1928 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
1929 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
1930 7352d33b Thomas Thrainer
                      prinode, needed_mem, n_img.mfree)
1931 7352d33b Thomas Thrainer
1932 7352d33b Thomas Thrainer
  @classmethod
1933 7352d33b Thomas Thrainer
  def _VerifyFiles(cls, errorif, nodeinfo, master_node, all_nvinfo,
1934 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
1935 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
1936 7352d33b Thomas Thrainer

1937 7352d33b Thomas Thrainer
    @param errorif: Callback for reporting errors
1938 7352d33b Thomas Thrainer
    @param nodeinfo: List of L{objects.Node} objects
1939 7352d33b Thomas Thrainer
    @param master_node: Name of master node
1940 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
1941 7352d33b Thomas Thrainer

1942 7352d33b Thomas Thrainer
    """
1943 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
1944 7352d33b Thomas Thrainer
    files2nodefn = [
1945 7352d33b Thomas Thrainer
      (files_all, None),
1946 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
1947 7352d33b Thomas Thrainer
                               node.name == master_node)),
1948 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
1949 7352d33b Thomas Thrainer
      ]
1950 7352d33b Thomas Thrainer
1951 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
1952 7352d33b Thomas Thrainer
    nodefiles = {}
1953 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
1954 7352d33b Thomas Thrainer
      if fn is None:
1955 7352d33b Thomas Thrainer
        filenodes = nodeinfo
1956 7352d33b Thomas Thrainer
      else:
1957 7352d33b Thomas Thrainer
        filenodes = filter(fn, nodeinfo)
1958 7352d33b Thomas Thrainer
      nodefiles.update((filename,
1959 7352d33b Thomas Thrainer
                        frozenset(map(operator.attrgetter("name"), filenodes)))
1960 7352d33b Thomas Thrainer
                       for filename in files)
1961 7352d33b Thomas Thrainer
1962 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
1963 7352d33b Thomas Thrainer
1964 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
1965 7352d33b Thomas Thrainer
    ignore_nodes = set()
1966 7352d33b Thomas Thrainer
1967 7352d33b Thomas Thrainer
    for node in nodeinfo:
1968 7352d33b Thomas Thrainer
      if node.offline:
1969 7352d33b Thomas Thrainer
        ignore_nodes.add(node.name)
1970 7352d33b Thomas Thrainer
        continue
1971 7352d33b Thomas Thrainer
1972 7352d33b Thomas Thrainer
      nresult = all_nvinfo[node.name]
1973 7352d33b Thomas Thrainer
1974 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
1975 7352d33b Thomas Thrainer
        node_files = None
1976 7352d33b Thomas Thrainer
      else:
1977 7352d33b Thomas Thrainer
        fingerprints = nresult.payload.get(constants.NV_FILELIST, None)
1978 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
1979 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
1980 7352d33b Thomas Thrainer
        del fingerprints
1981 7352d33b Thomas Thrainer
1982 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
1983 7352d33b Thomas Thrainer
      errorif(test, constants.CV_ENODEFILECHECK, node.name,
1984 7352d33b Thomas Thrainer
              "Node did not return file checksum data")
1985 7352d33b Thomas Thrainer
      if test:
1986 7352d33b Thomas Thrainer
        ignore_nodes.add(node.name)
1987 7352d33b Thomas Thrainer
        continue
1988 7352d33b Thomas Thrainer
1989 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
1990 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
1991 7352d33b Thomas Thrainer
        assert filename in nodefiles
1992 7352d33b Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.name)
1993 7352d33b Thomas Thrainer
1994 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
1995 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
1996 7352d33b Thomas Thrainer
1997 7352d33b Thomas Thrainer
      # Nodes having the file
1998 7352d33b Thomas Thrainer
      with_file = frozenset(node_name
1999 7352d33b Thomas Thrainer
                            for nodes in fileinfo[filename].values()
2000 7352d33b Thomas Thrainer
                            for node_name in nodes) - ignore_nodes
2001 7352d33b Thomas Thrainer
2002 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2003 7352d33b Thomas Thrainer
2004 7352d33b Thomas Thrainer
      # Nodes missing file
2005 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2006 7352d33b Thomas Thrainer
2007 7352d33b Thomas Thrainer
      if filename in files_opt:
2008 7352d33b Thomas Thrainer
        # All or no nodes
2009 7352d33b Thomas Thrainer
        errorif(missing_file and missing_file != expected_nodes,
2010 7352d33b Thomas Thrainer
                constants.CV_ECLUSTERFILECHECK, None,
2011 7352d33b Thomas Thrainer
                "File %s is optional, but it must exist on all or no"
2012 7352d33b Thomas Thrainer
                " nodes (not found on %s)",
2013 7352d33b Thomas Thrainer
                filename, utils.CommaJoin(utils.NiceSort(missing_file)))
2014 7352d33b Thomas Thrainer
      else:
2015 7352d33b Thomas Thrainer
        errorif(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2016 7352d33b Thomas Thrainer
                "File %s is missing from node(s) %s", filename,
2017 7352d33b Thomas Thrainer
                utils.CommaJoin(utils.NiceSort(missing_file)))
2018 7352d33b Thomas Thrainer
2019 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2020 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2021 7352d33b Thomas Thrainer
        errorif(unexpected,
2022 7352d33b Thomas Thrainer
                constants.CV_ECLUSTERFILECHECK, None,
2023 7352d33b Thomas Thrainer
                "File %s should not exist on node(s) %s",
2024 7352d33b Thomas Thrainer
                filename, utils.CommaJoin(utils.NiceSort(unexpected)))
2025 7352d33b Thomas Thrainer
2026 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2027 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2028 7352d33b Thomas Thrainer
      if test:
2029 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2030 7352d33b Thomas Thrainer
                    (idx + 1, utils.CommaJoin(utils.NiceSort(nodes)))
2031 7352d33b Thomas Thrainer
                    for (idx, (checksum, nodes)) in
2032 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2033 7352d33b Thomas Thrainer
      else:
2034 7352d33b Thomas Thrainer
        variants = []
2035 7352d33b Thomas Thrainer
2036 7352d33b Thomas Thrainer
      errorif(test, constants.CV_ECLUSTERFILECHECK, None,
2037 7352d33b Thomas Thrainer
              "File %s found with %s different checksums (%s)",
2038 7352d33b Thomas Thrainer
              filename, len(checksums), "; ".join(variants))
2039 7352d33b Thomas Thrainer
2040 7352d33b Thomas Thrainer
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2041 7352d33b Thomas Thrainer
                      drbd_map):
2042 7352d33b Thomas Thrainer
    """Verifies and the node DRBD status.
2043 7352d33b Thomas Thrainer

2044 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2045 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2046 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2047 7352d33b Thomas Thrainer
    @param instanceinfo: the dict of instances
2048 7352d33b Thomas Thrainer
    @param drbd_helper: the configured DRBD usermode helper
2049 7352d33b Thomas Thrainer
    @param drbd_map: the DRBD map as returned by
2050 7352d33b Thomas Thrainer
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2051 7352d33b Thomas Thrainer

2052 7352d33b Thomas Thrainer
    """
2053 7352d33b Thomas Thrainer
    node = ninfo.name
2054 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2055 7352d33b Thomas Thrainer
2056 7352d33b Thomas Thrainer
    if drbd_helper:
2057 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2058 7352d33b Thomas Thrainer
      test = (helper_result is None)
2059 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODEDRBDHELPER, node,
2060 7352d33b Thomas Thrainer
               "no drbd usermode helper returned")
2061 7352d33b Thomas Thrainer
      if helper_result:
2062 7352d33b Thomas Thrainer
        status, payload = helper_result
2063 7352d33b Thomas Thrainer
        test = not status
2064 7352d33b Thomas Thrainer
        _ErrorIf(test, constants.CV_ENODEDRBDHELPER, node,
2065 7352d33b Thomas Thrainer
                 "drbd usermode helper check unsuccessful: %s", payload)
2066 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2067 7352d33b Thomas Thrainer
        _ErrorIf(test, constants.CV_ENODEDRBDHELPER, node,
2068 7352d33b Thomas Thrainer
                 "wrong drbd usermode helper: %s", payload)
2069 7352d33b Thomas Thrainer
2070 7352d33b Thomas Thrainer
    # compute the DRBD minors
2071 7352d33b Thomas Thrainer
    node_drbd = {}
2072 7352d33b Thomas Thrainer
    for minor, instance in drbd_map[node].items():
2073 7352d33b Thomas Thrainer
      test = instance not in instanceinfo
2074 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2075 7352d33b Thomas Thrainer
               "ghost instance '%s' in temporary DRBD map", instance)
2076 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2077 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2078 7352d33b Thomas Thrainer
        # unallocated minor in use)
2079 7352d33b Thomas Thrainer
      if test:
2080 7352d33b Thomas Thrainer
        node_drbd[minor] = (instance, False)
2081 7352d33b Thomas Thrainer
      else:
2082 7352d33b Thomas Thrainer
        instance = instanceinfo[instance]
2083 1d4a4b26 Thomas Thrainer
        node_drbd[minor] = (instance.name, instance.disks_active)
2084 7352d33b Thomas Thrainer
2085 7352d33b Thomas Thrainer
    # and now check them
2086 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2087 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2088 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODEDRBD, node,
2089 7352d33b Thomas Thrainer
             "cannot parse drbd status file: %s", str(used_minors))
2090 7352d33b Thomas Thrainer
    if test:
2091 7352d33b Thomas Thrainer
      # we cannot check drbd status
2092 7352d33b Thomas Thrainer
      return
2093 7352d33b Thomas Thrainer
2094 7352d33b Thomas Thrainer
    for minor, (iname, must_exist) in node_drbd.items():
2095 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2096 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODEDRBD, node,
2097 7352d33b Thomas Thrainer
               "drbd minor %d of instance %s is not active", minor, iname)
2098 7352d33b Thomas Thrainer
    for minor in used_minors:
2099 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2100 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODEDRBD, node,
2101 7352d33b Thomas Thrainer
               "unallocated drbd minor %d is in use", minor)
2102 7352d33b Thomas Thrainer
2103 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2104 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2105 7352d33b Thomas Thrainer

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

2111 7352d33b Thomas Thrainer
    """
2112 7352d33b Thomas Thrainer
    node = ninfo.name
2113 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2114 7352d33b Thomas Thrainer
2115 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2116 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2117 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2118 7352d33b Thomas Thrainer
                           for v in remote_os))
2119 7352d33b Thomas Thrainer
2120 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODEOS, node,
2121 7352d33b Thomas Thrainer
             "node hasn't returned valid OS data")
2122 7352d33b Thomas Thrainer
2123 7352d33b Thomas Thrainer
    nimg.os_fail = test
2124 7352d33b Thomas Thrainer
2125 7352d33b Thomas Thrainer
    if test:
2126 7352d33b Thomas Thrainer
      return
2127 7352d33b Thomas Thrainer
2128 7352d33b Thomas Thrainer
    os_dict = {}
2129 7352d33b Thomas Thrainer
2130 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2131 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2132 7352d33b Thomas Thrainer
2133 7352d33b Thomas Thrainer
      if name not in os_dict:
2134 7352d33b Thomas Thrainer
        os_dict[name] = []
2135 7352d33b Thomas Thrainer
2136 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2137 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2138 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2139 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2140 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2141 7352d33b Thomas Thrainer
2142 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2143 7352d33b Thomas Thrainer
2144 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2145 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2146 7352d33b Thomas Thrainer

2147 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2148 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2149 7352d33b Thomas Thrainer
    @param nimg: the node image object
2150 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2151 7352d33b Thomas Thrainer

2152 7352d33b Thomas Thrainer
    """
2153 7352d33b Thomas Thrainer
    node = ninfo.name
2154 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2155 7352d33b Thomas Thrainer
2156 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2157 7352d33b Thomas Thrainer
2158 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2159 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2160 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2161 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2162 7352d33b Thomas Thrainer
      _ErrorIf(not f_status, constants.CV_ENODEOS, node,
2163 7352d33b Thomas Thrainer
               "Invalid OS %s (located at %s): %s", os_name, f_path, f_diag)
2164 7352d33b Thomas Thrainer
      _ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, node,
2165 7352d33b Thomas Thrainer
               "OS '%s' has multiple entries (first one shadows the rest): %s",
2166 7352d33b Thomas Thrainer
               os_name, utils.CommaJoin([v[0] for v in os_data]))
2167 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2168 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2169 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODEOS, node,
2170 7352d33b Thomas Thrainer
               "Extra OS %s not present on reference node (%s)",
2171 7352d33b Thomas Thrainer
               os_name, base.name)
2172 7352d33b Thomas Thrainer
      if test:
2173 7352d33b Thomas Thrainer
        continue
2174 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2175 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2176 7352d33b Thomas Thrainer
      if not b_status:
2177 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2178 7352d33b Thomas Thrainer
        continue
2179 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2180 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2181 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2182 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2183 7352d33b Thomas Thrainer
        _ErrorIf(a != b, constants.CV_ENODEOS, node,
2184 7352d33b Thomas Thrainer
                 "OS %s for %s differs from reference node %s: [%s] vs. [%s]",
2185 7352d33b Thomas Thrainer
                 kind, os_name, base.name,
2186 7352d33b Thomas Thrainer
                 utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2187 7352d33b Thomas Thrainer
2188 7352d33b Thomas Thrainer
    # check any missing OSes
2189 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2190 7352d33b Thomas Thrainer
    _ErrorIf(missing, constants.CV_ENODEOS, node,
2191 7352d33b Thomas Thrainer
             "OSes present on reference node %s but missing on this node: %s",
2192 7352d33b Thomas Thrainer
             base.name, utils.CommaJoin(missing))
2193 7352d33b Thomas Thrainer
2194 7352d33b Thomas Thrainer
  def _VerifyFileStoragePaths(self, ninfo, nresult, is_master):
2195 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2196 7352d33b Thomas Thrainer

2197 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2198 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2199 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2200 7352d33b Thomas Thrainer
    @type is_master: bool
2201 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2202 7352d33b Thomas Thrainer

2203 7352d33b Thomas Thrainer
    """
2204 7352d33b Thomas Thrainer
    node = ninfo.name
2205 7352d33b Thomas Thrainer
2206 7352d33b Thomas Thrainer
    if (is_master and
2207 7352d33b Thomas Thrainer
        (constants.ENABLE_FILE_STORAGE or
2208 7352d33b Thomas Thrainer
         constants.ENABLE_SHARED_FILE_STORAGE)):
2209 7352d33b Thomas Thrainer
      try:
2210 7352d33b Thomas Thrainer
        fspaths = nresult[constants.NV_FILE_STORAGE_PATHS]
2211 7352d33b Thomas Thrainer
      except KeyError:
2212 7352d33b Thomas Thrainer
        # This should never happen
2213 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, node,
2214 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2215 7352d33b Thomas Thrainer
      else:
2216 7352d33b Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, node,
2217 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2218 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2219 7352d33b Thomas Thrainer
    else:
2220 7352d33b Thomas Thrainer
      self._ErrorIf(constants.NV_FILE_STORAGE_PATHS in nresult,
2221 7352d33b Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, node,
2222 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2223 7352d33b Thomas Thrainer
                    " paths")
2224 7352d33b Thomas Thrainer
2225 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2226 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2227 7352d33b Thomas Thrainer

2228 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2229 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2230 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2231 7352d33b Thomas Thrainer

2232 7352d33b Thomas Thrainer
    """
2233 7352d33b Thomas Thrainer
    node = ninfo.name
2234 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2235 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2236 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2237 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2238 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2239 7352d33b Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH, node, path_result)
2240 7352d33b Thomas Thrainer
2241 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2242 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2243 7352d33b Thomas Thrainer

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

2247 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2248 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2249 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2250 7352d33b Thomas Thrainer
    @param nimg: the node image object
2251 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2252 7352d33b Thomas Thrainer

2253 7352d33b Thomas Thrainer
    """
2254 7352d33b Thomas Thrainer
    node = ninfo.name
2255 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2256 7352d33b Thomas Thrainer
2257 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2258 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2259 7352d33b Thomas Thrainer
    if vg_name is None:
2260 7352d33b Thomas Thrainer
      pass
2261 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2262 7352d33b Thomas Thrainer
      _ErrorIf(True, constants.CV_ENODELVM, node, "LVM problem on node: %s",
2263 7352d33b Thomas Thrainer
               utils.SafeEncode(lvdata))
2264 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2265 7352d33b Thomas Thrainer
      _ErrorIf(True, constants.CV_ENODELVM, node,
2266 7352d33b Thomas Thrainer
               "rpc call to node failed (lvlist)")
2267 7352d33b Thomas Thrainer
    else:
2268 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2269 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2270 7352d33b Thomas Thrainer
2271 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2272 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2273 7352d33b Thomas Thrainer

2274 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2275 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2276 7352d33b Thomas Thrainer
    list key.
2277 7352d33b Thomas Thrainer

2278 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2279 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2280 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2281 7352d33b Thomas Thrainer
    @param nimg: the node image object
2282 7352d33b Thomas Thrainer

2283 7352d33b Thomas Thrainer
    """
2284 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2285 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2286 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2287 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2288 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2289 7352d33b Thomas Thrainer
    if test:
2290 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2291 7352d33b Thomas Thrainer
    else:
2292 7352d33b Thomas Thrainer
      nimg.instances = idata
2293 7352d33b Thomas Thrainer
2294 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2295 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2296 7352d33b Thomas Thrainer

2297 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2298 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2299 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2300 7352d33b Thomas Thrainer
    @param nimg: the node image object
2301 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2302 7352d33b Thomas Thrainer

2303 7352d33b Thomas Thrainer
    """
2304 7352d33b Thomas Thrainer
    node = ninfo.name
2305 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2306 7352d33b Thomas Thrainer
2307 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2308 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2309 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2310 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODEHV, node,
2311 7352d33b Thomas Thrainer
             "rpc call to node failed (hvinfo)")
2312 7352d33b Thomas Thrainer
    if not test:
2313 7352d33b Thomas Thrainer
      try:
2314 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2315 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2316 7352d33b Thomas Thrainer
        _ErrorIf(True, constants.CV_ENODERPC, node,
2317 7352d33b Thomas Thrainer
                 "node returned invalid nodeinfo, check hypervisor")
2318 7352d33b Thomas Thrainer
2319 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2320 7352d33b Thomas Thrainer
    if vg_name is not None:
2321 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2322 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2323 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODELVM, node,
2324 7352d33b Thomas Thrainer
               "node didn't return data for the volume group '%s'"
2325 7352d33b Thomas Thrainer
               " - it is either missing or broken", vg_name)
2326 7352d33b Thomas Thrainer
      if not test:
2327 7352d33b Thomas Thrainer
        try:
2328 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2329 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2330 7352d33b Thomas Thrainer
          _ErrorIf(True, constants.CV_ENODERPC, node,
2331 7352d33b Thomas Thrainer
                   "node returned invalid LVM info, check LVM status")
2332 7352d33b Thomas Thrainer
2333 7352d33b Thomas Thrainer
  def _CollectDiskInfo(self, nodelist, node_image, instanceinfo):
2334 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2335 7352d33b Thomas Thrainer

2336 7352d33b Thomas Thrainer
    @type nodelist: list of strings
2337 7352d33b Thomas Thrainer
    @param nodelist: Node names
2338 7352d33b Thomas Thrainer
    @type node_image: dict of (name, L{objects.Node})
2339 7352d33b Thomas Thrainer
    @param node_image: Node objects
2340 7352d33b Thomas Thrainer
    @type instanceinfo: dict of (name, L{objects.Instance})
2341 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2342 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2343 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2344 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2345 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2346 7352d33b Thomas Thrainer

2347 7352d33b Thomas Thrainer
    """
2348 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2349 7352d33b Thomas Thrainer
2350 7352d33b Thomas Thrainer
    node_disks = {}
2351 7352d33b Thomas Thrainer
    node_disks_devonly = {}
2352 7352d33b Thomas Thrainer
    diskless_instances = set()
2353 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2354 7352d33b Thomas Thrainer
2355 7352d33b Thomas Thrainer
    for nname in nodelist:
2356 7352d33b Thomas Thrainer
      node_instances = list(itertools.chain(node_image[nname].pinst,
2357 7352d33b Thomas Thrainer
                                            node_image[nname].sinst))
2358 7352d33b Thomas Thrainer
      diskless_instances.update(inst for inst in node_instances
2359 7352d33b Thomas Thrainer
                                if instanceinfo[inst].disk_template == diskless)
2360 7352d33b Thomas Thrainer
      disks = [(inst, disk)
2361 7352d33b Thomas Thrainer
               for inst in node_instances
2362 7352d33b Thomas Thrainer
               for disk in instanceinfo[inst].disks]
2363 7352d33b Thomas Thrainer
2364 7352d33b Thomas Thrainer
      if not disks:
2365 7352d33b Thomas Thrainer
        # No need to collect data
2366 7352d33b Thomas Thrainer
        continue
2367 7352d33b Thomas Thrainer
2368 7352d33b Thomas Thrainer
      node_disks[nname] = disks
2369 7352d33b Thomas Thrainer
2370 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2371 7352d33b Thomas Thrainer
      devonly = []
2372 7352d33b Thomas Thrainer
      for (inst, dev) in disks:
2373 5eacbcae Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst], [dev], self.cfg)
2374 7352d33b Thomas Thrainer
        self.cfg.SetDiskID(anno_disk, nname)
2375 7352d33b Thomas Thrainer
        devonly.append(anno_disk)
2376 7352d33b Thomas Thrainer
2377 7352d33b Thomas Thrainer
      node_disks_devonly[nname] = devonly
2378 7352d33b Thomas Thrainer
2379 7352d33b Thomas Thrainer
    assert len(node_disks) == len(node_disks_devonly)
2380 7352d33b Thomas Thrainer
2381 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2382 7352d33b Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(node_disks.keys(),
2383 7352d33b Thomas Thrainer
                                                          node_disks_devonly)
2384 7352d33b Thomas Thrainer
2385 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2386 7352d33b Thomas Thrainer
2387 7352d33b Thomas Thrainer
    instdisk = {}
2388 7352d33b Thomas Thrainer
2389 7352d33b Thomas Thrainer
    for (nname, nres) in result.items():
2390 7352d33b Thomas Thrainer
      disks = node_disks[nname]
2391 7352d33b Thomas Thrainer
2392 7352d33b Thomas Thrainer
      if nres.offline:
2393 7352d33b Thomas Thrainer
        # No data from this node
2394 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2395 7352d33b Thomas Thrainer
      else:
2396 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2397 7352d33b Thomas Thrainer
        _ErrorIf(msg, constants.CV_ENODERPC, nname,
2398 7352d33b Thomas Thrainer
                 "while getting disk information: %s", msg)
2399 7352d33b Thomas Thrainer
        if msg:
2400 7352d33b Thomas Thrainer
          # No data from this node
2401 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2402 7352d33b Thomas Thrainer
        else:
2403 7352d33b Thomas Thrainer
          data = []
2404 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2405 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2406 7352d33b Thomas Thrainer
              data.append(i)
2407 7352d33b Thomas Thrainer
            else:
2408 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2409 7352d33b Thomas Thrainer
                              nname, idx, i)
2410 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2411 7352d33b Thomas Thrainer
2412 7352d33b Thomas Thrainer
      for ((inst, _), status) in zip(disks, data):
2413 7352d33b Thomas Thrainer
        instdisk.setdefault(inst, {}).setdefault(nname, []).append(status)
2414 7352d33b Thomas Thrainer
2415 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2416 7352d33b Thomas Thrainer
    for inst in diskless_instances:
2417 7352d33b Thomas Thrainer
      assert inst not in instdisk
2418 7352d33b Thomas Thrainer
      instdisk[inst] = {}
2419 7352d33b Thomas Thrainer
2420 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
2421 7352d33b Thomas Thrainer
                      len(nnames) <= len(instanceinfo[inst].all_nodes) and
2422 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
2423 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
2424 7352d33b Thomas Thrainer
                      for inst, nnames in instdisk.items()
2425 7352d33b Thomas Thrainer
                      for nname, statuses in nnames.items())
2426 7352d33b Thomas Thrainer
    if __debug__:
2427 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
2428 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
2429 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
2430 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
2431 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
2432 7352d33b Thomas Thrainer
2433 7352d33b Thomas Thrainer
    return instdisk
2434 7352d33b Thomas Thrainer
2435 7352d33b Thomas Thrainer
  @staticmethod
2436 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
2437 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
2438 7352d33b Thomas Thrainer

2439 7352d33b Thomas Thrainer
    """
2440 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
2441 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
2442 7352d33b Thomas Thrainer
                 not node.offline)]
2443 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
2444 7352d33b Thomas Thrainer
2445 7352d33b Thomas Thrainer
    return map(itertools.cycle,
2446 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
2447 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2448 7352d33b Thomas Thrainer
                                                  keyfunc)])
2449 7352d33b Thomas Thrainer
2450 7352d33b Thomas Thrainer
  @classmethod
2451 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2452 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
2453 7352d33b Thomas Thrainer

2454 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
2455 7352d33b Thomas Thrainer
    every other group.
2456 7352d33b Thomas Thrainer

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

2461 7352d33b Thomas Thrainer
    """
2462 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2463 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2464 7352d33b Thomas Thrainer
2465 7352d33b Thomas Thrainer
    return (online_nodes,
2466 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
2467 7352d33b Thomas Thrainer
                 for name in online_nodes))
2468 7352d33b Thomas Thrainer
2469 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
2470 7352d33b Thomas Thrainer
    """Build hooks env.
2471 7352d33b Thomas Thrainer

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

2475 7352d33b Thomas Thrainer
    """
2476 7352d33b Thomas Thrainer
    env = {
2477 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
2478 7352d33b Thomas Thrainer
      }
2479 7352d33b Thomas Thrainer
2480 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
2481 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
2482 7352d33b Thomas Thrainer
2483 7352d33b Thomas Thrainer
    return env
2484 7352d33b Thomas Thrainer
2485 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
2486 7352d33b Thomas Thrainer
    """Build hooks nodes.
2487 7352d33b Thomas Thrainer

2488 7352d33b Thomas Thrainer
    """
2489 7352d33b Thomas Thrainer
    return ([], self.my_node_names)
2490 7352d33b Thomas Thrainer
2491 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
2492 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
2493 7352d33b Thomas Thrainer

2494 7352d33b Thomas Thrainer
    """
2495 7352d33b Thomas Thrainer
    # This method has too many local variables. pylint: disable=R0914
2496 7352d33b Thomas Thrainer
    feedback_fn("* Verifying group '%s'" % self.group_info.name)
2497 7352d33b Thomas Thrainer
2498 7352d33b Thomas Thrainer
    if not self.my_node_names:
2499 7352d33b Thomas Thrainer
      # empty node group
2500 7352d33b Thomas Thrainer
      feedback_fn("* Empty node group, skipping verification")
2501 7352d33b Thomas Thrainer
      return True
2502 7352d33b Thomas Thrainer
2503 7352d33b Thomas Thrainer
    self.bad = False
2504 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2505 7352d33b Thomas Thrainer
    verbose = self.op.verbose
2506 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
2507 7352d33b Thomas Thrainer
2508 7352d33b Thomas Thrainer
    vg_name = self.cfg.GetVGName()
2509 7352d33b Thomas Thrainer
    drbd_helper = self.cfg.GetDRBDHelper()
2510 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2511 7352d33b Thomas Thrainer
    hypervisors = cluster.enabled_hypervisors
2512 7352d33b Thomas Thrainer
    node_data_list = [self.my_node_info[name] for name in self.my_node_names]
2513 7352d33b Thomas Thrainer
2514 7352d33b Thomas Thrainer
    i_non_redundant = [] # Non redundant instances
2515 7352d33b Thomas Thrainer
    i_non_a_balanced = [] # Non auto-balanced instances
2516 7352d33b Thomas Thrainer
    i_offline = 0 # Count of offline instances
2517 7352d33b Thomas Thrainer
    n_offline = 0 # Count of offline nodes
2518 7352d33b Thomas Thrainer
    n_drained = 0 # Count of nodes being drained
2519 7352d33b Thomas Thrainer
    node_vol_should = {}
2520 7352d33b Thomas Thrainer
2521 7352d33b Thomas Thrainer
    # FIXME: verify OS list
2522 7352d33b Thomas Thrainer
2523 7352d33b Thomas Thrainer
    # File verification
2524 5eacbcae Thomas Thrainer
    filemap = ComputeAncillaryFiles(cluster, False)
2525 7352d33b Thomas Thrainer
2526 7352d33b Thomas Thrainer
    # do local checksums
2527 7352d33b Thomas Thrainer
    master_node = self.master_node = self.cfg.GetMasterNode()
2528 7352d33b Thomas Thrainer
    master_ip = self.cfg.GetMasterIP()
2529 7352d33b Thomas Thrainer
2530 7352d33b Thomas Thrainer
    feedback_fn("* Gathering data (%d nodes)" % len(self.my_node_names))
2531 7352d33b Thomas Thrainer
2532 7352d33b Thomas Thrainer
    user_scripts = []
2533 7352d33b Thomas Thrainer
    if self.cfg.GetUseExternalMipScript():
2534 7352d33b Thomas Thrainer
      user_scripts.append(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
2535 7352d33b Thomas Thrainer
2536 7352d33b Thomas Thrainer
    node_verify_param = {
2537 7352d33b Thomas Thrainer
      constants.NV_FILELIST:
2538 7352d33b Thomas Thrainer
        map(vcluster.MakeVirtualPath,
2539 7352d33b Thomas Thrainer
            utils.UniqueSequence(filename
2540 7352d33b Thomas Thrainer
                                 for files in filemap
2541 7352d33b Thomas Thrainer
                                 for filename in files)),
2542 7352d33b Thomas Thrainer
      constants.NV_NODELIST:
2543 7352d33b Thomas Thrainer
        self._SelectSshCheckNodes(node_data_list, self.group_uuid,
2544 7352d33b Thomas Thrainer
                                  self.all_node_info.values()),
2545 7352d33b Thomas Thrainer
      constants.NV_HYPERVISOR: hypervisors,
2546 7352d33b Thomas Thrainer
      constants.NV_HVPARAMS:
2547 7352d33b Thomas Thrainer
        _GetAllHypervisorParameters(cluster, self.all_inst_info.values()),
2548 7352d33b Thomas Thrainer
      constants.NV_NODENETTEST: [(node.name, node.primary_ip, node.secondary_ip)
2549 7352d33b Thomas Thrainer
                                 for node in node_data_list
2550 7352d33b Thomas Thrainer
                                 if not node.offline],
2551 7352d33b Thomas Thrainer
      constants.NV_INSTANCELIST: hypervisors,
2552 7352d33b Thomas Thrainer
      constants.NV_VERSION: None,
2553 7352d33b Thomas Thrainer
      constants.NV_HVINFO: self.cfg.GetHypervisorType(),
2554 7352d33b Thomas Thrainer
      constants.NV_NODESETUP: None,
2555 7352d33b Thomas Thrainer
      constants.NV_TIME: None,
2556 7352d33b Thomas Thrainer
      constants.NV_MASTERIP: (master_node, master_ip),
2557 7352d33b Thomas Thrainer
      constants.NV_OSLIST: None,
2558 7352d33b Thomas Thrainer
      constants.NV_VMNODES: self.cfg.GetNonVmCapableNodeList(),
2559 7352d33b Thomas Thrainer
      constants.NV_USERSCRIPTS: user_scripts,
2560 7352d33b Thomas Thrainer
      }
2561 7352d33b Thomas Thrainer
2562 7352d33b Thomas Thrainer
    if vg_name is not None:
2563 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_VGLIST] = None
2564 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_LVLIST] = vg_name
2565 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_PVLIST] = [vg_name]
2566 7352d33b Thomas Thrainer
2567 7352d33b Thomas Thrainer
    if drbd_helper:
2568 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_DRBDLIST] = None
2569 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_DRBDHELPER] = drbd_helper
2570 7352d33b Thomas Thrainer
2571 7352d33b Thomas Thrainer
    if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2572 7352d33b Thomas Thrainer
      # Load file storage paths only from master node
2573 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_FILE_STORAGE_PATHS] = master_node
2574 7352d33b Thomas Thrainer
2575 7352d33b Thomas Thrainer
    # bridge checks
2576 7352d33b Thomas Thrainer
    # FIXME: this needs to be changed per node-group, not cluster-wide
2577 7352d33b Thomas Thrainer
    bridges = set()
2578 7352d33b Thomas Thrainer
    default_nicpp = cluster.nicparams[constants.PP_DEFAULT]
2579 7352d33b Thomas Thrainer
    if default_nicpp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2580 7352d33b Thomas Thrainer
      bridges.add(default_nicpp[constants.NIC_LINK])
2581 7352d33b Thomas Thrainer
    for instance in self.my_inst_info.values():
2582 7352d33b Thomas Thrainer
      for nic in instance.nics:
2583 7352d33b Thomas Thrainer
        full_nic = cluster.SimpleFillNIC(nic.nicparams)
2584 7352d33b Thomas Thrainer
        if full_nic[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2585 7352d33b Thomas Thrainer
          bridges.add(full_nic[constants.NIC_LINK])
2586 7352d33b Thomas Thrainer
2587 7352d33b Thomas Thrainer
    if bridges:
2588 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_BRIDGES] = list(bridges)
2589 7352d33b Thomas Thrainer
2590 7352d33b Thomas Thrainer
    # Build our expected cluster state
2591 7352d33b Thomas Thrainer
    node_image = dict((node.name, self.NodeImage(offline=node.offline,
2592 7352d33b Thomas Thrainer
                                                 name=node.name,
2593 7352d33b Thomas Thrainer
                                                 vm_capable=node.vm_capable))
2594 7352d33b Thomas Thrainer
                      for node in node_data_list)
2595 7352d33b Thomas Thrainer
2596 7352d33b Thomas Thrainer
    # Gather OOB paths
2597 7352d33b Thomas Thrainer
    oob_paths = []
2598 7352d33b Thomas Thrainer
    for node in self.all_node_info.values():
2599 5eacbcae Thomas Thrainer
      path = SupportsOob(self.cfg, node)
2600 7352d33b Thomas Thrainer
      if path and path not in oob_paths:
2601 7352d33b Thomas Thrainer
        oob_paths.append(path)
2602 7352d33b Thomas Thrainer
2603 7352d33b Thomas Thrainer
    if oob_paths:
2604 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_OOB_PATHS] = oob_paths
2605 7352d33b Thomas Thrainer
2606 7352d33b Thomas Thrainer
    for instance in self.my_inst_names:
2607 7352d33b Thomas Thrainer
      inst_config = self.my_inst_info[instance]
2608 7352d33b Thomas Thrainer
      if inst_config.admin_state == constants.ADMINST_OFFLINE:
2609 7352d33b Thomas Thrainer
        i_offline += 1
2610 7352d33b Thomas Thrainer
2611 7352d33b Thomas Thrainer
      for nname in inst_config.all_nodes:
2612 7352d33b Thomas Thrainer
        if nname not in node_image:
2613 7352d33b Thomas Thrainer
          gnode = self.NodeImage(name=nname)
2614 7352d33b Thomas Thrainer
          gnode.ghost = (nname not in self.all_node_info)
2615 7352d33b Thomas Thrainer
          node_image[nname] = gnode
2616 7352d33b Thomas Thrainer
2617 7352d33b Thomas Thrainer
      inst_config.MapLVsByNode(node_vol_should)
2618 7352d33b Thomas Thrainer
2619 7352d33b Thomas Thrainer
      pnode = inst_config.primary_node
2620 7352d33b Thomas Thrainer
      node_image[pnode].pinst.append(instance)
2621 7352d33b Thomas Thrainer
2622 7352d33b Thomas Thrainer
      for snode in inst_config.secondary_nodes:
2623 7352d33b Thomas Thrainer
        nimg = node_image[snode]
2624 7352d33b Thomas Thrainer
        nimg.sinst.append(instance)
2625 7352d33b Thomas Thrainer
        if pnode not in nimg.sbp:
2626 7352d33b Thomas Thrainer
          nimg.sbp[pnode] = []
2627 7352d33b Thomas Thrainer
        nimg.sbp[pnode].append(instance)
2628 7352d33b Thomas Thrainer
2629 7352d33b Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodeNames(self.cfg, self.my_node_names)
2630 7352d33b Thomas Thrainer
    # The value of exclusive_storage should be the same across the group, so if
2631 7352d33b Thomas Thrainer
    # it's True for at least a node, we act as if it were set for all the nodes
2632 7352d33b Thomas Thrainer
    self._exclusive_storage = compat.any(es_flags.values())
2633 7352d33b Thomas Thrainer
    if self._exclusive_storage:
2634 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_EXCLUSIVEPVS] = True
2635 7352d33b Thomas Thrainer
2636 7352d33b Thomas Thrainer
    # At this point, we have the in-memory data structures complete,
2637 7352d33b Thomas Thrainer
    # except for the runtime information, which we'll gather next
2638 7352d33b Thomas Thrainer
2639 7352d33b Thomas Thrainer
    # Due to the way our RPC system works, exact response times cannot be
2640 7352d33b Thomas Thrainer
    # guaranteed (e.g. a broken node could run into a timeout). By keeping the
2641 7352d33b Thomas Thrainer
    # time before and after executing the request, we can at least have a time
2642 7352d33b Thomas Thrainer
    # window.
2643 7352d33b Thomas Thrainer
    nvinfo_starttime = time.time()
2644 7352d33b Thomas Thrainer
    all_nvinfo = self.rpc.call_node_verify(self.my_node_names,
2645 7352d33b Thomas Thrainer
                                           node_verify_param,
2646 7352d33b Thomas Thrainer
                                           self.cfg.GetClusterName())
2647 7352d33b Thomas Thrainer
    nvinfo_endtime = time.time()
2648 7352d33b Thomas Thrainer
2649 7352d33b Thomas Thrainer
    if self.extra_lv_nodes and vg_name is not None:
2650 7352d33b Thomas Thrainer
      extra_lv_nvinfo = \
2651 7352d33b Thomas Thrainer
          self.rpc.call_node_verify(self.extra_lv_nodes,
2652 7352d33b Thomas Thrainer
                                    {constants.NV_LVLIST: vg_name},
2653 7352d33b Thomas Thrainer
                                    self.cfg.GetClusterName())
2654 7352d33b Thomas Thrainer
    else:
2655 7352d33b Thomas Thrainer
      extra_lv_nvinfo = {}
2656 7352d33b Thomas Thrainer
2657 7352d33b Thomas Thrainer
    all_drbd_map = self.cfg.ComputeDRBDMap()
2658 7352d33b Thomas Thrainer
2659 7352d33b Thomas Thrainer
    feedback_fn("* Gathering disk information (%s nodes)" %
2660 7352d33b Thomas Thrainer
                len(self.my_node_names))
2661 7352d33b Thomas Thrainer
    instdisk = self._CollectDiskInfo(self.my_node_names, node_image,
2662 7352d33b Thomas Thrainer
                                     self.my_inst_info)
2663 7352d33b Thomas Thrainer
2664 7352d33b Thomas Thrainer
    feedback_fn("* Verifying configuration file consistency")
2665 7352d33b Thomas Thrainer
2666 7352d33b Thomas Thrainer
    # If not all nodes are being checked, we need to make sure the master node
2667 7352d33b Thomas Thrainer
    # and a non-checked vm_capable node are in the list.
2668 7352d33b Thomas Thrainer
    absent_nodes = set(self.all_node_info).difference(self.my_node_info)
2669 7352d33b Thomas Thrainer
    if absent_nodes:
2670 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo.copy()
2671 7352d33b Thomas Thrainer
      vf_node_info = list(self.my_node_info.values())
2672 7352d33b Thomas Thrainer
      additional_nodes = []
2673 7352d33b Thomas Thrainer
      if master_node not in self.my_node_info:
2674 7352d33b Thomas Thrainer
        additional_nodes.append(master_node)
2675 7352d33b Thomas Thrainer
        vf_node_info.append(self.all_node_info[master_node])
2676 7352d33b Thomas Thrainer
      # Add the first vm_capable node we find which is not included,
2677 7352d33b Thomas Thrainer
      # excluding the master node (which we already have)
2678 7352d33b Thomas Thrainer
      for node in absent_nodes:
2679 7352d33b Thomas Thrainer
        nodeinfo = self.all_node_info[node]
2680 7352d33b Thomas Thrainer
        if (nodeinfo.vm_capable and not nodeinfo.offline and
2681 7352d33b Thomas Thrainer
            node != master_node):
2682 7352d33b Thomas Thrainer
          additional_nodes.append(node)
2683 7352d33b Thomas Thrainer
          vf_node_info.append(self.all_node_info[node])
2684 7352d33b Thomas Thrainer
          break
2685 7352d33b Thomas Thrainer
      key = constants.NV_FILELIST
2686 7352d33b Thomas Thrainer
      vf_nvinfo.update(self.rpc.call_node_verify(additional_nodes,
2687 7352d33b Thomas Thrainer
                                                 {key: node_verify_param[key]},
2688 7352d33b Thomas Thrainer
                                                 self.cfg.GetClusterName()))
2689 7352d33b Thomas Thrainer
    else:
2690 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo
2691 7352d33b Thomas Thrainer
      vf_node_info = self.my_node_info.values()
2692 7352d33b Thomas Thrainer
2693 7352d33b Thomas Thrainer
    self._VerifyFiles(_ErrorIf, vf_node_info, master_node, vf_nvinfo, filemap)
2694 7352d33b Thomas Thrainer
2695 7352d33b Thomas Thrainer
    feedback_fn("* Verifying node status")
2696 7352d33b Thomas Thrainer
2697 7352d33b Thomas Thrainer
    refos_img = None
2698 7352d33b Thomas Thrainer
2699 7352d33b Thomas Thrainer
    for node_i in node_data_list:
2700 7352d33b Thomas Thrainer
      node = node_i.name
2701 7352d33b Thomas Thrainer
      nimg = node_image[node]
2702 7352d33b Thomas Thrainer
2703 7352d33b Thomas Thrainer
      if node_i.offline:
2704 7352d33b Thomas Thrainer
        if verbose:
2705 7352d33b Thomas Thrainer
          feedback_fn("* Skipping offline node %s" % (node,))
2706 7352d33b Thomas Thrainer
        n_offline += 1
2707 7352d33b Thomas Thrainer
        continue
2708 7352d33b Thomas Thrainer
2709 7352d33b Thomas Thrainer
      if node == master_node:
2710 7352d33b Thomas Thrainer
        ntype = "master"
2711 7352d33b Thomas Thrainer
      elif node_i.master_candidate:
2712 7352d33b Thomas Thrainer
        ntype = "master candidate"
2713 7352d33b Thomas Thrainer
      elif node_i.drained:
2714 7352d33b Thomas Thrainer
        ntype = "drained"
2715 7352d33b Thomas Thrainer
        n_drained += 1
2716 7352d33b Thomas Thrainer
      else:
2717 7352d33b Thomas Thrainer
        ntype = "regular"
2718 7352d33b Thomas Thrainer
      if verbose:
2719 7352d33b Thomas Thrainer
        feedback_fn("* Verifying node %s (%s)" % (node, ntype))
2720 7352d33b Thomas Thrainer
2721 7352d33b Thomas Thrainer
      msg = all_nvinfo[node].fail_msg
2722 7352d33b Thomas Thrainer
      _ErrorIf(msg, constants.CV_ENODERPC, node, "while contacting node: %s",
2723 7352d33b Thomas Thrainer
               msg)
2724 7352d33b Thomas Thrainer
      if msg:
2725 7352d33b Thomas Thrainer
        nimg.rpc_fail = True
2726 7352d33b Thomas Thrainer
        continue
2727 7352d33b Thomas Thrainer
2728 7352d33b Thomas Thrainer
      nresult = all_nvinfo[node].payload
2729 7352d33b Thomas Thrainer
2730 7352d33b Thomas Thrainer
      nimg.call_ok = self._VerifyNode(node_i, nresult)
2731 7352d33b Thomas Thrainer
      self._VerifyNodeTime(node_i, nresult, nvinfo_starttime, nvinfo_endtime)
2732 7352d33b Thomas Thrainer
      self._VerifyNodeNetwork(node_i, nresult)
2733 7352d33b Thomas Thrainer
      self._VerifyNodeUserScripts(node_i, nresult)
2734 7352d33b Thomas Thrainer
      self._VerifyOob(node_i, nresult)
2735 7352d33b Thomas Thrainer
      self._VerifyFileStoragePaths(node_i, nresult,
2736 7352d33b Thomas Thrainer
                                   node == master_node)
2737 7352d33b Thomas Thrainer
2738 7352d33b Thomas Thrainer
      if nimg.vm_capable:
2739 7352d33b Thomas Thrainer
        self._UpdateVerifyNodeLVM(node_i, nresult, vg_name, nimg)
2740 7352d33b Thomas Thrainer
        self._VerifyNodeDrbd(node_i, nresult, self.all_inst_info, drbd_helper,
2741 7352d33b Thomas Thrainer
                             all_drbd_map)
2742 7352d33b Thomas Thrainer
2743 7352d33b Thomas Thrainer
        self._UpdateNodeVolumes(node_i, nresult, nimg, vg_name)
2744 7352d33b Thomas Thrainer
        self._UpdateNodeInstances(node_i, nresult, nimg)
2745 7352d33b Thomas Thrainer
        self._UpdateNodeInfo(node_i, nresult, nimg, vg_name)
2746 7352d33b Thomas Thrainer
        self._UpdateNodeOS(node_i, nresult, nimg)
2747 7352d33b Thomas Thrainer
2748 7352d33b Thomas Thrainer
        if not nimg.os_fail:
2749 7352d33b Thomas Thrainer
          if refos_img is None:
2750 7352d33b Thomas Thrainer
            refos_img = nimg
2751 7352d33b Thomas Thrainer
          self._VerifyNodeOS(node_i, nimg, refos_img)
2752 7352d33b Thomas Thrainer
        self._VerifyNodeBridges(node_i, nresult, bridges)
2753 7352d33b Thomas Thrainer
2754 7352d33b Thomas Thrainer
        # Check whether all running instancies are primary for the node. (This
2755 7352d33b Thomas Thrainer
        # can no longer be done from _VerifyInstance below, since some of the
2756 7352d33b Thomas Thrainer
        # wrong instances could be from other node groups.)
2757 7352d33b Thomas Thrainer
        non_primary_inst = set(nimg.instances).difference(nimg.pinst)
2758 7352d33b Thomas Thrainer
2759 7352d33b Thomas Thrainer
        for inst in non_primary_inst:
2760 7352d33b Thomas Thrainer
          test = inst in self.all_inst_info
2761 7352d33b Thomas Thrainer
          _ErrorIf(test, constants.CV_EINSTANCEWRONGNODE, inst,
2762 7352d33b Thomas Thrainer
                   "instance should not run on node %s", node_i.name)
2763 7352d33b Thomas Thrainer
          _ErrorIf(not test, constants.CV_ENODEORPHANINSTANCE, node_i.name,
2764 7352d33b Thomas Thrainer
                   "node is running unknown instance %s", inst)
2765 7352d33b Thomas Thrainer
2766 7352d33b Thomas Thrainer
    self._VerifyGroupLVM(node_image, vg_name)
2767 7352d33b Thomas Thrainer
2768 7352d33b Thomas Thrainer
    for node, result in extra_lv_nvinfo.items():
2769 7352d33b Thomas Thrainer
      self._UpdateNodeVolumes(self.all_node_info[node], result.payload,
2770 7352d33b Thomas Thrainer
                              node_image[node], vg_name)
2771 7352d33b Thomas Thrainer
2772 7352d33b Thomas Thrainer
    feedback_fn("* Verifying instance status")
2773 7352d33b Thomas Thrainer
    for instance in self.my_inst_names:
2774 7352d33b Thomas Thrainer
      if verbose:
2775 7352d33b Thomas Thrainer
        feedback_fn("* Verifying instance %s" % instance)
2776 7352d33b Thomas Thrainer
      inst_config = self.my_inst_info[instance]
2777 7352d33b Thomas Thrainer
      self._VerifyInstance(instance, inst_config, node_image,
2778 7352d33b Thomas Thrainer
                           instdisk[instance])
2779 7352d33b Thomas Thrainer
2780 7352d33b Thomas Thrainer
      # If the instance is non-redundant we cannot survive losing its primary
2781 7352d33b Thomas Thrainer
      # node, so we are not N+1 compliant.
2782 7352d33b Thomas Thrainer
      if inst_config.disk_template not in constants.DTS_MIRRORED:
2783 7352d33b Thomas Thrainer
        i_non_redundant.append(instance)
2784 7352d33b Thomas Thrainer
2785 7352d33b Thomas Thrainer
      if not cluster.FillBE(inst_config)[constants.BE_AUTO_BALANCE]:
2786 7352d33b Thomas Thrainer
        i_non_a_balanced.append(instance)
2787 7352d33b Thomas Thrainer
2788 7352d33b Thomas Thrainer
    feedback_fn("* Verifying orphan volumes")
2789 7352d33b Thomas Thrainer
    reserved = utils.FieldSet(*cluster.reserved_lvs)
2790 7352d33b Thomas Thrainer
2791 7352d33b Thomas Thrainer
    # We will get spurious "unknown volume" warnings if any node of this group
2792 7352d33b Thomas Thrainer
    # is secondary for an instance whose primary is in another group. To avoid
2793 7352d33b Thomas Thrainer
    # them, we find these instances and add their volumes to node_vol_should.
2794 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
2795 7352d33b Thomas Thrainer
      for secondary in inst.secondary_nodes:
2796 7352d33b Thomas Thrainer
        if (secondary in self.my_node_info
2797 7352d33b Thomas Thrainer
            and inst.name not in self.my_inst_info):
2798 7352d33b Thomas Thrainer
          inst.MapLVsByNode(node_vol_should)
2799 7352d33b Thomas Thrainer
          break
2800 7352d33b Thomas Thrainer
2801 7352d33b Thomas Thrainer
    self._VerifyOrphanVolumes(node_vol_should, node_image, reserved)
2802 7352d33b Thomas Thrainer
2803 7352d33b Thomas Thrainer
    if constants.VERIFY_NPLUSONE_MEM not in self.op.skip_checks:
2804 7352d33b Thomas Thrainer
      feedback_fn("* Verifying N+1 Memory redundancy")
2805 7352d33b Thomas Thrainer
      self._VerifyNPlusOneMemory(node_image, self.my_inst_info)
2806 7352d33b Thomas Thrainer
2807 7352d33b Thomas Thrainer
    feedback_fn("* Other Notes")
2808 7352d33b Thomas Thrainer
    if i_non_redundant:
2809 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-redundant instance(s) found."
2810 7352d33b Thomas Thrainer
                  % len(i_non_redundant))
2811 7352d33b Thomas Thrainer
2812 7352d33b Thomas Thrainer
    if i_non_a_balanced:
2813 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-auto-balanced instance(s) found."
2814 7352d33b Thomas Thrainer
                  % len(i_non_a_balanced))
2815 7352d33b Thomas Thrainer
2816 7352d33b Thomas Thrainer
    if i_offline:
2817 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline instance(s) found." % i_offline)
2818 7352d33b Thomas Thrainer
2819 7352d33b Thomas Thrainer
    if n_offline:
2820 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline node(s) found." % n_offline)
2821 7352d33b Thomas Thrainer
2822 7352d33b Thomas Thrainer
    if n_drained:
2823 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d drained node(s) found." % n_drained)
2824 7352d33b Thomas Thrainer
2825 7352d33b Thomas Thrainer
    return not self.bad
2826 7352d33b Thomas Thrainer
2827 7352d33b Thomas Thrainer
  def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
2828 7352d33b Thomas Thrainer
    """Analyze the post-hooks' result
2829 7352d33b Thomas Thrainer

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

2833 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
2834 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
2835 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
2836 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
2837 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
2838 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
2839 7352d33b Thomas Thrainer
        and hook results
2840 7352d33b Thomas Thrainer

2841 7352d33b Thomas Thrainer
    """
2842 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
2843 7352d33b Thomas Thrainer
    # and are only interested in their results
2844 7352d33b Thomas Thrainer
    if not self.my_node_names:
2845 7352d33b Thomas Thrainer
      # empty node group
2846 7352d33b Thomas Thrainer
      pass
2847 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
2848 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
2849 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
2850 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
2851 7352d33b Thomas Thrainer
2852 7352d33b Thomas Thrainer
      for node_name in hooks_results:
2853 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
2854 7352d33b Thomas Thrainer
        msg = res.fail_msg
2855 7352d33b Thomas Thrainer
        test = msg and not res.offline
2856 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
2857 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
2858 7352d33b Thomas Thrainer
        if res.offline or msg:
2859 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
2860 7352d33b Thomas Thrainer
          # an error.
2861 7352d33b Thomas Thrainer
          continue
2862 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
2863 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
2864 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
2865 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
2866 7352d33b Thomas Thrainer
          if test:
2867 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
2868 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
2869 7352d33b Thomas Thrainer
            lu_result = False
2870 7352d33b Thomas Thrainer
2871 7352d33b Thomas Thrainer
    return lu_result
2872 7352d33b Thomas Thrainer
2873 7352d33b Thomas Thrainer
2874 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
2875 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
2876 7352d33b Thomas Thrainer

2877 7352d33b Thomas Thrainer
  """
2878 7352d33b Thomas Thrainer
  REQ_BGL = False
2879 7352d33b Thomas Thrainer
2880 7352d33b Thomas Thrainer
  def ExpandNames(self):
2881 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
2882 7352d33b Thomas Thrainer
    self.needed_locks = {
2883 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
2884 7352d33b Thomas Thrainer
      }
2885 7352d33b Thomas Thrainer
2886 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
2887 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
2888 7352d33b Thomas Thrainer
2889 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
2890 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
2891 7352d33b Thomas Thrainer
                           for group in group_names])