Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ fb62843c

History | View | Annotate | Download (117.8 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 4e771a95 Helga Velroyen
  CheckIpolicyVsDiskTemplates
61 7352d33b Thomas Thrainer
62 7352d33b Thomas Thrainer
import ganeti.masterd.instance
63 7352d33b Thomas Thrainer
64 7352d33b Thomas Thrainer
65 7352d33b Thomas Thrainer
class LUClusterActivateMasterIp(NoHooksLU):
66 7352d33b Thomas Thrainer
  """Activate the master IP on the master node.
67 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

656 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
657 e8b5640e Helga Velroyen

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

668 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
669 e8b5640e Helga Velroyen

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

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

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

725 7352d33b Thomas Thrainer
    """
726 7352d33b Thomas Thrainer
    return {
727 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
728 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
729 7352d33b Thomas Thrainer
      }
730 7352d33b Thomas Thrainer
731 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
732 7352d33b Thomas Thrainer
    """Build hooks nodes.
733 7352d33b Thomas Thrainer

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

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

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

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

811 c89eb67d Helga Velroyen
    """
812 c89eb67d Helga Velroyen
    return self._GetEnabledDiskTemplatesInner(self.op.enabled_disk_templates,
813 c89eb67d Helga Velroyen
                                              cluster.enabled_disk_templates)
814 c89eb67d Helga Velroyen
815 33a6464e Helga Velroyen
  def _CheckIpolicy(self, cluster, enabled_disk_templates):
816 1532b078 Helga Velroyen
    """Checks the ipolicy.
817 1532b078 Helga Velroyen

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

824 1532b078 Helga Velroyen
    """
825 33a6464e Helga Velroyen
    # FIXME: write unit tests for this
826 1532b078 Helga Velroyen
    if self.op.ipolicy:
827 1532b078 Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
828 1532b078 Helga Velroyen
                                           group_policy=False)
829 1532b078 Helga Velroyen
830 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(self.new_ipolicy,
831 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
832 33a6464e Helga Velroyen
833 1532b078 Helga Velroyen
      all_instances = self.cfg.GetAllInstancesInfo().values()
834 1532b078 Helga Velroyen
      violations = set()
835 1532b078 Helga Velroyen
      for group in self.cfg.GetAllNodeGroupsInfo().values():
836 1532b078 Helga Velroyen
        instances = frozenset([inst for inst in all_instances
837 1532b078 Helga Velroyen
                               if compat.any(nuuid in group.members
838 1532b078 Helga Velroyen
                                             for nuuid in inst.all_nodes)])
839 1532b078 Helga Velroyen
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
840 1532b078 Helga Velroyen
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
841 1532b078 Helga Velroyen
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
842 1532b078 Helga Velroyen
                                           self.cfg)
843 1532b078 Helga Velroyen
        if new:
844 1532b078 Helga Velroyen
          violations.update(new)
845 1532b078 Helga Velroyen
846 1532b078 Helga Velroyen
      if violations:
847 1532b078 Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
848 1532b078 Helga Velroyen
                        " violate them: %s",
849 1532b078 Helga Velroyen
                        utils.CommaJoin(utils.NiceSort(violations)))
850 33a6464e Helga Velroyen
    else:
851 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(cluster.ipolicy,
852 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
853 1532b078 Helga Velroyen
854 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
855 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
856 1bb99a33 Bernardo Dal Seno

857 1bb99a33 Bernardo Dal Seno
    This checks whether the given params don't conflict and
858 1bb99a33 Bernardo Dal Seno
    if the given volume group is valid.
859 1bb99a33 Bernardo Dal Seno

860 1bb99a33 Bernardo Dal Seno
    """
861 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None and not self.op.drbd_helper:
862 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_DRBD8):
863 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
864 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
865 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
866 7352d33b Thomas Thrainer
867 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
868 1bb99a33 Bernardo Dal Seno
    self.cluster = cluster = self.cfg.GetClusterInfo()
869 7352d33b Thomas Thrainer
870 1c3231aa Thomas Thrainer
    vm_capable_node_uuids = [node.uuid
871 1c3231aa Thomas Thrainer
                             for node in self.cfg.GetAllNodesInfo().values()
872 1c3231aa Thomas Thrainer
                             if node.uuid in node_uuids and node.vm_capable]
873 7352d33b Thomas Thrainer
874 1bb99a33 Bernardo Dal Seno
    (enabled_disk_templates, new_enabled_disk_templates) = \
875 1bb99a33 Bernardo Dal Seno
      self._GetEnabledDiskTemplates(cluster)
876 1bb99a33 Bernardo Dal Seno
877 1c3231aa Thomas Thrainer
    self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
878 1bb99a33 Bernardo Dal Seno
                      new_enabled_disk_templates)
879 7352d33b Thomas Thrainer
880 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
881 3039e2dc Helga Velroyen
      CheckFileStoragePathVsEnabledDiskTemplates(
882 3039e2dc Helga Velroyen
          self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
883 3039e2dc Helga Velroyen
884 4e6cfd11 Helga Velroyen
    if self.op.shared_file_storage_dir is not None:
885 4e6cfd11 Helga Velroyen
      CheckSharedFileStoragePathVsEnabledDiskTemplates(
886 4e6cfd11 Helga Velroyen
          self.LogWarning, self.op.shared_file_storage_dir,
887 4e6cfd11 Helga Velroyen
          enabled_disk_templates)
888 4e6cfd11 Helga Velroyen
889 7352d33b Thomas Thrainer
    if self.op.drbd_helper:
890 7352d33b Thomas Thrainer
      # checks given drbd helper on all nodes
891 1c3231aa Thomas Thrainer
      helpers = self.rpc.call_drbd_helper(node_uuids)
892 1c3231aa Thomas Thrainer
      for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
893 7352d33b Thomas Thrainer
        if ninfo.offline:
894 1c3231aa Thomas Thrainer
          self.LogInfo("Not checking drbd helper on offline node %s",
895 1c3231aa Thomas Thrainer
                       ninfo.name)
896 7352d33b Thomas Thrainer
          continue
897 1c3231aa Thomas Thrainer
        msg = helpers[ninfo.uuid].fail_msg
898 7352d33b Thomas Thrainer
        if msg:
899 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error checking drbd helper on node"
900 1c3231aa Thomas Thrainer
                                     " '%s': %s" % (ninfo.name, msg),
901 7352d33b Thomas Thrainer
                                     errors.ECODE_ENVIRON)
902 1c3231aa Thomas Thrainer
        node_helper = helpers[ninfo.uuid].payload
903 7352d33b Thomas Thrainer
        if node_helper != self.op.drbd_helper:
904 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
905 1c3231aa Thomas Thrainer
                                     (ninfo.name, node_helper),
906 1c3231aa Thomas Thrainer
                                     errors.ECODE_ENVIRON)
907 7352d33b Thomas Thrainer
908 7352d33b Thomas Thrainer
    # validate params changes
909 7352d33b Thomas Thrainer
    if self.op.beparams:
910 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
911 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
912 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
913 7352d33b Thomas Thrainer
914 7352d33b Thomas Thrainer
    if self.op.ndparams:
915 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
916 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
917 7352d33b Thomas Thrainer
918 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
919 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
920 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
921 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
922 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
923 7352d33b Thomas Thrainer
924 7352d33b Thomas Thrainer
    if self.op.hv_state:
925 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
926 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
927 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
928 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
929 7352d33b Thomas Thrainer
930 7352d33b Thomas Thrainer
    if self.op.disk_state:
931 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
932 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
933 7352d33b Thomas Thrainer
      self.new_disk_state = \
934 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
935 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
936 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
937 7352d33b Thomas Thrainer
938 33a6464e Helga Velroyen
    self._CheckIpolicy(cluster, enabled_disk_templates)
939 7352d33b Thomas Thrainer
940 7352d33b Thomas Thrainer
    if self.op.nicparams:
941 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
942 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
943 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
944 7352d33b Thomas Thrainer
      nic_errors = []
945 7352d33b Thomas Thrainer
946 7352d33b Thomas Thrainer
      # check all instances for consistency
947 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
948 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
949 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
950 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
951 7352d33b Thomas Thrainer
952 7352d33b Thomas Thrainer
          # check parameter syntax
953 7352d33b Thomas Thrainer
          try:
954 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
955 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
956 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
957 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
958 7352d33b Thomas Thrainer
959 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
960 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
961 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
962 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
963 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
964 7352d33b Thomas Thrainer
      if nic_errors:
965 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
966 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
967 7352d33b Thomas Thrainer
968 7352d33b Thomas Thrainer
    # hypervisor list/parameters
969 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
970 7352d33b Thomas Thrainer
    if self.op.hvparams:
971 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
972 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
973 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
974 7352d33b Thomas Thrainer
        else:
975 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
976 7352d33b Thomas Thrainer
977 7352d33b Thomas Thrainer
    # disk template parameters
978 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
979 7352d33b Thomas Thrainer
    if self.op.diskparams:
980 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
981 f06af3ca Thomas Thrainer
        if dt_name not in self.new_diskparams:
982 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
983 7352d33b Thomas Thrainer
        else:
984 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
985 7352d33b Thomas Thrainer
986 7352d33b Thomas Thrainer
    # os hypervisor parameters
987 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
988 7352d33b Thomas Thrainer
    if self.op.os_hvp:
989 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
990 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
991 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
992 7352d33b Thomas Thrainer
        else:
993 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
994 7352d33b Thomas Thrainer
            if hv_dict is None:
995 7352d33b Thomas Thrainer
              # Delete if it exists
996 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
997 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
998 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
999 7352d33b Thomas Thrainer
            else:
1000 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
1001 7352d33b Thomas Thrainer
1002 7352d33b Thomas Thrainer
    # os parameters
1003 7352d33b Thomas Thrainer
    self.new_osp = objects.FillDict(cluster.osparams, {})
1004 7352d33b Thomas Thrainer
    if self.op.osparams:
1005 7352d33b Thomas Thrainer
      for os_name, osp in self.op.osparams.items():
1006 7352d33b Thomas Thrainer
        if os_name not in self.new_osp:
1007 7352d33b Thomas Thrainer
          self.new_osp[os_name] = {}
1008 7352d33b Thomas Thrainer
1009 5eacbcae Thomas Thrainer
        self.new_osp[os_name] = GetUpdatedParams(self.new_osp[os_name], osp,
1010 5eacbcae Thomas Thrainer
                                                 use_none=True)
1011 7352d33b Thomas Thrainer
1012 7352d33b Thomas Thrainer
        if not self.new_osp[os_name]:
1013 7352d33b Thomas Thrainer
          # we removed all parameters
1014 7352d33b Thomas Thrainer
          del self.new_osp[os_name]
1015 7352d33b Thomas Thrainer
        else:
1016 7352d33b Thomas Thrainer
          # check the parameter validity (remote check)
1017 5eacbcae Thomas Thrainer
          CheckOSParams(self, False, [self.cfg.GetMasterNode()],
1018 5eacbcae Thomas Thrainer
                        os_name, self.new_osp[os_name])
1019 7352d33b Thomas Thrainer
1020 7352d33b Thomas Thrainer
    # changes to the hypervisor list
1021 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1022 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
1023 7352d33b Thomas Thrainer
      for hv in self.hv_list:
1024 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
1025 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
1026 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
1027 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
1028 7352d33b Thomas Thrainer
        # enabled before
1029 7352d33b Thomas Thrainer
        if hv not in new_hvp:
1030 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
1031 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
1032 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
1033 7352d33b Thomas Thrainer
    else:
1034 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
1035 7352d33b Thomas Thrainer
1036 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
1037 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
1038 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
1039 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
1040 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
1041 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
1042 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
1043 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1044 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1045 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
1046 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, hv_params)
1047 7352d33b Thomas Thrainer
1048 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
1049 7352d33b Thomas Thrainer
1050 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1051 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
1052 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
1053 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
1054 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
1055 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1056 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
1057 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
1058 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
1059 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1060 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
1061 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, new_osp)
1062 7352d33b Thomas Thrainer
1063 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
1064 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
1065 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
1066 7352d33b Thomas Thrainer
                                    os.path.isfile)
1067 7352d33b Thomas Thrainer
      if alloc_script is None:
1068 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
1069 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
1070 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1071 7352d33b Thomas Thrainer
1072 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
1073 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
1074 7352d33b Thomas Thrainer
       are still in use by some instances.
1075 7352d33b Thomas Thrainer

1076 7352d33b Thomas Thrainer
    """
1077 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
1078 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1079 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
1080 7352d33b Thomas Thrainer
1081 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
1082 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
1083 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
1084 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
1085 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
1086 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
1087 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
1088 7352d33b Thomas Thrainer
1089 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
1090 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
1091 7352d33b Thomas Thrainer

1092 7352d33b Thomas Thrainer
    """
1093 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
1094 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
1095 7352d33b Thomas Thrainer
      if not new_volume:
1096 7352d33b Thomas Thrainer
        new_volume = None
1097 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
1098 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
1099 7352d33b Thomas Thrainer
      else:
1100 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
1101 7352d33b Thomas Thrainer
                    " state, not changing")
1102 1bb99a33 Bernardo Dal Seno
1103 3039e2dc Helga Velroyen
  def _SetFileStorageDir(self, feedback_fn):
1104 3039e2dc Helga Velroyen
    """Set the file storage directory.
1105 3039e2dc Helga Velroyen

1106 3039e2dc Helga Velroyen
    """
1107 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1108 3039e2dc Helga Velroyen
      if self.cluster.file_storage_dir == self.op.file_storage_dir:
1109 3039e2dc Helga Velroyen
        feedback_fn("Global file storage dir already set to value '%s'"
1110 3039e2dc Helga Velroyen
                    % self.cluster.file_storage_dir)
1111 3039e2dc Helga Velroyen
      else:
1112 3039e2dc Helga Velroyen
        self.cluster.file_storage_dir = self.op.file_storage_dir
1113 3039e2dc Helga Velroyen
1114 1bb99a33 Bernardo Dal Seno
  def Exec(self, feedback_fn):
1115 1bb99a33 Bernardo Dal Seno
    """Change the parameters of the cluster.
1116 1bb99a33 Bernardo Dal Seno

1117 1bb99a33 Bernardo Dal Seno
    """
1118 1bb99a33 Bernardo Dal Seno
    if self.op.enabled_disk_templates:
1119 1bb99a33 Bernardo Dal Seno
      self.cluster.enabled_disk_templates = \
1120 1bb99a33 Bernardo Dal Seno
        list(set(self.op.enabled_disk_templates))
1121 1bb99a33 Bernardo Dal Seno
1122 1bb99a33 Bernardo Dal Seno
    self._SetVgName(feedback_fn)
1123 3039e2dc Helga Velroyen
    self._SetFileStorageDir(feedback_fn)
1124 1bb99a33 Bernardo Dal Seno
1125 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1126 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1127 1bb99a33 Bernardo Dal Seno
        feedback_fn("Note that you specified a drbd user helper, but did"
1128 1bb99a33 Bernardo Dal Seno
                    " enabled the drbd disk template.")
1129 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1130 7352d33b Thomas Thrainer
      if not new_helper:
1131 7352d33b Thomas Thrainer
        new_helper = None
1132 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1133 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1134 7352d33b Thomas Thrainer
      else:
1135 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1136 7352d33b Thomas Thrainer
                    " not changing")
1137 7352d33b Thomas Thrainer
    if self.op.hvparams:
1138 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1139 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1140 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1141 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1142 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1143 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1144 7352d33b Thomas Thrainer
    if self.op.beparams:
1145 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1146 7352d33b Thomas Thrainer
    if self.op.nicparams:
1147 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1148 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1149 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1150 7352d33b Thomas Thrainer
    if self.op.osparams:
1151 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1152 7352d33b Thomas Thrainer
    if self.op.ndparams:
1153 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1154 7352d33b Thomas Thrainer
    if self.op.diskparams:
1155 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1156 7352d33b Thomas Thrainer
    if self.op.hv_state:
1157 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1158 7352d33b Thomas Thrainer
    if self.op.disk_state:
1159 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1160 7352d33b Thomas Thrainer
1161 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1162 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1163 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1164 5eacbcae Thomas Thrainer
      AdjustCandidatePool(self, [])
1165 7352d33b Thomas Thrainer
1166 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1167 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1168 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1169 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1170 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1171 7352d33b Thomas Thrainer
1172 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1173 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1174 75f2ff7d Michele Tartara
1175 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1176 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1177 7352d33b Thomas Thrainer
1178 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1179 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1180 7352d33b Thomas Thrainer
1181 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1182 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1183 7352d33b Thomas Thrainer
1184 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1185 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1186 7352d33b Thomas Thrainer
1187 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1188 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1189 7352d33b Thomas Thrainer
1190 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1191 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1192 7352d33b Thomas Thrainer
1193 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1194 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1195 7352d33b Thomas Thrainer
1196 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1197 7352d33b Thomas Thrainer
      desc += " OS list"
1198 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1199 7352d33b Thomas Thrainer
      for key, val in mods:
1200 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1201 7352d33b Thomas Thrainer
          if val in lst:
1202 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1203 7352d33b Thomas Thrainer
          else:
1204 7352d33b Thomas Thrainer
            lst.append(val)
1205 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1206 7352d33b Thomas Thrainer
          if val in lst:
1207 7352d33b Thomas Thrainer
            lst.remove(val)
1208 7352d33b Thomas Thrainer
          else:
1209 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1210 7352d33b Thomas Thrainer
        else:
1211 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1212 7352d33b Thomas Thrainer
1213 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1214 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1215 7352d33b Thomas Thrainer
1216 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1217 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1218 7352d33b Thomas Thrainer
1219 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1220 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1221 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1222 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1223 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1224 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1225 7352d33b Thomas Thrainer
                                                       master_params, ems)
1226 e5c92cfb Klaus Aehlig
      if not self.op.force:
1227 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1228 e5c92cfb Klaus Aehlig
      else:
1229 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1230 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1231 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1232 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1233 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1234 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1235 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1236 7352d33b Thomas Thrainer
1237 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1238 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1239 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1240 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1241 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1242 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1243 1c3231aa Thomas Thrainer
                 master_params.netdev)
1244 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1245 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1246 7352d33b Thomas Thrainer
1247 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1248 7352d33b Thomas Thrainer
1249 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1250 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1251 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1252 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1253 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1254 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1255 7352d33b Thomas Thrainer
                                                     master_params, ems)
1256 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1257 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1258 7352d33b Thomas Thrainer
1259 7352d33b Thomas Thrainer
1260 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1261 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1262 7352d33b Thomas Thrainer

1263 7352d33b Thomas Thrainer
  """
1264 7352d33b Thomas Thrainer
  REQ_BGL = False
1265 7352d33b Thomas Thrainer
1266 7352d33b Thomas Thrainer
  def ExpandNames(self):
1267 7352d33b Thomas Thrainer
    self.needed_locks = {}
1268 7352d33b Thomas Thrainer
1269 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1270 7352d33b Thomas Thrainer
    jobs = []
1271 7352d33b Thomas Thrainer
1272 7352d33b Thomas Thrainer
    if self.op.group_name:
1273 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1274 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1275 7352d33b Thomas Thrainer
    else:
1276 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1277 7352d33b Thomas Thrainer
1278 7352d33b Thomas Thrainer
      # Verify global configuration
1279 7352d33b Thomas Thrainer
      jobs.append([
1280 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1281 7352d33b Thomas Thrainer
        ])
1282 7352d33b Thomas Thrainer
1283 7352d33b Thomas Thrainer
      # Always depend on global verification
1284 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1285 7352d33b Thomas Thrainer
1286 7352d33b Thomas Thrainer
    jobs.extend(
1287 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1288 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1289 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1290 7352d33b Thomas Thrainer
      for group in groups)
1291 7352d33b Thomas Thrainer
1292 7352d33b Thomas Thrainer
    # Fix up all parameters
1293 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1294 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1295 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1296 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1297 7352d33b Thomas Thrainer
      try:
1298 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1299 7352d33b Thomas Thrainer
      except AttributeError:
1300 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1301 7352d33b Thomas Thrainer
1302 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1303 7352d33b Thomas Thrainer
1304 7352d33b Thomas Thrainer
1305 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1306 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1307 7352d33b Thomas Thrainer

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

1311 7352d33b Thomas Thrainer
  """
1312 7352d33b Thomas Thrainer
1313 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1314 7352d33b Thomas Thrainer
  ETYPE_ERROR = "ERROR"
1315 7352d33b Thomas Thrainer
  ETYPE_WARNING = "WARNING"
1316 7352d33b Thomas Thrainer
1317 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1318 7352d33b Thomas Thrainer
    """Format an error message.
1319 7352d33b Thomas Thrainer

1320 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1321 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1322 7352d33b Thomas Thrainer

1323 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1324 7352d33b Thomas Thrainer

1325 7352d33b Thomas Thrainer
    """
1326 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1327 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1328 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1329 7352d33b Thomas Thrainer
    # warning
1330 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1331 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1332 7352d33b Thomas Thrainer
    # first complete the msg
1333 7352d33b Thomas Thrainer
    if args:
1334 7352d33b Thomas Thrainer
      msg = msg % args
1335 7352d33b Thomas Thrainer
    # then format the whole message
1336 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1337 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1338 7352d33b Thomas Thrainer
    else:
1339 7352d33b Thomas Thrainer
      if item:
1340 7352d33b Thomas Thrainer
        item = " " + item
1341 7352d33b Thomas Thrainer
      else:
1342 7352d33b Thomas Thrainer
        item = ""
1343 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1344 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1345 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1346 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1347 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1348 7352d33b Thomas Thrainer
      self.bad = True
1349 7352d33b Thomas Thrainer
1350 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1351 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1352 7352d33b Thomas Thrainer

1353 7352d33b Thomas Thrainer
    """
1354 7352d33b Thomas Thrainer
    if (bool(cond)
1355 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1356 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1357 7352d33b Thomas Thrainer
1358 7352d33b Thomas Thrainer
1359 7352d33b Thomas Thrainer
def _VerifyCertificate(filename):
1360 7352d33b Thomas Thrainer
  """Verifies a certificate for L{LUClusterVerifyConfig}.
1361 7352d33b Thomas Thrainer

1362 7352d33b Thomas Thrainer
  @type filename: string
1363 7352d33b Thomas Thrainer
  @param filename: Path to PEM file
1364 7352d33b Thomas Thrainer

1365 7352d33b Thomas Thrainer
  """
1366 7352d33b Thomas Thrainer
  try:
1367 7352d33b Thomas Thrainer
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
1368 7352d33b Thomas Thrainer
                                           utils.ReadFile(filename))
1369 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
1370 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR,
1371 7352d33b Thomas Thrainer
            "Failed to load X509 certificate %s: %s" % (filename, err))
1372 7352d33b Thomas Thrainer
1373 7352d33b Thomas Thrainer
  (errcode, msg) = \
1374 7352d33b Thomas Thrainer
    utils.VerifyX509Certificate(cert, constants.SSL_CERT_EXPIRATION_WARN,
1375 7352d33b Thomas Thrainer
                                constants.SSL_CERT_EXPIRATION_ERROR)
1376 7352d33b Thomas Thrainer
1377 7352d33b Thomas Thrainer
  if msg:
1378 7352d33b Thomas Thrainer
    fnamemsg = "While verifying %s: %s" % (filename, msg)
1379 7352d33b Thomas Thrainer
  else:
1380 7352d33b Thomas Thrainer
    fnamemsg = None
1381 7352d33b Thomas Thrainer
1382 7352d33b Thomas Thrainer
  if errcode is None:
1383 7352d33b Thomas Thrainer
    return (None, fnamemsg)
1384 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_WARNING:
1385 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_WARNING, fnamemsg)
1386 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_ERROR:
1387 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR, fnamemsg)
1388 7352d33b Thomas Thrainer
1389 7352d33b Thomas Thrainer
  raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode)
1390 7352d33b Thomas Thrainer
1391 7352d33b Thomas Thrainer
1392 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1393 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1394 7352d33b Thomas Thrainer

1395 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1396 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1397 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1398 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1399 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1400 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1401 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1402 7352d33b Thomas Thrainer

1403 7352d33b Thomas Thrainer
  """
1404 7352d33b Thomas Thrainer
  hvp_data = []
1405 7352d33b Thomas Thrainer
1406 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1407 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1408 7352d33b Thomas Thrainer
1409 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1410 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1411 7352d33b Thomas Thrainer
      if hv_params:
1412 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1413 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1414 7352d33b Thomas Thrainer
1415 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1416 7352d33b Thomas Thrainer
  for instance in instances:
1417 7352d33b Thomas Thrainer
    if instance.hvparams:
1418 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1419 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1420 7352d33b Thomas Thrainer
1421 7352d33b Thomas Thrainer
  return hvp_data
1422 7352d33b Thomas Thrainer
1423 7352d33b Thomas Thrainer
1424 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1425 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1426 7352d33b Thomas Thrainer

1427 7352d33b Thomas Thrainer
  """
1428 7352d33b Thomas Thrainer
  REQ_BGL = False
1429 7352d33b Thomas Thrainer
1430 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1431 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1432 7352d33b Thomas Thrainer

1433 7352d33b Thomas Thrainer
    """
1434 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1435 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1436 7352d33b Thomas Thrainer
             (item, hv_name))
1437 7352d33b Thomas Thrainer
      try:
1438 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1439 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1440 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1441 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1442 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1443 7352d33b Thomas Thrainer
1444 7352d33b Thomas Thrainer
  def ExpandNames(self):
1445 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1446 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1447 7352d33b Thomas Thrainer
1448 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1449 7352d33b Thomas Thrainer
    """Check prerequisites.
1450 7352d33b Thomas Thrainer

1451 7352d33b Thomas Thrainer
    """
1452 7352d33b Thomas Thrainer
    # Retrieve all information
1453 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1454 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1455 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1456 7352d33b Thomas Thrainer
1457 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1458 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1459 7352d33b Thomas Thrainer

1460 7352d33b Thomas Thrainer
    """
1461 7352d33b Thomas Thrainer
    self.bad = False
1462 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1463 7352d33b Thomas Thrainer
1464 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1465 7352d33b Thomas Thrainer
1466 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1467 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1468 7352d33b Thomas Thrainer
1469 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1470 7352d33b Thomas Thrainer
1471 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1472 7352d33b Thomas Thrainer
      (errcode, msg) = _VerifyCertificate(cert_filename)
1473 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1474 7352d33b Thomas Thrainer
1475 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1476 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1477 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1478 69ac3b74 Michele Tartara
                  None,
1479 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1480 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1481 69ac3b74 Michele Tartara
1482 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1483 7352d33b Thomas Thrainer
1484 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1485 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1486 7352d33b Thomas Thrainer
1487 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1488 7352d33b Thomas Thrainer
1489 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1490 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1491 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1492 7352d33b Thomas Thrainer
1493 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1494 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1495 7352d33b Thomas Thrainer
1496 7352d33b Thomas Thrainer
    dangling_instances = {}
1497 7352d33b Thomas Thrainer
    no_node_instances = []
1498 7352d33b Thomas Thrainer
1499 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1500 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1501 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1502 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1503 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1504 7352d33b Thomas Thrainer
1505 7352d33b Thomas Thrainer
    pretty_dangling = [
1506 7352d33b Thomas Thrainer
        "%s (%s)" %
1507 7352d33b Thomas Thrainer
        (node.name,
1508 24e96ef6 Thomas Thrainer
         utils.CommaJoin(inst.name for
1509 24e96ef6 Thomas Thrainer
                         inst in dangling_instances.get(node.uuid, [])))
1510 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1511 7352d33b Thomas Thrainer
1512 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1513 7352d33b Thomas Thrainer
                  None,
1514 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1515 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1516 7352d33b Thomas Thrainer
1517 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1518 7352d33b Thomas Thrainer
                  None,
1519 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1520 24e96ef6 Thomas Thrainer
                  " %s", utils.CommaJoin(inst.name for
1521 24e96ef6 Thomas Thrainer
                                         inst in no_node_instances))
1522 7352d33b Thomas Thrainer
1523 7352d33b Thomas Thrainer
    return not self.bad
1524 7352d33b Thomas Thrainer
1525 7352d33b Thomas Thrainer
1526 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1527 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1528 7352d33b Thomas Thrainer

1529 7352d33b Thomas Thrainer
  """
1530 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1531 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1532 7352d33b Thomas Thrainer
  REQ_BGL = False
1533 7352d33b Thomas Thrainer
1534 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1535 7352d33b Thomas Thrainer
1536 7352d33b Thomas Thrainer
  class NodeImage(object):
1537 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1538 7352d33b Thomas Thrainer

1539 1c3231aa Thomas Thrainer
    @type uuid: string
1540 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1541 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1542 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1543 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1544 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1545 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1546 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1547 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1548 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1549 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1550 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1551 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1552 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1553 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1554 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1555 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1556 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1557 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1558 7352d33b Thomas Thrainer
    @type ghost: boolean
1559 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1560 7352d33b Thomas Thrainer
    @type os_fail: boolean
1561 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1562 7352d33b Thomas Thrainer
    @type oslist: list
1563 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1564 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1565 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1566 7352d33b Thomas Thrainer
    @type pv_min: float
1567 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1568 7352d33b Thomas Thrainer
    @type pv_max: float
1569 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1570 7352d33b Thomas Thrainer

1571 7352d33b Thomas Thrainer
    """
1572 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1573 1c3231aa Thomas Thrainer
      self.uuid = uuid
1574 7352d33b Thomas Thrainer
      self.volumes = {}
1575 7352d33b Thomas Thrainer
      self.instances = []
1576 7352d33b Thomas Thrainer
      self.pinst = []
1577 7352d33b Thomas Thrainer
      self.sinst = []
1578 7352d33b Thomas Thrainer
      self.sbp = {}
1579 7352d33b Thomas Thrainer
      self.mfree = 0
1580 7352d33b Thomas Thrainer
      self.dfree = 0
1581 7352d33b Thomas Thrainer
      self.offline = offline
1582 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1583 7352d33b Thomas Thrainer
      self.rpc_fail = False
1584 7352d33b Thomas Thrainer
      self.lvm_fail = False
1585 7352d33b Thomas Thrainer
      self.hyp_fail = False
1586 7352d33b Thomas Thrainer
      self.ghost = False
1587 7352d33b Thomas Thrainer
      self.os_fail = False
1588 7352d33b Thomas Thrainer
      self.oslist = {}
1589 7352d33b Thomas Thrainer
      self.pv_min = None
1590 7352d33b Thomas Thrainer
      self.pv_max = None
1591 7352d33b Thomas Thrainer
1592 7352d33b Thomas Thrainer
  def ExpandNames(self):
1593 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1594 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1595 7352d33b Thomas Thrainer
1596 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1597 da4a52a3 Thomas Thrainer
    inst_uuids = \
1598 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1599 7352d33b Thomas Thrainer
1600 7352d33b Thomas Thrainer
    self.needed_locks = {
1601 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1602 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1603 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1604 7352d33b Thomas Thrainer
1605 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1606 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1607 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1608 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1609 7352d33b Thomas Thrainer
      }
1610 7352d33b Thomas Thrainer
1611 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1612 7352d33b Thomas Thrainer
1613 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1614 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1615 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1616 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1617 7352d33b Thomas Thrainer
1618 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1619 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1620 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1621 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1622 7352d33b Thomas Thrainer
      # be locked.
1623 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1624 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1625 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1626 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1627 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1628 7352d33b Thomas Thrainer
1629 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1630 7352d33b Thomas Thrainer
1631 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1632 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1633 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1634 7352d33b Thomas Thrainer
1635 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1636 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1637 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1638 7352d33b Thomas Thrainer
1639 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1640 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1641 7352d33b Thomas Thrainer
1642 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
1643 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
1644 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
1645 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
1646 7352d33b Thomas Thrainer
1647 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
1648 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
1649 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
1650 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
1651 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
1652 7352d33b Thomas Thrainer
1653 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
1654 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
1655 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
1656 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
1657 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
1658 7352d33b Thomas Thrainer
1659 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1660 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1661 7352d33b Thomas Thrainer
1662 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
1663 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
1664 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
1665 7352d33b Thomas Thrainer
1666 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
1667 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
1668 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
1669 7352d33b Thomas Thrainer
1670 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1671 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1672 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1673 7352d33b Thomas Thrainer
1674 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1675 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1676 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
1677 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
1678 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
1679 7352d33b Thomas Thrainer
1680 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1681 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1682 7352d33b Thomas Thrainer
1683 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1684 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1685 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1686 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1687 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1688 7352d33b Thomas Thrainer
1689 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1690 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1691 7352d33b Thomas Thrainer

1692 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1693 7352d33b Thomas Thrainer
        mandatory fields
1694 7352d33b Thomas Thrainer
      - check ganeti version
1695 7352d33b Thomas Thrainer

1696 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1697 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1698 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1699 7352d33b Thomas Thrainer
    @rtype: boolean
1700 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1701 7352d33b Thomas Thrainer
         reasonable values in the respose)
1702 7352d33b Thomas Thrainer

1703 7352d33b Thomas Thrainer
    """
1704 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1705 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1706 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1707 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1708 7352d33b Thomas Thrainer
    if test:
1709 7352d33b Thomas Thrainer
      return False
1710 7352d33b Thomas Thrainer
1711 7352d33b Thomas Thrainer
    # compares ganeti version
1712 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1713 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1714 7352d33b Thomas Thrainer
    test = not (remote_version and
1715 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1716 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1717 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1718 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
1719 7352d33b Thomas Thrainer
    if test:
1720 7352d33b Thomas Thrainer
      return False
1721 7352d33b Thomas Thrainer
1722 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1723 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
1724 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
1725 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
1726 7352d33b Thomas Thrainer
    if test:
1727 7352d33b Thomas Thrainer
      return False
1728 7352d33b Thomas Thrainer
1729 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1730 7352d33b Thomas Thrainer
1731 7352d33b Thomas Thrainer
    # full package version
1732 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1733 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
1734 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1735 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1736 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1737 7352d33b Thomas Thrainer
1738 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1739 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1740 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1741 7352d33b Thomas Thrainer
        test = hv_result is not None
1742 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
1743 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1744 7352d33b Thomas Thrainer
1745 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1746 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1747 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1748 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
1749 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
1750 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
1751 7352d33b Thomas Thrainer
1752 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1753 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1754 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
1755 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
1756 7352d33b Thomas Thrainer
1757 7352d33b Thomas Thrainer
    return True
1758 7352d33b Thomas Thrainer
1759 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1760 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1761 7352d33b Thomas Thrainer
    """Check the node time.
1762 7352d33b Thomas Thrainer

1763 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1764 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1765 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1766 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1767 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1768 7352d33b Thomas Thrainer

1769 7352d33b Thomas Thrainer
    """
1770 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1771 7352d33b Thomas Thrainer
    try:
1772 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1773 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1774 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
1775 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
1776 7352d33b Thomas Thrainer
      return
1777 7352d33b Thomas Thrainer
1778 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1779 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1780 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1781 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1782 7352d33b Thomas Thrainer
    else:
1783 7352d33b Thomas Thrainer
      ntime_diff = None
1784 7352d33b Thomas Thrainer
1785 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
1786 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
1787 d0d7d7cf Thomas Thrainer
                  ntime_diff)
1788 7352d33b Thomas Thrainer
1789 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1790 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1791 7352d33b Thomas Thrainer

1792 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1793 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1794 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1795 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1796 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1797 7352d33b Thomas Thrainer
    @param nimg: node image
1798 7352d33b Thomas Thrainer

1799 7352d33b Thomas Thrainer
    """
1800 7352d33b Thomas Thrainer
    if vg_name is None:
1801 7352d33b Thomas Thrainer
      return
1802 7352d33b Thomas Thrainer
1803 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1804 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1805 7352d33b Thomas Thrainer
    test = not vglist
1806 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
1807 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
1808 7352d33b Thomas Thrainer
    if not test:
1809 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1810 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1811 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
1812 7352d33b Thomas Thrainer
1813 7352d33b Thomas Thrainer
    # Check PVs
1814 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
1815 7352d33b Thomas Thrainer
    for em in errmsgs:
1816 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
1817 7352d33b Thomas Thrainer
    if pvminmax is not None:
1818 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1819 7352d33b Thomas Thrainer
1820 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
1821 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
1822 1bb99a33 Bernardo Dal Seno

1823 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
1824 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
1825 1bb99a33 Bernardo Dal Seno
      node_verify call.
1826 1bb99a33 Bernardo Dal Seno

1827 1bb99a33 Bernardo Dal Seno
    """
1828 1bb99a33 Bernardo Dal Seno
    node_versions = {}
1829 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
1830 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
1831 fb62843c Klaus Aehlig
      if nresult:
1832 fb62843c Klaus Aehlig
        version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
1833 fb62843c Klaus Aehlig
        node_versions[node_uuid] = version
1834 1bb99a33 Bernardo Dal Seno
1835 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
1836 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
1837 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
1838 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
1839 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
1840 1bb99a33 Bernardo Dal Seno
1841 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
1842 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
1843 7352d33b Thomas Thrainer

1844 7352d33b Thomas Thrainer
    @type node_image: dict
1845 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
1846 7352d33b Thomas Thrainer
      L{NodeImage} objects
1847 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1848 7352d33b Thomas Thrainer

1849 7352d33b Thomas Thrainer
    """
1850 7352d33b Thomas Thrainer
    if vg_name is None:
1851 7352d33b Thomas Thrainer
      return
1852 7352d33b Thomas Thrainer
1853 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
1854 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
1855 7352d33b Thomas Thrainer
      return
1856 7352d33b Thomas Thrainer
1857 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
1858 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
1859 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
1860 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
1861 7352d33b Thomas Thrainer
    if not vals:
1862 7352d33b Thomas Thrainer
      return
1863 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
1864 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
1865 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
1866 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
1867 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
1868 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
1869 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
1870 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
1871 7352d33b Thomas Thrainer
1872 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
1873 7352d33b Thomas Thrainer
    """Check the node bridges.
1874 7352d33b Thomas Thrainer

1875 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1876 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1877 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1878 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
1879 7352d33b Thomas Thrainer

1880 7352d33b Thomas Thrainer
    """
1881 7352d33b Thomas Thrainer
    if not bridges:
1882 7352d33b Thomas Thrainer
      return
1883 7352d33b Thomas Thrainer
1884 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
1885 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
1886 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1887 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
1888 7352d33b Thomas Thrainer
    if not test:
1889 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
1890 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
1891 7352d33b Thomas Thrainer
1892 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
1893 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
1894 7352d33b Thomas Thrainer

1895 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1896 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1897 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1898 7352d33b Thomas Thrainer

1899 7352d33b Thomas Thrainer
    """
1900 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
1901 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
1902 7352d33b Thomas Thrainer
                  "did not return user scripts information")
1903 7352d33b Thomas Thrainer
1904 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
1905 7352d33b Thomas Thrainer
    if not test:
1906 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
1907 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
1908 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
1909 7352d33b Thomas Thrainer
1910 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
1911 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
1912 7352d33b Thomas Thrainer

1913 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1914 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1915 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1916 7352d33b Thomas Thrainer

1917 7352d33b Thomas Thrainer
    """
1918 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
1919 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
1920 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
1921 7352d33b Thomas Thrainer
    if not test:
1922 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
1923 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
1924 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
1925 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
1926 7352d33b Thomas Thrainer
1927 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
1928 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1929 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
1930 7352d33b Thomas Thrainer
    if not test:
1931 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
1932 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
1933 7352d33b Thomas Thrainer
        for anode in nlist:
1934 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
1935 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
1936 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
1937 7352d33b Thomas Thrainer
1938 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
1939 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1940 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
1941 7352d33b Thomas Thrainer
    if not test:
1942 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
1943 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
1944 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
1945 7352d33b Thomas Thrainer
        else:
1946 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
1947 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
1948 7352d33b Thomas Thrainer
1949 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
1950 7352d33b Thomas Thrainer
    """Verify an instance.
1951 7352d33b Thomas Thrainer

1952 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
1953 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
1954 7352d33b Thomas Thrainer
    state.
1955 7352d33b Thomas Thrainer

1956 7352d33b Thomas Thrainer
    """
1957 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
1958 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
1959 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
1960 7352d33b Thomas Thrainer
1961 7352d33b Thomas Thrainer
    node_vol_should = {}
1962 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
1963 7352d33b Thomas Thrainer
1964 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1965 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
1966 7352d33b Thomas Thrainer
                                                            self.group_info)
1967 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
1968 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
1969 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
1970 7352d33b Thomas Thrainer
1971 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
1972 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
1973 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
1974 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
1975 7352d33b Thomas Thrainer
        continue
1976 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
1977 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
1978 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
1979 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
1980 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
1981 7352d33b Thomas Thrainer
1982 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
1983 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
1984 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
1985 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
1986 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
1987 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
1988 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
1989 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
1990 7352d33b Thomas Thrainer
1991 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
1992 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
1993 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
1994 7352d33b Thomas Thrainer
1995 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
1996 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
1997 7352d33b Thomas Thrainer
      # node here
1998 7352d33b Thomas Thrainer
      snode = node_image[nname]
1999 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
2000 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
2001 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
2002 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
2003 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
2004 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
2005 9b0e86e2 Thomas Thrainer
2006 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
2007 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
2008 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
2009 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
2010 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
2011 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
2012 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
2013 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
2014 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
2015 9b0e86e2 Thomas Thrainer
2016 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
2017 d0d7d7cf Thomas Thrainer
2018 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
2019 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
2020 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
2021 da4a52a3 Thomas Thrainer
                  instance.name)
2022 da4a52a3 Thomas Thrainer
2023 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
2024 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
2025 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
2026 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
2027 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
2028 7352d33b Thomas Thrainer
2029 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
2030 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
2031 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
2032 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
2033 c69b147d Bernardo Dal Seno
        # node should have the flag set
2034 c69b147d Bernardo Dal Seno
        es_nodes = [n
2035 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
2036 c69b147d Bernardo Dal Seno
                    if es]
2037 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
2038 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
2039 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
2040 da4a52a3 Thomas Thrainer
                    instance.disk_template,
2041 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
2042 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
2043 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
2044 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
2045 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
2046 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
2047 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
2048 7352d33b Thomas Thrainer
2049 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2050 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
2051 7352d33b Thomas Thrainer
      instance_groups = {}
2052 7352d33b Thomas Thrainer
2053 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
2054 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
2055 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
2056 7352d33b Thomas Thrainer
2057 7352d33b Thomas Thrainer
      pretty_list = [
2058 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
2059 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
2060 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
2061 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
2062 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
2063 7352d33b Thomas Thrainer
                                   reverse=True)]
2064 7352d33b Thomas Thrainer
2065 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
2066 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
2067 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
2068 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
2069 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
2070 7352d33b Thomas Thrainer
2071 7352d33b Thomas Thrainer
    inst_nodes_offline = []
2072 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
2073 7352d33b Thomas Thrainer
      s_img = node_image[snode]
2074 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
2075 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
2076 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
2077 da4a52a3 Thomas Thrainer
                    instance.name)
2078 7352d33b Thomas Thrainer
2079 7352d33b Thomas Thrainer
      if s_img.offline:
2080 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
2081 7352d33b Thomas Thrainer
2082 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
2083 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
2084 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
2085 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
2086 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
2087 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
2088 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
2089 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
2090 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2091 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
2092 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
2093 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
2094 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2095 7352d33b Thomas Thrainer
2096 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
2097 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
2098 7352d33b Thomas Thrainer

2099 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
2100 7352d33b Thomas Thrainer
    reported as unknown.
2101 7352d33b Thomas Thrainer

2102 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
2103 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
2104 7352d33b Thomas Thrainer

2105 7352d33b Thomas Thrainer
    """
2106 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2107 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
2108 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
2109 7352d33b Thomas Thrainer
        # skip non-healthy nodes
2110 7352d33b Thomas Thrainer
        continue
2111 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
2112 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
2113 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2114 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2115 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2116 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2117 7352d33b Thomas Thrainer
                      "volume %s is unknown", volume)
2118 7352d33b Thomas Thrainer
2119 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2120 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2121 7352d33b Thomas Thrainer

2122 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2123 7352d33b Thomas Thrainer
    instances it was primary for.
2124 7352d33b Thomas Thrainer

2125 7352d33b Thomas Thrainer
    """
2126 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2127 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2128 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2129 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2130 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2131 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2132 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2133 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2134 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2135 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2136 1c3231aa Thomas Thrainer
      if n_img.offline or \
2137 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2138 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2139 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2140 7352d33b Thomas Thrainer
        # infromation from them; we already list instances living on such
2141 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2142 7352d33b Thomas Thrainer
        continue
2143 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2144 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2145 7352d33b Thomas Thrainer
        needed_mem = 0
2146 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2147 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2148 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2149 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2150 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2151 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2152 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2153 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2154 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2155 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2156 7352d33b Thomas Thrainer
2157 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2158 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2159 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2160 7352d33b Thomas Thrainer

2161 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2162 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2163 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2164 7352d33b Thomas Thrainer

2165 7352d33b Thomas Thrainer
    """
2166 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2167 7352d33b Thomas Thrainer
    files2nodefn = [
2168 7352d33b Thomas Thrainer
      (files_all, None),
2169 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2170 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2171 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2172 7352d33b Thomas Thrainer
      ]
2173 7352d33b Thomas Thrainer
2174 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2175 7352d33b Thomas Thrainer
    nodefiles = {}
2176 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2177 7352d33b Thomas Thrainer
      if fn is None:
2178 1c3231aa Thomas Thrainer
        filenodes = nodes
2179 7352d33b Thomas Thrainer
      else:
2180 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2181 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2182 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2183 7352d33b Thomas Thrainer
                       for filename in files)
2184 7352d33b Thomas Thrainer
2185 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2186 7352d33b Thomas Thrainer
2187 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2188 7352d33b Thomas Thrainer
    ignore_nodes = set()
2189 7352d33b Thomas Thrainer
2190 1c3231aa Thomas Thrainer
    for node in nodes:
2191 7352d33b Thomas Thrainer
      if node.offline:
2192 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2193 7352d33b Thomas Thrainer
        continue
2194 7352d33b Thomas Thrainer
2195 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2196 7352d33b Thomas Thrainer
2197 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2198 7352d33b Thomas Thrainer
        node_files = None
2199 7352d33b Thomas Thrainer
      else:
2200 7352d33b Thomas Thrainer
        fingerprints = nresult.payload.get(constants.NV_FILELIST, None)
2201 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2202 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2203 7352d33b Thomas Thrainer
        del fingerprints
2204 7352d33b Thomas Thrainer
2205 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2206 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2207 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2208 7352d33b Thomas Thrainer
      if test:
2209 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2210 7352d33b Thomas Thrainer
        continue
2211 7352d33b Thomas Thrainer
2212 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2213 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2214 7352d33b Thomas Thrainer
        assert filename in nodefiles
2215 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2216 7352d33b Thomas Thrainer
2217 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2218 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2219 7352d33b Thomas Thrainer
2220 7352d33b Thomas Thrainer
      # Nodes having the file
2221 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2222 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2223 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2224 7352d33b Thomas Thrainer
2225 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2226 7352d33b Thomas Thrainer
2227 7352d33b Thomas Thrainer
      # Nodes missing file
2228 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2229 7352d33b Thomas Thrainer
2230 7352d33b Thomas Thrainer
      if filename in files_opt:
2231 7352d33b Thomas Thrainer
        # All or no nodes
2232 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2233 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2234 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2235 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2236 1c3231aa Thomas Thrainer
                      filename,
2237 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2238 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2239 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2240 7352d33b Thomas Thrainer
      else:
2241 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2242 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2243 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2244 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2245 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2246 7352d33b Thomas Thrainer
2247 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2248 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2249 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2250 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2251 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2252 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2253 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2254 7352d33b Thomas Thrainer
2255 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2256 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2257 7352d33b Thomas Thrainer
      if test:
2258 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2259 1c3231aa Thomas Thrainer
                    (idx + 1,
2260 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2261 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2262 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2263 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2264 7352d33b Thomas Thrainer
      else:
2265 7352d33b Thomas Thrainer
        variants = []
2266 7352d33b Thomas Thrainer
2267 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2268 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2269 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2270 7352d33b Thomas Thrainer
2271 7352d33b Thomas Thrainer
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2272 7352d33b Thomas Thrainer
                      drbd_map):
2273 7352d33b Thomas Thrainer
    """Verifies and the node DRBD status.
2274 7352d33b Thomas Thrainer

2275 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2276 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2277 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2278 7352d33b Thomas Thrainer
    @param instanceinfo: the dict of instances
2279 7352d33b Thomas Thrainer
    @param drbd_helper: the configured DRBD usermode helper
2280 7352d33b Thomas Thrainer
    @param drbd_map: the DRBD map as returned by
2281 7352d33b Thomas Thrainer
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2282 7352d33b Thomas Thrainer

2283 7352d33b Thomas Thrainer
    """
2284 7352d33b Thomas Thrainer
    if drbd_helper:
2285 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2286 7352d33b Thomas Thrainer
      test = (helper_result is None)
2287 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2288 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2289 7352d33b Thomas Thrainer
      if helper_result:
2290 7352d33b Thomas Thrainer
        status, payload = helper_result
2291 7352d33b Thomas Thrainer
        test = not status
2292 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2293 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2294 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2295 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2296 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2297 7352d33b Thomas Thrainer
2298 7352d33b Thomas Thrainer
    # compute the DRBD minors
2299 7352d33b Thomas Thrainer
    node_drbd = {}
2300 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2301 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2302 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2303 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2304 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2305 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2306 7352d33b Thomas Thrainer
        # unallocated minor in use)
2307 7352d33b Thomas Thrainer
      if test:
2308 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2309 7352d33b Thomas Thrainer
      else:
2310 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2311 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2312 7352d33b Thomas Thrainer
2313 7352d33b Thomas Thrainer
    # and now check them
2314 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2315 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2316 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2317 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2318 7352d33b Thomas Thrainer
    if test:
2319 7352d33b Thomas Thrainer
      # we cannot check drbd status
2320 7352d33b Thomas Thrainer
      return
2321 7352d33b Thomas Thrainer
2322 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2323 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2324 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2325 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2326 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2327 7352d33b Thomas Thrainer
    for minor in used_minors:
2328 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2329 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2330 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2331 7352d33b Thomas Thrainer
2332 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2333 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2334 7352d33b Thomas Thrainer

2335 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2336 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2337 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2338 7352d33b Thomas Thrainer
    @param nimg: the node image object
2339 7352d33b Thomas Thrainer

2340 7352d33b Thomas Thrainer
    """
2341 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2342 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2343 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2344 7352d33b Thomas Thrainer
                           for v in remote_os))
2345 7352d33b Thomas Thrainer
2346 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2347 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2348 7352d33b Thomas Thrainer
2349 7352d33b Thomas Thrainer
    nimg.os_fail = test
2350 7352d33b Thomas Thrainer
2351 7352d33b Thomas Thrainer
    if test:
2352 7352d33b Thomas Thrainer
      return
2353 7352d33b Thomas Thrainer
2354 7352d33b Thomas Thrainer
    os_dict = {}
2355 7352d33b Thomas Thrainer
2356 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2357 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2358 7352d33b Thomas Thrainer
2359 7352d33b Thomas Thrainer
      if name not in os_dict:
2360 7352d33b Thomas Thrainer
        os_dict[name] = []
2361 7352d33b Thomas Thrainer
2362 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2363 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2364 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2365 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2366 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2367 7352d33b Thomas Thrainer
2368 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2369 7352d33b Thomas Thrainer
2370 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2371 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2372 7352d33b Thomas Thrainer

2373 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2374 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2375 7352d33b Thomas Thrainer
    @param nimg: the node image object
2376 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2377 7352d33b Thomas Thrainer

2378 7352d33b Thomas Thrainer
    """
2379 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2380 7352d33b Thomas Thrainer
2381 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2382 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2383 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2384 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2385 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2386 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2387 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2388 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2389 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2390 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2391 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2392 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2393 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2394 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2395 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2396 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2397 7352d33b Thomas Thrainer
      if test:
2398 7352d33b Thomas Thrainer
        continue
2399 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2400 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2401 7352d33b Thomas Thrainer
      if not b_status:
2402 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2403 7352d33b Thomas Thrainer
        continue
2404 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2405 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2406 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2407 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2408 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2409 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2410 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2411 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2412 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2413 7352d33b Thomas Thrainer
2414 7352d33b Thomas Thrainer
    # check any missing OSes
2415 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2416 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2417 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2418 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2419 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2420 7352d33b Thomas Thrainer
2421 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2422 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2423 7352d33b Thomas Thrainer

2424 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2425 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2426 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2427 7352d33b Thomas Thrainer
    @type is_master: bool
2428 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2429 7352d33b Thomas Thrainer

2430 7352d33b Thomas Thrainer
    """
2431 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2432 7352d33b Thomas Thrainer
    if (is_master and
2433 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2434 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2435 7352d33b Thomas Thrainer
      try:
2436 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2437 7352d33b Thomas Thrainer
      except KeyError:
2438 7352d33b Thomas Thrainer
        # This should never happen
2439 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2440 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2441 7352d33b Thomas Thrainer
      else:
2442 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2443 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2444 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2445 7352d33b Thomas Thrainer
    else:
2446 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2447 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2448 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2449 7352d33b Thomas Thrainer
                    " paths")
2450 7352d33b Thomas Thrainer
2451 4b322a76 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult, file_disk_template,
2452 4b322a76 Helga Velroyen
                          verify_key, error_key):
2453 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2454 9c1c3c19 Helga Velroyen

2455 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2456 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2457 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2458 4b322a76 Helga Velroyen
    @type file_disk_template: string
2459 4b322a76 Helga Velroyen
    @param file_disk_template: file-based disk template, whose directory
2460 4b322a76 Helga Velroyen
        is supposed to be verified
2461 4b322a76 Helga Velroyen
    @type verify_key: string
2462 4b322a76 Helga Velroyen
    @param verify_key: key for the verification map of this file
2463 4b322a76 Helga Velroyen
        verification step
2464 4b322a76 Helga Velroyen
    @param error_key: error key to be added to the verification results
2465 4b322a76 Helga Velroyen
        in case something goes wrong in this verification step
2466 9c1c3c19 Helga Velroyen

2467 9c1c3c19 Helga Velroyen
    """
2468 4b322a76 Helga Velroyen
    assert (file_disk_template in
2469 4b322a76 Helga Velroyen
            utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE))
2470 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2471 4b322a76 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(file_disk_template):
2472 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2473 4b322a76 Helga Velroyen
          verify_key in nresult,
2474 4b322a76 Helga Velroyen
          error_key, ninfo.name,
2475 4b322a76 Helga Velroyen
          "The configured %s storage path is unusable: %s" %
2476 4b322a76 Helga Velroyen
          (file_disk_template, nresult.get(verify_key)))
2477 4b322a76 Helga Velroyen
2478 4b322a76 Helga Velroyen
  def _VerifyFileStoragePaths(self, ninfo, nresult):
2479 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2480 4b322a76 Helga Velroyen

2481 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2482 4b322a76 Helga Velroyen

2483 4b322a76 Helga Velroyen
    """
2484 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2485 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_FILE,
2486 4b322a76 Helga Velroyen
        constants.NV_FILE_STORAGE_PATH,
2487 4b322a76 Helga Velroyen
        constants.CV_ENODEFILESTORAGEPATHUNUSABLE)
2488 4b322a76 Helga Velroyen
2489 4b322a76 Helga Velroyen
  def _VerifySharedFileStoragePaths(self, ninfo, nresult):
2490 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2491 4b322a76 Helga Velroyen

2492 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2493 4b322a76 Helga Velroyen

2494 4b322a76 Helga Velroyen
    """
2495 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2496 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_SHARED_FILE,
2497 4b322a76 Helga Velroyen
        constants.NV_SHARED_FILE_STORAGE_PATH,
2498 4b322a76 Helga Velroyen
        constants.CV_ENODESHAREDFILESTORAGEPATHUNUSABLE)
2499 9c1c3c19 Helga Velroyen
2500 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2501 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2502 7352d33b Thomas Thrainer

2503 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2504 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2505 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2506 7352d33b Thomas Thrainer

2507 7352d33b Thomas Thrainer
    """
2508 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2509 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2510 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2511 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2512 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2513 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2514 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2515 7352d33b Thomas Thrainer
2516 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2517 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2518 7352d33b Thomas Thrainer

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

2522 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2523 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2524 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2525 7352d33b Thomas Thrainer
    @param nimg: the node image object
2526 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2527 7352d33b Thomas Thrainer

2528 7352d33b Thomas Thrainer
    """
2529 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2530 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2531 7352d33b Thomas Thrainer
    if vg_name is None:
2532 7352d33b Thomas Thrainer
      pass
2533 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2534 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2535 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2536 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2537 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2538 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2539 7352d33b Thomas Thrainer
    else:
2540 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2541 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2542 7352d33b Thomas Thrainer
2543 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2544 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2545 7352d33b Thomas Thrainer

2546 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2547 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2548 7352d33b Thomas Thrainer
    list key.
2549 7352d33b Thomas Thrainer

2550 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2551 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2552 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2553 7352d33b Thomas Thrainer
    @param nimg: the node image object
2554 7352d33b Thomas Thrainer

2555 7352d33b Thomas Thrainer
    """
2556 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2557 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2558 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2559 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2560 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2561 7352d33b Thomas Thrainer
    if test:
2562 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2563 7352d33b Thomas Thrainer
    else:
2564 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
2565 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
2566 7352d33b Thomas Thrainer
2567 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2568 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2569 7352d33b Thomas Thrainer

2570 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2571 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2572 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2573 7352d33b Thomas Thrainer
    @param nimg: the node image object
2574 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2575 7352d33b Thomas Thrainer

2576 7352d33b Thomas Thrainer
    """
2577 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2578 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2579 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2580 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2581 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
2582 7352d33b Thomas Thrainer
    if not test:
2583 7352d33b Thomas Thrainer
      try:
2584 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2585 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2586 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2587 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
2588 7352d33b Thomas Thrainer
2589 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2590 7352d33b Thomas Thrainer
    if vg_name is not None:
2591 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2592 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2593 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2594 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
2595 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
2596 7352d33b Thomas Thrainer
      if not test:
2597 7352d33b Thomas Thrainer
        try:
2598 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2599 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2600 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2601 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
2602 7352d33b Thomas Thrainer
2603 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
2604 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2605 7352d33b Thomas Thrainer

2606 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
2607 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
2608 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
2609 7352d33b Thomas Thrainer
    @param node_image: Node objects
2610 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
2611 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2612 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2613 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2614 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2615 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2616 7352d33b Thomas Thrainer

2617 7352d33b Thomas Thrainer
    """
2618 7352d33b Thomas Thrainer
    node_disks = {}
2619 7352d33b Thomas Thrainer
    node_disks_devonly = {}
2620 7352d33b Thomas Thrainer
    diskless_instances = set()
2621 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2622 7352d33b Thomas Thrainer
2623 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
2624 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
2625 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
2626 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
2627 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
2628 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
2629 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
2630 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
2631 7352d33b Thomas Thrainer
2632 7352d33b Thomas Thrainer
      if not disks:
2633 7352d33b Thomas Thrainer
        # No need to collect data
2634 7352d33b Thomas Thrainer
        continue
2635 7352d33b Thomas Thrainer
2636 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
2637 7352d33b Thomas Thrainer
2638 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2639 7352d33b Thomas Thrainer
      devonly = []
2640 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
2641 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
2642 da4a52a3 Thomas Thrainer
                                          self.cfg)
2643 1c3231aa Thomas Thrainer
        self.cfg.SetDiskID(anno_disk, nuuid)
2644 7352d33b Thomas Thrainer
        devonly.append(anno_disk)
2645 7352d33b Thomas Thrainer
2646 1c3231aa Thomas Thrainer
      node_disks_devonly[nuuid] = devonly
2647 7352d33b Thomas Thrainer
2648 7352d33b Thomas Thrainer
    assert len(node_disks) == len(node_disks_devonly)
2649 7352d33b Thomas Thrainer
2650 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2651 7352d33b Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(node_disks.keys(),
2652 7352d33b Thomas Thrainer
                                                          node_disks_devonly)
2653 7352d33b Thomas Thrainer
2654 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2655 7352d33b Thomas Thrainer
2656 7352d33b Thomas Thrainer
    instdisk = {}
2657 7352d33b Thomas Thrainer
2658 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
2659 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
2660 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
2661 7352d33b Thomas Thrainer
2662 7352d33b Thomas Thrainer
      if nres.offline:
2663 7352d33b Thomas Thrainer
        # No data from this node
2664 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2665 7352d33b Thomas Thrainer
      else:
2666 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2667 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
2668 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
2669 7352d33b Thomas Thrainer
        if msg:
2670 7352d33b Thomas Thrainer
          # No data from this node
2671 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2672 7352d33b Thomas Thrainer
        else:
2673 7352d33b Thomas Thrainer
          data = []
2674 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2675 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2676 7352d33b Thomas Thrainer
              data.append(i)
2677 7352d33b Thomas Thrainer
            else:
2678 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2679 1c3231aa Thomas Thrainer
                              node.name, idx, i)
2680 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2681 7352d33b Thomas Thrainer
2682 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
2683 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
2684 da4a52a3 Thomas Thrainer
          .append(status)
2685 7352d33b Thomas Thrainer
2686 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2687 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
2688 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
2689 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
2690 7352d33b Thomas Thrainer
2691 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
2692 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
2693 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
2694 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
2695 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
2696 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
2697 7352d33b Thomas Thrainer
    if __debug__:
2698 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
2699 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
2700 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
2701 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
2702 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
2703 7352d33b Thomas Thrainer
2704 7352d33b Thomas Thrainer
    return instdisk
2705 7352d33b Thomas Thrainer
2706 7352d33b Thomas Thrainer
  @staticmethod
2707 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
2708 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
2709 7352d33b Thomas Thrainer

2710 7352d33b Thomas Thrainer
    """
2711 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
2712 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
2713 7352d33b Thomas Thrainer
                 not node.offline)]
2714 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
2715 7352d33b Thomas Thrainer
2716 7352d33b Thomas Thrainer
    return map(itertools.cycle,
2717 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
2718 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2719 7352d33b Thomas Thrainer
                                                  keyfunc)])
2720 7352d33b Thomas Thrainer
2721 7352d33b Thomas Thrainer
  @classmethod
2722 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2723 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
2724 7352d33b Thomas Thrainer

2725 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
2726 7352d33b Thomas Thrainer
    every other group.
2727 7352d33b Thomas Thrainer

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

2732 7352d33b Thomas Thrainer
    """
2733 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2734 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2735 7352d33b Thomas Thrainer
2736 7352d33b Thomas Thrainer
    return (online_nodes,
2737 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
2738 7352d33b Thomas Thrainer
                 for name in online_nodes))
2739 7352d33b Thomas Thrainer
2740 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
2741 7352d33b Thomas Thrainer
    """Build hooks env.
2742 7352d33b Thomas Thrainer

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

2746 7352d33b Thomas Thrainer
    """
2747 7352d33b Thomas Thrainer
    env = {
2748 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
2749 7352d33b Thomas Thrainer
      }
2750 7352d33b Thomas Thrainer
2751 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
2752 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
2753 7352d33b Thomas Thrainer
2754 7352d33b Thomas Thrainer
    return env
2755 7352d33b Thomas Thrainer
2756 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
2757 7352d33b Thomas Thrainer
    """Build hooks nodes.
2758 7352d33b Thomas Thrainer

2759 7352d33b Thomas Thrainer
    """
2760 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
2761 7352d33b Thomas Thrainer
2762 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
2763 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
2764 7352d33b Thomas Thrainer

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

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

3114 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
3115 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
3116 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
3117 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
3118 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
3119 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
3120 7352d33b Thomas Thrainer
        and hook results
3121 7352d33b Thomas Thrainer

3122 7352d33b Thomas Thrainer
    """
3123 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
3124 7352d33b Thomas Thrainer
    # and are only interested in their results
3125 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3126 7352d33b Thomas Thrainer
      # empty node group
3127 7352d33b Thomas Thrainer
      pass
3128 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
3129 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
3130 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
3131 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
3132 7352d33b Thomas Thrainer
3133 18397489 Thomas Thrainer
      for node_name in hooks_results:
3134 18397489 Thomas Thrainer
        res = hooks_results[node_name]
3135 7352d33b Thomas Thrainer
        msg = res.fail_msg
3136 7352d33b Thomas Thrainer
        test = msg and not res.offline
3137 18397489 Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3138 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
3139 7352d33b Thomas Thrainer
        if res.offline or msg:
3140 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
3141 7352d33b Thomas Thrainer
          # an error.
3142 7352d33b Thomas Thrainer
          continue
3143 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
3144 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
3145 18397489 Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3146 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
3147 7352d33b Thomas Thrainer
          if test:
3148 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3149 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3150 7352d33b Thomas Thrainer
            lu_result = False
3151 7352d33b Thomas Thrainer
3152 7352d33b Thomas Thrainer
    return lu_result
3153 7352d33b Thomas Thrainer
3154 7352d33b Thomas Thrainer
3155 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3156 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3157 7352d33b Thomas Thrainer

3158 7352d33b Thomas Thrainer
  """
3159 7352d33b Thomas Thrainer
  REQ_BGL = False
3160 7352d33b Thomas Thrainer
3161 7352d33b Thomas Thrainer
  def ExpandNames(self):
3162 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3163 7352d33b Thomas Thrainer
    self.needed_locks = {
3164 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3165 7352d33b Thomas Thrainer
      }
3166 7352d33b Thomas Thrainer
3167 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3168 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3169 7352d33b Thomas Thrainer
3170 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3171 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3172 7352d33b Thomas Thrainer
                           for group in group_names])