Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ a9f33339

History | View | Annotate | Download (122.9 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

180 7352d33b Thomas Thrainer
  """
181 7352d33b Thomas Thrainer
  HPATH = "cluster-init"
182 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
183 7352d33b Thomas Thrainer
184 25ea5b98 Sebastian Gebhard
  def CheckArguments(self):
185 25ea5b98 Sebastian Gebhard
    self.master_uuid = self.cfg.GetMasterNode()
186 25ea5b98 Sebastian Gebhard
    self.master_ndparams = self.cfg.GetNdParams(self.cfg.GetMasterNodeInfo())
187 25ea5b98 Sebastian Gebhard
188 25ea5b98 Sebastian Gebhard
    # TODO: When Issue 584 is solved, and None is properly parsed when used
189 25ea5b98 Sebastian Gebhard
    # as a default value, ndparams.get(.., None) can be changed to
190 25ea5b98 Sebastian Gebhard
    # ndparams[..] to access the values directly
191 25ea5b98 Sebastian Gebhard
192 25ea5b98 Sebastian Gebhard
    # OpenvSwitch: Warn user if link is missing
193 25ea5b98 Sebastian Gebhard
    if (self.master_ndparams[constants.ND_OVS] and not
194 25ea5b98 Sebastian Gebhard
        self.master_ndparams.get(constants.ND_OVS_LINK, None)):
195 25ea5b98 Sebastian Gebhard
      self.LogInfo("No physical interface for OpenvSwitch was given."
196 25ea5b98 Sebastian Gebhard
                   " OpenvSwitch will not have an outside connection. This"
197 25ea5b98 Sebastian Gebhard
                   " might not be what you want.")
198 25ea5b98 Sebastian Gebhard
199 25ea5b98 Sebastian Gebhard
    # OpenvSwitch: Warn if parameters are given, but OVS is not enabled.
200 25ea5b98 Sebastian Gebhard
    if (not self.master_ndparams[constants.ND_OVS] and
201 25ea5b98 Sebastian Gebhard
        (self.master_ndparams[constants.ND_OVS_NAME] or
202 25ea5b98 Sebastian Gebhard
         self.master_ndparams.get(constants.ND_OVS_LINK, None))):
203 25ea5b98 Sebastian Gebhard
      self.LogInfo("OpenvSwitch name or link were given, but"
204 25ea5b98 Sebastian Gebhard
                   " OpenvSwitch is not enabled. Please enable"
205 25ea5b98 Sebastian Gebhard
                   " OpenvSwitch with 'ovs=true' or create it manually")
206 25ea5b98 Sebastian Gebhard
207 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
208 7352d33b Thomas Thrainer
    """Build hooks env.
209 7352d33b Thomas Thrainer

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

218 7352d33b Thomas Thrainer
    """
219 7352d33b Thomas Thrainer
    return ([], [self.cfg.GetMasterNode()])
220 7352d33b Thomas Thrainer
221 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
222 25ea5b98 Sebastian Gebhard
    """Create and configure Open vSwitch
223 7352d33b Thomas Thrainer

224 7352d33b Thomas Thrainer
    """
225 25ea5b98 Sebastian Gebhard
    if self.master_ndparams[constants.ND_OVS]:
226 25ea5b98 Sebastian Gebhard
      result = self.rpc.call_node_configure_ovs(
227 25ea5b98 Sebastian Gebhard
                 self.master_uuid,
228 25ea5b98 Sebastian Gebhard
                 self.master_ndparams[constants.ND_OVS_NAME],
229 25ea5b98 Sebastian Gebhard
                 self.master_ndparams.get(constants.ND_OVS_LINK, None))
230 25ea5b98 Sebastian Gebhard
      result.Raise("Could not successully configure Open vSwitch")
231 7352d33b Thomas Thrainer
    return True
232 7352d33b Thomas Thrainer
233 7352d33b Thomas Thrainer
234 5eacbcae Thomas Thrainer
class ClusterQuery(QueryBase):
235 7352d33b Thomas Thrainer
  FIELDS = query.CLUSTER_FIELDS
236 7352d33b Thomas Thrainer
237 7352d33b Thomas Thrainer
  #: Do not sort (there is only one item)
238 7352d33b Thomas Thrainer
  SORT_FIELD = None
239 7352d33b Thomas Thrainer
240 7352d33b Thomas Thrainer
  def ExpandNames(self, lu):
241 7352d33b Thomas Thrainer
    lu.needed_locks = {}
242 7352d33b Thomas Thrainer
243 7352d33b Thomas Thrainer
    # The following variables interact with _QueryBase._GetNames
244 7352d33b Thomas Thrainer
    self.wanted = locking.ALL_SET
245 7352d33b Thomas Thrainer
    self.do_locking = self.use_locking
246 7352d33b Thomas Thrainer
247 7352d33b Thomas Thrainer
    if self.do_locking:
248 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Can not use locking for cluster queries",
249 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
250 7352d33b Thomas Thrainer
251 7352d33b Thomas Thrainer
  def DeclareLocks(self, lu, level):
252 7352d33b Thomas Thrainer
    pass
253 7352d33b Thomas Thrainer
254 7352d33b Thomas Thrainer
  def _GetQueryData(self, lu):
255 7352d33b Thomas Thrainer
    """Computes the list of nodes and their attributes.
256 7352d33b Thomas Thrainer

257 7352d33b Thomas Thrainer
    """
258 7352d33b Thomas Thrainer
    # Locking is not used
259 7352d33b Thomas Thrainer
    assert not (compat.any(lu.glm.is_owned(level)
260 7352d33b Thomas Thrainer
                           for level in locking.LEVELS
261 7352d33b Thomas Thrainer
                           if level != locking.LEVEL_CLUSTER) or
262 7352d33b Thomas Thrainer
                self.do_locking or self.use_locking)
263 7352d33b Thomas Thrainer
264 7352d33b Thomas Thrainer
    if query.CQ_CONFIG in self.requested_data:
265 7352d33b Thomas Thrainer
      cluster = lu.cfg.GetClusterInfo()
266 1c3231aa Thomas Thrainer
      nodes = lu.cfg.GetAllNodesInfo()
267 7352d33b Thomas Thrainer
    else:
268 7352d33b Thomas Thrainer
      cluster = NotImplemented
269 1c3231aa Thomas Thrainer
      nodes = NotImplemented
270 7352d33b Thomas Thrainer
271 7352d33b Thomas Thrainer
    if query.CQ_QUEUE_DRAINED in self.requested_data:
272 7352d33b Thomas Thrainer
      drain_flag = os.path.exists(pathutils.JOB_QUEUE_DRAIN_FILE)
273 7352d33b Thomas Thrainer
    else:
274 7352d33b Thomas Thrainer
      drain_flag = NotImplemented
275 7352d33b Thomas Thrainer
276 7352d33b Thomas Thrainer
    if query.CQ_WATCHER_PAUSE in self.requested_data:
277 1c3231aa Thomas Thrainer
      master_node_uuid = lu.cfg.GetMasterNode()
278 7352d33b Thomas Thrainer
279 1c3231aa Thomas Thrainer
      result = lu.rpc.call_get_watcher_pause(master_node_uuid)
280 7352d33b Thomas Thrainer
      result.Raise("Can't retrieve watcher pause from master node '%s'" %
281 1c3231aa Thomas Thrainer
                   lu.cfg.GetMasterNodeName())
282 7352d33b Thomas Thrainer
283 7352d33b Thomas Thrainer
      watcher_pause = result.payload
284 7352d33b Thomas Thrainer
    else:
285 7352d33b Thomas Thrainer
      watcher_pause = NotImplemented
286 7352d33b Thomas Thrainer
287 1c3231aa Thomas Thrainer
    return query.ClusterQueryData(cluster, nodes, drain_flag, watcher_pause)
288 7352d33b Thomas Thrainer
289 7352d33b Thomas Thrainer
290 7352d33b Thomas Thrainer
class LUClusterQuery(NoHooksLU):
291 7352d33b Thomas Thrainer
  """Query cluster configuration.
292 7352d33b Thomas Thrainer

293 7352d33b Thomas Thrainer
  """
294 7352d33b Thomas Thrainer
  REQ_BGL = False
295 7352d33b Thomas Thrainer
296 7352d33b Thomas Thrainer
  def ExpandNames(self):
297 7352d33b Thomas Thrainer
    self.needed_locks = {}
298 7352d33b Thomas Thrainer
299 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
300 7352d33b Thomas Thrainer
    """Return cluster config.
301 7352d33b Thomas Thrainer

302 7352d33b Thomas Thrainer
    """
303 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
304 7352d33b Thomas Thrainer
    os_hvp = {}
305 7352d33b Thomas Thrainer
306 7352d33b Thomas Thrainer
    # Filter just for enabled hypervisors
307 7352d33b Thomas Thrainer
    for os_name, hv_dict in cluster.os_hvp.items():
308 7352d33b Thomas Thrainer
      os_hvp[os_name] = {}
309 7352d33b Thomas Thrainer
      for hv_name, hv_params in hv_dict.items():
310 7352d33b Thomas Thrainer
        if hv_name in cluster.enabled_hypervisors:
311 7352d33b Thomas Thrainer
          os_hvp[os_name][hv_name] = hv_params
312 7352d33b Thomas Thrainer
313 7352d33b Thomas Thrainer
    # Convert ip_family to ip_version
314 7352d33b Thomas Thrainer
    primary_ip_version = constants.IP4_VERSION
315 7352d33b Thomas Thrainer
    if cluster.primary_ip_family == netutils.IP6Address.family:
316 7352d33b Thomas Thrainer
      primary_ip_version = constants.IP6_VERSION
317 7352d33b Thomas Thrainer
318 7352d33b Thomas Thrainer
    result = {
319 7352d33b Thomas Thrainer
      "software_version": constants.RELEASE_VERSION,
320 7352d33b Thomas Thrainer
      "protocol_version": constants.PROTOCOL_VERSION,
321 7352d33b Thomas Thrainer
      "config_version": constants.CONFIG_VERSION,
322 7352d33b Thomas Thrainer
      "os_api_version": max(constants.OS_API_VERSIONS),
323 7352d33b Thomas Thrainer
      "export_version": constants.EXPORT_VERSION,
324 026f444f Thomas Thrainer
      "vcs_version": constants.VCS_VERSION,
325 7352d33b Thomas Thrainer
      "architecture": runtime.GetArchInfo(),
326 7352d33b Thomas Thrainer
      "name": cluster.cluster_name,
327 1c3231aa Thomas Thrainer
      "master": self.cfg.GetMasterNodeName(),
328 7352d33b Thomas Thrainer
      "default_hypervisor": cluster.primary_hypervisor,
329 7352d33b Thomas Thrainer
      "enabled_hypervisors": cluster.enabled_hypervisors,
330 7352d33b Thomas Thrainer
      "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
331 7352d33b Thomas Thrainer
                        for hypervisor_name in cluster.enabled_hypervisors]),
332 7352d33b Thomas Thrainer
      "os_hvp": os_hvp,
333 7352d33b Thomas Thrainer
      "beparams": cluster.beparams,
334 7352d33b Thomas Thrainer
      "osparams": cluster.osparams,
335 7352d33b Thomas Thrainer
      "ipolicy": cluster.ipolicy,
336 7352d33b Thomas Thrainer
      "nicparams": cluster.nicparams,
337 7352d33b Thomas Thrainer
      "ndparams": cluster.ndparams,
338 7352d33b Thomas Thrainer
      "diskparams": cluster.diskparams,
339 7352d33b Thomas Thrainer
      "candidate_pool_size": cluster.candidate_pool_size,
340 7352d33b Thomas Thrainer
      "master_netdev": cluster.master_netdev,
341 7352d33b Thomas Thrainer
      "master_netmask": cluster.master_netmask,
342 7352d33b Thomas Thrainer
      "use_external_mip_script": cluster.use_external_mip_script,
343 7352d33b Thomas Thrainer
      "volume_group_name": cluster.volume_group_name,
344 7352d33b Thomas Thrainer
      "drbd_usermode_helper": cluster.drbd_usermode_helper,
345 7352d33b Thomas Thrainer
      "file_storage_dir": cluster.file_storage_dir,
346 7352d33b Thomas Thrainer
      "shared_file_storage_dir": cluster.shared_file_storage_dir,
347 7352d33b Thomas Thrainer
      "maintain_node_health": cluster.maintain_node_health,
348 7352d33b Thomas Thrainer
      "ctime": cluster.ctime,
349 7352d33b Thomas Thrainer
      "mtime": cluster.mtime,
350 7352d33b Thomas Thrainer
      "uuid": cluster.uuid,
351 7352d33b Thomas Thrainer
      "tags": list(cluster.GetTags()),
352 7352d33b Thomas Thrainer
      "uid_pool": cluster.uid_pool,
353 7352d33b Thomas Thrainer
      "default_iallocator": cluster.default_iallocator,
354 7352d33b Thomas Thrainer
      "reserved_lvs": cluster.reserved_lvs,
355 7352d33b Thomas Thrainer
      "primary_ip_version": primary_ip_version,
356 7352d33b Thomas Thrainer
      "prealloc_wipe_disks": cluster.prealloc_wipe_disks,
357 7352d33b Thomas Thrainer
      "hidden_os": cluster.hidden_os,
358 7352d33b Thomas Thrainer
      "blacklisted_os": cluster.blacklisted_os,
359 fe782deb Helga Velroyen
      "enabled_disk_templates": cluster.enabled_disk_templates,
360 7352d33b Thomas Thrainer
      }
361 7352d33b Thomas Thrainer
362 7352d33b Thomas Thrainer
    return result
363 7352d33b Thomas Thrainer
364 7352d33b Thomas Thrainer
365 7352d33b Thomas Thrainer
class LUClusterRedistConf(NoHooksLU):
366 7352d33b Thomas Thrainer
  """Force the redistribution of cluster configuration.
367 7352d33b Thomas Thrainer

368 7352d33b Thomas Thrainer
  This is a very simple LU.
369 7352d33b Thomas Thrainer

370 7352d33b Thomas Thrainer
  """
371 7352d33b Thomas Thrainer
  REQ_BGL = False
372 7352d33b Thomas Thrainer
373 7352d33b Thomas Thrainer
  def ExpandNames(self):
374 7352d33b Thomas Thrainer
    self.needed_locks = {
375 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
376 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
377 7352d33b Thomas Thrainer
    }
378 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
379 7352d33b Thomas Thrainer
380 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
381 7352d33b Thomas Thrainer
    """Redistribute the configuration.
382 7352d33b Thomas Thrainer

383 7352d33b Thomas Thrainer
    """
384 7352d33b Thomas Thrainer
    self.cfg.Update(self.cfg.GetClusterInfo(), feedback_fn)
385 5eacbcae Thomas Thrainer
    RedistributeAncillaryFiles(self)
386 7352d33b Thomas Thrainer
387 7352d33b Thomas Thrainer
388 7352d33b Thomas Thrainer
class LUClusterRename(LogicalUnit):
389 7352d33b Thomas Thrainer
  """Rename the cluster.
390 7352d33b Thomas Thrainer

391 7352d33b Thomas Thrainer
  """
392 7352d33b Thomas Thrainer
  HPATH = "cluster-rename"
393 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
394 7352d33b Thomas Thrainer
395 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
396 7352d33b Thomas Thrainer
    """Build hooks env.
397 7352d33b Thomas Thrainer

398 7352d33b Thomas Thrainer
    """
399 7352d33b Thomas Thrainer
    return {
400 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
401 7352d33b Thomas Thrainer
      "NEW_NAME": self.op.name,
402 7352d33b Thomas Thrainer
      }
403 7352d33b Thomas Thrainer
404 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
405 7352d33b Thomas Thrainer
    """Build hooks nodes.
406 7352d33b Thomas Thrainer

407 7352d33b Thomas Thrainer
    """
408 7352d33b Thomas Thrainer
    return ([self.cfg.GetMasterNode()], self.cfg.GetNodeList())
409 7352d33b Thomas Thrainer
410 7352d33b Thomas Thrainer
  def CheckPrereq(self):
411 7352d33b Thomas Thrainer
    """Verify that the passed name is a valid one.
412 7352d33b Thomas Thrainer

413 7352d33b Thomas Thrainer
    """
414 7352d33b Thomas Thrainer
    hostname = netutils.GetHostname(name=self.op.name,
415 7352d33b Thomas Thrainer
                                    family=self.cfg.GetPrimaryIPFamily())
416 7352d33b Thomas Thrainer
417 7352d33b Thomas Thrainer
    new_name = hostname.name
418 7352d33b Thomas Thrainer
    self.ip = new_ip = hostname.ip
419 7352d33b Thomas Thrainer
    old_name = self.cfg.GetClusterName()
420 7352d33b Thomas Thrainer
    old_ip = self.cfg.GetMasterIP()
421 7352d33b Thomas Thrainer
    if new_name == old_name and new_ip == old_ip:
422 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Neither the name nor the IP address of the"
423 7352d33b Thomas Thrainer
                                 " cluster has changed",
424 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
425 7352d33b Thomas Thrainer
    if new_ip != old_ip:
426 7352d33b Thomas Thrainer
      if netutils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT):
427 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("The given cluster IP address (%s) is"
428 7352d33b Thomas Thrainer
                                   " reachable on the network" %
429 7352d33b Thomas Thrainer
                                   new_ip, errors.ECODE_NOTUNIQUE)
430 7352d33b Thomas Thrainer
431 7352d33b Thomas Thrainer
    self.op.name = new_name
432 7352d33b Thomas Thrainer
433 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
434 7352d33b Thomas Thrainer
    """Rename the cluster.
435 7352d33b Thomas Thrainer

436 7352d33b Thomas Thrainer
    """
437 7352d33b Thomas Thrainer
    clustername = self.op.name
438 7352d33b Thomas Thrainer
    new_ip = self.ip
439 7352d33b Thomas Thrainer
440 7352d33b Thomas Thrainer
    # shutdown the master IP
441 7352d33b Thomas Thrainer
    master_params = self.cfg.GetMasterNetworkParameters()
442 7352d33b Thomas Thrainer
    ems = self.cfg.GetUseExternalMipScript()
443 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
444 7352d33b Thomas Thrainer
                                                     master_params, ems)
445 7352d33b Thomas Thrainer
    result.Raise("Could not disable the master role")
446 7352d33b Thomas Thrainer
447 7352d33b Thomas Thrainer
    try:
448 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
449 7352d33b Thomas Thrainer
      cluster.cluster_name = clustername
450 7352d33b Thomas Thrainer
      cluster.master_ip = new_ip
451 7352d33b Thomas Thrainer
      self.cfg.Update(cluster, feedback_fn)
452 7352d33b Thomas Thrainer
453 7352d33b Thomas Thrainer
      # update the known hosts file
454 7352d33b Thomas Thrainer
      ssh.WriteKnownHostsFile(self.cfg, pathutils.SSH_KNOWN_HOSTS_FILE)
455 7352d33b Thomas Thrainer
      node_list = self.cfg.GetOnlineNodeList()
456 7352d33b Thomas Thrainer
      try:
457 1c3231aa Thomas Thrainer
        node_list.remove(master_params.uuid)
458 7352d33b Thomas Thrainer
      except ValueError:
459 7352d33b Thomas Thrainer
        pass
460 5eacbcae Thomas Thrainer
      UploadHelper(self, node_list, pathutils.SSH_KNOWN_HOSTS_FILE)
461 7352d33b Thomas Thrainer
    finally:
462 7352d33b Thomas Thrainer
      master_params.ip = new_ip
463 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
464 7352d33b Thomas Thrainer
                                                     master_params, ems)
465 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master role on the master,"
466 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
467 7352d33b Thomas Thrainer
468 7352d33b Thomas Thrainer
    return clustername
469 7352d33b Thomas Thrainer
470 7352d33b Thomas Thrainer
471 7352d33b Thomas Thrainer
class LUClusterRepairDiskSizes(NoHooksLU):
472 7352d33b Thomas Thrainer
  """Verifies the cluster disks sizes.
473 7352d33b Thomas Thrainer

474 7352d33b Thomas Thrainer
  """
475 7352d33b Thomas Thrainer
  REQ_BGL = False
476 7352d33b Thomas Thrainer
477 7352d33b Thomas Thrainer
  def ExpandNames(self):
478 7352d33b Thomas Thrainer
    if self.op.instances:
479 da4a52a3 Thomas Thrainer
      (_, self.wanted_names) = GetWantedInstances(self, self.op.instances)
480 7352d33b Thomas Thrainer
      # Not getting the node allocation lock as only a specific set of
481 7352d33b Thomas Thrainer
      # instances (and their nodes) is going to be acquired
482 7352d33b Thomas Thrainer
      self.needed_locks = {
483 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: [],
484 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: self.wanted_names,
485 7352d33b Thomas Thrainer
        }
486 7352d33b Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
487 7352d33b Thomas Thrainer
    else:
488 7352d33b Thomas Thrainer
      self.wanted_names = None
489 7352d33b Thomas Thrainer
      self.needed_locks = {
490 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_RES: locking.ALL_SET,
491 7352d33b Thomas Thrainer
        locking.LEVEL_INSTANCE: locking.ALL_SET,
492 7352d33b Thomas Thrainer
493 7352d33b Thomas Thrainer
        # This opcode is acquires the node locks for all instances
494 7352d33b Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
495 7352d33b Thomas Thrainer
        }
496 7352d33b Thomas Thrainer
497 7352d33b Thomas Thrainer
    self.share_locks = {
498 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_RES: 1,
499 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: 0,
500 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: 1,
501 7352d33b Thomas Thrainer
      }
502 7352d33b Thomas Thrainer
503 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
504 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and self.wanted_names is not None:
505 7352d33b Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=level)
506 7352d33b Thomas Thrainer
507 7352d33b Thomas Thrainer
  def CheckPrereq(self):
508 7352d33b Thomas Thrainer
    """Check prerequisites.
509 7352d33b Thomas Thrainer

510 7352d33b Thomas Thrainer
    This only checks the optional instance list against the existing names.
511 7352d33b Thomas Thrainer

512 7352d33b Thomas Thrainer
    """
513 7352d33b Thomas Thrainer
    if self.wanted_names is None:
514 7352d33b Thomas Thrainer
      self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE)
515 7352d33b Thomas Thrainer
516 7352d33b Thomas Thrainer
    self.wanted_instances = \
517 da4a52a3 Thomas Thrainer
        map(compat.snd, self.cfg.GetMultiInstanceInfoByName(self.wanted_names))
518 7352d33b Thomas Thrainer
519 7352d33b Thomas Thrainer
  def _EnsureChildSizes(self, disk):
520 7352d33b Thomas Thrainer
    """Ensure children of the disk have the needed disk size.
521 7352d33b Thomas Thrainer

522 7352d33b Thomas Thrainer
    This is valid mainly for DRBD8 and fixes an issue where the
523 7352d33b Thomas Thrainer
    children have smaller disk size.
524 7352d33b Thomas Thrainer

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

527 7352d33b Thomas Thrainer
    """
528 cd3b4ff4 Helga Velroyen
    if disk.dev_type == constants.DT_DRBD8:
529 7352d33b Thomas Thrainer
      assert disk.children, "Empty children for DRBD8?"
530 7352d33b Thomas Thrainer
      fchild = disk.children[0]
531 7352d33b Thomas Thrainer
      mismatch = fchild.size < disk.size
532 7352d33b Thomas Thrainer
      if mismatch:
533 7352d33b Thomas Thrainer
        self.LogInfo("Child disk has size %d, parent %d, fixing",
534 7352d33b Thomas Thrainer
                     fchild.size, disk.size)
535 7352d33b Thomas Thrainer
        fchild.size = disk.size
536 7352d33b Thomas Thrainer
537 7352d33b Thomas Thrainer
      # and we recurse on this child only, not on the metadev
538 7352d33b Thomas Thrainer
      return self._EnsureChildSizes(fchild) or mismatch
539 7352d33b Thomas Thrainer
    else:
540 7352d33b Thomas Thrainer
      return False
541 7352d33b Thomas Thrainer
542 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
543 7352d33b Thomas Thrainer
    """Verify the size of cluster disks.
544 7352d33b Thomas Thrainer

545 7352d33b Thomas Thrainer
    """
546 7352d33b Thomas Thrainer
    # TODO: check child disks too
547 7352d33b Thomas Thrainer
    # TODO: check differences in size between primary/secondary nodes
548 7352d33b Thomas Thrainer
    per_node_disks = {}
549 7352d33b Thomas Thrainer
    for instance in self.wanted_instances:
550 7352d33b Thomas Thrainer
      pnode = instance.primary_node
551 7352d33b Thomas Thrainer
      if pnode not in per_node_disks:
552 7352d33b Thomas Thrainer
        per_node_disks[pnode] = []
553 7352d33b Thomas Thrainer
      for idx, disk in enumerate(instance.disks):
554 7352d33b Thomas Thrainer
        per_node_disks[pnode].append((instance, idx, disk))
555 7352d33b Thomas Thrainer
556 7352d33b Thomas Thrainer
    assert not (frozenset(per_node_disks.keys()) -
557 7352d33b Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE_RES)), \
558 7352d33b Thomas Thrainer
      "Not owning correct locks"
559 7352d33b Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE)
560 7352d33b Thomas Thrainer
561 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
562 1c3231aa Thomas Thrainer
                                               per_node_disks.keys())
563 40d93e3b Bernardo Dal Seno
564 7352d33b Thomas Thrainer
    changed = []
565 1c3231aa Thomas Thrainer
    for node_uuid, dskl in per_node_disks.items():
566 0c3d9c7c Thomas Thrainer
      if not dskl:
567 0c3d9c7c Thomas Thrainer
        # no disks on the node
568 0c3d9c7c Thomas Thrainer
        continue
569 0c3d9c7c Thomas Thrainer
570 d66acf3d Thomas Thrainer
      newl = [([v[2].Copy()], v[0]) for v in dskl]
571 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
572 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_getdimensions(node_uuid, newl)
573 7352d33b Thomas Thrainer
      if result.fail_msg:
574 6ef8077e Bernardo Dal Seno
        self.LogWarning("Failure in blockdev_getdimensions call to node"
575 1c3231aa Thomas Thrainer
                        " %s, ignoring", node_name)
576 7352d33b Thomas Thrainer
        continue
577 7352d33b Thomas Thrainer
      if len(result.payload) != len(dskl):
578 7352d33b Thomas Thrainer
        logging.warning("Invalid result from node %s: len(dksl)=%d,"
579 1c3231aa Thomas Thrainer
                        " result.payload=%s", node_name, len(dskl),
580 1c3231aa Thomas Thrainer
                        result.payload)
581 7352d33b Thomas Thrainer
        self.LogWarning("Invalid result from node %s, ignoring node results",
582 1c3231aa Thomas Thrainer
                        node_name)
583 7352d33b Thomas Thrainer
        continue
584 6ef8077e Bernardo Dal Seno
      for ((instance, idx, disk), dimensions) in zip(dskl, result.payload):
585 6ef8077e Bernardo Dal Seno
        if dimensions is None:
586 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return size"
587 7352d33b Thomas Thrainer
                          " information, ignoring", idx, instance.name)
588 7352d33b Thomas Thrainer
          continue
589 6ef8077e Bernardo Dal Seno
        if not isinstance(dimensions, (tuple, list)):
590 6ef8077e Bernardo Dal Seno
          self.LogWarning("Disk %d of instance %s did not return valid"
591 6ef8077e Bernardo Dal Seno
                          " dimension information, ignoring", idx,
592 6ef8077e Bernardo Dal Seno
                          instance.name)
593 6ef8077e Bernardo Dal Seno
          continue
594 40d93e3b Bernardo Dal Seno
        (size, spindles) = dimensions
595 7352d33b Thomas Thrainer
        if not isinstance(size, (int, long)):
596 7352d33b Thomas Thrainer
          self.LogWarning("Disk %d of instance %s did not return valid"
597 7352d33b Thomas Thrainer
                          " size information, ignoring", idx, instance.name)
598 7352d33b Thomas Thrainer
          continue
599 7352d33b Thomas Thrainer
        size = size >> 20
600 7352d33b Thomas Thrainer
        if size != disk.size:
601 7352d33b Thomas Thrainer
          self.LogInfo("Disk %d of instance %s has mismatched size,"
602 7352d33b Thomas Thrainer
                       " correcting: recorded %d, actual %d", idx,
603 7352d33b Thomas Thrainer
                       instance.name, disk.size, size)
604 7352d33b Thomas Thrainer
          disk.size = size
605 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
606 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", size))
607 1c3231aa Thomas Thrainer
        if es_flags[node_uuid]:
608 40d93e3b Bernardo Dal Seno
          if spindles is None:
609 40d93e3b Bernardo Dal Seno
            self.LogWarning("Disk %d of instance %s did not return valid"
610 40d93e3b Bernardo Dal Seno
                            " spindles information, ignoring", idx,
611 40d93e3b Bernardo Dal Seno
                            instance.name)
612 40d93e3b Bernardo Dal Seno
          elif disk.spindles is None or disk.spindles != spindles:
613 40d93e3b Bernardo Dal Seno
            self.LogInfo("Disk %d of instance %s has mismatched spindles,"
614 40d93e3b Bernardo Dal Seno
                         " correcting: recorded %s, actual %s",
615 40d93e3b Bernardo Dal Seno
                         idx, instance.name, disk.spindles, spindles)
616 40d93e3b Bernardo Dal Seno
            disk.spindles = spindles
617 40d93e3b Bernardo Dal Seno
            self.cfg.Update(instance, feedback_fn)
618 40d93e3b Bernardo Dal Seno
            changed.append((instance.name, idx, "spindles", disk.spindles))
619 7352d33b Thomas Thrainer
        if self._EnsureChildSizes(disk):
620 7352d33b Thomas Thrainer
          self.cfg.Update(instance, feedback_fn)
621 40d93e3b Bernardo Dal Seno
          changed.append((instance.name, idx, "size", disk.size))
622 7352d33b Thomas Thrainer
    return changed
623 7352d33b Thomas Thrainer
624 7352d33b Thomas Thrainer
625 7352d33b Thomas Thrainer
def _ValidateNetmask(cfg, netmask):
626 7352d33b Thomas Thrainer
  """Checks if a netmask is valid.
627 7352d33b Thomas Thrainer

628 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
629 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
630 7352d33b Thomas Thrainer
  @type netmask: int
631 7352d33b Thomas Thrainer
  @param netmask: the netmask to be verified
632 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the validation fails
633 7352d33b Thomas Thrainer

634 7352d33b Thomas Thrainer
  """
635 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
636 7352d33b Thomas Thrainer
  try:
637 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
638 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
639 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
640 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
641 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
642 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
643 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
644 7352d33b Thomas Thrainer
645 7352d33b Thomas Thrainer
646 e8b5640e Helga Velroyen
def CheckFileBasedStoragePathVsEnabledDiskTemplates(
647 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates,
648 e8b5640e Helga Velroyen
    file_disk_template):
649 e8b5640e Helga Velroyen
  """Checks whether the given file-based storage directory is acceptable.
650 e8b5640e Helga Velroyen

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

653 3039e2dc Helga Velroyen
  @type logging_warn_fn: function
654 3039e2dc Helga Velroyen
  @param logging_warn_fn: function which accepts a string and logs it
655 3039e2dc Helga Velroyen
  @type file_storage_dir: string
656 3039e2dc Helga Velroyen
  @param file_storage_dir: the directory to be used for file-based instances
657 3039e2dc Helga Velroyen
  @type enabled_disk_templates: list of string
658 3039e2dc Helga Velroyen
  @param enabled_disk_templates: the list of enabled disk templates
659 e8b5640e Helga Velroyen
  @type file_disk_template: string
660 e8b5640e Helga Velroyen
  @param file_disk_template: the file-based disk template for which the
661 e8b5640e Helga Velroyen
      path should be checked
662 3039e2dc Helga Velroyen

663 3039e2dc Helga Velroyen
  """
664 e8b5640e Helga Velroyen
  assert (file_disk_template in
665 e8b5640e Helga Velroyen
          utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE))
666 e8b5640e Helga Velroyen
  file_storage_enabled = file_disk_template in enabled_disk_templates
667 3039e2dc Helga Velroyen
  if file_storage_dir is not None:
668 3039e2dc Helga Velroyen
    if file_storage_dir == "":
669 3039e2dc Helga Velroyen
      if file_storage_enabled:
670 e8b5640e Helga Velroyen
        raise errors.OpPrereqError(
671 e8b5640e Helga Velroyen
            "Unsetting the '%s' storage directory while having '%s' storage"
672 e8b5640e Helga Velroyen
            " enabled is not permitted." %
673 e8b5640e Helga Velroyen
            (file_disk_template, file_disk_template))
674 3039e2dc Helga Velroyen
    else:
675 3039e2dc Helga Velroyen
      if not file_storage_enabled:
676 e8b5640e Helga Velroyen
        logging_warn_fn(
677 e8b5640e Helga Velroyen
            "Specified a %s storage directory, although %s storage is not"
678 e8b5640e Helga Velroyen
            " enabled." % (file_disk_template, file_disk_template))
679 3039e2dc Helga Velroyen
  else:
680 e8b5640e Helga Velroyen
    raise errors.ProgrammerError("Received %s storage dir with value"
681 e8b5640e Helga Velroyen
                                 " 'None'." % file_disk_template)
682 e8b5640e Helga Velroyen
683 e8b5640e Helga Velroyen
684 e8b5640e Helga Velroyen
def CheckFileStoragePathVsEnabledDiskTemplates(
685 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
686 e8b5640e Helga Velroyen
  """Checks whether the given file storage directory is acceptable.
687 e8b5640e Helga Velroyen

688 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
689 e8b5640e Helga Velroyen

690 e8b5640e Helga Velroyen
  """
691 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
692 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
693 e8b5640e Helga Velroyen
      constants.DT_FILE)
694 e8b5640e Helga Velroyen
695 e8b5640e Helga Velroyen
696 e8b5640e Helga Velroyen
def CheckSharedFileStoragePathVsEnabledDiskTemplates(
697 e8b5640e Helga Velroyen
    logging_warn_fn, file_storage_dir, enabled_disk_templates):
698 e8b5640e Helga Velroyen
  """Checks whether the given shared file storage directory is acceptable.
699 e8b5640e Helga Velroyen

700 e8b5640e Helga Velroyen
  @see: C{CheckFileBasedStoragePathVsEnabledDiskTemplates}
701 e8b5640e Helga Velroyen

702 e8b5640e Helga Velroyen
  """
703 e8b5640e Helga Velroyen
  CheckFileBasedStoragePathVsEnabledDiskTemplates(
704 e8b5640e Helga Velroyen
      logging_warn_fn, file_storage_dir, enabled_disk_templates,
705 e8b5640e Helga Velroyen
      constants.DT_SHARED_FILE)
706 3039e2dc Helga Velroyen
707 3039e2dc Helga Velroyen
708 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
709 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
710 7352d33b Thomas Thrainer

711 7352d33b Thomas Thrainer
  """
712 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
713 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
714 7352d33b Thomas Thrainer
  REQ_BGL = False
715 7352d33b Thomas Thrainer
716 7352d33b Thomas Thrainer
  def CheckArguments(self):
717 7352d33b Thomas Thrainer
    """Check parameters
718 7352d33b Thomas Thrainer

719 7352d33b Thomas Thrainer
    """
720 7352d33b Thomas Thrainer
    if self.op.uid_pool:
721 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.uid_pool)
722 7352d33b Thomas Thrainer
723 7352d33b Thomas Thrainer
    if self.op.add_uids:
724 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.add_uids)
725 7352d33b Thomas Thrainer
726 7352d33b Thomas Thrainer
    if self.op.remove_uids:
727 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.remove_uids)
728 7352d33b Thomas Thrainer
729 7352d33b Thomas Thrainer
    if self.op.master_netmask is not None:
730 7352d33b Thomas Thrainer
      _ValidateNetmask(self.cfg, self.op.master_netmask)
731 7352d33b Thomas Thrainer
732 7352d33b Thomas Thrainer
    if self.op.diskparams:
733 7352d33b Thomas Thrainer
      for dt_params in self.op.diskparams.values():
734 7352d33b Thomas Thrainer
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
735 7352d33b Thomas Thrainer
      try:
736 7352d33b Thomas Thrainer
        utils.VerifyDictOptions(self.op.diskparams, constants.DISK_DT_DEFAULTS)
737 294254b1 Raffa Santi
        CheckDiskAccessModeValidity(self.op.diskparams)
738 7352d33b Thomas Thrainer
      except errors.OpPrereqError, err:
739 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
740 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
741 7352d33b Thomas Thrainer
742 7352d33b Thomas Thrainer
  def ExpandNames(self):
743 7352d33b Thomas Thrainer
    # FIXME: in the future maybe other cluster params won't require checking on
744 7352d33b Thomas Thrainer
    # all nodes to be modified.
745 7352d33b Thomas Thrainer
    # FIXME: This opcode changes cluster-wide settings. Is acquiring all
746 7352d33b Thomas Thrainer
    # resource locks the right thing, shouldn't it be the BGL instead?
747 7352d33b Thomas Thrainer
    self.needed_locks = {
748 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
749 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: locking.ALL_SET,
750 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
751 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
752 7352d33b Thomas Thrainer
    }
753 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
754 7352d33b Thomas Thrainer
755 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
756 7352d33b Thomas Thrainer
    """Build hooks env.
757 7352d33b Thomas Thrainer

758 7352d33b Thomas Thrainer
    """
759 7352d33b Thomas Thrainer
    return {
760 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
761 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
762 7352d33b Thomas Thrainer
      }
763 7352d33b Thomas Thrainer
764 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
765 7352d33b Thomas Thrainer
    """Build hooks nodes.
766 7352d33b Thomas Thrainer

767 7352d33b Thomas Thrainer
    """
768 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
769 7352d33b Thomas Thrainer
    return ([mn], [mn])
770 7352d33b Thomas Thrainer
771 1c3231aa Thomas Thrainer
  def _CheckVgName(self, node_uuids, enabled_disk_templates,
772 1bb99a33 Bernardo Dal Seno
                   new_enabled_disk_templates):
773 1bb99a33 Bernardo Dal Seno
    """Check the consistency of the vg name on all nodes and in case it gets
774 1bb99a33 Bernardo Dal Seno
       unset whether there are instances still using it.
775 7352d33b Thomas Thrainer

776 7352d33b Thomas Thrainer
    """
777 c89eb67d Helga Velroyen
    lvm_is_enabled = utils.IsLvmEnabled(enabled_disk_templates)
778 c89eb67d Helga Velroyen
    lvm_gets_enabled = utils.LvmGetsEnabled(enabled_disk_templates,
779 c89eb67d Helga Velroyen
                                            new_enabled_disk_templates)
780 c89eb67d Helga Velroyen
    current_vg_name = self.cfg.GetVGName()
781 c89eb67d Helga Velroyen
782 c89eb67d Helga Velroyen
    if self.op.vg_name == '':
783 c89eb67d Helga Velroyen
      if lvm_is_enabled:
784 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
785 c89eb67d Helga Velroyen
                                   " disk templates are or get enabled.")
786 c89eb67d Helga Velroyen
787 c89eb67d Helga Velroyen
    if self.op.vg_name is None:
788 c89eb67d Helga Velroyen
      if current_vg_name is None and lvm_is_enabled:
789 c89eb67d Helga Velroyen
        raise errors.OpPrereqError("Please specify a volume group when"
790 c89eb67d Helga Velroyen
                                   " enabling lvm-based disk-templates.")
791 c89eb67d Helga Velroyen
792 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
793 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_PLAIN):
794 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
795 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
796 7352d33b Thomas Thrainer
797 c89eb67d Helga Velroyen
    if (self.op.vg_name is not None and lvm_is_enabled) or \
798 c89eb67d Helga Velroyen
        (self.cfg.GetVGName() is not None and lvm_gets_enabled):
799 1c3231aa Thomas Thrainer
      self._CheckVgNameOnNodes(node_uuids)
800 1bb99a33 Bernardo Dal Seno
801 1c3231aa Thomas Thrainer
  def _CheckVgNameOnNodes(self, node_uuids):
802 1bb99a33 Bernardo Dal Seno
    """Check the status of the volume group on each node.
803 1bb99a33 Bernardo Dal Seno

804 1bb99a33 Bernardo Dal Seno
    """
805 1c3231aa Thomas Thrainer
    vglist = self.rpc.call_vg_list(node_uuids)
806 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
807 1c3231aa Thomas Thrainer
      msg = vglist[node_uuid].fail_msg
808 1bb99a33 Bernardo Dal Seno
      if msg:
809 1bb99a33 Bernardo Dal Seno
        # ignoring down node
810 1bb99a33 Bernardo Dal Seno
        self.LogWarning("Error while gathering data on node %s"
811 1c3231aa Thomas Thrainer
                        " (ignoring node): %s",
812 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
813 1bb99a33 Bernardo Dal Seno
        continue
814 1c3231aa Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist[node_uuid].payload,
815 1bb99a33 Bernardo Dal Seno
                                            self.op.vg_name,
816 1bb99a33 Bernardo Dal Seno
                                            constants.MIN_VG_SIZE)
817 1bb99a33 Bernardo Dal Seno
      if vgstatus:
818 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Error on node '%s': %s" %
819 1c3231aa Thomas Thrainer
                                   (self.cfg.GetNodeName(node_uuid), vgstatus),
820 1c3231aa Thomas Thrainer
                                   errors.ECODE_ENVIRON)
821 1bb99a33 Bernardo Dal Seno
822 c89eb67d Helga Velroyen
  @staticmethod
823 87e23f2d Helga Velroyen
  def _GetDiskTemplateSetsInner(op_enabled_disk_templates,
824 87e23f2d Helga Velroyen
                                old_enabled_disk_templates):
825 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
826 6e513917 Helga Velroyen

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

829 1bb99a33 Bernardo Dal Seno
    """
830 1bb99a33 Bernardo Dal Seno
    enabled_disk_templates = None
831 1bb99a33 Bernardo Dal Seno
    new_enabled_disk_templates = []
832 6e513917 Helga Velroyen
    disabled_disk_templates = []
833 c89eb67d Helga Velroyen
    if op_enabled_disk_templates:
834 c89eb67d Helga Velroyen
      enabled_disk_templates = op_enabled_disk_templates
835 1bb99a33 Bernardo Dal Seno
      new_enabled_disk_templates = \
836 5808df30 Helga Velroyen
        list(set(enabled_disk_templates)
837 5808df30 Helga Velroyen
             - set(old_enabled_disk_templates))
838 6e513917 Helga Velroyen
      disabled_disk_templates = \
839 5808df30 Helga Velroyen
        list(set(old_enabled_disk_templates)
840 5808df30 Helga Velroyen
             - set(enabled_disk_templates))
841 1bb99a33 Bernardo Dal Seno
    else:
842 c89eb67d Helga Velroyen
      enabled_disk_templates = old_enabled_disk_templates
843 6e513917 Helga Velroyen
    return (enabled_disk_templates, new_enabled_disk_templates,
844 6e513917 Helga Velroyen
            disabled_disk_templates)
845 1bb99a33 Bernardo Dal Seno
846 87e23f2d Helga Velroyen
  def _GetDiskTemplateSets(self, cluster):
847 6e513917 Helga Velroyen
    """Computes three sets of disk templates.
848 6e513917 Helga Velroyen

849 6e513917 Helga Velroyen
    The three sets are:
850 6e513917 Helga Velroyen
      - disk templates that will be enabled after this operation (no matter if
851 6e513917 Helga Velroyen
        they were enabled before or not)
852 6e513917 Helga Velroyen
      - disk templates that get enabled by this operation (thus haven't been
853 6e513917 Helga Velroyen
        enabled before.)
854 6e513917 Helga Velroyen
      - disk templates that get disabled by this operation
855 c89eb67d Helga Velroyen

856 c89eb67d Helga Velroyen
    """
857 87e23f2d Helga Velroyen
    return self._GetDiskTemplateSetsInner(self.op.enabled_disk_templates,
858 87e23f2d Helga Velroyen
                                          cluster.enabled_disk_templates)
859 c89eb67d Helga Velroyen
860 33a6464e Helga Velroyen
  def _CheckIpolicy(self, cluster, enabled_disk_templates):
861 1532b078 Helga Velroyen
    """Checks the ipolicy.
862 1532b078 Helga Velroyen

863 1532b078 Helga Velroyen
    @type cluster: C{objects.Cluster}
864 1532b078 Helga Velroyen
    @param cluster: the cluster's configuration
865 33a6464e Helga Velroyen
    @type enabled_disk_templates: list of string
866 33a6464e Helga Velroyen
    @param enabled_disk_templates: list of (possibly newly) enabled disk
867 33a6464e Helga Velroyen
      templates
868 1532b078 Helga Velroyen

869 1532b078 Helga Velroyen
    """
870 33a6464e Helga Velroyen
    # FIXME: write unit tests for this
871 1532b078 Helga Velroyen
    if self.op.ipolicy:
872 1532b078 Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
873 1532b078 Helga Velroyen
                                           group_policy=False)
874 1532b078 Helga Velroyen
875 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(self.new_ipolicy,
876 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
877 33a6464e Helga Velroyen
878 1532b078 Helga Velroyen
      all_instances = self.cfg.GetAllInstancesInfo().values()
879 1532b078 Helga Velroyen
      violations = set()
880 1532b078 Helga Velroyen
      for group in self.cfg.GetAllNodeGroupsInfo().values():
881 1532b078 Helga Velroyen
        instances = frozenset([inst for inst in all_instances
882 1532b078 Helga Velroyen
                               if compat.any(nuuid in group.members
883 1532b078 Helga Velroyen
                                             for nuuid in inst.all_nodes)])
884 1532b078 Helga Velroyen
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
885 1532b078 Helga Velroyen
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
886 1532b078 Helga Velroyen
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
887 1532b078 Helga Velroyen
                                           self.cfg)
888 1532b078 Helga Velroyen
        if new:
889 1532b078 Helga Velroyen
          violations.update(new)
890 1532b078 Helga Velroyen
891 1532b078 Helga Velroyen
      if violations:
892 1532b078 Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
893 1532b078 Helga Velroyen
                        " violate them: %s",
894 1532b078 Helga Velroyen
                        utils.CommaJoin(utils.NiceSort(violations)))
895 33a6464e Helga Velroyen
    else:
896 4e771a95 Helga Velroyen
      CheckIpolicyVsDiskTemplates(cluster.ipolicy,
897 4e771a95 Helga Velroyen
                                  enabled_disk_templates)
898 1532b078 Helga Velroyen
899 31ccfc0e Helga Velroyen
  def _CheckDrbdHelperOnNodes(self, drbd_helper, node_uuids):
900 31ccfc0e Helga Velroyen
    """Checks whether the set DRBD helper actually exists on the nodes.
901 31ccfc0e Helga Velroyen

902 31ccfc0e Helga Velroyen
    @type drbd_helper: string
903 31ccfc0e Helga Velroyen
    @param drbd_helper: path of the drbd usermode helper binary
904 31ccfc0e Helga Velroyen
    @type node_uuids: list of strings
905 31ccfc0e Helga Velroyen
    @param node_uuids: list of node UUIDs to check for the helper
906 31ccfc0e Helga Velroyen

907 31ccfc0e Helga Velroyen
    """
908 31ccfc0e Helga Velroyen
    # checks given drbd helper on all nodes
909 31ccfc0e Helga Velroyen
    helpers = self.rpc.call_drbd_helper(node_uuids)
910 31ccfc0e Helga Velroyen
    for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
911 31ccfc0e Helga Velroyen
      if ninfo.offline:
912 31ccfc0e Helga Velroyen
        self.LogInfo("Not checking drbd helper on offline node %s",
913 31ccfc0e Helga Velroyen
                     ninfo.name)
914 31ccfc0e Helga Velroyen
        continue
915 31ccfc0e Helga Velroyen
      msg = helpers[ninfo.uuid].fail_msg
916 31ccfc0e Helga Velroyen
      if msg:
917 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error checking drbd helper on node"
918 31ccfc0e Helga Velroyen
                                   " '%s': %s" % (ninfo.name, msg),
919 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
920 31ccfc0e Helga Velroyen
      node_helper = helpers[ninfo.uuid].payload
921 31ccfc0e Helga Velroyen
      if node_helper != drbd_helper:
922 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
923 31ccfc0e Helga Velroyen
                                   (ninfo.name, node_helper),
924 31ccfc0e Helga Velroyen
                                   errors.ECODE_ENVIRON)
925 31ccfc0e Helga Velroyen
926 31ccfc0e Helga Velroyen
  def _CheckDrbdHelper(self, node_uuids, drbd_enabled, drbd_gets_enabled):
927 7c577910 Helga Velroyen
    """Check the DRBD usermode helper.
928 1bb99a33 Bernardo Dal Seno

929 7c577910 Helga Velroyen
    @type node_uuids: list of strings
930 7c577910 Helga Velroyen
    @param node_uuids: a list of nodes' UUIDs
931 31ccfc0e Helga Velroyen
    @type drbd_enabled: boolean
932 31ccfc0e Helga Velroyen
    @param drbd_enabled: whether DRBD will be enabled after this operation
933 31ccfc0e Helga Velroyen
      (no matter if it was disabled before or not)
934 31ccfc0e Helga Velroyen
    @type drbd_gets_enabled: boolen
935 31ccfc0e Helga Velroyen
    @param drbd_gets_enabled: true if DRBD was disabled before this
936 31ccfc0e Helga Velroyen
      operation, but will be enabled afterwards
937 1bb99a33 Bernardo Dal Seno

938 1bb99a33 Bernardo Dal Seno
    """
939 31ccfc0e Helga Velroyen
    if self.op.drbd_helper == '':
940 31ccfc0e Helga Velroyen
      if drbd_enabled:
941 31ccfc0e Helga Velroyen
        raise errors.OpPrereqError("Cannot disable drbd helper while"
942 31ccfc0e Helga Velroyen
                                   " DRBD is enabled.")
943 cd3b4ff4 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(constants.DT_DRBD8):
944 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
945 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
946 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
947 7352d33b Thomas Thrainer
948 31ccfc0e Helga Velroyen
    else:
949 31ccfc0e Helga Velroyen
      if self.op.drbd_helper is not None and drbd_enabled:
950 31ccfc0e Helga Velroyen
        self._CheckDrbdHelperOnNodes(self.op.drbd_helper, node_uuids)
951 31ccfc0e Helga Velroyen
      else:
952 31ccfc0e Helga Velroyen
        if drbd_gets_enabled:
953 31ccfc0e Helga Velroyen
          current_drbd_helper = self.cfg.GetClusterInfo().drbd_usermode_helper
954 31ccfc0e Helga Velroyen
          if current_drbd_helper is not None:
955 31ccfc0e Helga Velroyen
            self._CheckDrbdHelperOnNodes(current_drbd_helper, node_uuids)
956 31ccfc0e Helga Velroyen
          else:
957 31ccfc0e Helga Velroyen
            raise errors.OpPrereqError("Cannot enable DRBD without a"
958 31ccfc0e Helga Velroyen
                                       " DRBD usermode helper set.")
959 7c577910 Helga Velroyen
960 c2e984e2 Helga Velroyen
  def _CheckInstancesOfDisabledDiskTemplates(
961 c2e984e2 Helga Velroyen
      self, disabled_disk_templates):
962 5808df30 Helga Velroyen
    """Check whether we try to disable a disk template that is in use.
963 c2e984e2 Helga Velroyen

964 c2e984e2 Helga Velroyen
    @type disabled_disk_templates: list of string
965 c2e984e2 Helga Velroyen
    @param disabled_disk_templates: list of disk templates that are going to
966 c2e984e2 Helga Velroyen
      be disabled by this operation
967 c2e984e2 Helga Velroyen

968 c2e984e2 Helga Velroyen
    """
969 c2e984e2 Helga Velroyen
    for disk_template in disabled_disk_templates:
970 c2e984e2 Helga Velroyen
      if self.cfg.HasAnyDiskOfType(disk_template):
971 c2e984e2 Helga Velroyen
        raise errors.OpPrereqError(
972 c2e984e2 Helga Velroyen
            "Cannot disable disk template '%s', because there is at least one"
973 c2e984e2 Helga Velroyen
            " instance using it." % disk_template)
974 c2e984e2 Helga Velroyen
975 7c577910 Helga Velroyen
  def CheckPrereq(self):
976 7c577910 Helga Velroyen
    """Check prerequisites.
977 7c577910 Helga Velroyen

978 7c577910 Helga Velroyen
    This checks whether the given params don't conflict and
979 7c577910 Helga Velroyen
    if the given volume group is valid.
980 7c577910 Helga Velroyen

981 7c577910 Helga Velroyen
    """
982 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
983 1bb99a33 Bernardo Dal Seno
    self.cluster = cluster = self.cfg.GetClusterInfo()
984 7352d33b Thomas Thrainer
985 1c3231aa Thomas Thrainer
    vm_capable_node_uuids = [node.uuid
986 1c3231aa Thomas Thrainer
                             for node in self.cfg.GetAllNodesInfo().values()
987 1c3231aa Thomas Thrainer
                             if node.uuid in node_uuids and node.vm_capable]
988 7352d33b Thomas Thrainer
989 6e513917 Helga Velroyen
    (enabled_disk_templates, new_enabled_disk_templates,
990 5808df30 Helga Velroyen
      disabled_disk_templates) = self._GetDiskTemplateSets(cluster)
991 6e513917 Helga Velroyen
    self._CheckInstancesOfDisabledDiskTemplates(disabled_disk_templates)
992 1bb99a33 Bernardo Dal Seno
993 1c3231aa Thomas Thrainer
    self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
994 1bb99a33 Bernardo Dal Seno
                      new_enabled_disk_templates)
995 7352d33b Thomas Thrainer
996 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
997 3039e2dc Helga Velroyen
      CheckFileStoragePathVsEnabledDiskTemplates(
998 3039e2dc Helga Velroyen
          self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
999 3039e2dc Helga Velroyen
1000 4e6cfd11 Helga Velroyen
    if self.op.shared_file_storage_dir is not None:
1001 4e6cfd11 Helga Velroyen
      CheckSharedFileStoragePathVsEnabledDiskTemplates(
1002 4e6cfd11 Helga Velroyen
          self.LogWarning, self.op.shared_file_storage_dir,
1003 4e6cfd11 Helga Velroyen
          enabled_disk_templates)
1004 4e6cfd11 Helga Velroyen
1005 31ccfc0e Helga Velroyen
    drbd_enabled = constants.DT_DRBD8 in enabled_disk_templates
1006 31ccfc0e Helga Velroyen
    drbd_gets_enabled = constants.DT_DRBD8 in new_enabled_disk_templates
1007 31ccfc0e Helga Velroyen
    self._CheckDrbdHelper(node_uuids, drbd_enabled, drbd_gets_enabled)
1008 7352d33b Thomas Thrainer
1009 7352d33b Thomas Thrainer
    # validate params changes
1010 7352d33b Thomas Thrainer
    if self.op.beparams:
1011 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
1012 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
1013 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
1014 7352d33b Thomas Thrainer
1015 7352d33b Thomas Thrainer
    if self.op.ndparams:
1016 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
1017 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
1018 7352d33b Thomas Thrainer
1019 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
1020 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
1021 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
1022 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
1023 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
1024 7352d33b Thomas Thrainer
1025 7352d33b Thomas Thrainer
    if self.op.hv_state:
1026 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
1027 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
1028 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
1029 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
1030 7352d33b Thomas Thrainer
1031 7352d33b Thomas Thrainer
    if self.op.disk_state:
1032 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
1033 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
1034 7352d33b Thomas Thrainer
      self.new_disk_state = \
1035 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
1036 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
1037 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
1038 7352d33b Thomas Thrainer
1039 33a6464e Helga Velroyen
    self._CheckIpolicy(cluster, enabled_disk_templates)
1040 7352d33b Thomas Thrainer
1041 7352d33b Thomas Thrainer
    if self.op.nicparams:
1042 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
1043 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
1044 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
1045 7352d33b Thomas Thrainer
      nic_errors = []
1046 7352d33b Thomas Thrainer
1047 7352d33b Thomas Thrainer
      # check all instances for consistency
1048 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
1049 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
1050 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
1051 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
1052 7352d33b Thomas Thrainer
1053 7352d33b Thomas Thrainer
          # check parameter syntax
1054 7352d33b Thomas Thrainer
          try:
1055 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
1056 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
1057 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
1058 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
1059 7352d33b Thomas Thrainer
1060 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
1061 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
1062 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
1063 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
1064 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
1065 7352d33b Thomas Thrainer
      if nic_errors:
1066 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
1067 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
1068 7352d33b Thomas Thrainer
1069 7352d33b Thomas Thrainer
    # hypervisor list/parameters
1070 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
1071 7352d33b Thomas Thrainer
    if self.op.hvparams:
1072 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
1073 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
1074 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
1075 7352d33b Thomas Thrainer
        else:
1076 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
1077 7352d33b Thomas Thrainer
1078 7352d33b Thomas Thrainer
    # disk template parameters
1079 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
1080 7352d33b Thomas Thrainer
    if self.op.diskparams:
1081 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
1082 f06af3ca Thomas Thrainer
        if dt_name not in self.new_diskparams:
1083 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
1084 7352d33b Thomas Thrainer
        else:
1085 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
1086 294254b1 Raffa Santi
      CheckDiskAccessModeConsistency(self.op.diskparams, self.cfg)
1087 7352d33b Thomas Thrainer
1088 7352d33b Thomas Thrainer
    # os hypervisor parameters
1089 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
1090 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1091 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
1092 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
1093 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
1094 7352d33b Thomas Thrainer
        else:
1095 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
1096 7352d33b Thomas Thrainer
            if hv_dict is None:
1097 7352d33b Thomas Thrainer
              # Delete if it exists
1098 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
1099 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
1100 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
1101 7352d33b Thomas Thrainer
            else:
1102 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
1103 7352d33b Thomas Thrainer
1104 7352d33b Thomas Thrainer
    # os parameters
1105 7352d33b Thomas Thrainer
    self.new_osp = objects.FillDict(cluster.osparams, {})
1106 7352d33b Thomas Thrainer
    if self.op.osparams:
1107 7352d33b Thomas Thrainer
      for os_name, osp in self.op.osparams.items():
1108 7352d33b Thomas Thrainer
        if os_name not in self.new_osp:
1109 7352d33b Thomas Thrainer
          self.new_osp[os_name] = {}
1110 7352d33b Thomas Thrainer
1111 5eacbcae Thomas Thrainer
        self.new_osp[os_name] = GetUpdatedParams(self.new_osp[os_name], osp,
1112 5eacbcae Thomas Thrainer
                                                 use_none=True)
1113 7352d33b Thomas Thrainer
1114 7352d33b Thomas Thrainer
        if not self.new_osp[os_name]:
1115 7352d33b Thomas Thrainer
          # we removed all parameters
1116 7352d33b Thomas Thrainer
          del self.new_osp[os_name]
1117 7352d33b Thomas Thrainer
        else:
1118 7352d33b Thomas Thrainer
          # check the parameter validity (remote check)
1119 5eacbcae Thomas Thrainer
          CheckOSParams(self, False, [self.cfg.GetMasterNode()],
1120 5eacbcae Thomas Thrainer
                        os_name, self.new_osp[os_name])
1121 7352d33b Thomas Thrainer
1122 7352d33b Thomas Thrainer
    # changes to the hypervisor list
1123 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1124 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
1125 7352d33b Thomas Thrainer
      for hv in self.hv_list:
1126 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
1127 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
1128 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
1129 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
1130 7352d33b Thomas Thrainer
        # enabled before
1131 7352d33b Thomas Thrainer
        if hv not in new_hvp:
1132 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
1133 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
1134 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
1135 7352d33b Thomas Thrainer
    else:
1136 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
1137 7352d33b Thomas Thrainer
1138 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
1139 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
1140 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
1141 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
1142 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
1143 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
1144 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
1145 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1146 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1147 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
1148 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, hv_params)
1149 7352d33b Thomas Thrainer
1150 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
1151 7352d33b Thomas Thrainer
1152 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1153 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
1154 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
1155 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
1156 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
1157 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1158 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
1159 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
1160 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
1161 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
1162 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
1163 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, new_osp)
1164 7352d33b Thomas Thrainer
1165 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
1166 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
1167 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
1168 7352d33b Thomas Thrainer
                                    os.path.isfile)
1169 7352d33b Thomas Thrainer
      if alloc_script is None:
1170 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
1171 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
1172 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
1173 7352d33b Thomas Thrainer
1174 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
1175 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
1176 7352d33b Thomas Thrainer
       are still in use by some instances.
1177 7352d33b Thomas Thrainer

1178 7352d33b Thomas Thrainer
    """
1179 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
1180 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1181 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
1182 7352d33b Thomas Thrainer
1183 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
1184 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
1185 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
1186 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
1187 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
1188 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
1189 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
1190 7352d33b Thomas Thrainer
1191 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
1192 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
1193 7352d33b Thomas Thrainer

1194 7352d33b Thomas Thrainer
    """
1195 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
1196 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
1197 7352d33b Thomas Thrainer
      if not new_volume:
1198 7352d33b Thomas Thrainer
        new_volume = None
1199 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
1200 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
1201 7352d33b Thomas Thrainer
      else:
1202 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
1203 7352d33b Thomas Thrainer
                    " state, not changing")
1204 1bb99a33 Bernardo Dal Seno
1205 3039e2dc Helga Velroyen
  def _SetFileStorageDir(self, feedback_fn):
1206 3039e2dc Helga Velroyen
    """Set the file storage directory.
1207 3039e2dc Helga Velroyen

1208 3039e2dc Helga Velroyen
    """
1209 3039e2dc Helga Velroyen
    if self.op.file_storage_dir is not None:
1210 3039e2dc Helga Velroyen
      if self.cluster.file_storage_dir == self.op.file_storage_dir:
1211 3039e2dc Helga Velroyen
        feedback_fn("Global file storage dir already set to value '%s'"
1212 3039e2dc Helga Velroyen
                    % self.cluster.file_storage_dir)
1213 3039e2dc Helga Velroyen
      else:
1214 3039e2dc Helga Velroyen
        self.cluster.file_storage_dir = self.op.file_storage_dir
1215 3039e2dc Helga Velroyen
1216 7c577910 Helga Velroyen
  def _SetDrbdHelper(self, feedback_fn):
1217 7c577910 Helga Velroyen
    """Set the DRBD usermode helper.
1218 1bb99a33 Bernardo Dal Seno

1219 1bb99a33 Bernardo Dal Seno
    """
1220 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1221 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1222 a794b8d7 Thomas Thrainer
        feedback_fn("Note that you specified a drbd user helper, but did not"
1223 a794b8d7 Thomas Thrainer
                    " enable the drbd disk template.")
1224 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1225 7352d33b Thomas Thrainer
      if not new_helper:
1226 7352d33b Thomas Thrainer
        new_helper = None
1227 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1228 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1229 7352d33b Thomas Thrainer
      else:
1230 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1231 7352d33b Thomas Thrainer
                    " not changing")
1232 7c577910 Helga Velroyen
1233 7c577910 Helga Velroyen
  def Exec(self, feedback_fn):
1234 7c577910 Helga Velroyen
    """Change the parameters of the cluster.
1235 7c577910 Helga Velroyen

1236 7c577910 Helga Velroyen
    """
1237 7c577910 Helga Velroyen
    if self.op.enabled_disk_templates:
1238 7c577910 Helga Velroyen
      self.cluster.enabled_disk_templates = \
1239 8b95dfdc Helga Velroyen
        list(self.op.enabled_disk_templates)
1240 7c577910 Helga Velroyen
1241 7c577910 Helga Velroyen
    self._SetVgName(feedback_fn)
1242 7c577910 Helga Velroyen
    self._SetFileStorageDir(feedback_fn)
1243 7c577910 Helga Velroyen
    self._SetDrbdHelper(feedback_fn)
1244 7c577910 Helga Velroyen
1245 7352d33b Thomas Thrainer
    if self.op.hvparams:
1246 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1247 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1248 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1249 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1250 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1251 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1252 7352d33b Thomas Thrainer
    if self.op.beparams:
1253 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1254 7352d33b Thomas Thrainer
    if self.op.nicparams:
1255 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1256 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1257 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1258 7352d33b Thomas Thrainer
    if self.op.osparams:
1259 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1260 7352d33b Thomas Thrainer
    if self.op.ndparams:
1261 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1262 7352d33b Thomas Thrainer
    if self.op.diskparams:
1263 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1264 7352d33b Thomas Thrainer
    if self.op.hv_state:
1265 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1266 7352d33b Thomas Thrainer
    if self.op.disk_state:
1267 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1268 7352d33b Thomas Thrainer
1269 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1270 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1271 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1272 5eacbcae Thomas Thrainer
      AdjustCandidatePool(self, [])
1273 7352d33b Thomas Thrainer
1274 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1275 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1276 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1277 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1278 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1279 7352d33b Thomas Thrainer
1280 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1281 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1282 75f2ff7d Michele Tartara
1283 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1284 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1285 7352d33b Thomas Thrainer
1286 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1287 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1288 7352d33b Thomas Thrainer
1289 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1290 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1291 7352d33b Thomas Thrainer
1292 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1293 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1294 7352d33b Thomas Thrainer
1295 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1296 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1297 7352d33b Thomas Thrainer
1298 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1299 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1300 7352d33b Thomas Thrainer
1301 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1302 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1303 7352d33b Thomas Thrainer
1304 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1305 7352d33b Thomas Thrainer
      desc += " OS list"
1306 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1307 7352d33b Thomas Thrainer
      for key, val in mods:
1308 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1309 7352d33b Thomas Thrainer
          if val in lst:
1310 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1311 7352d33b Thomas Thrainer
          else:
1312 7352d33b Thomas Thrainer
            lst.append(val)
1313 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1314 7352d33b Thomas Thrainer
          if val in lst:
1315 7352d33b Thomas Thrainer
            lst.remove(val)
1316 7352d33b Thomas Thrainer
          else:
1317 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1318 7352d33b Thomas Thrainer
        else:
1319 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1320 7352d33b Thomas Thrainer
1321 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1322 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1323 7352d33b Thomas Thrainer
1324 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1325 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1326 7352d33b Thomas Thrainer
1327 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1328 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1329 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1330 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1331 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1332 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1333 7352d33b Thomas Thrainer
                                                       master_params, ems)
1334 e5c92cfb Klaus Aehlig
      if not self.op.force:
1335 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1336 e5c92cfb Klaus Aehlig
      else:
1337 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1338 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1339 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1340 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1341 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1342 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1343 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1344 7352d33b Thomas Thrainer
1345 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1346 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1347 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1348 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1349 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1350 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1351 1c3231aa Thomas Thrainer
                 master_params.netdev)
1352 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1353 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1354 7352d33b Thomas Thrainer
1355 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1356 7352d33b Thomas Thrainer
1357 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1358 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1359 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1360 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1361 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1362 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1363 7352d33b Thomas Thrainer
                                                     master_params, ems)
1364 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1365 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1366 7352d33b Thomas Thrainer
1367 7352d33b Thomas Thrainer
1368 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1369 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1370 7352d33b Thomas Thrainer

1371 7352d33b Thomas Thrainer
  """
1372 7352d33b Thomas Thrainer
  REQ_BGL = False
1373 7352d33b Thomas Thrainer
1374 7352d33b Thomas Thrainer
  def ExpandNames(self):
1375 7352d33b Thomas Thrainer
    self.needed_locks = {}
1376 7352d33b Thomas Thrainer
1377 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1378 7352d33b Thomas Thrainer
    jobs = []
1379 7352d33b Thomas Thrainer
1380 7352d33b Thomas Thrainer
    if self.op.group_name:
1381 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1382 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1383 7352d33b Thomas Thrainer
    else:
1384 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1385 7352d33b Thomas Thrainer
1386 7352d33b Thomas Thrainer
      # Verify global configuration
1387 7352d33b Thomas Thrainer
      jobs.append([
1388 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1389 7352d33b Thomas Thrainer
        ])
1390 7352d33b Thomas Thrainer
1391 7352d33b Thomas Thrainer
      # Always depend on global verification
1392 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1393 7352d33b Thomas Thrainer
1394 7352d33b Thomas Thrainer
    jobs.extend(
1395 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1396 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1397 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1398 7352d33b Thomas Thrainer
      for group in groups)
1399 7352d33b Thomas Thrainer
1400 7352d33b Thomas Thrainer
    # Fix up all parameters
1401 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1402 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1403 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1404 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1405 7352d33b Thomas Thrainer
      try:
1406 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1407 7352d33b Thomas Thrainer
      except AttributeError:
1408 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1409 7352d33b Thomas Thrainer
1410 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1411 7352d33b Thomas Thrainer
1412 7352d33b Thomas Thrainer
1413 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1414 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1415 7352d33b Thomas Thrainer

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

1419 7352d33b Thomas Thrainer
  """
1420 7352d33b Thomas Thrainer
1421 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1422 7352d33b Thomas Thrainer
  ETYPE_ERROR = "ERROR"
1423 7352d33b Thomas Thrainer
  ETYPE_WARNING = "WARNING"
1424 7352d33b Thomas Thrainer
1425 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1426 7352d33b Thomas Thrainer
    """Format an error message.
1427 7352d33b Thomas Thrainer

1428 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1429 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1430 7352d33b Thomas Thrainer

1431 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1432 7352d33b Thomas Thrainer

1433 7352d33b Thomas Thrainer
    """
1434 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1435 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1436 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1437 7352d33b Thomas Thrainer
    # warning
1438 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1439 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1440 7352d33b Thomas Thrainer
    # first complete the msg
1441 7352d33b Thomas Thrainer
    if args:
1442 7352d33b Thomas Thrainer
      msg = msg % args
1443 7352d33b Thomas Thrainer
    # then format the whole message
1444 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1445 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1446 7352d33b Thomas Thrainer
    else:
1447 7352d33b Thomas Thrainer
      if item:
1448 7352d33b Thomas Thrainer
        item = " " + item
1449 7352d33b Thomas Thrainer
      else:
1450 7352d33b Thomas Thrainer
        item = ""
1451 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1452 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1453 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1454 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1455 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1456 7352d33b Thomas Thrainer
      self.bad = True
1457 7352d33b Thomas Thrainer
1458 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1459 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1460 7352d33b Thomas Thrainer

1461 7352d33b Thomas Thrainer
    """
1462 7352d33b Thomas Thrainer
    if (bool(cond)
1463 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1464 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1465 7352d33b Thomas Thrainer
1466 7352d33b Thomas Thrainer
1467 7352d33b Thomas Thrainer
def _VerifyCertificate(filename):
1468 7352d33b Thomas Thrainer
  """Verifies a certificate for L{LUClusterVerifyConfig}.
1469 7352d33b Thomas Thrainer

1470 7352d33b Thomas Thrainer
  @type filename: string
1471 7352d33b Thomas Thrainer
  @param filename: Path to PEM file
1472 7352d33b Thomas Thrainer

1473 7352d33b Thomas Thrainer
  """
1474 7352d33b Thomas Thrainer
  try:
1475 7352d33b Thomas Thrainer
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
1476 7352d33b Thomas Thrainer
                                           utils.ReadFile(filename))
1477 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
1478 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR,
1479 7352d33b Thomas Thrainer
            "Failed to load X509 certificate %s: %s" % (filename, err))
1480 7352d33b Thomas Thrainer
1481 7352d33b Thomas Thrainer
  (errcode, msg) = \
1482 7352d33b Thomas Thrainer
    utils.VerifyX509Certificate(cert, constants.SSL_CERT_EXPIRATION_WARN,
1483 7352d33b Thomas Thrainer
                                constants.SSL_CERT_EXPIRATION_ERROR)
1484 7352d33b Thomas Thrainer
1485 7352d33b Thomas Thrainer
  if msg:
1486 7352d33b Thomas Thrainer
    fnamemsg = "While verifying %s: %s" % (filename, msg)
1487 7352d33b Thomas Thrainer
  else:
1488 7352d33b Thomas Thrainer
    fnamemsg = None
1489 7352d33b Thomas Thrainer
1490 7352d33b Thomas Thrainer
  if errcode is None:
1491 7352d33b Thomas Thrainer
    return (None, fnamemsg)
1492 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_WARNING:
1493 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_WARNING, fnamemsg)
1494 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_ERROR:
1495 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR, fnamemsg)
1496 7352d33b Thomas Thrainer
1497 7352d33b Thomas Thrainer
  raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode)
1498 7352d33b Thomas Thrainer
1499 7352d33b Thomas Thrainer
1500 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1501 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1502 7352d33b Thomas Thrainer

1503 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1504 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1505 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1506 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1507 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1508 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1509 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1510 7352d33b Thomas Thrainer

1511 7352d33b Thomas Thrainer
  """
1512 7352d33b Thomas Thrainer
  hvp_data = []
1513 7352d33b Thomas Thrainer
1514 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1515 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1516 7352d33b Thomas Thrainer
1517 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1518 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1519 7352d33b Thomas Thrainer
      if hv_params:
1520 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1521 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1522 7352d33b Thomas Thrainer
1523 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1524 7352d33b Thomas Thrainer
  for instance in instances:
1525 7352d33b Thomas Thrainer
    if instance.hvparams:
1526 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1527 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1528 7352d33b Thomas Thrainer
1529 7352d33b Thomas Thrainer
  return hvp_data
1530 7352d33b Thomas Thrainer
1531 7352d33b Thomas Thrainer
1532 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1533 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1534 7352d33b Thomas Thrainer

1535 7352d33b Thomas Thrainer
  """
1536 7352d33b Thomas Thrainer
  REQ_BGL = False
1537 7352d33b Thomas Thrainer
1538 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1539 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1540 7352d33b Thomas Thrainer

1541 7352d33b Thomas Thrainer
    """
1542 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1543 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1544 7352d33b Thomas Thrainer
             (item, hv_name))
1545 7352d33b Thomas Thrainer
      try:
1546 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1547 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1548 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1549 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1550 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1551 7352d33b Thomas Thrainer
1552 7352d33b Thomas Thrainer
  def ExpandNames(self):
1553 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1554 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1555 7352d33b Thomas Thrainer
1556 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1557 7352d33b Thomas Thrainer
    """Check prerequisites.
1558 7352d33b Thomas Thrainer

1559 7352d33b Thomas Thrainer
    """
1560 7352d33b Thomas Thrainer
    # Retrieve all information
1561 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1562 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1563 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1564 7352d33b Thomas Thrainer
1565 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1566 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1567 7352d33b Thomas Thrainer

1568 7352d33b Thomas Thrainer
    """
1569 7352d33b Thomas Thrainer
    self.bad = False
1570 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1571 7352d33b Thomas Thrainer
1572 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1573 7352d33b Thomas Thrainer
1574 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1575 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1576 7352d33b Thomas Thrainer
1577 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1578 7352d33b Thomas Thrainer
1579 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1580 7352d33b Thomas Thrainer
      (errcode, msg) = _VerifyCertificate(cert_filename)
1581 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1582 7352d33b Thomas Thrainer
1583 a5b9e2f2 Thomas Thrainer
    self._ErrorIf(not utils.CanRead(constants.LUXID_USER,
1584 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1585 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1586 69ac3b74 Michele Tartara
                  None,
1587 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1588 a5b9e2f2 Thomas Thrainer
                    constants.LUXID_USER + " user")
1589 69ac3b74 Michele Tartara
1590 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1591 7352d33b Thomas Thrainer
1592 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1593 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1594 7352d33b Thomas Thrainer
1595 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1596 7352d33b Thomas Thrainer
1597 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1598 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1599 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1600 7352d33b Thomas Thrainer
1601 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1602 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1603 7352d33b Thomas Thrainer
1604 7352d33b Thomas Thrainer
    dangling_instances = {}
1605 7352d33b Thomas Thrainer
    no_node_instances = []
1606 7352d33b Thomas Thrainer
1607 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1608 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1609 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1610 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1611 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1612 7352d33b Thomas Thrainer
1613 7352d33b Thomas Thrainer
    pretty_dangling = [
1614 7352d33b Thomas Thrainer
        "%s (%s)" %
1615 7352d33b Thomas Thrainer
        (node.name,
1616 24e96ef6 Thomas Thrainer
         utils.CommaJoin(inst.name for
1617 24e96ef6 Thomas Thrainer
                         inst in dangling_instances.get(node.uuid, [])))
1618 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1619 7352d33b Thomas Thrainer
1620 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1621 7352d33b Thomas Thrainer
                  None,
1622 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1623 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1624 7352d33b Thomas Thrainer
1625 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1626 7352d33b Thomas Thrainer
                  None,
1627 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1628 24e96ef6 Thomas Thrainer
                  " %s", utils.CommaJoin(inst.name for
1629 24e96ef6 Thomas Thrainer
                                         inst in no_node_instances))
1630 7352d33b Thomas Thrainer
1631 7352d33b Thomas Thrainer
    return not self.bad
1632 7352d33b Thomas Thrainer
1633 7352d33b Thomas Thrainer
1634 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1635 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1636 7352d33b Thomas Thrainer

1637 7352d33b Thomas Thrainer
  """
1638 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1639 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1640 7352d33b Thomas Thrainer
  REQ_BGL = False
1641 7352d33b Thomas Thrainer
1642 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1643 7352d33b Thomas Thrainer
1644 7352d33b Thomas Thrainer
  class NodeImage(object):
1645 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1646 7352d33b Thomas Thrainer

1647 1c3231aa Thomas Thrainer
    @type uuid: string
1648 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1649 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1650 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1651 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1652 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1653 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1654 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1655 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1656 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1657 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1658 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1659 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1660 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1661 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1662 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1663 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1664 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1665 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1666 7352d33b Thomas Thrainer
    @type ghost: boolean
1667 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1668 7352d33b Thomas Thrainer
    @type os_fail: boolean
1669 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1670 7352d33b Thomas Thrainer
    @type oslist: list
1671 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1672 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1673 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1674 7352d33b Thomas Thrainer
    @type pv_min: float
1675 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1676 7352d33b Thomas Thrainer
    @type pv_max: float
1677 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1678 7352d33b Thomas Thrainer

1679 7352d33b Thomas Thrainer
    """
1680 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1681 1c3231aa Thomas Thrainer
      self.uuid = uuid
1682 7352d33b Thomas Thrainer
      self.volumes = {}
1683 7352d33b Thomas Thrainer
      self.instances = []
1684 7352d33b Thomas Thrainer
      self.pinst = []
1685 7352d33b Thomas Thrainer
      self.sinst = []
1686 7352d33b Thomas Thrainer
      self.sbp = {}
1687 7352d33b Thomas Thrainer
      self.mfree = 0
1688 7352d33b Thomas Thrainer
      self.dfree = 0
1689 7352d33b Thomas Thrainer
      self.offline = offline
1690 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1691 7352d33b Thomas Thrainer
      self.rpc_fail = False
1692 7352d33b Thomas Thrainer
      self.lvm_fail = False
1693 7352d33b Thomas Thrainer
      self.hyp_fail = False
1694 7352d33b Thomas Thrainer
      self.ghost = False
1695 7352d33b Thomas Thrainer
      self.os_fail = False
1696 7352d33b Thomas Thrainer
      self.oslist = {}
1697 7352d33b Thomas Thrainer
      self.pv_min = None
1698 7352d33b Thomas Thrainer
      self.pv_max = None
1699 7352d33b Thomas Thrainer
1700 7352d33b Thomas Thrainer
  def ExpandNames(self):
1701 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1702 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1703 7352d33b Thomas Thrainer
1704 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1705 da4a52a3 Thomas Thrainer
    inst_uuids = \
1706 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1707 7352d33b Thomas Thrainer
1708 7352d33b Thomas Thrainer
    self.needed_locks = {
1709 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1710 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1711 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1712 7352d33b Thomas Thrainer
1713 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1714 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1715 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1716 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1717 7352d33b Thomas Thrainer
      }
1718 7352d33b Thomas Thrainer
1719 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1720 7352d33b Thomas Thrainer
1721 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1722 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1723 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1724 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1725 7352d33b Thomas Thrainer
1726 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1727 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1728 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1729 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1730 7352d33b Thomas Thrainer
      # be locked.
1731 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1732 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1733 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1734 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1735 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1736 7352d33b Thomas Thrainer
1737 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1738 7352d33b Thomas Thrainer
1739 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1740 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1741 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1742 7352d33b Thomas Thrainer
1743 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1744 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1745 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1746 7352d33b Thomas Thrainer
1747 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1748 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1749 7352d33b Thomas Thrainer
1750 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
1751 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
1752 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
1753 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
1754 7352d33b Thomas Thrainer
1755 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
1756 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
1757 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
1758 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
1759 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
1760 7352d33b Thomas Thrainer
1761 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
1762 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
1763 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
1764 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
1765 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
1766 7352d33b Thomas Thrainer
1767 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1768 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1769 7352d33b Thomas Thrainer
1770 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
1771 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
1772 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
1773 7352d33b Thomas Thrainer
1774 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
1775 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
1776 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
1777 7352d33b Thomas Thrainer
1778 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1779 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1780 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1781 7352d33b Thomas Thrainer
1782 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1783 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1784 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
1785 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
1786 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
1787 7352d33b Thomas Thrainer
1788 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1789 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1790 7352d33b Thomas Thrainer
1791 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1792 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1793 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1794 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1795 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1796 7352d33b Thomas Thrainer
1797 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1798 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1799 7352d33b Thomas Thrainer

1800 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1801 7352d33b Thomas Thrainer
        mandatory fields
1802 7352d33b Thomas Thrainer
      - check ganeti version
1803 7352d33b Thomas Thrainer

1804 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1805 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1806 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1807 7352d33b Thomas Thrainer
    @rtype: boolean
1808 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1809 7352d33b Thomas Thrainer
         reasonable values in the respose)
1810 7352d33b Thomas Thrainer

1811 7352d33b Thomas Thrainer
    """
1812 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1813 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1814 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1815 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1816 7352d33b Thomas Thrainer
    if test:
1817 7352d33b Thomas Thrainer
      return False
1818 7352d33b Thomas Thrainer
1819 7352d33b Thomas Thrainer
    # compares ganeti version
1820 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1821 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1822 7352d33b Thomas Thrainer
    test = not (remote_version and
1823 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1824 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1825 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1826 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
1827 7352d33b Thomas Thrainer
    if test:
1828 7352d33b Thomas Thrainer
      return False
1829 7352d33b Thomas Thrainer
1830 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1831 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
1832 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
1833 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
1834 7352d33b Thomas Thrainer
    if test:
1835 7352d33b Thomas Thrainer
      return False
1836 7352d33b Thomas Thrainer
1837 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1838 7352d33b Thomas Thrainer
1839 7352d33b Thomas Thrainer
    # full package version
1840 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1841 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
1842 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1843 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1844 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1845 7352d33b Thomas Thrainer
1846 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1847 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1848 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1849 7352d33b Thomas Thrainer
        test = hv_result is not None
1850 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
1851 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1852 7352d33b Thomas Thrainer
1853 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1854 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1855 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1856 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
1857 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
1858 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
1859 7352d33b Thomas Thrainer
1860 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1861 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1862 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
1863 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
1864 7352d33b Thomas Thrainer
1865 7352d33b Thomas Thrainer
    return True
1866 7352d33b Thomas Thrainer
1867 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1868 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1869 7352d33b Thomas Thrainer
    """Check the node time.
1870 7352d33b Thomas Thrainer

1871 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1872 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1873 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1874 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1875 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1876 7352d33b Thomas Thrainer

1877 7352d33b Thomas Thrainer
    """
1878 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1879 7352d33b Thomas Thrainer
    try:
1880 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1881 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1882 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
1883 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
1884 7352d33b Thomas Thrainer
      return
1885 7352d33b Thomas Thrainer
1886 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1887 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1888 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1889 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1890 7352d33b Thomas Thrainer
    else:
1891 7352d33b Thomas Thrainer
      ntime_diff = None
1892 7352d33b Thomas Thrainer
1893 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
1894 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
1895 d0d7d7cf Thomas Thrainer
                  ntime_diff)
1896 7352d33b Thomas Thrainer
1897 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1898 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1899 7352d33b Thomas Thrainer

1900 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1901 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1902 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1903 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1904 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1905 7352d33b Thomas Thrainer
    @param nimg: node image
1906 7352d33b Thomas Thrainer

1907 7352d33b Thomas Thrainer
    """
1908 7352d33b Thomas Thrainer
    if vg_name is None:
1909 7352d33b Thomas Thrainer
      return
1910 7352d33b Thomas Thrainer
1911 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1912 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1913 7352d33b Thomas Thrainer
    test = not vglist
1914 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
1915 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
1916 7352d33b Thomas Thrainer
    if not test:
1917 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1918 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1919 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
1920 7352d33b Thomas Thrainer
1921 7352d33b Thomas Thrainer
    # Check PVs
1922 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
1923 7352d33b Thomas Thrainer
    for em in errmsgs:
1924 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
1925 7352d33b Thomas Thrainer
    if pvminmax is not None:
1926 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1927 7352d33b Thomas Thrainer
1928 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
1929 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
1930 1bb99a33 Bernardo Dal Seno

1931 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
1932 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
1933 1bb99a33 Bernardo Dal Seno
      node_verify call.
1934 1bb99a33 Bernardo Dal Seno

1935 1bb99a33 Bernardo Dal Seno
    """
1936 1bb99a33 Bernardo Dal Seno
    node_versions = {}
1937 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
1938 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
1939 1bb99a33 Bernardo Dal Seno
      version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
1940 1c3231aa Thomas Thrainer
      node_versions[node_uuid] = version
1941 1bb99a33 Bernardo Dal Seno
1942 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
1943 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
1944 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
1945 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
1946 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
1947 1bb99a33 Bernardo Dal Seno
1948 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
1949 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
1950 7352d33b Thomas Thrainer

1951 7352d33b Thomas Thrainer
    @type node_image: dict
1952 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
1953 7352d33b Thomas Thrainer
      L{NodeImage} objects
1954 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1955 7352d33b Thomas Thrainer

1956 7352d33b Thomas Thrainer
    """
1957 7352d33b Thomas Thrainer
    if vg_name is None:
1958 7352d33b Thomas Thrainer
      return
1959 7352d33b Thomas Thrainer
1960 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
1961 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
1962 7352d33b Thomas Thrainer
      return
1963 7352d33b Thomas Thrainer
1964 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
1965 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
1966 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
1967 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
1968 7352d33b Thomas Thrainer
    if not vals:
1969 7352d33b Thomas Thrainer
      return
1970 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
1971 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
1972 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
1973 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
1974 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
1975 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
1976 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
1977 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
1978 7352d33b Thomas Thrainer
1979 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
1980 7352d33b Thomas Thrainer
    """Check the node bridges.
1981 7352d33b Thomas Thrainer

1982 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1983 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1984 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1985 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
1986 7352d33b Thomas Thrainer

1987 7352d33b Thomas Thrainer
    """
1988 7352d33b Thomas Thrainer
    if not bridges:
1989 7352d33b Thomas Thrainer
      return
1990 7352d33b Thomas Thrainer
1991 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
1992 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
1993 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1994 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
1995 7352d33b Thomas Thrainer
    if not test:
1996 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
1997 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
1998 7352d33b Thomas Thrainer
1999 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
2000 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
2001 7352d33b Thomas Thrainer

2002 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2003 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2004 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2005 7352d33b Thomas Thrainer

2006 7352d33b Thomas Thrainer
    """
2007 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
2008 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2009 7352d33b Thomas Thrainer
                  "did not return user scripts information")
2010 7352d33b Thomas Thrainer
2011 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
2012 7352d33b Thomas Thrainer
    if not test:
2013 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
2014 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
2015 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
2016 7352d33b Thomas Thrainer
2017 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
2018 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
2019 7352d33b Thomas Thrainer

2020 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2021 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2022 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2023 7352d33b Thomas Thrainer

2024 7352d33b Thomas Thrainer
    """
2025 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
2026 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
2027 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
2028 7352d33b Thomas Thrainer
    if not test:
2029 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
2030 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
2031 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
2032 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
2033 7352d33b Thomas Thrainer
2034 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
2035 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2036 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
2037 7352d33b Thomas Thrainer
    if not test:
2038 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
2039 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
2040 7352d33b Thomas Thrainer
        for anode in nlist:
2041 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
2042 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
2043 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
2044 7352d33b Thomas Thrainer
2045 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
2046 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
2047 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
2048 7352d33b Thomas Thrainer
    if not test:
2049 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
2050 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
2051 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
2052 7352d33b Thomas Thrainer
        else:
2053 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
2054 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
2055 7352d33b Thomas Thrainer
2056 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
2057 7352d33b Thomas Thrainer
    """Verify an instance.
2058 7352d33b Thomas Thrainer

2059 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
2060 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
2061 7352d33b Thomas Thrainer
    state.
2062 7352d33b Thomas Thrainer

2063 7352d33b Thomas Thrainer
    """
2064 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
2065 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
2066 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
2067 7352d33b Thomas Thrainer
2068 7352d33b Thomas Thrainer
    node_vol_should = {}
2069 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
2070 7352d33b Thomas Thrainer
2071 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2072 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
2073 7352d33b Thomas Thrainer
                                                            self.group_info)
2074 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
2075 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
2076 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
2077 7352d33b Thomas Thrainer
2078 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
2079 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
2080 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
2081 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
2082 7352d33b Thomas Thrainer
        continue
2083 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
2084 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
2085 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
2086 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
2087 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
2088 7352d33b Thomas Thrainer
2089 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
2090 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
2091 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
2092 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
2093 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
2094 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
2095 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
2096 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
2097 7352d33b Thomas Thrainer
2098 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
2099 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
2100 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
2101 7352d33b Thomas Thrainer
2102 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
2103 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
2104 7352d33b Thomas Thrainer
      # node here
2105 7352d33b Thomas Thrainer
      snode = node_image[nname]
2106 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
2107 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
2108 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
2109 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
2110 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
2111 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
2112 9b0e86e2 Thomas Thrainer
2113 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
2114 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
2115 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
2116 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
2117 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
2118 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
2119 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
2120 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
2121 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
2122 9b0e86e2 Thomas Thrainer
2123 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
2124 d0d7d7cf Thomas Thrainer
2125 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
2126 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
2127 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
2128 da4a52a3 Thomas Thrainer
                  instance.name)
2129 da4a52a3 Thomas Thrainer
2130 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
2131 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
2132 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
2133 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
2134 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
2135 7352d33b Thomas Thrainer
2136 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
2137 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
2138 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
2139 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
2140 c69b147d Bernardo Dal Seno
        # node should have the flag set
2141 c69b147d Bernardo Dal Seno
        es_nodes = [n
2142 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
2143 c69b147d Bernardo Dal Seno
                    if es]
2144 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
2145 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
2146 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
2147 da4a52a3 Thomas Thrainer
                    instance.disk_template,
2148 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
2149 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
2150 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
2151 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
2152 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
2153 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
2154 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
2155 7352d33b Thomas Thrainer
2156 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2157 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
2158 7352d33b Thomas Thrainer
      instance_groups = {}
2159 7352d33b Thomas Thrainer
2160 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
2161 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
2162 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
2163 7352d33b Thomas Thrainer
2164 7352d33b Thomas Thrainer
      pretty_list = [
2165 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
2166 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
2167 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
2168 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
2169 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
2170 7352d33b Thomas Thrainer
                                   reverse=True)]
2171 7352d33b Thomas Thrainer
2172 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
2173 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
2174 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
2175 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
2176 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
2177 7352d33b Thomas Thrainer
2178 7352d33b Thomas Thrainer
    inst_nodes_offline = []
2179 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
2180 7352d33b Thomas Thrainer
      s_img = node_image[snode]
2181 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
2182 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
2183 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
2184 da4a52a3 Thomas Thrainer
                    instance.name)
2185 7352d33b Thomas Thrainer
2186 7352d33b Thomas Thrainer
      if s_img.offline:
2187 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
2188 7352d33b Thomas Thrainer
2189 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
2190 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
2191 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
2192 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
2193 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
2194 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
2195 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
2196 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
2197 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2198 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
2199 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
2200 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
2201 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
2202 7352d33b Thomas Thrainer
2203 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
2204 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
2205 7352d33b Thomas Thrainer

2206 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
2207 7352d33b Thomas Thrainer
    reported as unknown.
2208 7352d33b Thomas Thrainer

2209 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
2210 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
2211 7352d33b Thomas Thrainer

2212 7352d33b Thomas Thrainer
    """
2213 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2214 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
2215 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
2216 7352d33b Thomas Thrainer
        # skip non-healthy nodes
2217 7352d33b Thomas Thrainer
        continue
2218 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
2219 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
2220 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2221 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2222 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2223 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2224 7352d33b Thomas Thrainer
                      "volume %s is unknown", volume)
2225 7352d33b Thomas Thrainer
2226 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2227 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2228 7352d33b Thomas Thrainer

2229 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2230 7352d33b Thomas Thrainer
    instances it was primary for.
2231 7352d33b Thomas Thrainer

2232 7352d33b Thomas Thrainer
    """
2233 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2234 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2235 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2236 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2237 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2238 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2239 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2240 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2241 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2242 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2243 1c3231aa Thomas Thrainer
      if n_img.offline or \
2244 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2245 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2246 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2247 9fdb10be Thomas Thrainer
        # information from them; we already list instances living on such
2248 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2249 7352d33b Thomas Thrainer
        continue
2250 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2251 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2252 7352d33b Thomas Thrainer
        needed_mem = 0
2253 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2254 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2255 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2256 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2257 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2258 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2259 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2260 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2261 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2262 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2263 7352d33b Thomas Thrainer
2264 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2265 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2266 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2267 7352d33b Thomas Thrainer

2268 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2269 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2270 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2271 7352d33b Thomas Thrainer

2272 7352d33b Thomas Thrainer
    """
2273 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2274 7352d33b Thomas Thrainer
    files2nodefn = [
2275 7352d33b Thomas Thrainer
      (files_all, None),
2276 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2277 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2278 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2279 7352d33b Thomas Thrainer
      ]
2280 7352d33b Thomas Thrainer
2281 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2282 7352d33b Thomas Thrainer
    nodefiles = {}
2283 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2284 7352d33b Thomas Thrainer
      if fn is None:
2285 1c3231aa Thomas Thrainer
        filenodes = nodes
2286 7352d33b Thomas Thrainer
      else:
2287 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2288 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2289 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2290 7352d33b Thomas Thrainer
                       for filename in files)
2291 7352d33b Thomas Thrainer
2292 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2293 7352d33b Thomas Thrainer
2294 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2295 7352d33b Thomas Thrainer
    ignore_nodes = set()
2296 7352d33b Thomas Thrainer
2297 1c3231aa Thomas Thrainer
    for node in nodes:
2298 7352d33b Thomas Thrainer
      if node.offline:
2299 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2300 7352d33b Thomas Thrainer
        continue
2301 7352d33b Thomas Thrainer
2302 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2303 7352d33b Thomas Thrainer
2304 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2305 7352d33b Thomas Thrainer
        node_files = None
2306 7352d33b Thomas Thrainer
      else:
2307 7352d33b Thomas Thrainer
        fingerprints = nresult.payload.get(constants.NV_FILELIST, None)
2308 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2309 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2310 7352d33b Thomas Thrainer
        del fingerprints
2311 7352d33b Thomas Thrainer
2312 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2313 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2314 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2315 7352d33b Thomas Thrainer
      if test:
2316 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2317 7352d33b Thomas Thrainer
        continue
2318 7352d33b Thomas Thrainer
2319 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2320 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2321 7352d33b Thomas Thrainer
        assert filename in nodefiles
2322 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2323 7352d33b Thomas Thrainer
2324 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2325 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2326 7352d33b Thomas Thrainer
2327 7352d33b Thomas Thrainer
      # Nodes having the file
2328 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2329 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2330 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2331 7352d33b Thomas Thrainer
2332 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2333 7352d33b Thomas Thrainer
2334 7352d33b Thomas Thrainer
      # Nodes missing file
2335 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2336 7352d33b Thomas Thrainer
2337 7352d33b Thomas Thrainer
      if filename in files_opt:
2338 7352d33b Thomas Thrainer
        # All or no nodes
2339 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2340 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2341 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2342 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2343 1c3231aa Thomas Thrainer
                      filename,
2344 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2345 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2346 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2347 7352d33b Thomas Thrainer
      else:
2348 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2349 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2350 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2351 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2352 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2353 7352d33b Thomas Thrainer
2354 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2355 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2356 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2357 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2358 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2359 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2360 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2361 7352d33b Thomas Thrainer
2362 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2363 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2364 7352d33b Thomas Thrainer
      if test:
2365 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2366 1c3231aa Thomas Thrainer
                    (idx + 1,
2367 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2368 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2369 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2370 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2371 7352d33b Thomas Thrainer
      else:
2372 7352d33b Thomas Thrainer
        variants = []
2373 7352d33b Thomas Thrainer
2374 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2375 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2376 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2377 7352d33b Thomas Thrainer
2378 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbdHelper(self, ninfo, nresult, drbd_helper):
2379 9af7ece3 Helga Velroyen
    """Verify the drbd helper.
2380 7352d33b Thomas Thrainer

2381 7352d33b Thomas Thrainer
    """
2382 7352d33b Thomas Thrainer
    if drbd_helper:
2383 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2384 7352d33b Thomas Thrainer
      test = (helper_result is None)
2385 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2386 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2387 7352d33b Thomas Thrainer
      if helper_result:
2388 7352d33b Thomas Thrainer
        status, payload = helper_result
2389 7352d33b Thomas Thrainer
        test = not status
2390 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2391 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2392 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2393 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2394 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2395 7352d33b Thomas Thrainer
2396 9af7ece3 Helga Velroyen
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2397 9af7ece3 Helga Velroyen
                      drbd_map):
2398 9af7ece3 Helga Velroyen
    """Verifies and the node DRBD status.
2399 9af7ece3 Helga Velroyen

2400 9af7ece3 Helga Velroyen
    @type ninfo: L{objects.Node}
2401 9af7ece3 Helga Velroyen
    @param ninfo: the node to check
2402 9af7ece3 Helga Velroyen
    @param nresult: the remote results for the node
2403 9af7ece3 Helga Velroyen
    @param instanceinfo: the dict of instances
2404 9af7ece3 Helga Velroyen
    @param drbd_helper: the configured DRBD usermode helper
2405 9af7ece3 Helga Velroyen
    @param drbd_map: the DRBD map as returned by
2406 9af7ece3 Helga Velroyen
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2407 9af7ece3 Helga Velroyen

2408 9af7ece3 Helga Velroyen
    """
2409 9af7ece3 Helga Velroyen
    self._VerifyNodeDrbdHelper(ninfo, nresult, drbd_helper)
2410 9af7ece3 Helga Velroyen
2411 7352d33b Thomas Thrainer
    # compute the DRBD minors
2412 7352d33b Thomas Thrainer
    node_drbd = {}
2413 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2414 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2415 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2416 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2417 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2418 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2419 7352d33b Thomas Thrainer
        # unallocated minor in use)
2420 7352d33b Thomas Thrainer
      if test:
2421 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2422 7352d33b Thomas Thrainer
      else:
2423 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2424 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2425 7352d33b Thomas Thrainer
2426 7352d33b Thomas Thrainer
    # and now check them
2427 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2428 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2429 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2430 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2431 7352d33b Thomas Thrainer
    if test:
2432 7352d33b Thomas Thrainer
      # we cannot check drbd status
2433 7352d33b Thomas Thrainer
      return
2434 7352d33b Thomas Thrainer
2435 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2436 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2437 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2438 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2439 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2440 7352d33b Thomas Thrainer
    for minor in used_minors:
2441 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2442 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2443 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2444 7352d33b Thomas Thrainer
2445 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2446 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2447 7352d33b Thomas Thrainer

2448 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2449 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2450 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2451 7352d33b Thomas Thrainer
    @param nimg: the node image object
2452 7352d33b Thomas Thrainer

2453 7352d33b Thomas Thrainer
    """
2454 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2455 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2456 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2457 7352d33b Thomas Thrainer
                           for v in remote_os))
2458 7352d33b Thomas Thrainer
2459 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2460 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2461 7352d33b Thomas Thrainer
2462 7352d33b Thomas Thrainer
    nimg.os_fail = test
2463 7352d33b Thomas Thrainer
2464 7352d33b Thomas Thrainer
    if test:
2465 7352d33b Thomas Thrainer
      return
2466 7352d33b Thomas Thrainer
2467 7352d33b Thomas Thrainer
    os_dict = {}
2468 7352d33b Thomas Thrainer
2469 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2470 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2471 7352d33b Thomas Thrainer
2472 7352d33b Thomas Thrainer
      if name not in os_dict:
2473 7352d33b Thomas Thrainer
        os_dict[name] = []
2474 7352d33b Thomas Thrainer
2475 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2476 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2477 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2478 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2479 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2480 7352d33b Thomas Thrainer
2481 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2482 7352d33b Thomas Thrainer
2483 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2484 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2485 7352d33b Thomas Thrainer

2486 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2487 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2488 7352d33b Thomas Thrainer
    @param nimg: the node image object
2489 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2490 7352d33b Thomas Thrainer

2491 7352d33b Thomas Thrainer
    """
2492 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2493 7352d33b Thomas Thrainer
2494 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2495 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2496 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2497 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2498 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2499 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2500 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2501 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2502 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2503 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2504 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2505 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2506 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2507 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2508 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2509 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2510 7352d33b Thomas Thrainer
      if test:
2511 7352d33b Thomas Thrainer
        continue
2512 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2513 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2514 7352d33b Thomas Thrainer
      if not b_status:
2515 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2516 7352d33b Thomas Thrainer
        continue
2517 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2518 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2519 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2520 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2521 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2522 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2523 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2524 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2525 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2526 7352d33b Thomas Thrainer
2527 7352d33b Thomas Thrainer
    # check any missing OSes
2528 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2529 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2530 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2531 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2532 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2533 7352d33b Thomas Thrainer
2534 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2535 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2536 7352d33b Thomas Thrainer

2537 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2538 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2539 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2540 7352d33b Thomas Thrainer
    @type is_master: bool
2541 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2542 7352d33b Thomas Thrainer

2543 7352d33b Thomas Thrainer
    """
2544 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2545 7352d33b Thomas Thrainer
    if (is_master and
2546 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2547 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2548 7352d33b Thomas Thrainer
      try:
2549 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2550 7352d33b Thomas Thrainer
      except KeyError:
2551 7352d33b Thomas Thrainer
        # This should never happen
2552 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2553 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2554 7352d33b Thomas Thrainer
      else:
2555 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2556 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2557 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2558 7352d33b Thomas Thrainer
    else:
2559 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2560 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2561 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2562 7352d33b Thomas Thrainer
                    " paths")
2563 7352d33b Thomas Thrainer
2564 4b322a76 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult, file_disk_template,
2565 4b322a76 Helga Velroyen
                          verify_key, error_key):
2566 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2567 9c1c3c19 Helga Velroyen

2568 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2569 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2570 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2571 4b322a76 Helga Velroyen
    @type file_disk_template: string
2572 4b322a76 Helga Velroyen
    @param file_disk_template: file-based disk template, whose directory
2573 4b322a76 Helga Velroyen
        is supposed to be verified
2574 4b322a76 Helga Velroyen
    @type verify_key: string
2575 4b322a76 Helga Velroyen
    @param verify_key: key for the verification map of this file
2576 4b322a76 Helga Velroyen
        verification step
2577 4b322a76 Helga Velroyen
    @param error_key: error key to be added to the verification results
2578 4b322a76 Helga Velroyen
        in case something goes wrong in this verification step
2579 9c1c3c19 Helga Velroyen

2580 9c1c3c19 Helga Velroyen
    """
2581 4b322a76 Helga Velroyen
    assert (file_disk_template in
2582 4b322a76 Helga Velroyen
            utils.storage.GetDiskTemplatesOfStorageType(constants.ST_FILE))
2583 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2584 4b322a76 Helga Velroyen
    if cluster.IsDiskTemplateEnabled(file_disk_template):
2585 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2586 4b322a76 Helga Velroyen
          verify_key in nresult,
2587 4b322a76 Helga Velroyen
          error_key, ninfo.name,
2588 4b322a76 Helga Velroyen
          "The configured %s storage path is unusable: %s" %
2589 4b322a76 Helga Velroyen
          (file_disk_template, nresult.get(verify_key)))
2590 4b322a76 Helga Velroyen
2591 4b322a76 Helga Velroyen
  def _VerifyFileStoragePaths(self, ninfo, nresult):
2592 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2593 4b322a76 Helga Velroyen

2594 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2595 4b322a76 Helga Velroyen

2596 4b322a76 Helga Velroyen
    """
2597 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2598 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_FILE,
2599 4b322a76 Helga Velroyen
        constants.NV_FILE_STORAGE_PATH,
2600 4b322a76 Helga Velroyen
        constants.CV_ENODEFILESTORAGEPATHUNUSABLE)
2601 4b322a76 Helga Velroyen
2602 4b322a76 Helga Velroyen
  def _VerifySharedFileStoragePaths(self, ninfo, nresult):
2603 4b322a76 Helga Velroyen
    """Verifies (file) storage paths.
2604 4b322a76 Helga Velroyen

2605 4b322a76 Helga Velroyen
    @see: C{_VerifyStoragePaths}
2606 4b322a76 Helga Velroyen

2607 4b322a76 Helga Velroyen
    """
2608 4b322a76 Helga Velroyen
    self._VerifyStoragePaths(
2609 4b322a76 Helga Velroyen
        ninfo, nresult, constants.DT_SHARED_FILE,
2610 4b322a76 Helga Velroyen
        constants.NV_SHARED_FILE_STORAGE_PATH,
2611 4b322a76 Helga Velroyen
        constants.CV_ENODESHAREDFILESTORAGEPATHUNUSABLE)
2612 9c1c3c19 Helga Velroyen
2613 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2614 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2615 7352d33b Thomas Thrainer

2616 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2617 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2618 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2619 7352d33b Thomas Thrainer

2620 7352d33b Thomas Thrainer
    """
2621 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2622 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2623 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2624 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2625 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2626 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2627 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2628 7352d33b Thomas Thrainer
2629 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2630 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2631 7352d33b Thomas Thrainer

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

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

2641 7352d33b Thomas Thrainer
    """
2642 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2643 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2644 7352d33b Thomas Thrainer
    if vg_name is None:
2645 7352d33b Thomas Thrainer
      pass
2646 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2647 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2648 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2649 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2650 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2651 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2652 7352d33b Thomas Thrainer
    else:
2653 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2654 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2655 7352d33b Thomas Thrainer
2656 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2657 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2658 7352d33b Thomas Thrainer

2659 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2660 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2661 7352d33b Thomas Thrainer
    list key.
2662 7352d33b Thomas Thrainer

2663 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2664 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2665 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2666 7352d33b Thomas Thrainer
    @param nimg: the node image object
2667 7352d33b Thomas Thrainer

2668 7352d33b Thomas Thrainer
    """
2669 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2670 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2671 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2672 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2673 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2674 7352d33b Thomas Thrainer
    if test:
2675 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2676 7352d33b Thomas Thrainer
    else:
2677 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
2678 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
2679 7352d33b Thomas Thrainer
2680 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2681 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2682 7352d33b Thomas Thrainer

2683 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2684 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2685 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2686 7352d33b Thomas Thrainer
    @param nimg: the node image object
2687 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2688 7352d33b Thomas Thrainer

2689 7352d33b Thomas Thrainer
    """
2690 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2691 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2692 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2693 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2694 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
2695 7352d33b Thomas Thrainer
    if not test:
2696 7352d33b Thomas Thrainer
      try:
2697 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2698 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2699 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2700 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
2701 7352d33b Thomas Thrainer
2702 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2703 7352d33b Thomas Thrainer
    if vg_name is not None:
2704 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2705 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2706 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2707 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
2708 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
2709 7352d33b Thomas Thrainer
      if not test:
2710 7352d33b Thomas Thrainer
        try:
2711 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2712 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2713 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2714 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
2715 7352d33b Thomas Thrainer
2716 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
2717 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2718 7352d33b Thomas Thrainer

2719 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
2720 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
2721 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
2722 7352d33b Thomas Thrainer
    @param node_image: Node objects
2723 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
2724 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2725 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2726 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2727 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2728 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2729 7352d33b Thomas Thrainer

2730 7352d33b Thomas Thrainer
    """
2731 7352d33b Thomas Thrainer
    node_disks = {}
2732 0c3d9c7c Thomas Thrainer
    node_disks_dev_inst_only = {}
2733 7352d33b Thomas Thrainer
    diskless_instances = set()
2734 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2735 7352d33b Thomas Thrainer
2736 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
2737 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
2738 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
2739 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
2740 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
2741 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
2742 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
2743 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
2744 7352d33b Thomas Thrainer
2745 7352d33b Thomas Thrainer
      if not disks:
2746 7352d33b Thomas Thrainer
        # No need to collect data
2747 7352d33b Thomas Thrainer
        continue
2748 7352d33b Thomas Thrainer
2749 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
2750 7352d33b Thomas Thrainer
2751 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2752 0c3d9c7c Thomas Thrainer
      dev_inst_only = []
2753 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
2754 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
2755 da4a52a3 Thomas Thrainer
                                          self.cfg)
2756 0c3d9c7c Thomas Thrainer
        dev_inst_only.append((anno_disk, instanceinfo[inst_uuid]))
2757 7352d33b Thomas Thrainer
2758 0c3d9c7c Thomas Thrainer
      node_disks_dev_inst_only[nuuid] = dev_inst_only
2759 7352d33b Thomas Thrainer
2760 0c3d9c7c Thomas Thrainer
    assert len(node_disks) == len(node_disks_dev_inst_only)
2761 7352d33b Thomas Thrainer
2762 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2763 0c3d9c7c Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(
2764 0c3d9c7c Thomas Thrainer
               node_disks.keys(), node_disks_dev_inst_only)
2765 7352d33b Thomas Thrainer
2766 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2767 7352d33b Thomas Thrainer
2768 7352d33b Thomas Thrainer
    instdisk = {}
2769 7352d33b Thomas Thrainer
2770 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
2771 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
2772 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
2773 7352d33b Thomas Thrainer
2774 7352d33b Thomas Thrainer
      if nres.offline:
2775 7352d33b Thomas Thrainer
        # No data from this node
2776 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2777 7352d33b Thomas Thrainer
      else:
2778 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2779 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
2780 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
2781 7352d33b Thomas Thrainer
        if msg:
2782 7352d33b Thomas Thrainer
          # No data from this node
2783 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2784 7352d33b Thomas Thrainer
        else:
2785 7352d33b Thomas Thrainer
          data = []
2786 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2787 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2788 7352d33b Thomas Thrainer
              data.append(i)
2789 7352d33b Thomas Thrainer
            else:
2790 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2791 1c3231aa Thomas Thrainer
                              node.name, idx, i)
2792 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2793 7352d33b Thomas Thrainer
2794 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
2795 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
2796 da4a52a3 Thomas Thrainer
          .append(status)
2797 7352d33b Thomas Thrainer
2798 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2799 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
2800 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
2801 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
2802 7352d33b Thomas Thrainer
2803 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
2804 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
2805 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
2806 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
2807 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
2808 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
2809 7352d33b Thomas Thrainer
    if __debug__:
2810 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
2811 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
2812 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
2813 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
2814 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
2815 7352d33b Thomas Thrainer
2816 7352d33b Thomas Thrainer
    return instdisk
2817 7352d33b Thomas Thrainer
2818 7352d33b Thomas Thrainer
  @staticmethod
2819 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
2820 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
2821 7352d33b Thomas Thrainer

2822 7352d33b Thomas Thrainer
    """
2823 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
2824 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
2825 7352d33b Thomas Thrainer
                 not node.offline)]
2826 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
2827 7352d33b Thomas Thrainer
2828 7352d33b Thomas Thrainer
    return map(itertools.cycle,
2829 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
2830 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2831 7352d33b Thomas Thrainer
                                                  keyfunc)])
2832 7352d33b Thomas Thrainer
2833 7352d33b Thomas Thrainer
  @classmethod
2834 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2835 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
2836 7352d33b Thomas Thrainer

2837 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
2838 7352d33b Thomas Thrainer
    every other group.
2839 7352d33b Thomas Thrainer

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

2844 7352d33b Thomas Thrainer
    """
2845 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2846 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2847 7352d33b Thomas Thrainer
2848 7352d33b Thomas Thrainer
    return (online_nodes,
2849 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
2850 7352d33b Thomas Thrainer
                 for name in online_nodes))
2851 7352d33b Thomas Thrainer
2852 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
2853 7352d33b Thomas Thrainer
    """Build hooks env.
2854 7352d33b Thomas Thrainer

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

2858 7352d33b Thomas Thrainer
    """
2859 7352d33b Thomas Thrainer
    env = {
2860 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
2861 7352d33b Thomas Thrainer
      }
2862 7352d33b Thomas Thrainer
2863 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
2864 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
2865 7352d33b Thomas Thrainer
2866 7352d33b Thomas Thrainer
    return env
2867 7352d33b Thomas Thrainer
2868 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
2869 7352d33b Thomas Thrainer
    """Build hooks nodes.
2870 7352d33b Thomas Thrainer

2871 7352d33b Thomas Thrainer
    """
2872 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
2873 7352d33b Thomas Thrainer
2874 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
2875 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
2876 7352d33b Thomas Thrainer

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

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

3237 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
3238 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
3239 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
3240 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
3241 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
3242 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
3243 7352d33b Thomas Thrainer
        and hook results
3244 7352d33b Thomas Thrainer

3245 7352d33b Thomas Thrainer
    """
3246 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
3247 7352d33b Thomas Thrainer
    # and are only interested in their results
3248 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
3249 7352d33b Thomas Thrainer
      # empty node group
3250 7352d33b Thomas Thrainer
      pass
3251 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
3252 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
3253 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
3254 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
3255 7352d33b Thomas Thrainer
3256 7352d33b Thomas Thrainer
      for node_name in hooks_results:
3257 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
3258 7352d33b Thomas Thrainer
        msg = res.fail_msg
3259 7352d33b Thomas Thrainer
        test = msg and not res.offline
3260 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3261 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
3262 7352d33b Thomas Thrainer
        if res.offline or msg:
3263 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
3264 7352d33b Thomas Thrainer
          # an error.
3265 7352d33b Thomas Thrainer
          continue
3266 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
3267 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
3268 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
3269 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
3270 7352d33b Thomas Thrainer
          if test:
3271 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3272 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3273 7352d33b Thomas Thrainer
            lu_result = False
3274 7352d33b Thomas Thrainer
3275 7352d33b Thomas Thrainer
    return lu_result
3276 7352d33b Thomas Thrainer
3277 7352d33b Thomas Thrainer
3278 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3279 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3280 7352d33b Thomas Thrainer

3281 7352d33b Thomas Thrainer
  """
3282 7352d33b Thomas Thrainer
  REQ_BGL = False
3283 7352d33b Thomas Thrainer
3284 7352d33b Thomas Thrainer
  def ExpandNames(self):
3285 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3286 7352d33b Thomas Thrainer
    self.needed_locks = {
3287 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3288 7352d33b Thomas Thrainer
      }
3289 7352d33b Thomas Thrainer
3290 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3291 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3292 7352d33b Thomas Thrainer
3293 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3294 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3295 7352d33b Thomas Thrainer
                           for group in group_names])