Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ 84ad6b78

History | View | Annotate | Download (120 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 4e771a95 Helga Velroyen
  ComputeIPolicyInstanceViolation, AnnotateDiskParams, SupportsOob, \
60 294254b1 Raffa Santi
  CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \
61 294254b1 Raffa Santi
  CheckDiskAccessModeConsistency
62 7352d33b Thomas Thrainer
63 7352d33b Thomas Thrainer
import ganeti.masterd.instance
64 7352d33b Thomas Thrainer
65 7352d33b Thomas Thrainer
66 7352d33b Thomas Thrainer
class LUClusterActivateMasterIp(NoHooksLU):
67 7352d33b Thomas Thrainer
  """Activate the master IP on the master node.
68 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

143 7352d33b Thomas Thrainer
    This checks whether the cluster is empty.
144 7352d33b Thomas Thrainer

145 7352d33b Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
146 7352d33b Thomas Thrainer

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

164 7352d33b Thomas Thrainer
    """
165 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
166 7352d33b Thomas Thrainer
167 7352d33b Thomas Thrainer
    # Run post hooks on master node before it's removed
168 1c3231aa Thomas Thrainer
    RunPostHook(self, self.cfg.GetNodeName(master_params.uuid))
169 7352d33b Thomas Thrainer
170 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
171 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
172 7352d33b Thomas Thrainer
                                                     master_params, ems)
173 c7dd65be Klaus Aehlig
    result.Warn("Error disabling the master IP address", self.LogWarning)
174 1c3231aa Thomas Thrainer
    return master_params.uuid
175 7352d33b Thomas Thrainer
176 7352d33b Thomas Thrainer
177 7352d33b Thomas Thrainer
class LUClusterPostInit(LogicalUnit):
178 7352d33b Thomas Thrainer
  """Logical unit for running hooks after cluster initialization.
179 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

516 7352d33b Thomas Thrainer
    """
517 7352d33b Thomas Thrainer
    # TODO: check child disks too
518 7352d33b Thomas Thrainer
    # TODO: check differences in size between primary/secondary nodes
519 7352d33b Thomas Thrainer
    per_node_disks = {}
520 7352d33b Thomas Thrainer
    for instance in self.wanted_instances:
521 7352d33b Thomas Thrainer
      pnode = instance.primary_node
522 7352d33b Thomas Thrainer
      if pnode not in per_node_disks:
523 7352d33b Thomas Thrainer
        per_node_disks[pnode] = []
524 7352d33b Thomas Thrainer
      for idx, disk in enumerate(instance.disks):
525 7352d33b Thomas Thrainer
        per_node_disks[pnode].append((instance, idx, disk))
526 7352d33b Thomas Thrainer
527 7352d33b Thomas Thrainer
    assert not (frozenset(per_node_disks.keys()) -
528 7352d33b Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE_RES)), \
529 7352d33b Thomas Thrainer
      "Not owning correct locks"
530 7352d33b Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE)
531 7352d33b Thomas Thrainer
532 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
533 1c3231aa Thomas Thrainer
                                               per_node_disks.keys())
534 40d93e3b Bernardo Dal Seno
535 7352d33b Thomas Thrainer
    changed = []
536 1c3231aa Thomas Thrainer
    for node_uuid, dskl in per_node_disks.items():
537 0c3d9c7c Thomas Thrainer
      if not dskl:
538 0c3d9c7c Thomas Thrainer
        # no disks on the node
539 0c3d9c7c Thomas Thrainer
        continue
540 0c3d9c7c Thomas Thrainer
541 d66acf3d Thomas Thrainer
      newl = [([v[2].Copy()], v[0]) for v in dskl]
542 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
543 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_getdimensions(node_uuid, newl)
544 7352d33b Thomas Thrainer
      if result.fail_msg:
545 6ef8077e Bernardo Dal Seno
        self.LogWarning("Failure in blockdev_getdimensions call to node"
546 1c3231aa Thomas Thrainer
                        " %s, ignoring", node_name)
547 7352d33b Thomas Thrainer
        continue
548 7352d33b Thomas Thrainer
      if len(result.payload) != len(dskl):
549 7352d33b Thomas Thrainer
        logging.warning("Invalid result from node %s: len(dksl)=%d,"
550 1c3231aa Thomas Thrainer
                        " result.payload=%s", node_name, len(dskl),
551 1c3231aa Thomas Thrainer
                        result.payload)
552 7352d33b Thomas Thrainer
        self.LogWarning("Invalid result from node %s, ignoring node results",
553 1c3231aa Thomas Thrainer
                        node_name)
554 7352d33b Thomas Thrainer
        continue
555 6ef8077e Bernardo Dal Seno
      for ((instance, idx, disk), dimensions) in zip(dskl, result.payload):
556 6ef8077e Bernardo Dal Seno
        if dimensions is None:
557 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return size"
558 7352d33b Thomas Thrainer
                          " information, ignoring", idx, instance.name)
559 7352d33b Thomas Thrainer
          continue
560 6ef8077e Bernardo Dal Seno
        if not isinstance(dimensions, (tuple, list)):
561 6ef8077e Bernardo Dal Seno
          self.LogWarning("Disk %d of instance %s did not return valid"
562 6ef8077e Bernardo Dal Seno
                          " dimension information, ignoring", idx,
563 6ef8077e Bernardo Dal Seno
                          instance.name)
564 6ef8077e Bernardo Dal Seno
          continue
565 40d93e3b Bernardo Dal Seno
        (size, spindles) = dimensions
566 7352d33b Thomas Thrainer
        if not isinstance(size, (int, long)):
567 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return valid"
568 7352d33b Thomas Thrainer
                          " size information, ignoring", idx, instance.name)
569 7352d33b Thomas Thrainer
          continue
570 7352d33b Thomas Thrainer
        size = size >> 20
571 7352d33b Thomas Thrainer
        if size != disk.size:
572 7352d33b Thomas Thrainer
          self.LogInfo("Disk %d of instance %s has mismatched size,"
573 7352d33b Thomas Thrainer
                       " correcting: recorded %d, actual %d", idx,
574 7352d33b Thomas Thrainer
                       instance.name, disk.size, size)
575 7352d33b Thomas Thrainer
          disk.size = size
576 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
577 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", size))
578 1c3231aa Thomas Thrainer
        if es_flags[node_uuid]:
579 40d93e3b Bernardo Dal Seno
          if spindles is None:
580 40d93e3b Bernardo Dal Seno
            self.LogWarning("Disk %d of instance %s did not return valid"
581 40d93e3b Bernardo Dal Seno
                            " spindles information, ignoring", idx,
582 40d93e3b Bernardo Dal Seno
                            instance.name)
583 40d93e3b Bernardo Dal Seno
          elif disk.spindles is None or disk.spindles != spindles:
584 40d93e3b Bernardo Dal Seno
            self.LogInfo("Disk %d of instance %s has mismatched spindles,"
585 40d93e3b Bernardo Dal Seno
                         " correcting: recorded %s, actual %s",
586 40d93e3b Bernardo Dal Seno
                         idx, instance.name, disk.spindles, spindles)
587 40d93e3b Bernardo Dal Seno
            disk.spindles = spindles
588 40d93e3b Bernardo Dal Seno
            self.cfg.Update(instance, feedback_fn)
589 40d93e3b Bernardo Dal Seno
            changed.append((instance.name, idx, "spindles", disk.spindles))
590 7352d33b Thomas Thrainer
        if self._EnsureChildSizes(disk):
591 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
592 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", disk.size))
593 7352d33b Thomas Thrainer
    return changed
594 7352d33b Thomas Thrainer
595 7352d33b Thomas Thrainer
596 7352d33b Thomas Thrainer
def _ValidateNetmask(cfg, netmask):
597 7352d33b Thomas Thrainer
  """Checks if a netmask is valid.
598 7352d33b Thomas Thrainer

599 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
600 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
601 7352d33b Thomas Thrainer
  @type netmask: int
602 7352d33b Thomas Thrainer
  @param netmask: the netmask to be verified
603 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the validation fails
604 7352d33b Thomas Thrainer

605 7352d33b Thomas Thrainer
  """
606 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
607 7352d33b Thomas Thrainer
  try:
608 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
609 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
610 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
611 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
612 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
613 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
614 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
615 7352d33b Thomas Thrainer
616 7352d33b Thomas Thrainer
617 e8b5640e Helga Velroyen
def CheckFileBasedStoragePathVsEnabledDiskTemplates(
618 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates,
619 e8b5640e Helga Velroyen
    file_disk_template):
620 e8b5640e Helga Velroyen
  """Checks whether the given file-based storage directory is acceptable.
621 e8b5640e Helga Velroyen

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

624 3039e2dc Helga Velroyen
  @type logging_warn_fn: function
625 3039e2dc Helga Velroyen
  @param logging_warn_fn: function which accepts a string and logs it
626 3039e2dc Helga Velroyen
  @type file_storage_dir: string
627 3039e2dc Helga Velroyen
  @param file_storage_dir: the directory to be used for file-based instances
628 3039e2dc Helga Velroyen
  @type enabled_disk_templates: list of string
629 3039e2dc Helga Velroyen
  @param enabled_disk_templates: the list of enabled disk templates
630 e8b5640e Helga Velroyen
  @type file_disk_template: string
631 e8b5640e Helga Velroyen
  @param file_disk_template: the file-based disk template for which the
632 e8b5640e Helga Velroyen
      path should be checked
633 3039e2dc Helga Velroyen

634 3039e2dc Helga Velroyen
  """
635 e8b5640e Helga Velroyen
  assert (file_disk_template in
636 e8b5640e Helga Velroyen
          utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE))
637 e8b5640e Helga Velroyen
  file_storage_enabled = file_disk_template in enabled_disk_templates
638 3039e2dc Helga Velroyen
  if file_storage_dir is not None:
639 3039e2dc Helga Velroyen
    if file_storage_dir == "":
640 3039e2dc Helga Velroyen
      if file_storage_enabled:
641 e8b5640e Helga Velroyen
        raise errors.OpPrereqError(
642 e8b5640e Helga Velroyen
            "Unsetting the '%s' storage directory while having '%s' storage"
643 e8b5640e Helga Velroyen
            " enabled is not permitted." %
644 e8b5640e Helga Velroyen
            (file_disk_template, file_disk_template))
645 3039e2dc Helga Velroyen
    else:
646 3039e2dc Helga Velroyen
      if not file_storage_enabled:
647 e8b5640e Helga Velroyen
        logging_warn_fn(
648 e8b5640e Helga Velroyen
            "Specified a %s storage directory, although %s storage is not"
649 e8b5640e Helga Velroyen
            " enabled." % (file_disk_template, file_disk_template))
650 3039e2dc Helga Velroyen
  else:
651 e8b5640e Helga Velroyen
    raise errors.ProgrammerError("Received %s storage dir with value"
652 e8b5640e Helga Velroyen
                                 " 'None'." % file_disk_template)
653 e8b5640e Helga Velroyen
654 e8b5640e Helga Velroyen
655 e8b5640e Helga Velroyen
def CheckFileStoragePathVsEnabledDiskTemplates(
656 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
657 e8b5640e Helga Velroyen
  """Checks whether the given file storage directory is acceptable.
658 e8b5640e Helga Velroyen

659 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
660 e8b5640e Helga Velroyen

661 e8b5640e Helga Velroyen
  """
662 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
663 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
664 e8b5640e Helga Velroyen
      constants.DT_FILE)
665 e8b5640e Helga Velroyen
666 e8b5640e Helga Velroyen
667 e8b5640e Helga Velroyen
def CheckSharedFileStoragePathVsEnabledDiskTemplates(
668 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
669 e8b5640e Helga Velroyen
  """Checks whether the given shared file storage directory is acceptable.
670 e8b5640e Helga Velroyen

671 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
672 e8b5640e Helga Velroyen

673 e8b5640e Helga Velroyen
  """
674 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
675 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
676 e8b5640e Helga Velroyen
      constants.DT_SHARED_FILE)
677 3039e2dc Helga Velroyen
678 3039e2dc Helga Velroyen
679 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
680 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
681 7352d33b Thomas Thrainer

682 7352d33b Thomas Thrainer
  """
683 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
684 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
685 7352d33b Thomas Thrainer
  REQ_BGL = False
686 7352d33b Thomas Thrainer
687 7352d33b Thomas Thrainer
  def CheckArguments(self):
688 7352d33b Thomas Thrainer
    """Check parameters
689 7352d33b Thomas Thrainer

690 7352d33b Thomas Thrainer
    """
691 7352d33b Thomas Thrainer
    if self.op.uid_pool:
692 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.uid_pool)
693 7352d33b Thomas Thrainer
694 7352d33b Thomas Thrainer
    if self.op.add_uids:
695 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.add_uids)
696 7352d33b Thomas Thrainer
697 7352d33b Thomas Thrainer
    if self.op.remove_uids:
698 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.remove_uids)
699 7352d33b Thomas Thrainer
700 7352d33b Thomas Thrainer
    if self.op.master_netmask is not None:
701 7352d33b Thomas Thrainer
      _ValidateNetmask(self.cfg, self.op.master_netmask)
702 7352d33b Thomas Thrainer
703 7352d33b Thomas Thrainer
    if self.op.diskparams:
704 7352d33b Thomas Thrainer
      for dt_params in self.op.diskparams.values():
705 7352d33b Thomas Thrainer
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
706 7352d33b Thomas Thrainer
      try:
707 7352d33b Thomas Thrainer
        utils.VerifyDictOptions(self.op.diskparams, constants.DISK_DT_DEFAULTS)
708 294254b1 Raffa Santi
        CheckDiskAccessModeValidity(self.op.diskparams)
709 7352d33b Thomas Thrainer
      except errors.OpPrereqError, err:
710 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
711 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
712 7352d33b Thomas Thrainer
713 7352d33b Thomas Thrainer
  def ExpandNames(self):
714 7352d33b Thomas Thrainer
    # FIXME: in the future maybe other cluster params won't require checking on
715 7352d33b Thomas Thrainer
    # all nodes to be modified.
716 7352d33b Thomas Thrainer
    # FIXME: This opcode changes cluster-wide settings. Is acquiring all
717 7352d33b Thomas Thrainer
    # resource locks the right thing, shouldn't it be the BGL instead?
718 7352d33b Thomas Thrainer
    self.needed_locks = {
719 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
720 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: locking.ALL_SET,
721 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
722 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
723 7352d33b Thomas Thrainer
    }
724 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
725 7352d33b Thomas Thrainer
726 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
727 7352d33b Thomas Thrainer
    """Build hooks env.
728 7352d33b Thomas Thrainer

729 7352d33b Thomas Thrainer
    """
730 7352d33b Thomas Thrainer
    return {
731 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
732 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
733 7352d33b Thomas Thrainer
      }
734 7352d33b Thomas Thrainer
735 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
736 7352d33b Thomas Thrainer
    """Build hooks nodes.
737 7352d33b Thomas Thrainer

738 7352d33b Thomas Thrainer
    """
739 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
740 7352d33b Thomas Thrainer
    return ([mn], [mn])
741 7352d33b Thomas Thrainer
742 1c3231aa Thomas Thrainer
  def _CheckVgName(self, node_uuids, enabled_disk_templates,
743 1bb99a33 Bernardo Dal Seno
                   new_enabled_disk_templates):
744 1bb99a33 Bernardo Dal Seno
    """Check the consistency of the vg name on all nodes and in case it gets
745 1bb99a33 Bernardo Dal Seno
       unset whether there are instances still using it.
746 7352d33b Thomas Thrainer

747 7352d33b Thomas Thrainer
    """
748 c89eb67d Helga Velroyen
    lvm_is_enabled = utils.IsLvmEnabled(enabled_disk_templates)
749 c89eb67d Helga Velroyen
    lvm_gets_enabled = utils.LvmGetsEnabled(enabled_disk_templates,
750 c89eb67d Helga Velroyen
                                            new_enabled_disk_templates)
751 c89eb67d Helga Velroyen
    current_vg_name = self.cfg.GetVGName()
752 c89eb67d Helga Velroyen
753 c89eb67d Helga Velroyen
    if self.op.vg_name == '':
754 c89eb67d Helga Velroyen
      if lvm_is_enabled:
755 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
756 c89eb67d Helga Velroyen
                                   " disk templates are or get enabled.")
757 c89eb67d Helga Velroyen
758 c89eb67d Helga Velroyen
    if self.op.vg_name is None:
759 c89eb67d Helga Velroyen
      if current_vg_name is None and lvm_is_enabled:
760 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Please specify a volume group when"
761 c89eb67d Helga Velroyen
                                   " enabling lvm-based disk-templates.")
762 c89eb67d Helga Velroyen
763 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
764 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_PLAIN):
765 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
766 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
767 7352d33b Thomas Thrainer
768 c89eb67d Helga Velroyen
    if (self.op.vg_name is not None and lvm_is_enabled) or \
769 c89eb67d Helga Velroyen
        (self.cfg.GetVGName() is not None and lvm_gets_enabled):
770 1c3231aa Thomas Thrainer
      self._CheckVgNameOnNodes(node_uuids)
771 1bb99a33 Bernardo Dal Seno
772 1c3231aa Thomas Thrainer
  def _CheckVgNameOnNodes(self, node_uuids):
773 1bb99a33 Bernardo Dal Seno
    """Check the status of the volume group on each node.
774 1bb99a33 Bernardo Dal Seno

775 1bb99a33 Bernardo Dal Seno
    """
776 1c3231aa Thomas Thrainer
    vglist = self.rpc.call_vg_list(node_uuids)
777 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
778 1c3231aa Thomas Thrainer
      msg = vglist[node_uuid].fail_msg
779 1bb99a33 Bernardo Dal Seno
      if msg:
780 1bb99a33 Bernardo Dal Seno
        # ignoring down node
781 1bb99a33 Bernardo Dal Seno
        self.LogWarning("Error while gathering data on node %s"
782 1c3231aa Thomas Thrainer
                        " (ignoring node): %s",
783 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
784 1bb99a33 Bernardo Dal Seno
        continue
785 1c3231aa Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist[node_uuid].payload,
786 1bb99a33 Bernardo Dal Seno
                                            self.op.vg_name,
787 1bb99a33 Bernardo Dal Seno
                                            constants.MIN_VG_SIZE)
788 1bb99a33 Bernardo Dal Seno
      if vgstatus:
789 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Error on node '%s': %s" %
790 1c3231aa Thomas Thrainer
                                   (self.cfg.GetNodeName(node_uuid), vgstatus),
791 1c3231aa Thomas Thrainer
                                   errors.ECODE_ENVIRON)
792 1bb99a33 Bernardo Dal Seno
793 c89eb67d Helga Velroyen
  @staticmethod
794 c89eb67d Helga Velroyen
  def _GetEnabledDiskTemplatesInner(op_enabled_disk_templates,
795 c89eb67d Helga Velroyen
                                    old_enabled_disk_templates):
796 1bb99a33 Bernardo Dal Seno
    """Determines the enabled disk templates and the subset of disk templates
797 1bb99a33 Bernardo Dal Seno
       that are newly enabled by this operation.
798 1bb99a33 Bernardo Dal Seno

799 1bb99a33 Bernardo Dal Seno
    """
800 1bb99a33 Bernardo Dal Seno
    enabled_disk_templates = None
801 1bb99a33 Bernardo Dal Seno
    new_enabled_disk_templates = []
802 c89eb67d Helga Velroyen
    if op_enabled_disk_templates:
803 c89eb67d Helga Velroyen
      enabled_disk_templates = op_enabled_disk_templates
804 1bb99a33 Bernardo Dal Seno
      new_enabled_disk_templates = \
805 1bb99a33 Bernardo Dal Seno
        list(set(enabled_disk_templates)
806 c89eb67d Helga Velroyen
             - set(old_enabled_disk_templates))
807 1bb99a33 Bernardo Dal Seno
    else:
808 c89eb67d Helga Velroyen
      enabled_disk_templates = old_enabled_disk_templates
809 1bb99a33 Bernardo Dal Seno
    return (enabled_disk_templates, new_enabled_disk_templates)
810 1bb99a33 Bernardo Dal Seno
811 c89eb67d Helga Velroyen
  def _GetEnabledDiskTemplates(self, cluster):
812 c89eb67d Helga Velroyen
    """Determines the enabled disk templates and the subset of disk templates
813 c89eb67d Helga Velroyen
       that are newly enabled by this operation.
814 c89eb67d Helga Velroyen

815 c89eb67d Helga Velroyen
    """
816 c89eb67d Helga Velroyen
    return self._GetEnabledDiskTemplatesInner(self.op.enabled_disk_templates,
817 c89eb67d Helga Velroyen
                                              cluster.enabled_disk_templates)
818 c89eb67d Helga Velroyen
819 33a6464e Helga Velroyen
  def _CheckIpolicy(self, cluster, enabled_disk_templates):
820 1532b078 Helga Velroyen
    """Checks the ipolicy.
821 1532b078 Helga Velroyen

822 1532b078 Helga Velroyen
    @type cluster: C{objects.Cluster}
823 1532b078 Helga Velroyen
    @param cluster: the cluster's configuration
824 33a6464e Helga Velroyen
    @type enabled_disk_templates: list of string
825 33a6464e Helga Velroyen
    @param enabled_disk_templates: list of (possibly newly) enabled disk
826 33a6464e Helga Velroyen
      templates
827 1532b078 Helga Velroyen

828 1532b078 Helga Velroyen
    """
829 33a6464e Helga Velroyen
    # FIXME: write unit tests for this
830 1532b078 Helga Velroyen
    if self.op.ipolicy:
831 1532b078 Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
832 1532b078 Helga Velroyen
                                           group_policy=False)
833 1532b078 Helga Velroyen
834 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(self.new_ipolicy,
835 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
836 33a6464e Helga Velroyen
837 1532b078 Helga Velroyen
      all_instances = self.cfg.GetAllInstancesInfo().values()
838 1532b078 Helga Velroyen
      violations = set()
839 1532b078 Helga Velroyen
      for group in self.cfg.GetAllNodeGroupsInfo().values():
840 1532b078 Helga Velroyen
        instances = frozenset([inst for inst in all_instances
841 1532b078 Helga Velroyen
                               if compat.any(nuuid in group.members
842 1532b078 Helga Velroyen
                                             for nuuid in inst.all_nodes)])
843 1532b078 Helga Velroyen
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
844 1532b078 Helga Velroyen
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
845 1532b078 Helga Velroyen
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
846 1532b078 Helga Velroyen
                                           self.cfg)
847 1532b078 Helga Velroyen
        if new:
848 1532b078 Helga Velroyen
          violations.update(new)
849 1532b078 Helga Velroyen
850 1532b078 Helga Velroyen
      if violations:
851 1532b078 Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
852 1532b078 Helga Velroyen
                        " violate them: %s",
853 1532b078 Helga Velroyen
                        utils.CommaJoin(utils.NiceSort(violations)))
854 33a6464e Helga Velroyen
    else:
855 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(cluster.ipolicy,
856 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
857 1532b078 Helga Velroyen
858 31ccfc0e Helga Velroyen
  def _CheckDrbdHelperOnNodes(self, drbd_helper, node_uuids):
859 31ccfc0e Helga Velroyen
    """Checks whether the set DRBD helper actually exists on the nodes.
860 31ccfc0e Helga Velroyen

861 31ccfc0e Helga Velroyen
    @type drbd_helper: string
862 31ccfc0e Helga Velroyen
    @param drbd_helper: path of the drbd usermode helper binary
863 31ccfc0e Helga Velroyen
    @type node_uuids: list of strings
864 31ccfc0e Helga Velroyen
    @param node_uuids: list of node UUIDs to check for the helper
865 31ccfc0e Helga Velroyen

866 31ccfc0e Helga Velroyen
    """
867 31ccfc0e Helga Velroyen
    # checks given drbd helper on all nodes
868 31ccfc0e Helga Velroyen
    helpers = self.rpc.call_drbd_helper(node_uuids)
869 31ccfc0e Helga Velroyen
    for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
870 31ccfc0e Helga Velroyen
      if ninfo.offline:
871 31ccfc0e Helga Velroyen
        self.LogInfo("Not checking drbd helper on offline node %s",
872 31ccfc0e Helga Velroyen
                     ninfo.name)
873 31ccfc0e Helga Velroyen
        continue
874 31ccfc0e Helga Velroyen
      msg = helpers[ninfo.uuid].fail_msg
875 31ccfc0e Helga Velroyen
      if msg:
876 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error checking drbd helper on node"
877 31ccfc0e Helga Velroyen
                                   " '%s': %s" % (ninfo.name, msg),
878 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
879 31ccfc0e Helga Velroyen
      node_helper = helpers[ninfo.uuid].payload
880 31ccfc0e Helga Velroyen
      if node_helper != drbd_helper:
881 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
882 31ccfc0e Helga Velroyen
                                   (ninfo.name, node_helper),
883 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
884 31ccfc0e Helga Velroyen
885 31ccfc0e Helga Velroyen
  def _CheckDrbdHelper(self, node_uuids, drbd_enabled, drbd_gets_enabled):
886 7c577910 Helga Velroyen
    """Check the DRBD usermode helper.
887 1bb99a33 Bernardo Dal Seno

888 7c577910 Helga Velroyen
    @type node_uuids: list of strings
889 7c577910 Helga Velroyen
    @param node_uuids: a list of nodes' UUIDs
890 31ccfc0e Helga Velroyen
    @type drbd_enabled: boolean
891 31ccfc0e Helga Velroyen
    @param drbd_enabled: whether DRBD will be enabled after this operation
892 31ccfc0e Helga Velroyen
      (no matter if it was disabled before or not)
893 31ccfc0e Helga Velroyen
    @type drbd_gets_enabled: boolen
894 31ccfc0e Helga Velroyen
    @param drbd_gets_enabled: true if DRBD was disabled before this
895 31ccfc0e Helga Velroyen
      operation, but will be enabled afterwards
896 1bb99a33 Bernardo Dal Seno

897 1bb99a33 Bernardo Dal Seno
    """
898 31ccfc0e Helga Velroyen
    if self.op.drbd_helper == '':
899 31ccfc0e Helga Velroyen
      if drbd_enabled:
900 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Cannot disable drbd helper while"
901 31ccfc0e Helga Velroyen
                                   " DRBD is enabled.")
902 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_DRBD8):
903 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
904 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
905 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
906 7352d33b Thomas Thrainer
907 31ccfc0e Helga Velroyen
    else:
908 31ccfc0e Helga Velroyen
      if self.op.drbd_helper is not None and drbd_enabled:
909 31ccfc0e Helga Velroyen
        self._CheckDrbdHelperOnNodes(self.op.drbd_helper, node_uuids)
910 31ccfc0e Helga Velroyen
      else:
911 31ccfc0e Helga Velroyen
        if drbd_gets_enabled:
912 31ccfc0e Helga Velroyen
          current_drbd_helper = self.cfg.GetClusterInfo().drbd_usermode_helper
913 31ccfc0e Helga Velroyen
          if current_drbd_helper is not None:
914 31ccfc0e Helga Velroyen
            self._CheckDrbdHelperOnNodes(current_drbd_helper, node_uuids)
915 31ccfc0e Helga Velroyen
          else:
916 31ccfc0e Helga Velroyen
            raise errors.OpPrereqError("Cannot enable DRBD without a"
917 31ccfc0e Helga Velroyen
                                       " DRBD usermode helper set.")
918 7c577910 Helga Velroyen
919 7c577910 Helga Velroyen
  def CheckPrereq(self):
920 7c577910 Helga Velroyen
    """Check prerequisites.
921 7c577910 Helga Velroyen

922 7c577910 Helga Velroyen
    This checks whether the given params don't conflict and
923 7c577910 Helga Velroyen
    if the given volume group is valid.
924 7c577910 Helga Velroyen

925 7c577910 Helga Velroyen
    """
926 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
927 1bb99a33 Bernardo Dal Seno
    self.cluster = cluster = self.cfg.GetClusterInfo()
928 7352d33b Thomas Thrainer
929 1c3231aa Thomas Thrainer
    vm_capable_node_uuids = [node.uuid
930 1c3231aa Thomas Thrainer
                             for node in self.cfg.GetAllNodesInfo().values()
931 1c3231aa Thomas Thrainer
                             if node.uuid in node_uuids and node.vm_capable]
932 7352d33b Thomas Thrainer
933 1bb99a33 Bernardo Dal Seno
    (enabled_disk_templates, new_enabled_disk_templates) = \
934 1bb99a33 Bernardo Dal Seno
      self._GetEnabledDiskTemplates(cluster)
935 1bb99a33 Bernardo Dal Seno
936 1c3231aa Thomas Thrainer
    self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
937 1bb99a33 Bernardo Dal Seno
                      new_enabled_disk_templates)
938 7352d33b Thomas Thrainer
939 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
940 3039e2dc Helga Velroyen
      CheckFileStoragePathVsEnabledDiskTemplates(
941 3039e2dc Helga Velroyen
          self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
942 3039e2dc Helga Velroyen
943 4e6cfd11 Helga Velroyen
    if self.op.shared_file_storage_dir is not None:
944 4e6cfd11 Helga Velroyen
      CheckSharedFileStoragePathVsEnabledDiskTemplates(
945 4e6cfd11 Helga Velroyen
          self.LogWarning, self.op.shared_file_storage_dir,
946 4e6cfd11 Helga Velroyen
          enabled_disk_templates)
947 4e6cfd11 Helga Velroyen
948 31ccfc0e Helga Velroyen
    drbd_enabled = constants.DT_DRBD8 in enabled_disk_templates
949 31ccfc0e Helga Velroyen
    drbd_gets_enabled = constants.DT_DRBD8 in new_enabled_disk_templates
950 31ccfc0e Helga Velroyen
    self._CheckDrbdHelper(node_uuids, drbd_enabled, drbd_gets_enabled)
951 7352d33b Thomas Thrainer
952 7352d33b Thomas Thrainer
    # validate params changes
953 7352d33b Thomas Thrainer
    if self.op.beparams:
954 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
955 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
956 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
957 7352d33b Thomas Thrainer
958 7352d33b Thomas Thrainer
    if self.op.ndparams:
959 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
960 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
961 7352d33b Thomas Thrainer
962 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
963 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
964 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
965 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
966 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
967 7352d33b Thomas Thrainer
968 7352d33b Thomas Thrainer
    if self.op.hv_state:
969 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
970 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
971 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
972 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
973 7352d33b Thomas Thrainer
974 7352d33b Thomas Thrainer
    if self.op.disk_state:
975 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
976 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
977 7352d33b Thomas Thrainer
      self.new_disk_state = \
978 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
979 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
980 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
981 7352d33b Thomas Thrainer
982 33a6464e Helga Velroyen
    self._CheckIpolicy(cluster, enabled_disk_templates)
983 7352d33b Thomas Thrainer
984 7352d33b Thomas Thrainer
    if self.op.nicparams:
985 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
986 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
987 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
988 7352d33b Thomas Thrainer
      nic_errors = []
989 7352d33b Thomas Thrainer
990 7352d33b Thomas Thrainer
      # check all instances for consistency
991 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
992 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
993 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
994 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
995 7352d33b Thomas Thrainer
996 7352d33b Thomas Thrainer
          # check parameter syntax
997 7352d33b Thomas Thrainer
          try:
998 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
999 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
1000 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
1001 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
1002 7352d33b Thomas Thrainer
1003 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
1004 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
1005 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
1006 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
1007 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
1008 7352d33b Thomas Thrainer
      if nic_errors:
1009 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
1010 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
1011 7352d33b Thomas Thrainer
1012 7352d33b Thomas Thrainer
    # hypervisor list/parameters
1013 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
1014 7352d33b Thomas Thrainer
    if self.op.hvparams:
1015 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
1016 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
1017 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
1018 7352d33b Thomas Thrainer
        else:
1019 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
1020 7352d33b Thomas Thrainer
1021 7352d33b Thomas Thrainer
    # disk template parameters
1022 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
1023 7352d33b Thomas Thrainer
    if self.op.diskparams:
1024 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
1025 f06af3ca Thomas Thrainer
        if dt_name not in self.new_diskparams:
1026 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
1027 7352d33b Thomas Thrainer
        else:
1028 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
1029 294254b1 Raffa Santi
      CheckDiskAccessModeConsistency(self.op.diskparams, self.cfg)
1030 7352d33b Thomas Thrainer
1031 7352d33b Thomas Thrainer
    # os hypervisor parameters
1032 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
1033 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1034 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
1035 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
1036 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
1037 7352d33b Thomas Thrainer
        else:
1038 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
1039 7352d33b Thomas Thrainer
            if hv_dict is None:
1040 7352d33b Thomas Thrainer
              # Delete if it exists
1041 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
1042 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
1043 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
1044 7352d33b Thomas Thrainer
            else:
1045 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
1046 7352d33b Thomas Thrainer
1047 7352d33b Thomas Thrainer
    # os parameters
1048 7352d33b Thomas Thrainer
    self.new_osp = objects.FillDict(cluster.osparams, {})
1049 7352d33b Thomas Thrainer
    if self.op.osparams:
1050 7352d33b Thomas Thrainer
      for os_name, osp in self.op.osparams.items():
1051 7352d33b Thomas Thrainer
        if os_name not in self.new_osp:
1052 7352d33b Thomas Thrainer
          self.new_osp[os_name] = {}
1053 7352d33b Thomas Thrainer
1054 5eacbcae Thomas Thrainer
        self.new_osp[os_name] = GetUpdatedParams(self.new_osp[os_name], osp,
1055 5eacbcae Thomas Thrainer
                                                 use_none=True)
1056 7352d33b Thomas Thrainer
1057 7352d33b Thomas Thrainer
        if not self.new_osp[os_name]:
1058 7352d33b Thomas Thrainer
          # we removed all parameters
1059 7352d33b Thomas Thrainer
          del self.new_osp[os_name]
1060 7352d33b Thomas Thrainer
        else:
1061 7352d33b Thomas Thrainer
          # check the parameter validity (remote check)
1062 5eacbcae Thomas Thrainer
          CheckOSParams(self, False, [self.cfg.GetMasterNode()],
1063 5eacbcae Thomas Thrainer
                        os_name, self.new_osp[os_name])
1064 7352d33b Thomas Thrainer
1065 7352d33b Thomas Thrainer
    # changes to the hypervisor list
1066 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1067 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
1068 7352d33b Thomas Thrainer
      for hv in self.hv_list:
1069 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
1070 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
1071 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
1072 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
1073 7352d33b Thomas Thrainer
        # enabled before
1074 7352d33b Thomas Thrainer
        if hv not in new_hvp:
1075 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
1076 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
1077 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
1078 7352d33b Thomas Thrainer
    else:
1079 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
1080 7352d33b Thomas Thrainer
1081 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
1082 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
1083 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
1084 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
1085 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
1086 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
1087 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
1088 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1089 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1090 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
1091 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, hv_params)
1092 7352d33b Thomas Thrainer
1093 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
1094 7352d33b Thomas Thrainer
1095 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1096 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
1097 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
1098 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
1099 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
1100 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1101 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
1102 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
1103 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
1104 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1105 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
1106 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, new_osp)
1107 7352d33b Thomas Thrainer
1108 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
1109 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
1110 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
1111 7352d33b Thomas Thrainer
                                    os.path.isfile)
1112 7352d33b Thomas Thrainer
      if alloc_script is None:
1113 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
1114 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
1115 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1116 7352d33b Thomas Thrainer
1117 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
1118 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
1119 7352d33b Thomas Thrainer
       are still in use by some instances.
1120 7352d33b Thomas Thrainer

1121 7352d33b Thomas Thrainer
    """
1122 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
1123 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1124 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
1125 7352d33b Thomas Thrainer
1126 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
1127 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
1128 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
1129 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
1130 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
1131 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
1132 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
1133 7352d33b Thomas Thrainer
1134 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
1135 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
1136 7352d33b Thomas Thrainer

1137 7352d33b Thomas Thrainer
    """
1138 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
1139 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
1140 7352d33b Thomas Thrainer
      if not new_volume:
1141 7352d33b Thomas Thrainer
        new_volume = None
1142 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
1143 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
1144 7352d33b Thomas Thrainer
      else:
1145 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
1146 7352d33b Thomas Thrainer
                    " state, not changing")
1147 1bb99a33 Bernardo Dal Seno
1148 3039e2dc Helga Velroyen
  def _SetFileStorageDir(self, feedback_fn):
1149 3039e2dc Helga Velroyen
    """Set the file storage directory.
1150 3039e2dc Helga Velroyen

1151 3039e2dc Helga Velroyen
    """
1152 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1153 3039e2dc Helga Velroyen
      if self.cluster.file_storage_dir == self.op.file_storage_dir:
1154 3039e2dc Helga Velroyen
        feedback_fn("Global file storage dir already set to value '%s'"
1155 3039e2dc Helga Velroyen
                    % self.cluster.file_storage_dir)
1156 3039e2dc Helga Velroyen
      else:
1157 3039e2dc Helga Velroyen
        self.cluster.file_storage_dir = self.op.file_storage_dir
1158 3039e2dc Helga Velroyen
1159 7c577910 Helga Velroyen
  def _SetDrbdHelper(self, feedback_fn):
1160 7c577910 Helga Velroyen
    """Set the DRBD usermode helper.
1161 1bb99a33 Bernardo Dal Seno

1162 1bb99a33 Bernardo Dal Seno
    """
1163 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1164 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1165 a794b8d7 Thomas Thrainer
        feedback_fn("Note that you specified a drbd user helper, but did not"
1166 a794b8d7 Thomas Thrainer
                    " enable the drbd disk template.")
1167 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1168 7352d33b Thomas Thrainer
      if not new_helper:
1169 7352d33b Thomas Thrainer
        new_helper = None
1170 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1171 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1172 7352d33b Thomas Thrainer
      else:
1173 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1174 7352d33b Thomas Thrainer
                    " not changing")
1175 7c577910 Helga Velroyen
1176 7c577910 Helga Velroyen
  def Exec(self, feedback_fn):
1177 7c577910 Helga Velroyen
    """Change the parameters of the cluster.
1178 7c577910 Helga Velroyen

1179 7c577910 Helga Velroyen
    """
1180 7c577910 Helga Velroyen
    if self.op.enabled_disk_templates:
1181 7c577910 Helga Velroyen
      self.cluster.enabled_disk_templates = \
1182 7c577910 Helga Velroyen
        list(set(self.op.enabled_disk_templates))
1183 7c577910 Helga Velroyen
1184 7c577910 Helga Velroyen
    self._SetVgName(feedback_fn)
1185 7c577910 Helga Velroyen
    self._SetFileStorageDir(feedback_fn)
1186 7c577910 Helga Velroyen
    self._SetDrbdHelper(feedback_fn)
1187 7c577910 Helga Velroyen
1188 7352d33b Thomas Thrainer
    if self.op.hvparams:
1189 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1190 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1191 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1192 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1193 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1194 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1195 7352d33b Thomas Thrainer
    if self.op.beparams:
1196 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1197 7352d33b Thomas Thrainer
    if self.op.nicparams:
1198 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1199 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1200 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1201 7352d33b Thomas Thrainer
    if self.op.osparams:
1202 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1203 7352d33b Thomas Thrainer
    if self.op.ndparams:
1204 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1205 7352d33b Thomas Thrainer
    if self.op.diskparams:
1206 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1207 7352d33b Thomas Thrainer
    if self.op.hv_state:
1208 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1209 7352d33b Thomas Thrainer
    if self.op.disk_state:
1210 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1211 7352d33b Thomas Thrainer
1212 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1213 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1214 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1215 5eacbcae Thomas Thrainer
      AdjustCandidatePool(self, [])
1216 7352d33b Thomas Thrainer
1217 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1218 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1219 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1220 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1221 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1222 7352d33b Thomas Thrainer
1223 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1224 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1225 75f2ff7d Michele Tartara
1226 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1227 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1228 7352d33b Thomas Thrainer
1229 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1230 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1231 7352d33b Thomas Thrainer
1232 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1233 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1234 7352d33b Thomas Thrainer
1235 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1236 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1237 7352d33b Thomas Thrainer
1238 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1239 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1240 7352d33b Thomas Thrainer
1241 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1242 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1243 7352d33b Thomas Thrainer
1244 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1245 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1246 7352d33b Thomas Thrainer
1247 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1248 7352d33b Thomas Thrainer
      desc += " OS list"
1249 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1250 7352d33b Thomas Thrainer
      for key, val in mods:
1251 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1252 7352d33b Thomas Thrainer
          if val in lst:
1253 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1254 7352d33b Thomas Thrainer
          else:
1255 7352d33b Thomas Thrainer
            lst.append(val)
1256 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1257 7352d33b Thomas Thrainer
          if val in lst:
1258 7352d33b Thomas Thrainer
            lst.remove(val)
1259 7352d33b Thomas Thrainer
          else:
1260 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1261 7352d33b Thomas Thrainer
        else:
1262 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1263 7352d33b Thomas Thrainer
1264 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1265 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1266 7352d33b Thomas Thrainer
1267 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1268 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1269 7352d33b Thomas Thrainer
1270 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1271 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1272 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1273 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1274 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1275 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1276 7352d33b Thomas Thrainer
                                                       master_params, ems)
1277 e5c92cfb Klaus Aehlig
      if not self.op.force:
1278 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1279 e5c92cfb Klaus Aehlig
      else:
1280 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1281 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1282 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1283 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1284 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1285 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1286 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1287 7352d33b Thomas Thrainer
1288 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1289 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1290 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1291 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1292 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1293 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1294 1c3231aa Thomas Thrainer
                 master_params.netdev)
1295 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1296 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1297 7352d33b Thomas Thrainer
1298 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1299 7352d33b Thomas Thrainer
1300 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1301 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1302 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1303 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1304 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1305 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1306 7352d33b Thomas Thrainer
                                                     master_params, ems)
1307 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1308 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1309 7352d33b Thomas Thrainer
1310 7352d33b Thomas Thrainer
1311 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1312 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1313 7352d33b Thomas Thrainer

1314 7352d33b Thomas Thrainer
  """
1315 7352d33b Thomas Thrainer
  REQ_BGL = False
1316 7352d33b Thomas Thrainer
1317 7352d33b Thomas Thrainer
  def ExpandNames(self):
1318 7352d33b Thomas Thrainer
    self.needed_locks = {}
1319 7352d33b Thomas Thrainer
1320 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1321 7352d33b Thomas Thrainer
    jobs = []
1322 7352d33b Thomas Thrainer
1323 7352d33b Thomas Thrainer
    if self.op.group_name:
1324 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1325 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1326 7352d33b Thomas Thrainer
    else:
1327 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1328 7352d33b Thomas Thrainer
1329 7352d33b Thomas Thrainer
      # Verify global configuration
1330 7352d33b Thomas Thrainer
      jobs.append([
1331 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1332 7352d33b Thomas Thrainer
        ])
1333 7352d33b Thomas Thrainer
1334 7352d33b Thomas Thrainer
      # Always depend on global verification
1335 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1336 7352d33b Thomas Thrainer
1337 7352d33b Thomas Thrainer
    jobs.extend(
1338 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1339 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1340 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1341 7352d33b Thomas Thrainer
      for group in groups)
1342 7352d33b Thomas Thrainer
1343 7352d33b Thomas Thrainer
    # Fix up all parameters
1344 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1345 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1346 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1347 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1348 7352d33b Thomas Thrainer
      try:
1349 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1350 7352d33b Thomas Thrainer
      except AttributeError:
1351 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1352 7352d33b Thomas Thrainer
1353 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1354 7352d33b Thomas Thrainer
1355 7352d33b Thomas Thrainer
1356 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1357 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1358 7352d33b Thomas Thrainer

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

1362 7352d33b Thomas Thrainer
  """
1363 7352d33b Thomas Thrainer
1364 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1365 7352d33b Thomas Thrainer
  ETYPE_ERROR = "ERROR"
1366 7352d33b Thomas Thrainer
  ETYPE_WARNING = "WARNING"
1367 7352d33b Thomas Thrainer
1368 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1369 7352d33b Thomas Thrainer
    """Format an error message.
1370 7352d33b Thomas Thrainer

1371 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1372 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1373 7352d33b Thomas Thrainer

1374 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1375 7352d33b Thomas Thrainer

1376 7352d33b Thomas Thrainer
    """
1377 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1378 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1379 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1380 7352d33b Thomas Thrainer
    # warning
1381 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1382 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1383 7352d33b Thomas Thrainer
    # first complete the msg
1384 7352d33b Thomas Thrainer
    if args:
1385 7352d33b Thomas Thrainer
      msg = msg % args
1386 7352d33b Thomas Thrainer
    # then format the whole message
1387 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1388 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1389 7352d33b Thomas Thrainer
    else:
1390 7352d33b Thomas Thrainer
      if item:
1391 7352d33b Thomas Thrainer
        item = " " + item
1392 7352d33b Thomas Thrainer
      else:
1393 7352d33b Thomas Thrainer
        item = ""
1394 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1395 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1396 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1397 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1398 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1399 7352d33b Thomas Thrainer
      self.bad = True
1400 7352d33b Thomas Thrainer
1401 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1402 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1403 7352d33b Thomas Thrainer

1404 7352d33b Thomas Thrainer
    """
1405 7352d33b Thomas Thrainer
    if (bool(cond)
1406 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1407 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1408 7352d33b Thomas Thrainer
1409 7352d33b Thomas Thrainer
1410 7352d33b Thomas Thrainer
def _VerifyCertificate(filename):
1411 7352d33b Thomas Thrainer
  """Verifies a certificate for L{LUClusterVerifyConfig}.
1412 7352d33b Thomas Thrainer

1413 7352d33b Thomas Thrainer
  @type filename: string
1414 7352d33b Thomas Thrainer
  @param filename: Path to PEM file
1415 7352d33b Thomas Thrainer

1416 7352d33b Thomas Thrainer
  """
1417 7352d33b Thomas Thrainer
  try:
1418 7352d33b Thomas Thrainer
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
1419 7352d33b Thomas Thrainer
                                           utils.ReadFile(filename))
1420 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
1421 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR,
1422 7352d33b Thomas Thrainer
            "Failed to load X509 certificate %s: %s" % (filename, err))
1423 7352d33b Thomas Thrainer
1424 7352d33b Thomas Thrainer
  (errcode, msg) = \
1425 7352d33b Thomas Thrainer
    utils.VerifyX509Certificate(cert, constants.SSL_CERT_EXPIRATION_WARN,
1426 7352d33b Thomas Thrainer
                                constants.SSL_CERT_EXPIRATION_ERROR)
1427 7352d33b Thomas Thrainer
1428 7352d33b Thomas Thrainer
  if msg:
1429 7352d33b Thomas Thrainer
    fnamemsg = "While verifying %s: %s" % (filename, msg)
1430 7352d33b Thomas Thrainer
  else:
1431 7352d33b Thomas Thrainer
    fnamemsg = None
1432 7352d33b Thomas Thrainer
1433 7352d33b Thomas Thrainer
  if errcode is None:
1434 7352d33b Thomas Thrainer
    return (None, fnamemsg)
1435 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_WARNING:
1436 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_WARNING, fnamemsg)
1437 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_ERROR:
1438 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR, fnamemsg)
1439 7352d33b Thomas Thrainer
1440 7352d33b Thomas Thrainer
  raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode)
1441 7352d33b Thomas Thrainer
1442 7352d33b Thomas Thrainer
1443 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1444 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1445 7352d33b Thomas Thrainer

1446 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1447 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1448 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1449 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1450 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1451 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1452 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1453 7352d33b Thomas Thrainer

1454 7352d33b Thomas Thrainer
  """
1455 7352d33b Thomas Thrainer
  hvp_data = []
1456 7352d33b Thomas Thrainer
1457 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1458 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1459 7352d33b Thomas Thrainer
1460 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1461 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1462 7352d33b Thomas Thrainer
      if hv_params:
1463 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1464 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1465 7352d33b Thomas Thrainer
1466 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1467 7352d33b Thomas Thrainer
  for instance in instances:
1468 7352d33b Thomas Thrainer
    if instance.hvparams:
1469 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1470 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1471 7352d33b Thomas Thrainer
1472 7352d33b Thomas Thrainer
  return hvp_data
1473 7352d33b Thomas Thrainer
1474 7352d33b Thomas Thrainer
1475 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1476 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1477 7352d33b Thomas Thrainer

1478 7352d33b Thomas Thrainer
  """
1479 7352d33b Thomas Thrainer
  REQ_BGL = False
1480 7352d33b Thomas Thrainer
1481 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1482 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1483 7352d33b Thomas Thrainer

1484 7352d33b Thomas Thrainer
    """
1485 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1486 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1487 7352d33b Thomas Thrainer
             (item, hv_name))
1488 7352d33b Thomas Thrainer
      try:
1489 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1490 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1491 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1492 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1493 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1494 7352d33b Thomas Thrainer
1495 7352d33b Thomas Thrainer
  def ExpandNames(self):
1496 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1497 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1498 7352d33b Thomas Thrainer
1499 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1500 7352d33b Thomas Thrainer
    """Check prerequisites.
1501 7352d33b Thomas Thrainer

1502 7352d33b Thomas Thrainer
    """
1503 7352d33b Thomas Thrainer
    # Retrieve all information
1504 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1505 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1506 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1507 7352d33b Thomas Thrainer
1508 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1509 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1510 7352d33b Thomas Thrainer

1511 7352d33b Thomas Thrainer
    """
1512 7352d33b Thomas Thrainer
    self.bad = False
1513 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1514 7352d33b Thomas Thrainer
1515 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1516 7352d33b Thomas Thrainer
1517 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1518 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1519 7352d33b Thomas Thrainer
1520 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1521 7352d33b Thomas Thrainer
1522 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1523 7352d33b Thomas Thrainer
      (errcode, msg) = _VerifyCertificate(cert_filename)
1524 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1525 7352d33b Thomas Thrainer
1526 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1527 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1528 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1529 69ac3b74 Michele Tartara
                  None,
1530 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1531 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1532 69ac3b74 Michele Tartara
1533 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1534 7352d33b Thomas Thrainer
1535 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1536 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1537 7352d33b Thomas Thrainer
1538 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1539 7352d33b Thomas Thrainer
1540 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1541 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1542 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1543 7352d33b Thomas Thrainer
1544 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1545 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1546 7352d33b Thomas Thrainer
1547 7352d33b Thomas Thrainer
    dangling_instances = {}
1548 7352d33b Thomas Thrainer
    no_node_instances = []
1549 7352d33b Thomas Thrainer
1550 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1551 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1552 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1553 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1554 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1555 7352d33b Thomas Thrainer
1556 7352d33b Thomas Thrainer
    pretty_dangling = [
1557 7352d33b Thomas Thrainer
        "%s (%s)" %
1558 7352d33b Thomas Thrainer
        (node.name,
1559 24e96ef6 Thomas Thrainer
         utils.CommaJoin(inst.name for
1560 24e96ef6 Thomas Thrainer
                         inst in dangling_instances.get(node.uuid, [])))
1561 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1562 7352d33b Thomas Thrainer
1563 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1564 7352d33b Thomas Thrainer
                  None,
1565 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1566 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1567 7352d33b Thomas Thrainer
1568 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1569 7352d33b Thomas Thrainer
                  None,
1570 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1571 24e96ef6 Thomas Thrainer
                  " %s", utils.CommaJoin(inst.name for
1572 24e96ef6 Thomas Thrainer
                                         inst in no_node_instances))
1573 7352d33b Thomas Thrainer
1574 7352d33b Thomas Thrainer
    return not self.bad
1575 7352d33b Thomas Thrainer
1576 7352d33b Thomas Thrainer
1577 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1578 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1579 7352d33b Thomas Thrainer

1580 7352d33b Thomas Thrainer
  """
1581 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1582 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1583 7352d33b Thomas Thrainer
  REQ_BGL = False
1584 7352d33b Thomas Thrainer
1585 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1586 7352d33b Thomas Thrainer
1587 7352d33b Thomas Thrainer
  class NodeImage(object):
1588 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1589 7352d33b Thomas Thrainer

1590 1c3231aa Thomas Thrainer
    @type uuid: string
1591 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1592 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1593 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1594 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1595 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1596 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1597 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1598 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1599 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1600 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1601 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1602 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1603 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1604 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1605 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1606 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1607 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1608 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1609 7352d33b Thomas Thrainer
    @type ghost: boolean
1610 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1611 7352d33b Thomas Thrainer
    @type os_fail: boolean
1612 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1613 7352d33b Thomas Thrainer
    @type oslist: list
1614 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1615 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1616 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1617 7352d33b Thomas Thrainer
    @type pv_min: float
1618 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1619 7352d33b Thomas Thrainer
    @type pv_max: float
1620 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1621 7352d33b Thomas Thrainer

1622 7352d33b Thomas Thrainer
    """
1623 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1624 1c3231aa Thomas Thrainer
      self.uuid = uuid
1625 7352d33b Thomas Thrainer
      self.volumes = {}
1626 7352d33b Thomas Thrainer
      self.instances = []
1627 7352d33b Thomas Thrainer
      self.pinst = []
1628 7352d33b Thomas Thrainer
      self.sinst = []
1629 7352d33b Thomas Thrainer
      self.sbp = {}
1630 7352d33b Thomas Thrainer
      self.mfree = 0
1631 7352d33b Thomas Thrainer
      self.dfree = 0
1632 7352d33b Thomas Thrainer
      self.offline = offline
1633 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1634 7352d33b Thomas Thrainer
      self.rpc_fail = False
1635 7352d33b Thomas Thrainer
      self.lvm_fail = False
1636 7352d33b Thomas Thrainer
      self.hyp_fail = False
1637 7352d33b Thomas Thrainer
      self.ghost = False
1638 7352d33b Thomas Thrainer
      self.os_fail = False
1639 7352d33b Thomas Thrainer
      self.oslist = {}
1640 7352d33b Thomas Thrainer
      self.pv_min = None
1641 7352d33b Thomas Thrainer
      self.pv_max = None
1642 7352d33b Thomas Thrainer
1643 7352d33b Thomas Thrainer
  def ExpandNames(self):
1644 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1645 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1646 7352d33b Thomas Thrainer
1647 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1648 da4a52a3 Thomas Thrainer
    inst_uuids = \
1649 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1650 7352d33b Thomas Thrainer
1651 7352d33b Thomas Thrainer
    self.needed_locks = {
1652 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1653 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1654 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1655 7352d33b Thomas Thrainer
1656 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1657 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1658 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1659 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1660 7352d33b Thomas Thrainer
      }
1661 7352d33b Thomas Thrainer
1662 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1663 7352d33b Thomas Thrainer
1664 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1665 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1666 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1667 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1668 7352d33b Thomas Thrainer
1669 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1670 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1671 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1672 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1673 7352d33b Thomas Thrainer
      # be locked.
1674 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1675 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1676 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1677 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1678 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1679 7352d33b Thomas Thrainer
1680 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1681 7352d33b Thomas Thrainer
1682 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1683 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1684 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1685 7352d33b Thomas Thrainer
1686 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1687 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1688 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1689 7352d33b Thomas Thrainer
1690 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1691 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1692 7352d33b Thomas Thrainer
1693 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
1694 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
1695 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
1696 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
1697 7352d33b Thomas Thrainer
1698 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
1699 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
1700 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
1701 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
1702 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
1703 7352d33b Thomas Thrainer
1704 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
1705 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
1706 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
1707 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
1708 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
1709 7352d33b Thomas Thrainer
1710 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1711 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1712 7352d33b Thomas Thrainer
1713 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
1714 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
1715 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
1716 7352d33b Thomas Thrainer
1717 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
1718 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
1719 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
1720 7352d33b Thomas Thrainer
1721 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1722 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1723 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1724 7352d33b Thomas Thrainer
1725 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1726 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1727 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
1728 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
1729 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
1730 7352d33b Thomas Thrainer
1731 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1732 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1733 7352d33b Thomas Thrainer
1734 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1735 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1736 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1737 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1738 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1739 7352d33b Thomas Thrainer
1740 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1741 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1742 7352d33b Thomas Thrainer

1743 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1744 7352d33b Thomas Thrainer
        mandatory fields
1745 7352d33b Thomas Thrainer
      - check ganeti version
1746 7352d33b Thomas Thrainer

1747 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1748 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1749 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1750 7352d33b Thomas Thrainer
    @rtype: boolean
1751 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1752 7352d33b Thomas Thrainer
         reasonable values in the respose)
1753 7352d33b Thomas Thrainer

1754 7352d33b Thomas Thrainer
    """
1755 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1756 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1757 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1758 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1759 7352d33b Thomas Thrainer
    if test:
1760 7352d33b Thomas Thrainer
      return False
1761 7352d33b Thomas Thrainer
1762 7352d33b Thomas Thrainer
    # compares ganeti version
1763 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1764 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1765 7352d33b Thomas Thrainer
    test = not (remote_version and
1766 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1767 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1768 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1769 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
1770 7352d33b Thomas Thrainer
    if test:
1771 7352d33b Thomas Thrainer
      return False
1772 7352d33b Thomas Thrainer
1773 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1774 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
1775 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
1776 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
1777 7352d33b Thomas Thrainer
    if test:
1778 7352d33b Thomas Thrainer
      return False
1779 7352d33b Thomas Thrainer
1780 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1781 7352d33b Thomas Thrainer
1782 7352d33b Thomas Thrainer
    # full package version
1783 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1784 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
1785 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1786 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1787 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1788 7352d33b Thomas Thrainer
1789 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1790 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1791 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1792 7352d33b Thomas Thrainer
        test = hv_result is not None
1793 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
1794 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1795 7352d33b Thomas Thrainer
1796 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1797 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1798 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1799 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
1800 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
1801 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
1802 7352d33b Thomas Thrainer
1803 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1804 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1805 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
1806 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
1807 7352d33b Thomas Thrainer
1808 7352d33b Thomas Thrainer
    return True
1809 7352d33b Thomas Thrainer
1810 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1811 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1812 7352d33b Thomas Thrainer
    """Check the node time.
1813 7352d33b Thomas Thrainer

1814 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1815 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1816 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1817 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1818 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1819 7352d33b Thomas Thrainer

1820 7352d33b Thomas Thrainer
    """
1821 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1822 7352d33b Thomas Thrainer
    try:
1823 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1824 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1825 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
1826 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
1827 7352d33b Thomas Thrainer
      return
1828 7352d33b Thomas Thrainer
1829 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1830 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1831 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1832 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1833 7352d33b Thomas Thrainer
    else:
1834 7352d33b Thomas Thrainer
      ntime_diff = None
1835 7352d33b Thomas Thrainer
1836 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
1837 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
1838 d0d7d7cf Thomas Thrainer
                  ntime_diff)
1839 7352d33b Thomas Thrainer
1840 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1841 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1842 7352d33b Thomas Thrainer

1843 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1844 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1845 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1846 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1847 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1848 7352d33b Thomas Thrainer
    @param nimg: node image
1849 7352d33b Thomas Thrainer

1850 7352d33b Thomas Thrainer
    """
1851 7352d33b Thomas Thrainer
    if vg_name is None:
1852 7352d33b Thomas Thrainer
      return
1853 7352d33b Thomas Thrainer
1854 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1855 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1856 7352d33b Thomas Thrainer
    test = not vglist
1857 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
1858 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
1859 7352d33b Thomas Thrainer
    if not test:
1860 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1861 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1862 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
1863 7352d33b Thomas Thrainer
1864 7352d33b Thomas Thrainer
    # Check PVs
1865 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
1866 7352d33b Thomas Thrainer
    for em in errmsgs:
1867 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
1868 7352d33b Thomas Thrainer
    if pvminmax is not None:
1869 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1870 7352d33b Thomas Thrainer
1871 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
1872 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
1873 1bb99a33 Bernardo Dal Seno

1874 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
1875 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
1876 1bb99a33 Bernardo Dal Seno
      node_verify call.
1877 1bb99a33 Bernardo Dal Seno

1878 1bb99a33 Bernardo Dal Seno
    """
1879 1bb99a33 Bernardo Dal Seno
    node_versions = {}
1880 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
1881 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
1882 1bb99a33 Bernardo Dal Seno
      version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
1883 1c3231aa Thomas Thrainer
      node_versions[node_uuid] = version
1884 1bb99a33 Bernardo Dal Seno
1885 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
1886 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
1887 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
1888 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
1889 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
1890 1bb99a33 Bernardo Dal Seno
1891 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
1892 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
1893 7352d33b Thomas Thrainer

1894 7352d33b Thomas Thrainer
    @type node_image: dict
1895 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
1896 7352d33b Thomas Thrainer
      L{NodeImage} objects
1897 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1898 7352d33b Thomas Thrainer

1899 7352d33b Thomas Thrainer
    """
1900 7352d33b Thomas Thrainer
    if vg_name is None:
1901 7352d33b Thomas Thrainer
      return
1902 7352d33b Thomas Thrainer
1903 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
1904 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
1905 7352d33b Thomas Thrainer
      return
1906 7352d33b Thomas Thrainer
1907 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
1908 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
1909 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
1910 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
1911 7352d33b Thomas Thrainer
    if not vals:
1912 7352d33b Thomas Thrainer
      return
1913 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
1914 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
1915 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
1916 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
1917 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
1918 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
1919 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
1920 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
1921 7352d33b Thomas Thrainer
1922 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
1923 7352d33b Thomas Thrainer
    """Check the node bridges.
1924 7352d33b Thomas Thrainer

1925 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1926 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1927 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1928 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
1929 7352d33b Thomas Thrainer

1930 7352d33b Thomas Thrainer
    """
1931 7352d33b Thomas Thrainer
    if not bridges:
1932 7352d33b Thomas Thrainer
      return
1933 7352d33b Thomas Thrainer
1934 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
1935 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
1936 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1937 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
1938 7352d33b Thomas Thrainer
    if not test:
1939 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
1940 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
1941 7352d33b Thomas Thrainer
1942 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
1943 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
1944 7352d33b Thomas Thrainer

1945 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1946 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1947 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1948 7352d33b Thomas Thrainer

1949 7352d33b Thomas Thrainer
    """
1950 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
1951 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
1952 7352d33b Thomas Thrainer
                  "did not return user scripts information")
1953 7352d33b Thomas Thrainer
1954 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
1955 7352d33b Thomas Thrainer
    if not test:
1956 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
1957 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
1958 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
1959 7352d33b Thomas Thrainer
1960 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
1961 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
1962 7352d33b Thomas Thrainer

1963 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1964 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1965 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1966 7352d33b Thomas Thrainer

1967 7352d33b Thomas Thrainer
    """
1968 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
1969 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
1970 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
1971 7352d33b Thomas Thrainer
    if not test:
1972 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
1973 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
1974 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
1975 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
1976 7352d33b Thomas Thrainer
1977 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
1978 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1979 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
1980 7352d33b Thomas Thrainer
    if not test:
1981 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
1982 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
1983 7352d33b Thomas Thrainer
        for anode in nlist:
1984 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
1985 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
1986 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
1987 7352d33b Thomas Thrainer
1988 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
1989 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1990 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
1991 7352d33b Thomas Thrainer
    if not test:
1992 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
1993 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
1994 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
1995 7352d33b Thomas Thrainer
        else:
1996 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
1997 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
1998 7352d33b Thomas Thrainer
1999 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
2000 7352d33b Thomas Thrainer
    """Verify an instance.
2001 7352d33b Thomas Thrainer

2002 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
2003 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
2004 7352d33b Thomas Thrainer
    state.
2005 7352d33b Thomas Thrainer

2006 7352d33b Thomas Thrainer
    """
2007 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
2008 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
2009 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
2010 7352d33b Thomas Thrainer
2011 7352d33b Thomas Thrainer
    node_vol_should = {}
2012 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
2013 7352d33b Thomas Thrainer
2014 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2015 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
2016 7352d33b Thomas Thrainer
                                                            self.group_info)
2017 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
2018 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
2019 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
2020 7352d33b Thomas Thrainer
2021 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
2022 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
2023 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
2024 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
2025 7352d33b Thomas Thrainer
        continue
2026 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
2027 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
2028 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
2029 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
2030 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
2031 7352d33b Thomas Thrainer
2032 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
2033 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
2034 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
2035 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
2036 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
2037 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
2038 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
2039 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
2040 7352d33b Thomas Thrainer
2041 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
2042 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
2043 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
2044 7352d33b Thomas Thrainer
2045 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
2046 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
2047 7352d33b Thomas Thrainer
      # node here
2048 7352d33b Thomas Thrainer
      snode = node_image[nname]
2049 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
2050 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
2051 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
2052 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
2053 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
2054 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
2055 9b0e86e2 Thomas Thrainer
2056 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
2057 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
2058 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
2059 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
2060 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
2061 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
2062 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
2063 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
2064 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
2065 9b0e86e2 Thomas Thrainer
2066 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
2067 d0d7d7cf Thomas Thrainer
2068 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
2069 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
2070 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
2071 da4a52a3 Thomas Thrainer
                  instance.name)
2072 da4a52a3 Thomas Thrainer
2073 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
2074 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
2075 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
2076 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
2077 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
2078 7352d33b Thomas Thrainer
2079 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
2080 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
2081 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
2082 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
2083 c69b147d Bernardo Dal Seno
        # node should have the flag set
2084 c69b147d Bernardo Dal Seno
        es_nodes = [n
2085 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
2086 c69b147d Bernardo Dal Seno
                    if es]
2087 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
2088 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
2089 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
2090 da4a52a3 Thomas Thrainer
                    instance.disk_template,
2091 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
2092 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
2093 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
2094 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
2095 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
2096 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
2097 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
2098 7352d33b Thomas Thrainer
2099 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2100 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
2101 7352d33b Thomas Thrainer
      instance_groups = {}
2102 7352d33b Thomas Thrainer
2103 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
2104 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
2105 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
2106 7352d33b Thomas Thrainer
2107 7352d33b Thomas Thrainer
      pretty_list = [
2108 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
2109 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
2110 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
2111 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
2112 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
2113 7352d33b Thomas Thrainer
                                   reverse=True)]
2114 7352d33b Thomas Thrainer
2115 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
2116 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
2117 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
2118 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
2119 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
2120 7352d33b Thomas Thrainer
2121 7352d33b Thomas Thrainer
    inst_nodes_offline = []
2122 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
2123 7352d33b Thomas Thrainer
      s_img = node_image[snode]
2124 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
2125 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
2126 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
2127 da4a52a3 Thomas Thrainer
                    instance.name)
2128 7352d33b Thomas Thrainer
2129 7352d33b Thomas Thrainer
      if s_img.offline:
2130 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
2131 7352d33b Thomas Thrainer
2132 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
2133 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
2134 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
2135 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
2136 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
2137 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
2138 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
2139 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
2140 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2141 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
2142 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
2143 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
2144 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2145 7352d33b Thomas Thrainer
2146 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
2147 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
2148 7352d33b Thomas Thrainer

2149 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
2150 7352d33b Thomas Thrainer
    reported as unknown.
2151 7352d33b Thomas Thrainer

2152 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
2153 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
2154 7352d33b Thomas Thrainer

2155 7352d33b Thomas Thrainer
    """
2156 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2157 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
2158 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
2159 7352d33b Thomas Thrainer
        # skip non-healthy nodes
2160 7352d33b Thomas Thrainer
        continue
2161 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
2162 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
2163 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2164 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2165 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2166 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2167 7352d33b Thomas Thrainer
                      "volume %s is unknown", volume)
2168 7352d33b Thomas Thrainer
2169 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2170 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2171 7352d33b Thomas Thrainer

2172 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2173 7352d33b Thomas Thrainer
    instances it was primary for.
2174 7352d33b Thomas Thrainer

2175 7352d33b Thomas Thrainer
    """
2176 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2177 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2178 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2179 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2180 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2181 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2182 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2183 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2184 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2185 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2186 1c3231aa Thomas Thrainer
      if n_img.offline or \
2187 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2188 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2189 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2190 9fdb10be Thomas Thrainer
        # information from them; we already list instances living on such
2191 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2192 7352d33b Thomas Thrainer
        continue
2193 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2194 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2195 7352d33b Thomas Thrainer
        needed_mem = 0
2196 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2197 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2198 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2199 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2200 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2201 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2202 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2203 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2204 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2205 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2206 7352d33b Thomas Thrainer
2207 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2208 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2209 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2210 7352d33b Thomas Thrainer

2211 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2212 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2213 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2214 7352d33b Thomas Thrainer

2215 7352d33b Thomas Thrainer
    """
2216 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2217 7352d33b Thomas Thrainer
    files2nodefn = [
2218 7352d33b Thomas Thrainer
      (files_all, None),
2219 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2220 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2221 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2222 7352d33b Thomas Thrainer
      ]
2223 7352d33b Thomas Thrainer
2224 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2225 7352d33b Thomas Thrainer
    nodefiles = {}
2226 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2227 7352d33b Thomas Thrainer
      if fn is None:
2228 1c3231aa Thomas Thrainer
        filenodes = nodes
2229 7352d33b Thomas Thrainer
      else:
2230 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2231 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2232 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2233 7352d33b Thomas Thrainer
                       for filename in files)
2234 7352d33b Thomas Thrainer
2235 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2236 7352d33b Thomas Thrainer
2237 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2238 7352d33b Thomas Thrainer
    ignore_nodes = set()
2239 7352d33b Thomas Thrainer
2240 1c3231aa Thomas Thrainer
    for node in nodes:
2241 7352d33b Thomas Thrainer
      if node.offline:
2242 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2243 7352d33b Thomas Thrainer
        continue
2244 7352d33b Thomas Thrainer
2245 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2246 7352d33b Thomas Thrainer
2247 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2248 7352d33b Thomas Thrainer
        node_files = None
2249 7352d33b Thomas Thrainer
      else:
2250 7352d33b Thomas Thrainer
        fingerprints = nresult.payload.get(constants.NV_FILELIST, None)
2251 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2252 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2253 7352d33b Thomas Thrainer
        del fingerprints
2254 7352d33b Thomas Thrainer
2255 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2256 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2257 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2258 7352d33b Thomas Thrainer
      if test:
2259 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2260 7352d33b Thomas Thrainer
        continue
2261 7352d33b Thomas Thrainer
2262 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2263 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2264 7352d33b Thomas Thrainer
        assert filename in nodefiles
2265 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2266 7352d33b Thomas Thrainer
2267 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2268 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2269 7352d33b Thomas Thrainer
2270 7352d33b Thomas Thrainer
      # Nodes having the file
2271 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2272 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2273 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2274 7352d33b Thomas Thrainer
2275 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2276 7352d33b Thomas Thrainer
2277 7352d33b Thomas Thrainer
      # Nodes missing file
2278 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2279 7352d33b Thomas Thrainer
2280 7352d33b Thomas Thrainer
      if filename in files_opt:
2281 7352d33b Thomas Thrainer
        # All or no nodes
2282 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2283 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2284 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2285 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2286 1c3231aa Thomas Thrainer
                      filename,
2287 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2288 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2289 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2290 7352d33b Thomas Thrainer
      else:
2291 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2292 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2293 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2294 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2295 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2296 7352d33b Thomas Thrainer
2297 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2298 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2299 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2300 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2301 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2302 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2303 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2304 7352d33b Thomas Thrainer
2305 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2306 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2307 7352d33b Thomas Thrainer
      if test:
2308 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2309 1c3231aa Thomas Thrainer
                    (idx + 1,
2310 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2311 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2312 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2313 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2314 7352d33b Thomas Thrainer
      else:
2315 7352d33b Thomas Thrainer
        variants = []
2316 7352d33b Thomas Thrainer
2317 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2318 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2319 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2320 7352d33b Thomas Thrainer
2321 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbdHelper(self, ninfo, nresult, drbd_helper):
2322 9af7ece3 Helga Velroyen
    """Verify the drbd helper.
2323 7352d33b Thomas Thrainer

2324 7352d33b Thomas Thrainer
    """
2325 7352d33b Thomas Thrainer
    if drbd_helper:
2326 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2327 7352d33b Thomas Thrainer
      test = (helper_result is None)
2328 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2329 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2330 7352d33b Thomas Thrainer
      if helper_result:
2331 7352d33b Thomas Thrainer
        status, payload = helper_result
2332 7352d33b Thomas Thrainer
        test = not status
2333 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2334 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2335 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2336 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2337 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2338 7352d33b Thomas Thrainer
2339 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2340 9af7ece3 Helga Velroyen
                      drbd_map):
2341 9af7ece3 Helga Velroyen
    """Verifies and the node DRBD status.
2342 9af7ece3 Helga Velroyen

2343 9af7ece3 Helga Velroyen
    @type ninfo: L{objects.Node}
2344 9af7ece3 Helga Velroyen
    @param ninfo: the node to check
2345 9af7ece3 Helga Velroyen
    @param nresult: the remote results for the node
2346 9af7ece3 Helga Velroyen
    @param instanceinfo: the dict of instances
2347 9af7ece3 Helga Velroyen
    @param drbd_helper: the configured DRBD usermode helper
2348 9af7ece3 Helga Velroyen
    @param drbd_map: the DRBD map as returned by
2349 9af7ece3 Helga Velroyen
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2350 9af7ece3 Helga Velroyen

2351 9af7ece3 Helga Velroyen
    """
2352 9af7ece3 Helga Velroyen
    self._VerifyNodeDrbdHelper(ninfo, nresult, drbd_helper)
2353 9af7ece3 Helga Velroyen
2354 7352d33b Thomas Thrainer
    # compute the DRBD minors
2355 7352d33b Thomas Thrainer
    node_drbd = {}
2356 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2357 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2358 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2359 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2360 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2361 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2362 7352d33b Thomas Thrainer
        # unallocated minor in use)
2363 7352d33b Thomas Thrainer
      if test:
2364 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2365 7352d33b Thomas Thrainer
      else:
2366 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2367 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2368 7352d33b Thomas Thrainer
2369 7352d33b Thomas Thrainer
    # and now check them
2370 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2371 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2372 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2373 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2374 7352d33b Thomas Thrainer
    if test:
2375 7352d33b Thomas Thrainer
      # we cannot check drbd status
2376 7352d33b Thomas Thrainer
      return
2377 7352d33b Thomas Thrainer
2378 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2379 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2380 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2381 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2382 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2383 7352d33b Thomas Thrainer
    for minor in used_minors:
2384 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2385 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2386 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2387 7352d33b Thomas Thrainer
2388 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2389 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2390 7352d33b Thomas Thrainer

2391 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2392 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2393 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2394 7352d33b Thomas Thrainer
    @param nimg: the node image object
2395 7352d33b Thomas Thrainer

2396 7352d33b Thomas Thrainer
    """
2397 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2398 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2399 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2400 7352d33b Thomas Thrainer
                           for v in remote_os))
2401 7352d33b Thomas Thrainer
2402 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2403 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2404 7352d33b Thomas Thrainer
2405 7352d33b Thomas Thrainer
    nimg.os_fail = test
2406 7352d33b Thomas Thrainer
2407 7352d33b Thomas Thrainer
    if test:
2408 7352d33b Thomas Thrainer
      return
2409 7352d33b Thomas Thrainer
2410 7352d33b Thomas Thrainer
    os_dict = {}
2411 7352d33b Thomas Thrainer
2412 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2413 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2414 7352d33b Thomas Thrainer
2415 7352d33b Thomas Thrainer
      if name not in os_dict:
2416 7352d33b Thomas Thrainer
        os_dict[name] = []
2417 7352d33b Thomas Thrainer
2418 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2419 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2420 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2421 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2422 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2423 7352d33b Thomas Thrainer
2424 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2425 7352d33b Thomas Thrainer
2426 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2427 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2428 7352d33b Thomas Thrainer

2429 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2430 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2431 7352d33b Thomas Thrainer
    @param nimg: the node image object
2432 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2433 7352d33b Thomas Thrainer

2434 7352d33b Thomas Thrainer
    """
2435 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2436 7352d33b Thomas Thrainer
2437 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2438 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2439 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2440 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2441 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2442 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2443 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2444 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2445 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2446 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2447 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2448 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2449 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2450 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2451 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2452 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2453 7352d33b Thomas Thrainer
      if test:
2454 7352d33b Thomas Thrainer
        continue
2455 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2456 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2457 7352d33b Thomas Thrainer
      if not b_status:
2458 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2459 7352d33b Thomas Thrainer
        continue
2460 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2461 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2462 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2463 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2464 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2465 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2466 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2467 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2468 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2469 7352d33b Thomas Thrainer
2470 7352d33b Thomas Thrainer
    # check any missing OSes
2471 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2472 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2473 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2474 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2475 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2476 7352d33b Thomas Thrainer
2477 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2478 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2479 7352d33b Thomas Thrainer

2480 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2481 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2482 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2483 7352d33b Thomas Thrainer
    @type is_master: bool
2484 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2485 7352d33b Thomas Thrainer

2486 7352d33b Thomas Thrainer
    """
2487 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2488 7352d33b Thomas Thrainer
    if (is_master and
2489 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2490 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2491 7352d33b Thomas Thrainer
      try:
2492 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2493 7352d33b Thomas Thrainer
      except KeyError:
2494 7352d33b Thomas Thrainer
        # This should never happen
2495 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2496 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2497 7352d33b Thomas Thrainer
      else:
2498 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2499 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2500 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2501 7352d33b Thomas Thrainer
    else:
2502 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2503 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2504 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2505 7352d33b Thomas Thrainer
                    " paths")
2506 7352d33b Thomas Thrainer
2507 4b322a76 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult, file_disk_template,
2508 4b322a76 Helga Velroyen
                          verify_key, error_key):
2509 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2510 9c1c3c19 Helga Velroyen

2511 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2512 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2513 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2514 4b322a76 Helga Velroyen
    @type file_disk_template: string
2515 4b322a76 Helga Velroyen
    @param file_disk_template: file-based disk template, whose directory
2516 4b322a76 Helga Velroyen
        is supposed to be verified
2517 4b322a76 Helga Velroyen
    @type verify_key: string
2518 4b322a76 Helga Velroyen
    @param verify_key: key for the verification map of this file
2519 4b322a76 Helga Velroyen
        verification step
2520 4b322a76 Helga Velroyen
    @param error_key: error key to be added to the verification results
2521 4b322a76 Helga Velroyen
        in case something goes wrong in this verification step
2522 9c1c3c19 Helga Velroyen

2523 9c1c3c19 Helga Velroyen
    """
2524 4b322a76 Helga Velroyen
    assert (file_disk_template in
2525 4b322a76 Helga Velroyen
            utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE))
2526 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2527 4b322a76 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(file_disk_template):
2528 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2529 4b322a76 Helga Velroyen
          verify_key in nresult,
2530 4b322a76 Helga Velroyen
          error_key, ninfo.name,
2531 4b322a76 Helga Velroyen
          "The configured %s storage path is unusable: %s" %
2532 4b322a76 Helga Velroyen
          (file_disk_template, nresult.get(verify_key)))
2533 4b322a76 Helga Velroyen
2534 4b322a76 Helga Velroyen
  def _VerifyFileStoragePaths(self, ninfo, nresult):
2535 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2536 4b322a76 Helga Velroyen

2537 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2538 4b322a76 Helga Velroyen

2539 4b322a76 Helga Velroyen
    """
2540 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2541 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_FILE,
2542 4b322a76 Helga Velroyen
        constants.NV_FILE_STORAGE_PATH,
2543 4b322a76 Helga Velroyen
        constants.CV_ENODEFILESTORAGEPATHUNUSABLE)
2544 4b322a76 Helga Velroyen
2545 4b322a76 Helga Velroyen
  def _VerifySharedFileStoragePaths(self, ninfo, nresult):
2546 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2547 4b322a76 Helga Velroyen

2548 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2549 4b322a76 Helga Velroyen

2550 4b322a76 Helga Velroyen
    """
2551 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2552 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_SHARED_FILE,
2553 4b322a76 Helga Velroyen
        constants.NV_SHARED_FILE_STORAGE_PATH,
2554 4b322a76 Helga Velroyen
        constants.CV_ENODESHAREDFILESTORAGEPATHUNUSABLE)
2555 9c1c3c19 Helga Velroyen
2556 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2557 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2558 7352d33b Thomas Thrainer

2559 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2560 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2561 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2562 7352d33b Thomas Thrainer

2563 7352d33b Thomas Thrainer
    """
2564 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2565 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2566 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2567 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2568 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2569 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2570 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2571 7352d33b Thomas Thrainer
2572 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2573 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2574 7352d33b Thomas Thrainer

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

2578 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2579 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2580 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2581 7352d33b Thomas Thrainer
    @param nimg: the node image object
2582 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2583 7352d33b Thomas Thrainer

2584 7352d33b Thomas Thrainer
    """
2585 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2586 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2587 7352d33b Thomas Thrainer
    if vg_name is None:
2588 7352d33b Thomas Thrainer
      pass
2589 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2590 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2591 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2592 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2593 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2594 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2595 7352d33b Thomas Thrainer
    else:
2596 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2597 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2598 7352d33b Thomas Thrainer
2599 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2600 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2601 7352d33b Thomas Thrainer

2602 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2603 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2604 7352d33b Thomas Thrainer
    list key.
2605 7352d33b Thomas Thrainer

2606 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2607 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2608 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2609 7352d33b Thomas Thrainer
    @param nimg: the node image object
2610 7352d33b Thomas Thrainer

2611 7352d33b Thomas Thrainer
    """
2612 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2613 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2614 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2615 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2616 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2617 7352d33b Thomas Thrainer
    if test:
2618 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2619 7352d33b Thomas Thrainer
    else:
2620 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
2621 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
2622 7352d33b Thomas Thrainer
2623 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2624 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2625 7352d33b Thomas Thrainer

2626 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2627 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2628 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2629 7352d33b Thomas Thrainer
    @param nimg: the node image object
2630 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2631 7352d33b Thomas Thrainer

2632 7352d33b Thomas Thrainer
    """
2633 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2634 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2635 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2636 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2637 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
2638 7352d33b Thomas Thrainer
    if not test:
2639 7352d33b Thomas Thrainer
      try:
2640 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2641 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2642 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2643 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
2644 7352d33b Thomas Thrainer
2645 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2646 7352d33b Thomas Thrainer
    if vg_name is not None:
2647 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2648 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2649 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2650 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
2651 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
2652 7352d33b Thomas Thrainer
      if not test:
2653 7352d33b Thomas Thrainer
        try:
2654 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2655 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2656 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2657 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
2658 7352d33b Thomas Thrainer
2659 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
2660 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2661 7352d33b Thomas Thrainer

2662 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
2663 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
2664 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
2665 7352d33b Thomas Thrainer
    @param node_image: Node objects
2666 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
2667 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2668 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2669 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2670 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2671 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2672 7352d33b Thomas Thrainer

2673 7352d33b Thomas Thrainer
    """
2674 7352d33b Thomas Thrainer
    node_disks = {}
2675 0c3d9c7c Thomas Thrainer
    node_disks_dev_inst_only = {}
2676 7352d33b Thomas Thrainer
    diskless_instances = set()
2677 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2678 7352d33b Thomas Thrainer
2679 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
2680 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
2681 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
2682 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
2683 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
2684 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
2685 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
2686 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
2687 7352d33b Thomas Thrainer
2688 7352d33b Thomas Thrainer
      if not disks:
2689 7352d33b Thomas Thrainer
        # No need to collect data
2690 7352d33b Thomas Thrainer
        continue
2691 7352d33b Thomas Thrainer
2692 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
2693 7352d33b Thomas Thrainer
2694 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2695 0c3d9c7c Thomas Thrainer
      dev_inst_only = []
2696 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
2697 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
2698 da4a52a3 Thomas Thrainer
                                          self.cfg)
2699 0c3d9c7c Thomas Thrainer
        dev_inst_only.append((anno_disk, instanceinfo[inst_uuid]))
2700 7352d33b Thomas Thrainer
2701 0c3d9c7c Thomas Thrainer
      node_disks_dev_inst_only[nuuid] = dev_inst_only
2702 7352d33b Thomas Thrainer
2703 0c3d9c7c Thomas Thrainer
    assert len(node_disks) == len(node_disks_dev_inst_only)
2704 7352d33b Thomas Thrainer
2705 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2706 0c3d9c7c Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(
2707 0c3d9c7c Thomas Thrainer
               node_disks.keys(), node_disks_dev_inst_only)
2708 7352d33b Thomas Thrainer
2709 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2710 7352d33b Thomas Thrainer
2711 7352d33b Thomas Thrainer
    instdisk = {}
2712 7352d33b Thomas Thrainer
2713 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
2714 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
2715 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
2716 7352d33b Thomas Thrainer
2717 7352d33b Thomas Thrainer
      if nres.offline:
2718 7352d33b Thomas Thrainer
        # No data from this node
2719 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2720 7352d33b Thomas Thrainer
      else:
2721 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2722 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
2723 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
2724 7352d33b Thomas Thrainer
        if msg:
2725 7352d33b Thomas Thrainer
          # No data from this node
2726 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2727 7352d33b Thomas Thrainer
        else:
2728 7352d33b Thomas Thrainer
          data = []
2729 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2730 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2731 7352d33b Thomas Thrainer
              data.append(i)
2732 7352d33b Thomas Thrainer
            else:
2733 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2734 1c3231aa Thomas Thrainer
                              node.name, idx, i)
2735 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2736 7352d33b Thomas Thrainer
2737 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
2738 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
2739 da4a52a3 Thomas Thrainer
          .append(status)
2740 7352d33b Thomas Thrainer
2741 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2742 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
2743 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
2744 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
2745 7352d33b Thomas Thrainer
2746 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
2747 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
2748 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
2749 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
2750 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
2751 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
2752 7352d33b Thomas Thrainer
    if __debug__:
2753 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
2754 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
2755 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
2756 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
2757 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
2758 7352d33b Thomas Thrainer
2759 7352d33b Thomas Thrainer
    return instdisk
2760 7352d33b Thomas Thrainer
2761 7352d33b Thomas Thrainer
  @staticmethod
2762 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
2763 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
2764 7352d33b Thomas Thrainer

2765 7352d33b Thomas Thrainer
    """
2766 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
2767 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
2768 7352d33b Thomas Thrainer
                 not node.offline)]
2769 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
2770 7352d33b Thomas Thrainer
2771 7352d33b Thomas Thrainer
    return map(itertools.cycle,
2772 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
2773 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2774 7352d33b Thomas Thrainer
                                                  keyfunc)])
2775 7352d33b Thomas Thrainer
2776 7352d33b Thomas Thrainer
  @classmethod
2777 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2778 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
2779 7352d33b Thomas Thrainer

2780 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
2781 7352d33b Thomas Thrainer
    every other group.
2782 7352d33b Thomas Thrainer

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

2787 7352d33b Thomas Thrainer
    """
2788 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2789 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2790 7352d33b Thomas Thrainer
2791 7352d33b Thomas Thrainer
    return (online_nodes,
2792 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
2793 7352d33b Thomas Thrainer
                 for name in online_nodes))
2794 7352d33b Thomas Thrainer
2795 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
2796 7352d33b Thomas Thrainer
    """Build hooks env.
2797 7352d33b Thomas Thrainer

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

2801 7352d33b Thomas Thrainer
    """
2802 7352d33b Thomas Thrainer
    env = {
2803 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
2804 7352d33b Thomas Thrainer
      }
2805 7352d33b Thomas Thrainer
2806 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
2807 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
2808 7352d33b Thomas Thrainer
2809 7352d33b Thomas Thrainer
    return env
2810 7352d33b Thomas Thrainer
2811 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
2812 7352d33b Thomas Thrainer
    """Build hooks nodes.
2813 7352d33b Thomas Thrainer

2814 7352d33b Thomas Thrainer
    """
2815 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
2816 7352d33b Thomas Thrainer
2817 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
2818 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
2819 7352d33b Thomas Thrainer

2820 7352d33b Thomas Thrainer
    """
2821 7352d33b Thomas Thrainer
    # This method has too many local variables. pylint: disable=R0914
2822 7352d33b Thomas Thrainer
    feedback_fn("* Verifying group '%s'" % self.group_info.name)
2823 7352d33b Thomas Thrainer
2824 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
2825 7352d33b Thomas Thrainer
      # empty node group
2826 7352d33b Thomas Thrainer
      feedback_fn("* Empty node group, skipping verification")
2827 7352d33b Thomas Thrainer
      return True
2828 7352d33b Thomas Thrainer
2829 7352d33b Thomas Thrainer
    self.bad = False
2830 7352d33b Thomas Thrainer
    verbose = self.op.verbose
2831 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
2832 7352d33b Thomas Thrainer
2833 7352d33b Thomas Thrainer
    vg_name = self.cfg.GetVGName()
2834 7352d33b Thomas Thrainer
    drbd_helper = self.cfg.GetDRBDHelper()
2835 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2836 7352d33b Thomas Thrainer
    hypervisors = cluster.enabled_hypervisors
2837 1c3231aa Thomas Thrainer
    node_data_list = self.my_node_info.values()
2838 7352d33b Thomas Thrainer
2839 7352d33b Thomas Thrainer
    i_non_redundant = [] # Non redundant instances
2840 7352d33b Thomas Thrainer
    i_non_a_balanced = [] # Non auto-balanced instances
2841 7352d33b Thomas Thrainer
    i_offline = 0 # Count of offline instances
2842 7352d33b Thomas Thrainer
    n_offline = 0 # Count of offline nodes
2843 7352d33b Thomas Thrainer
    n_drained = 0 # Count of nodes being drained
2844 7352d33b Thomas Thrainer
    node_vol_should = {}
2845 7352d33b Thomas Thrainer
2846 7352d33b Thomas Thrainer
    # FIXME: verify OS list
2847 7352d33b Thomas Thrainer
2848 7352d33b Thomas Thrainer
    # File verification
2849 5eacbcae Thomas Thrainer
    filemap = ComputeAncillaryFiles(cluster, False)
2850 7352d33b Thomas Thrainer
2851 7352d33b Thomas Thrainer
    # do local checksums
2852 1c3231aa Thomas Thrainer
    master_node_uuid = self.master_node = self.cfg.GetMasterNode()
2853 7352d33b Thomas Thrainer
    master_ip = self.cfg.GetMasterIP()
2854 7352d33b Thomas Thrainer
2855 1c3231aa Thomas Thrainer
    feedback_fn("* Gathering data (%d nodes)" % len(self.my_node_uuids))
2856 7352d33b Thomas Thrainer
2857 7352d33b Thomas Thrainer
    user_scripts = []
2858 7352d33b Thomas Thrainer
    if self.cfg.GetUseExternalMipScript():
2859 7352d33b Thomas Thrainer
      user_scripts.append(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
2860 7352d33b Thomas Thrainer
2861 7352d33b Thomas Thrainer
    node_verify_param = {
2862 7352d33b Thomas Thrainer
      constants.NV_FILELIST:
2863 7352d33b Thomas Thrainer
        map(vcluster.MakeVirtualPath,
2864 7352d33b Thomas Thrainer
            utils.UniqueSequence(filename
2865 7352d33b Thomas Thrainer
                                 for files in filemap
2866 7352d33b Thomas Thrainer
                                 for filename in files)),
2867 7352d33b Thomas Thrainer
      constants.NV_NODELIST:
2868 7352d33b Thomas Thrainer
        self._SelectSshCheckNodes(node_data_list, self.group_uuid,
2869 7352d33b Thomas Thrainer
                                  self.all_node_info.values()),
2870 7352d33b Thomas Thrainer
      constants.NV_HYPERVISOR: hypervisors,
2871 7352d33b Thomas Thrainer
      constants.NV_HVPARAMS:
2872 7352d33b Thomas Thrainer
        _GetAllHypervisorParameters(cluster, self.all_inst_info.values()),
2873 7352d33b Thomas Thrainer
      constants.NV_NODENETTEST: [(node.name, node.primary_ip, node.secondary_ip)
2874 7352d33b Thomas Thrainer
                                 for node in node_data_list
2875 7352d33b Thomas Thrainer
                                 if not node.offline],
2876 7352d33b Thomas Thrainer
      constants.NV_INSTANCELIST: hypervisors,
2877 7352d33b Thomas Thrainer
      constants.NV_VERSION: None,
2878 7352d33b Thomas Thrainer
      constants.NV_HVINFO: self.cfg.GetHypervisorType(),
2879 7352d33b Thomas Thrainer
      constants.NV_NODESETUP: None,
2880 7352d33b Thomas Thrainer
      constants.NV_TIME: None,
2881 1c3231aa Thomas Thrainer
      constants.NV_MASTERIP: (self.cfg.GetMasterNodeName(), master_ip),
2882 7352d33b Thomas Thrainer
      constants.NV_OSLIST: None,
2883 7352d33b Thomas Thrainer
      constants.NV_VMNODES: self.cfg.GetNonVmCapableNodeList(),
2884 7352d33b Thomas Thrainer
      constants.NV_USERSCRIPTS: user_scripts,
2885 7352d33b Thomas Thrainer
      }
2886 7352d33b Thomas Thrainer
2887 7352d33b Thomas Thrainer
    if vg_name is not None:
2888 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_VGLIST] = None
2889 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_LVLIST] = vg_name
2890 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_PVLIST] = [vg_name]
2891 7352d33b Thomas Thrainer
2892 9af7ece3 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(constants.DT_DRBD8):
2893 9af7ece3 Helga Velroyen
      if drbd_helper:
2894 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDVERSION] = None
2895 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDLIST] = None
2896 9af7ece3 Helga Velroyen
        node_verify_param[constants.NV_DRBDHELPER] = drbd_helper
2897 7352d33b Thomas Thrainer
2898 850c53f1 Helga Velroyen
    if cluster.IsFileStorageEnabled() or \
2899 850c53f1 Helga Velroyen
        cluster.IsSharedFileStorageEnabled():
2900 7352d33b Thomas Thrainer
      # Load file storage paths only from master node
2901 13a6c760 Helga Velroyen
      node_verify_param[constants.NV_ACCEPTED_STORAGE_PATHS] = \
2902 1c3231aa Thomas Thrainer
        self.cfg.GetMasterNodeName()
2903 13a6c760 Helga Velroyen
      if cluster.IsFileStorageEnabled():
2904 13a6c760 Helga Velroyen
        node_verify_param[constants.NV_FILE_STORAGE_PATH] = \
2905 13a6c760 Helga Velroyen
          cluster.file_storage_dir
2906 7352d33b Thomas Thrainer
2907 7352d33b Thomas Thrainer
    # bridge checks
2908 7352d33b Thomas Thrainer
    # FIXME: this needs to be changed per node-group, not cluster-wide
2909 7352d33b Thomas Thrainer
    bridges = set()
2910 7352d33b Thomas Thrainer
    default_nicpp = cluster.nicparams[constants.PP_DEFAULT]
2911 7352d33b Thomas Thrainer
    if default_nicpp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2912 7352d33b Thomas Thrainer
      bridges.add(default_nicpp[constants.NIC_LINK])
2913 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_info.values():
2914 da4a52a3 Thomas Thrainer
      for nic in inst_uuid.nics:
2915 7352d33b Thomas Thrainer
        full_nic = cluster.SimpleFillNIC(nic.nicparams)
2916 7352d33b Thomas Thrainer
        if full_nic[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2917 7352d33b Thomas Thrainer
          bridges.add(full_nic[constants.NIC_LINK])
2918 7352d33b Thomas Thrainer
2919 7352d33b Thomas Thrainer
    if bridges:
2920 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_BRIDGES] = list(bridges)
2921 7352d33b Thomas Thrainer
2922 7352d33b Thomas Thrainer
    # Build our expected cluster state
2923 1c3231aa Thomas Thrainer
    node_image = dict((node.uuid, self.NodeImage(offline=node.offline,
2924 1c3231aa Thomas Thrainer
                                                 uuid=node.uuid,
2925 7352d33b Thomas Thrainer
                                                 vm_capable=node.vm_capable))
2926 7352d33b Thomas Thrainer
                      for node in node_data_list)
2927 7352d33b Thomas Thrainer
2928 7352d33b Thomas Thrainer
    # Gather OOB paths
2929 7352d33b Thomas Thrainer
    oob_paths = []
2930 7352d33b Thomas Thrainer
    for node in self.all_node_info.values():
2931 5eacbcae Thomas Thrainer
      path = SupportsOob(self.cfg, node)
2932 7352d33b Thomas Thrainer
      if path and path not in oob_paths:
2933 7352d33b Thomas Thrainer
        oob_paths.append(path)
2934 7352d33b Thomas Thrainer
2935 7352d33b Thomas Thrainer
    if oob_paths:
2936 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_OOB_PATHS] = oob_paths
2937 7352d33b Thomas Thrainer
2938 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
2939 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
2940 da4a52a3 Thomas Thrainer
      if instance.admin_state == constants.ADMINST_OFFLINE:
2941 7352d33b Thomas Thrainer
        i_offline += 1
2942 7352d33b Thomas Thrainer
2943 da4a52a3 Thomas Thrainer
      for nuuid in instance.all_nodes:
2944 1c3231aa Thomas Thrainer
        if nuuid not in node_image:
2945 1c3231aa Thomas Thrainer
          gnode = self.NodeImage(uuid=nuuid)
2946 1c3231aa Thomas Thrainer
          gnode.ghost = (nuuid not in self.all_node_info)
2947 1c3231aa Thomas Thrainer
          node_image[nuuid] = gnode
2948 7352d33b Thomas Thrainer
2949 da4a52a3 Thomas Thrainer
      instance.MapLVsByNode(node_vol_should)
2950 7352d33b Thomas Thrainer
2951 da4a52a3 Thomas Thrainer
      pnode = instance.primary_node
2952 da4a52a3 Thomas Thrainer
      node_image[pnode].pinst.append(instance.uuid)
2953 7352d33b Thomas Thrainer
2954 da4a52a3 Thomas Thrainer
      for snode in instance.secondary_nodes:
2955 7352d33b Thomas Thrainer
        nimg = node_image[snode]
2956 da4a52a3 Thomas Thrainer
        nimg.sinst.append(instance.uuid)
2957 7352d33b Thomas Thrainer
        if pnode not in nimg.sbp:
2958 7352d33b Thomas Thrainer
          nimg.sbp[pnode] = []
2959 da4a52a3 Thomas Thrainer
        nimg.sbp[pnode].append(instance.uuid)
2960 7352d33b Thomas Thrainer
2961 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
2962 1c3231aa Thomas Thrainer
                                               self.my_node_info.keys())
2963 7352d33b Thomas Thrainer
    # The value of exclusive_storage should be the same across the group, so if
2964 7352d33b Thomas Thrainer
    # it's True for at least a node, we act as if it were set for all the nodes
2965 7352d33b Thomas Thrainer
    self._exclusive_storage = compat.any(es_flags.values())
2966 7352d33b Thomas Thrainer
    if self._exclusive_storage:
2967 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_EXCLUSIVEPVS] = True
2968 7352d33b Thomas Thrainer
2969 7352d33b Thomas Thrainer
    # At this point, we have the in-memory data structures complete,
2970 7352d33b Thomas Thrainer
    # except for the runtime information, which we'll gather next
2971 7352d33b Thomas Thrainer
2972 7352d33b Thomas Thrainer
    # Due to the way our RPC system works, exact response times cannot be
2973 7352d33b Thomas Thrainer
    # guaranteed (e.g. a broken node could run into a timeout). By keeping the
2974 7352d33b Thomas Thrainer
    # time before and after executing the request, we can at least have a time
2975 7352d33b Thomas Thrainer
    # window.
2976 7352d33b Thomas Thrainer
    nvinfo_starttime = time.time()
2977 1c3231aa Thomas Thrainer
    all_nvinfo = self.rpc.call_node_verify(self.my_node_uuids,
2978 7352d33b Thomas Thrainer
                                           node_verify_param,
2979 5b0dfcef Helga Velroyen
                                           self.cfg.GetClusterName(),
2980 5b0dfcef Helga Velroyen
                                           self.cfg.GetClusterInfo().hvparams)
2981 7352d33b Thomas Thrainer
    nvinfo_endtime = time.time()
2982 7352d33b Thomas Thrainer
2983 7352d33b Thomas Thrainer
    if self.extra_lv_nodes and vg_name is not None:
2984 7352d33b Thomas Thrainer
      extra_lv_nvinfo = \
2985 7352d33b Thomas Thrainer
          self.rpc.call_node_verify(self.extra_lv_nodes,
2986 7352d33b Thomas Thrainer
                                    {constants.NV_LVLIST: vg_name},
2987 5b0dfcef Helga Velroyen
                                    self.cfg.GetClusterName(),
2988 5b0dfcef Helga Velroyen
                                    self.cfg.GetClusterInfo().hvparams)
2989 7352d33b Thomas Thrainer
    else:
2990 7352d33b Thomas Thrainer
      extra_lv_nvinfo = {}
2991 7352d33b Thomas Thrainer
2992 7352d33b Thomas Thrainer
    all_drbd_map = self.cfg.ComputeDRBDMap()
2993 7352d33b Thomas Thrainer
2994 7352d33b Thomas Thrainer
    feedback_fn("* Gathering disk information (%s nodes)" %
2995 1c3231aa Thomas Thrainer
                len(self.my_node_uuids))
2996 1c3231aa Thomas Thrainer
    instdisk = self._CollectDiskInfo(self.my_node_info.keys(), node_image,
2997 7352d33b Thomas Thrainer
                                     self.my_inst_info)
2998 7352d33b Thomas Thrainer
2999 7352d33b Thomas Thrainer
    feedback_fn("* Verifying configuration file consistency")
3000 7352d33b Thomas Thrainer
3001 7352d33b Thomas Thrainer
    # If not all nodes are being checked, we need to make sure the master node
3002 7352d33b Thomas Thrainer
    # and a non-checked vm_capable node are in the list.
3003 1c3231aa Thomas Thrainer
    absent_node_uuids = set(self.all_node_info).difference(self.my_node_info)
3004 1c3231aa Thomas Thrainer
    if absent_node_uuids:
3005 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo.copy()
3006 7352d33b Thomas Thrainer
      vf_node_info = list(self.my_node_info.values())
3007 1c3231aa Thomas Thrainer
      additional_node_uuids = []
3008 1c3231aa Thomas Thrainer
      if master_node_uuid not in self.my_node_info:
3009 1c3231aa Thomas Thrainer
        additional_node_uuids.append(master_node_uuid)
3010 1c3231aa Thomas Thrainer
        vf_node_info.append(self.all_node_info[master_node_uuid])
3011 7352d33b Thomas Thrainer
      # Add the first vm_capable node we find which is not included,
3012 7352d33b Thomas Thrainer
      # excluding the master node (which we already have)
3013 1c3231aa Thomas Thrainer
      for node_uuid in absent_node_uuids:
3014 1c3231aa Thomas Thrainer
        nodeinfo = self.all_node_info[node_uuid]
3015 7352d33b Thomas Thrainer
        if (nodeinfo.vm_capable and not nodeinfo.offline and
3016 1c3231aa Thomas Thrainer
            node_uuid != master_node_uuid):
3017 1c3231aa Thomas Thrainer
          additional_node_uuids.append(node_uuid)
3018 1c3231aa Thomas Thrainer
          vf_node_info.append(self.all_node_info[node_uuid])
3019 7352d33b Thomas Thrainer
          break
3020 7352d33b Thomas Thrainer
      key = constants.NV_FILELIST
3021 5b0dfcef Helga Velroyen
      vf_nvinfo.update(self.rpc.call_node_verify(
3022 1c3231aa Thomas Thrainer
         additional_node_uuids, {key: node_verify_param[key]},
3023 5b0dfcef Helga Velroyen
         self.cfg.GetClusterName(), self.cfg.GetClusterInfo().hvparams))
3024 7352d33b Thomas Thrainer
    else:
3025 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo
3026 7352d33b Thomas Thrainer
      vf_node_info = self.my_node_info.values()
3027 7352d33b Thomas Thrainer
3028 1c3231aa Thomas Thrainer
    self._VerifyFiles(vf_node_info, master_node_uuid, vf_nvinfo, filemap)
3029 7352d33b Thomas Thrainer
3030 7352d33b Thomas Thrainer
    feedback_fn("* Verifying node status")
3031 7352d33b Thomas Thrainer
3032 7352d33b Thomas Thrainer
    refos_img = None
3033 7352d33b Thomas Thrainer
3034 7352d33b Thomas Thrainer
    for node_i in node_data_list:
3035 1c3231aa Thomas Thrainer
      nimg = node_image[node_i.uuid]
3036 7352d33b Thomas Thrainer
3037 7352d33b Thomas Thrainer
      if node_i.offline:
3038 7352d33b Thomas Thrainer
        if verbose:
3039 1c3231aa Thomas Thrainer
          feedback_fn("* Skipping offline node %s" % (node_i.name,))
3040 7352d33b Thomas Thrainer
        n_offline += 1
3041 7352d33b Thomas Thrainer
        continue
3042 7352d33b Thomas Thrainer
3043 1c3231aa Thomas Thrainer
      if node_i.uuid == master_node_uuid:
3044 7352d33b Thomas Thrainer
        ntype = "master"
3045 7352d33b Thomas Thrainer
      elif node_i.master_candidate:
3046 7352d33b Thomas Thrainer
        ntype = "master candidate"
3047 7352d33b Thomas Thrainer
      elif node_i.drained:
3048 7352d33b Thomas Thrainer
        ntype = "drained"
3049 7352d33b Thomas Thrainer
        n_drained += 1
3050 7352d33b Thomas Thrainer
      else:
3051 7352d33b Thomas Thrainer
        ntype = "regular"
3052 7352d33b Thomas Thrainer
      if verbose:
3053 1c3231aa Thomas Thrainer
        feedback_fn("* Verifying node %s (%s)" % (node_i.name, ntype))
3054 7352d33b Thomas Thrainer
3055 1c3231aa Thomas Thrainer
      msg = all_nvinfo[node_i.uuid].fail_msg
3056 d0d7d7cf Thomas Thrainer
      self._ErrorIf(msg, constants.CV_ENODERPC, node_i.name,
3057 d0d7d7cf Thomas Thrainer
                    "while contacting node: %s", msg)
3058 7352d33b Thomas Thrainer
      if msg:
3059 7352d33b Thomas Thrainer
        nimg.rpc_fail = True
3060 7352d33b Thomas Thrainer
        continue
3061 7352d33b Thomas Thrainer
3062 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node_i.uuid].payload
3063 7352d33b Thomas Thrainer
3064 7352d33b Thomas Thrainer
      nimg.call_ok = self._VerifyNode(node_i, nresult)
3065 7352d33b Thomas Thrainer
      self._VerifyNodeTime(node_i, nresult, nvinfo_starttime, nvinfo_endtime)
3066 7352d33b Thomas Thrainer
      self._VerifyNodeNetwork(node_i, nresult)
3067 7352d33b Thomas Thrainer
      self._VerifyNodeUserScripts(node_i, nresult)
3068 7352d33b Thomas Thrainer
      self._VerifyOob(node_i, nresult)
3069 13a6c760 Helga Velroyen
      self._VerifyAcceptedFileStoragePaths(node_i, nresult,
3070 13a6c760 Helga Velroyen
                                           node_i.uuid == master_node_uuid)
3071 4b322a76 Helga Velroyen
      self._VerifyFileStoragePaths(node_i, nresult)
3072 4b322a76 Helga Velroyen
      self._VerifySharedFileStoragePaths(node_i, nresult)
3073 7352d33b Thomas Thrainer
3074 7352d33b Thomas Thrainer
      if nimg.vm_capable:
3075 7352d33b Thomas Thrainer
        self._UpdateVerifyNodeLVM(node_i, nresult, vg_name, nimg)
3076 7352d33b Thomas Thrainer
        self._VerifyNodeDrbd(node_i, nresult, self.all_inst_info, drbd_helper,
3077 7352d33b Thomas Thrainer
                             all_drbd_map)
3078 7352d33b Thomas Thrainer
3079 7352d33b Thomas Thrainer
        self._UpdateNodeVolumes(node_i, nresult, nimg, vg_name)
3080 7352d33b Thomas Thrainer
        self._UpdateNodeInstances(node_i, nresult, nimg)
3081 7352d33b Thomas Thrainer
        self._UpdateNodeInfo(node_i, nresult, nimg, vg_name)
3082 7352d33b Thomas Thrainer
        self._UpdateNodeOS(node_i, nresult, nimg)
3083 7352d33b Thomas Thrainer
3084 7352d33b Thomas Thrainer
        if not nimg.os_fail:
3085 7352d33b Thomas Thrainer
          if refos_img is None:
3086 7352d33b Thomas Thrainer
            refos_img = nimg
3087 7352d33b Thomas Thrainer
          self._VerifyNodeOS(node_i, nimg, refos_img)
3088 7352d33b Thomas Thrainer
        self._VerifyNodeBridges(node_i, nresult, bridges)
3089 7352d33b Thomas Thrainer
3090 da4a52a3 Thomas Thrainer
        # Check whether all running instances are primary for the node. (This
3091 7352d33b Thomas Thrainer
        # can no longer be done from _VerifyInstance below, since some of the
3092 7352d33b Thomas Thrainer
        # wrong instances could be from other node groups.)
3093 da4a52a3 Thomas Thrainer
        non_primary_inst_uuids = set(nimg.instances).difference(nimg.pinst)
3094 7352d33b Thomas Thrainer
3095 da4a52a3 Thomas Thrainer
        for inst_uuid in non_primary_inst_uuids:
3096 da4a52a3 Thomas Thrainer
          test = inst_uuid in self.all_inst_info
3097 da4a52a3 Thomas Thrainer
          self._ErrorIf(test, constants.CV_EINSTANCEWRONGNODE,
3098 da4a52a3 Thomas Thrainer
                        self.cfg.GetInstanceName(inst_uuid),
3099 d0d7d7cf Thomas Thrainer
                        "instance should not run on node %s", node_i.name)
3100 d0d7d7cf Thomas Thrainer
          self._ErrorIf(not test, constants.CV_ENODEORPHANINSTANCE, node_i.name,
3101 da4a52a3 Thomas Thrainer
                        "node is running unknown instance %s", inst_uuid)
3102 7352d33b Thomas Thrainer
3103 1bb99a33 Bernardo Dal Seno
    self._VerifyGroupDRBDVersion(all_nvinfo)
3104 7352d33b Thomas Thrainer
    self._VerifyGroupLVM(node_image, vg_name)
3105 7352d33b Thomas Thrainer
3106 1c3231aa Thomas Thrainer
    for node_uuid, result in extra_lv_nvinfo.items():
3107 1c3231aa Thomas Thrainer
      self._UpdateNodeVolumes(self.all_node_info[node_uuid], result.payload,
3108 1c3231aa Thomas Thrainer
                              node_image[node_uuid], vg_name)
3109 7352d33b Thomas Thrainer
3110 7352d33b Thomas Thrainer
    feedback_fn("* Verifying instance status")
3111 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
3112 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
3113 7352d33b Thomas Thrainer
      if verbose:
3114 da4a52a3 Thomas Thrainer
        feedback_fn("* Verifying instance %s" % instance.name)
3115 da4a52a3 Thomas Thrainer
      self._VerifyInstance(instance, node_image, instdisk[inst_uuid])
3116 7352d33b Thomas Thrainer
3117 7352d33b Thomas Thrainer
      # If the instance is non-redundant we cannot survive losing its primary
3118 7352d33b Thomas Thrainer
      # node, so we are not N+1 compliant.
3119 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_MIRRORED:
3120 7352d33b Thomas Thrainer
        i_non_redundant.append(instance)
3121 7352d33b Thomas Thrainer
3122 da4a52a3 Thomas Thrainer
      if not cluster.FillBE(instance)[constants.BE_AUTO_BALANCE]:
3123 7352d33b Thomas Thrainer
        i_non_a_balanced.append(instance)
3124 7352d33b Thomas Thrainer
3125 7352d33b Thomas Thrainer
    feedback_fn("* Verifying orphan volumes")
3126 7352d33b Thomas Thrainer
    reserved = utils.FieldSet(*cluster.reserved_lvs)
3127 7352d33b Thomas Thrainer
3128 7352d33b Thomas Thrainer
    # We will get spurious "unknown volume" warnings if any node of this group
3129 7352d33b Thomas Thrainer
    # is secondary for an instance whose primary is in another group. To avoid
3130 7352d33b Thomas Thrainer
    # them, we find these instances and add their volumes to node_vol_should.
3131 da4a52a3 Thomas Thrainer
    for instance in self.all_inst_info.values():
3132 da4a52a3 Thomas Thrainer
      for secondary in instance.secondary_nodes:
3133 7352d33b Thomas Thrainer
        if (secondary in self.my_node_info
3134 da4a52a3 Thomas Thrainer
            and instance.name not in self.my_inst_info):
3135 da4a52a3 Thomas Thrainer
          instance.MapLVsByNode(node_vol_should)
3136 7352d33b Thomas Thrainer
          break
3137 7352d33b Thomas Thrainer
3138 7352d33b Thomas Thrainer
    self._VerifyOrphanVolumes(node_vol_should, node_image, reserved)
3139 7352d33b Thomas Thrainer
3140 7352d33b Thomas Thrainer
    if constants.VERIFY_NPLUSONE_MEM not in self.op.skip_checks:
3141 7352d33b Thomas Thrainer
      feedback_fn("* Verifying N+1 Memory redundancy")
3142 7352d33b Thomas Thrainer
      self._VerifyNPlusOneMemory(node_image, self.my_inst_info)
3143 7352d33b Thomas Thrainer
3144 7352d33b Thomas Thrainer
    feedback_fn("* Other Notes")
3145 7352d33b Thomas Thrainer
    if i_non_redundant:
3146 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-redundant instance(s) found."
3147 7352d33b Thomas Thrainer
                  % len(i_non_redundant))
3148 7352d33b Thomas Thrainer
3149 7352d33b Thomas Thrainer
    if i_non_a_balanced:
3150 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-auto-balanced instance(s) found."
3151 7352d33b Thomas Thrainer
                  % len(i_non_a_balanced))
3152 7352d33b Thomas Thrainer
3153 7352d33b Thomas Thrainer
    if i_offline:
3154 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline instance(s) found." % i_offline)
3155 7352d33b Thomas Thrainer
3156 7352d33b Thomas Thrainer
    if n_offline:
3157 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline node(s) found." % n_offline)
3158 7352d33b Thomas Thrainer
3159 7352d33b Thomas Thrainer
    if n_drained:
3160 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d drained node(s) found." % n_drained)
3161 7352d33b Thomas Thrainer
3162 7352d33b Thomas Thrainer
    return not self.bad
3163 7352d33b Thomas Thrainer
3164 7352d33b Thomas Thrainer
  def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
3165 7352d33b Thomas Thrainer
    """Analyze the post-hooks' result
3166 7352d33b Thomas Thrainer

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

3170 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
3171 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
3172 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
3173 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
3174 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
3175 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
3176 7352d33b Thomas Thrainer
        and hook results
3177 7352d33b Thomas Thrainer

3178 7352d33b Thomas Thrainer
    """
3179 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
3180 7352d33b Thomas Thrainer
    # and are only interested in their results
3181 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3182 7352d33b Thomas Thrainer
      # empty node group
3183 7352d33b Thomas Thrainer
      pass
3184 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
3185 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
3186 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
3187 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
3188 7352d33b Thomas Thrainer
3189 7352d33b Thomas Thrainer
      for node_name in hooks_results:
3190 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
3191 7352d33b Thomas Thrainer
        msg = res.fail_msg
3192 7352d33b Thomas Thrainer
        test = msg and not res.offline
3193 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3194 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
3195 7352d33b Thomas Thrainer
        if res.offline or msg:
3196 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
3197 7352d33b Thomas Thrainer
          # an error.
3198 7352d33b Thomas Thrainer
          continue
3199 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
3200 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
3201 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3202 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
3203 7352d33b Thomas Thrainer
          if test:
3204 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3205 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3206 7352d33b Thomas Thrainer
            lu_result = False
3207 7352d33b Thomas Thrainer
3208 7352d33b Thomas Thrainer
    return lu_result
3209 7352d33b Thomas Thrainer
3210 7352d33b Thomas Thrainer
3211 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3212 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3213 7352d33b Thomas Thrainer

3214 7352d33b Thomas Thrainer
  """
3215 7352d33b Thomas Thrainer
  REQ_BGL = False
3216 7352d33b Thomas Thrainer
3217 7352d33b Thomas Thrainer
  def ExpandNames(self):
3218 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3219 7352d33b Thomas Thrainer
    self.needed_locks = {
3220 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3221 7352d33b Thomas Thrainer
      }
3222 7352d33b Thomas Thrainer
3223 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3224 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3225 7352d33b Thomas Thrainer
3226 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3227 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3228 7352d33b Thomas Thrainer
                           for group in group_names])