Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ 7ad422ec

History | View | Annotate | Download (112.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

336 7352d33b Thomas Thrainer
  This is a very simple LU.
337 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

478 7352d33b Thomas Thrainer
    This only checks the optional instance list against the existing names.
479 7352d33b Thomas Thrainer

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

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

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

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

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

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

600 7352d33b Thomas Thrainer
  """
601 7352d33b Thomas Thrainer
  ip_family = cfg.GetPrimaryIPFamily()
602 7352d33b Thomas Thrainer
  try:
603 7352d33b Thomas Thrainer
    ipcls = netutils.IPAddress.GetClassFromIpFamily(ip_family)
604 7352d33b Thomas Thrainer
  except errors.ProgrammerError:
605 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid primary ip family: %s." %
606 7352d33b Thomas Thrainer
                               ip_family, errors.ECODE_INVAL)
607 7352d33b Thomas Thrainer
  if not ipcls.ValidateNetmask(netmask):
608 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("CIDR netmask (%s) not valid" %
609 7352d33b Thomas Thrainer
                               (netmask), errors.ECODE_INVAL)
610 7352d33b Thomas Thrainer
611 7352d33b Thomas Thrainer
612 7352d33b Thomas Thrainer
class LUClusterSetParams(LogicalUnit):
613 7352d33b Thomas Thrainer
  """Change the parameters of the cluster.
614 7352d33b Thomas Thrainer

615 7352d33b Thomas Thrainer
  """
616 7352d33b Thomas Thrainer
  HPATH = "cluster-modify"
617 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
618 7352d33b Thomas Thrainer
  REQ_BGL = False
619 7352d33b Thomas Thrainer
620 7352d33b Thomas Thrainer
  def CheckArguments(self):
621 7352d33b Thomas Thrainer
    """Check parameters
622 7352d33b Thomas Thrainer

623 7352d33b Thomas Thrainer
    """
624 7352d33b Thomas Thrainer
    if self.op.uid_pool:
625 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.uid_pool)
626 7352d33b Thomas Thrainer
627 7352d33b Thomas Thrainer
    if self.op.add_uids:
628 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.add_uids)
629 7352d33b Thomas Thrainer
630 7352d33b Thomas Thrainer
    if self.op.remove_uids:
631 7352d33b Thomas Thrainer
      uidpool.CheckUidPool(self.op.remove_uids)
632 7352d33b Thomas Thrainer
633 7352d33b Thomas Thrainer
    if self.op.master_netmask is not None:
634 7352d33b Thomas Thrainer
      _ValidateNetmask(self.cfg, self.op.master_netmask)
635 7352d33b Thomas Thrainer
636 7352d33b Thomas Thrainer
    if self.op.diskparams:
637 7352d33b Thomas Thrainer
      for dt_params in self.op.diskparams.values():
638 7352d33b Thomas Thrainer
        utils.ForceDictType(dt_params, constants.DISK_DT_TYPES)
639 7352d33b Thomas Thrainer
      try:
640 7352d33b Thomas Thrainer
        utils.VerifyDictOptions(self.op.diskparams, constants.DISK_DT_DEFAULTS)
641 7352d33b Thomas Thrainer
      except errors.OpPrereqError, err:
642 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
643 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
644 7352d33b Thomas Thrainer
645 7352d33b Thomas Thrainer
  def ExpandNames(self):
646 7352d33b Thomas Thrainer
    # FIXME: in the future maybe other cluster params won't require checking on
647 7352d33b Thomas Thrainer
    # all nodes to be modified.
648 7352d33b Thomas Thrainer
    # FIXME: This opcode changes cluster-wide settings. Is acquiring all
649 7352d33b Thomas Thrainer
    # resource locks the right thing, shouldn't it be the BGL instead?
650 7352d33b Thomas Thrainer
    self.needed_locks = {
651 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: locking.ALL_SET,
652 7352d33b Thomas Thrainer
      locking.LEVEL_INSTANCE: locking.ALL_SET,
653 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
654 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
655 7352d33b Thomas Thrainer
    }
656 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
657 7352d33b Thomas Thrainer
658 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
659 7352d33b Thomas Thrainer
    """Build hooks env.
660 7352d33b Thomas Thrainer

661 7352d33b Thomas Thrainer
    """
662 7352d33b Thomas Thrainer
    return {
663 7352d33b Thomas Thrainer
      "OP_TARGET": self.cfg.GetClusterName(),
664 7352d33b Thomas Thrainer
      "NEW_VG_NAME": self.op.vg_name,
665 7352d33b Thomas Thrainer
      }
666 7352d33b Thomas Thrainer
667 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
668 7352d33b Thomas Thrainer
    """Build hooks nodes.
669 7352d33b Thomas Thrainer

670 7352d33b Thomas Thrainer
    """
671 7352d33b Thomas Thrainer
    mn = self.cfg.GetMasterNode()
672 7352d33b Thomas Thrainer
    return ([mn], [mn])
673 7352d33b Thomas Thrainer
674 1c3231aa Thomas Thrainer
  def _CheckVgName(self, node_uuids, enabled_disk_templates,
675 1bb99a33 Bernardo Dal Seno
                   new_enabled_disk_templates):
676 1bb99a33 Bernardo Dal Seno
    """Check the consistency of the vg name on all nodes and in case it gets
677 1bb99a33 Bernardo Dal Seno
       unset whether there are instances still using it.
678 7352d33b Thomas Thrainer

679 7352d33b Thomas Thrainer
    """
680 7352d33b Thomas Thrainer
    if self.op.vg_name is not None and not self.op.vg_name:
681 7352d33b Thomas Thrainer
      if self.cfg.HasAnyDiskOfType(constants.LD_LV):
682 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
683 7352d33b Thomas Thrainer
                                   " instances exist", errors.ECODE_INVAL)
684 7352d33b Thomas Thrainer
685 1bb99a33 Bernardo Dal Seno
    if (self.op.vg_name is not None and
686 1bb99a33 Bernardo Dal Seno
        utils.IsLvmEnabled(enabled_disk_templates)) or \
687 1bb99a33 Bernardo Dal Seno
           (self.cfg.GetVGName() is not None and
688 1bb99a33 Bernardo Dal Seno
            utils.LvmGetsEnabled(enabled_disk_templates,
689 1bb99a33 Bernardo Dal Seno
                                 new_enabled_disk_templates)):
690 1c3231aa Thomas Thrainer
      self._CheckVgNameOnNodes(node_uuids)
691 1bb99a33 Bernardo Dal Seno
692 1c3231aa Thomas Thrainer
  def _CheckVgNameOnNodes(self, node_uuids):
693 1bb99a33 Bernardo Dal Seno
    """Check the status of the volume group on each node.
694 1bb99a33 Bernardo Dal Seno

695 1bb99a33 Bernardo Dal Seno
    """
696 1c3231aa Thomas Thrainer
    vglist = self.rpc.call_vg_list(node_uuids)
697 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
698 1c3231aa Thomas Thrainer
      msg = vglist[node_uuid].fail_msg
699 1bb99a33 Bernardo Dal Seno
      if msg:
700 1bb99a33 Bernardo Dal Seno
        # ignoring down node
701 1bb99a33 Bernardo Dal Seno
        self.LogWarning("Error while gathering data on node %s"
702 1c3231aa Thomas Thrainer
                        " (ignoring node): %s",
703 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
704 1bb99a33 Bernardo Dal Seno
        continue
705 1c3231aa Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist[node_uuid].payload,
706 1bb99a33 Bernardo Dal Seno
                                            self.op.vg_name,
707 1bb99a33 Bernardo Dal Seno
                                            constants.MIN_VG_SIZE)
708 1bb99a33 Bernardo Dal Seno
      if vgstatus:
709 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Error on node '%s': %s" %
710 1c3231aa Thomas Thrainer
                                   (self.cfg.GetNodeName(node_uuid), vgstatus),
711 1c3231aa Thomas Thrainer
                                   errors.ECODE_ENVIRON)
712 1bb99a33 Bernardo Dal Seno
713 1bb99a33 Bernardo Dal Seno
  def _GetEnabledDiskTemplates(self, cluster):
714 1bb99a33 Bernardo Dal Seno
    """Determines the enabled disk templates and the subset of disk templates
715 1bb99a33 Bernardo Dal Seno
       that are newly enabled by this operation.
716 1bb99a33 Bernardo Dal Seno

717 1bb99a33 Bernardo Dal Seno
    """
718 1bb99a33 Bernardo Dal Seno
    enabled_disk_templates = None
719 1bb99a33 Bernardo Dal Seno
    new_enabled_disk_templates = []
720 1bb99a33 Bernardo Dal Seno
    if self.op.enabled_disk_templates:
721 1bb99a33 Bernardo Dal Seno
      enabled_disk_templates = self.op.enabled_disk_templates
722 1bb99a33 Bernardo Dal Seno
      new_enabled_disk_templates = \
723 1bb99a33 Bernardo Dal Seno
        list(set(enabled_disk_templates)
724 1bb99a33 Bernardo Dal Seno
             - set(cluster.enabled_disk_templates))
725 1bb99a33 Bernardo Dal Seno
    else:
726 1bb99a33 Bernardo Dal Seno
      enabled_disk_templates = cluster.enabled_disk_templates
727 1bb99a33 Bernardo Dal Seno
    return (enabled_disk_templates, new_enabled_disk_templates)
728 1bb99a33 Bernardo Dal Seno
729 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
730 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
731 1bb99a33 Bernardo Dal Seno

732 1bb99a33 Bernardo Dal Seno
    This checks whether the given params don't conflict and
733 1bb99a33 Bernardo Dal Seno
    if the given volume group is valid.
734 1bb99a33 Bernardo Dal Seno

735 1bb99a33 Bernardo Dal Seno
    """
736 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None and not self.op.drbd_helper:
737 7352d33b Thomas Thrainer
      if self.cfg.HasAnyDiskOfType(constants.LD_DRBD8):
738 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot disable drbd helper while"
739 7352d33b Thomas Thrainer
                                   " drbd-based instances exist",
740 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
741 7352d33b Thomas Thrainer
742 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
743 1bb99a33 Bernardo Dal Seno
    self.cluster = cluster = self.cfg.GetClusterInfo()
744 7352d33b Thomas Thrainer
745 1c3231aa Thomas Thrainer
    vm_capable_node_uuids = [node.uuid
746 1c3231aa Thomas Thrainer
                             for node in self.cfg.GetAllNodesInfo().values()
747 1c3231aa Thomas Thrainer
                             if node.uuid in node_uuids and node.vm_capable]
748 7352d33b Thomas Thrainer
749 1bb99a33 Bernardo Dal Seno
    (enabled_disk_templates, new_enabled_disk_templates) = \
750 1bb99a33 Bernardo Dal Seno
      self._GetEnabledDiskTemplates(cluster)
751 1bb99a33 Bernardo Dal Seno
752 1c3231aa Thomas Thrainer
    self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
753 1bb99a33 Bernardo Dal Seno
                      new_enabled_disk_templates)
754 7352d33b Thomas Thrainer
755 7352d33b Thomas Thrainer
    if self.op.drbd_helper:
756 7352d33b Thomas Thrainer
      # checks given drbd helper on all nodes
757 1c3231aa Thomas Thrainer
      helpers = self.rpc.call_drbd_helper(node_uuids)
758 1c3231aa Thomas Thrainer
      for (_, ninfo) in self.cfg.GetMultiNodeInfo(node_uuids):
759 7352d33b Thomas Thrainer
        if ninfo.offline:
760 1c3231aa Thomas Thrainer
          self.LogInfo("Not checking drbd helper on offline node %s",
761 1c3231aa Thomas Thrainer
                       ninfo.name)
762 7352d33b Thomas Thrainer
          continue
763 1c3231aa Thomas Thrainer
        msg = helpers[ninfo.uuid].fail_msg
764 7352d33b Thomas Thrainer
        if msg:
765 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error checking drbd helper on node"
766 1c3231aa Thomas Thrainer
                                     " '%s': %s" % (ninfo.name, msg),
767 7352d33b Thomas Thrainer
                                     errors.ECODE_ENVIRON)
768 1c3231aa Thomas Thrainer
        node_helper = helpers[ninfo.uuid].payload
769 7352d33b Thomas Thrainer
        if node_helper != self.op.drbd_helper:
770 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
771 1c3231aa Thomas Thrainer
                                     (ninfo.name, node_helper),
772 1c3231aa Thomas Thrainer
                                     errors.ECODE_ENVIRON)
773 7352d33b Thomas Thrainer
774 7352d33b Thomas Thrainer
    # validate params changes
775 7352d33b Thomas Thrainer
    if self.op.beparams:
776 7352d33b Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
777 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
778 7352d33b Thomas Thrainer
      self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
779 7352d33b Thomas Thrainer
780 7352d33b Thomas Thrainer
    if self.op.ndparams:
781 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
782 7352d33b Thomas Thrainer
      self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
783 7352d33b Thomas Thrainer
784 7352d33b Thomas Thrainer
      # TODO: we need a more general way to handle resetting
785 7352d33b Thomas Thrainer
      # cluster-level parameters to default values
786 7352d33b Thomas Thrainer
      if self.new_ndparams["oob_program"] == "":
787 7352d33b Thomas Thrainer
        self.new_ndparams["oob_program"] = \
788 7352d33b Thomas Thrainer
            constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
789 7352d33b Thomas Thrainer
790 7352d33b Thomas Thrainer
    if self.op.hv_state:
791 5eacbcae Thomas Thrainer
      new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
792 5eacbcae Thomas Thrainer
                                           self.cluster.hv_state_static)
793 7352d33b Thomas Thrainer
      self.new_hv_state = dict((hv, cluster.SimpleFillHvState(values))
794 7352d33b Thomas Thrainer
                               for hv, values in new_hv_state.items())
795 7352d33b Thomas Thrainer
796 7352d33b Thomas Thrainer
    if self.op.disk_state:
797 5eacbcae Thomas Thrainer
      new_disk_state = MergeAndVerifyDiskState(self.op.disk_state,
798 5eacbcae Thomas Thrainer
                                               self.cluster.disk_state_static)
799 7352d33b Thomas Thrainer
      self.new_disk_state = \
800 7352d33b Thomas Thrainer
        dict((storage, dict((name, cluster.SimpleFillDiskState(values))
801 7352d33b Thomas Thrainer
                            for name, values in svalues.items()))
802 7352d33b Thomas Thrainer
             for storage, svalues in new_disk_state.items())
803 7352d33b Thomas Thrainer
804 7352d33b Thomas Thrainer
    if self.op.ipolicy:
805 5eacbcae Thomas Thrainer
      self.new_ipolicy = GetUpdatedIPolicy(cluster.ipolicy, self.op.ipolicy,
806 5eacbcae Thomas Thrainer
                                           group_policy=False)
807 7352d33b Thomas Thrainer
808 7352d33b Thomas Thrainer
      all_instances = self.cfg.GetAllInstancesInfo().values()
809 7352d33b Thomas Thrainer
      violations = set()
810 7352d33b Thomas Thrainer
      for group in self.cfg.GetAllNodeGroupsInfo().values():
811 7352d33b Thomas Thrainer
        instances = frozenset([inst for inst in all_instances
812 1c3231aa Thomas Thrainer
                               if compat.any(nuuid in group.members
813 1c3231aa Thomas Thrainer
                                             for nuuid in inst.all_nodes)])
814 7352d33b Thomas Thrainer
        new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
815 7352d33b Thomas Thrainer
        ipol = masterd.instance.CalculateGroupIPolicy(cluster, group)
816 da4a52a3 Thomas Thrainer
        new = ComputeNewInstanceViolations(ipol, new_ipolicy, instances,
817 da4a52a3 Thomas Thrainer
                                           self.cfg)
818 7352d33b Thomas Thrainer
        if new:
819 7352d33b Thomas Thrainer
          violations.update(new)
820 7352d33b Thomas Thrainer
821 7352d33b Thomas Thrainer
      if violations:
822 7352d33b Thomas Thrainer
        self.LogWarning("After the ipolicy change the following instances"
823 7352d33b Thomas Thrainer
                        " violate them: %s",
824 7352d33b Thomas Thrainer
                        utils.CommaJoin(utils.NiceSort(violations)))
825 7352d33b Thomas Thrainer
826 7352d33b Thomas Thrainer
    if self.op.nicparams:
827 7352d33b Thomas Thrainer
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
828 7352d33b Thomas Thrainer
      self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
829 7352d33b Thomas Thrainer
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
830 7352d33b Thomas Thrainer
      nic_errors = []
831 7352d33b Thomas Thrainer
832 7352d33b Thomas Thrainer
      # check all instances for consistency
833 7352d33b Thomas Thrainer
      for instance in self.cfg.GetAllInstancesInfo().values():
834 7352d33b Thomas Thrainer
        for nic_idx, nic in enumerate(instance.nics):
835 7352d33b Thomas Thrainer
          params_copy = copy.deepcopy(nic.nicparams)
836 7352d33b Thomas Thrainer
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
837 7352d33b Thomas Thrainer
838 7352d33b Thomas Thrainer
          # check parameter syntax
839 7352d33b Thomas Thrainer
          try:
840 7352d33b Thomas Thrainer
            objects.NIC.CheckParameterSyntax(params_filled)
841 7352d33b Thomas Thrainer
          except errors.ConfigurationError, err:
842 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: %s" %
843 7352d33b Thomas Thrainer
                              (instance.name, nic_idx, err))
844 7352d33b Thomas Thrainer
845 7352d33b Thomas Thrainer
          # if we're moving instances to routed, check that they have an ip
846 7352d33b Thomas Thrainer
          target_mode = params_filled[constants.NIC_MODE]
847 7352d33b Thomas Thrainer
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
848 7352d33b Thomas Thrainer
            nic_errors.append("Instance %s, nic/%d: routed NIC with no ip"
849 7352d33b Thomas Thrainer
                              " address" % (instance.name, nic_idx))
850 7352d33b Thomas Thrainer
      if nic_errors:
851 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
852 7352d33b Thomas Thrainer
                                   "\n".join(nic_errors), errors.ECODE_INVAL)
853 7352d33b Thomas Thrainer
854 7352d33b Thomas Thrainer
    # hypervisor list/parameters
855 7352d33b Thomas Thrainer
    self.new_hvparams = new_hvp = objects.FillDict(cluster.hvparams, {})
856 7352d33b Thomas Thrainer
    if self.op.hvparams:
857 7352d33b Thomas Thrainer
      for hv_name, hv_dict in self.op.hvparams.items():
858 7352d33b Thomas Thrainer
        if hv_name not in self.new_hvparams:
859 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name] = hv_dict
860 7352d33b Thomas Thrainer
        else:
861 7352d33b Thomas Thrainer
          self.new_hvparams[hv_name].update(hv_dict)
862 7352d33b Thomas Thrainer
863 7352d33b Thomas Thrainer
    # disk template parameters
864 7352d33b Thomas Thrainer
    self.new_diskparams = objects.FillDict(cluster.diskparams, {})
865 7352d33b Thomas Thrainer
    if self.op.diskparams:
866 7352d33b Thomas Thrainer
      for dt_name, dt_params in self.op.diskparams.items():
867 7352d33b Thomas Thrainer
        if dt_name not in self.op.diskparams:
868 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name] = dt_params
869 7352d33b Thomas Thrainer
        else:
870 7352d33b Thomas Thrainer
          self.new_diskparams[dt_name].update(dt_params)
871 7352d33b Thomas Thrainer
872 7352d33b Thomas Thrainer
    # os hypervisor parameters
873 7352d33b Thomas Thrainer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
874 7352d33b Thomas Thrainer
    if self.op.os_hvp:
875 7352d33b Thomas Thrainer
      for os_name, hvs in self.op.os_hvp.items():
876 7352d33b Thomas Thrainer
        if os_name not in self.new_os_hvp:
877 7352d33b Thomas Thrainer
          self.new_os_hvp[os_name] = hvs
878 7352d33b Thomas Thrainer
        else:
879 7352d33b Thomas Thrainer
          for hv_name, hv_dict in hvs.items():
880 7352d33b Thomas Thrainer
            if hv_dict is None:
881 7352d33b Thomas Thrainer
              # Delete if it exists
882 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name].pop(hv_name, None)
883 7352d33b Thomas Thrainer
            elif hv_name not in self.new_os_hvp[os_name]:
884 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name] = hv_dict
885 7352d33b Thomas Thrainer
            else:
886 7352d33b Thomas Thrainer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
887 7352d33b Thomas Thrainer
888 7352d33b Thomas Thrainer
    # os parameters
889 7352d33b Thomas Thrainer
    self.new_osp = objects.FillDict(cluster.osparams, {})
890 7352d33b Thomas Thrainer
    if self.op.osparams:
891 7352d33b Thomas Thrainer
      for os_name, osp in self.op.osparams.items():
892 7352d33b Thomas Thrainer
        if os_name not in self.new_osp:
893 7352d33b Thomas Thrainer
          self.new_osp[os_name] = {}
894 7352d33b Thomas Thrainer
895 5eacbcae Thomas Thrainer
        self.new_osp[os_name] = GetUpdatedParams(self.new_osp[os_name], osp,
896 5eacbcae Thomas Thrainer
                                                 use_none=True)
897 7352d33b Thomas Thrainer
898 7352d33b Thomas Thrainer
        if not self.new_osp[os_name]:
899 7352d33b Thomas Thrainer
          # we removed all parameters
900 7352d33b Thomas Thrainer
          del self.new_osp[os_name]
901 7352d33b Thomas Thrainer
        else:
902 7352d33b Thomas Thrainer
          # check the parameter validity (remote check)
903 5eacbcae Thomas Thrainer
          CheckOSParams(self, False, [self.cfg.GetMasterNode()],
904 5eacbcae Thomas Thrainer
                        os_name, self.new_osp[os_name])
905 7352d33b Thomas Thrainer
906 7352d33b Thomas Thrainer
    # changes to the hypervisor list
907 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
908 7352d33b Thomas Thrainer
      self.hv_list = self.op.enabled_hypervisors
909 7352d33b Thomas Thrainer
      for hv in self.hv_list:
910 7352d33b Thomas Thrainer
        # if the hypervisor doesn't already exist in the cluster
911 7352d33b Thomas Thrainer
        # hvparams, we initialize it to empty, and then (in both
912 7352d33b Thomas Thrainer
        # cases) we make sure to fill the defaults, as we might not
913 7352d33b Thomas Thrainer
        # have a complete defaults list if the hypervisor wasn't
914 7352d33b Thomas Thrainer
        # enabled before
915 7352d33b Thomas Thrainer
        if hv not in new_hvp:
916 7352d33b Thomas Thrainer
          new_hvp[hv] = {}
917 7352d33b Thomas Thrainer
        new_hvp[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], new_hvp[hv])
918 7352d33b Thomas Thrainer
        utils.ForceDictType(new_hvp[hv], constants.HVS_PARAMETER_TYPES)
919 7352d33b Thomas Thrainer
    else:
920 7352d33b Thomas Thrainer
      self.hv_list = cluster.enabled_hypervisors
921 7352d33b Thomas Thrainer
922 7352d33b Thomas Thrainer
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
923 7352d33b Thomas Thrainer
      # either the enabled list has changed, or the parameters have, validate
924 7352d33b Thomas Thrainer
      for hv_name, hv_params in self.new_hvparams.items():
925 7352d33b Thomas Thrainer
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
926 7352d33b Thomas Thrainer
            (self.op.enabled_hypervisors and
927 7352d33b Thomas Thrainer
             hv_name in self.op.enabled_hypervisors)):
928 7352d33b Thomas Thrainer
          # either this is a new hypervisor, or its parameters have changed
929 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
930 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
931 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(hv_params)
932 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, hv_params)
933 7352d33b Thomas Thrainer
934 7352d33b Thomas Thrainer
    self._CheckDiskTemplateConsistency()
935 7352d33b Thomas Thrainer
936 7352d33b Thomas Thrainer
    if self.op.os_hvp:
937 7352d33b Thomas Thrainer
      # no need to check any newly-enabled hypervisors, since the
938 7352d33b Thomas Thrainer
      # defaults have already been checked in the above code-block
939 7352d33b Thomas Thrainer
      for os_name, os_hvp in self.new_os_hvp.items():
940 7352d33b Thomas Thrainer
        for hv_name, hv_params in os_hvp.items():
941 7352d33b Thomas Thrainer
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
942 7352d33b Thomas Thrainer
          # we need to fill in the new os_hvp on top of the actual hv_p
943 7352d33b Thomas Thrainer
          cluster_defaults = self.new_hvparams.get(hv_name, {})
944 7352d33b Thomas Thrainer
          new_osp = objects.FillDict(cluster_defaults, hv_params)
945 7352d33b Thomas Thrainer
          hv_class = hypervisor.GetHypervisorClass(hv_name)
946 7352d33b Thomas Thrainer
          hv_class.CheckParameterSyntax(new_osp)
947 1c3231aa Thomas Thrainer
          CheckHVParams(self, node_uuids, hv_name, new_osp)
948 7352d33b Thomas Thrainer
949 7352d33b Thomas Thrainer
    if self.op.default_iallocator:
950 7352d33b Thomas Thrainer
      alloc_script = utils.FindFile(self.op.default_iallocator,
951 7352d33b Thomas Thrainer
                                    constants.IALLOCATOR_SEARCH_PATH,
952 7352d33b Thomas Thrainer
                                    os.path.isfile)
953 7352d33b Thomas Thrainer
      if alloc_script is None:
954 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Invalid default iallocator script '%s'"
955 7352d33b Thomas Thrainer
                                   " specified" % self.op.default_iallocator,
956 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
957 7352d33b Thomas Thrainer
958 7352d33b Thomas Thrainer
  def _CheckDiskTemplateConsistency(self):
959 7352d33b Thomas Thrainer
    """Check whether the disk templates that are going to be disabled
960 7352d33b Thomas Thrainer
       are still in use by some instances.
961 7352d33b Thomas Thrainer

962 7352d33b Thomas Thrainer
    """
963 7352d33b Thomas Thrainer
    if self.op.enabled_disk_templates:
964 7352d33b Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
965 7352d33b Thomas Thrainer
      instances = self.cfg.GetAllInstancesInfo()
966 7352d33b Thomas Thrainer
967 7352d33b Thomas Thrainer
      disk_templates_to_remove = set(cluster.enabled_disk_templates) \
968 7352d33b Thomas Thrainer
        - set(self.op.enabled_disk_templates)
969 7352d33b Thomas Thrainer
      for instance in instances.itervalues():
970 7352d33b Thomas Thrainer
        if instance.disk_template in disk_templates_to_remove:
971 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Cannot disable disk template '%s',"
972 7352d33b Thomas Thrainer
                                     " because instance '%s' is using it." %
973 7352d33b Thomas Thrainer
                                     (instance.disk_template, instance.name))
974 7352d33b Thomas Thrainer
975 1bb99a33 Bernardo Dal Seno
  def _SetVgName(self, feedback_fn):
976 1bb99a33 Bernardo Dal Seno
    """Determines and sets the new volume group name.
977 7352d33b Thomas Thrainer

978 7352d33b Thomas Thrainer
    """
979 7352d33b Thomas Thrainer
    if self.op.vg_name is not None:
980 1bb99a33 Bernardo Dal Seno
      if self.op.vg_name and not \
981 1bb99a33 Bernardo Dal Seno
           utils.IsLvmEnabled(self.cluster.enabled_disk_templates):
982 1bb99a33 Bernardo Dal Seno
        feedback_fn("Note that you specified a volume group, but did not"
983 1bb99a33 Bernardo Dal Seno
                    " enable any lvm disk template.")
984 7352d33b Thomas Thrainer
      new_volume = self.op.vg_name
985 7352d33b Thomas Thrainer
      if not new_volume:
986 1bb99a33 Bernardo Dal Seno
        if utils.IsLvmEnabled(self.cluster.enabled_disk_templates):
987 1bb99a33 Bernardo Dal Seno
          raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
988 1bb99a33 Bernardo Dal Seno
                                     " disk templates are enabled.")
989 7352d33b Thomas Thrainer
        new_volume = None
990 7352d33b Thomas Thrainer
      if new_volume != self.cfg.GetVGName():
991 7352d33b Thomas Thrainer
        self.cfg.SetVGName(new_volume)
992 7352d33b Thomas Thrainer
      else:
993 7352d33b Thomas Thrainer
        feedback_fn("Cluster LVM configuration already in desired"
994 7352d33b Thomas Thrainer
                    " state, not changing")
995 1bb99a33 Bernardo Dal Seno
    else:
996 1bb99a33 Bernardo Dal Seno
      if utils.IsLvmEnabled(self.cluster.enabled_disk_templates) and \
997 1bb99a33 Bernardo Dal Seno
          not self.cfg.GetVGName():
998 1bb99a33 Bernardo Dal Seno
        raise errors.OpPrereqError("Please specify a volume group when"
999 1bb99a33 Bernardo Dal Seno
                                   " enabling lvm-based disk-templates.")
1000 1bb99a33 Bernardo Dal Seno
1001 1bb99a33 Bernardo Dal Seno
  def Exec(self, feedback_fn):
1002 1bb99a33 Bernardo Dal Seno
    """Change the parameters of the cluster.
1003 1bb99a33 Bernardo Dal Seno

1004 1bb99a33 Bernardo Dal Seno
    """
1005 1bb99a33 Bernardo Dal Seno
    if self.op.enabled_disk_templates:
1006 1bb99a33 Bernardo Dal Seno
      self.cluster.enabled_disk_templates = \
1007 1bb99a33 Bernardo Dal Seno
        list(set(self.op.enabled_disk_templates))
1008 1bb99a33 Bernardo Dal Seno
1009 1bb99a33 Bernardo Dal Seno
    self._SetVgName(feedback_fn)
1010 1bb99a33 Bernardo Dal Seno
1011 7352d33b Thomas Thrainer
    if self.op.drbd_helper is not None:
1012 1bb99a33 Bernardo Dal Seno
      if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
1013 1bb99a33 Bernardo Dal Seno
        feedback_fn("Note that you specified a drbd user helper, but did"
1014 1bb99a33 Bernardo Dal Seno
                    " enabled the drbd disk template.")
1015 7352d33b Thomas Thrainer
      new_helper = self.op.drbd_helper
1016 7352d33b Thomas Thrainer
      if not new_helper:
1017 7352d33b Thomas Thrainer
        new_helper = None
1018 7352d33b Thomas Thrainer
      if new_helper != self.cfg.GetDRBDHelper():
1019 7352d33b Thomas Thrainer
        self.cfg.SetDRBDHelper(new_helper)
1020 7352d33b Thomas Thrainer
      else:
1021 7352d33b Thomas Thrainer
        feedback_fn("Cluster DRBD helper already in desired state,"
1022 7352d33b Thomas Thrainer
                    " not changing")
1023 7352d33b Thomas Thrainer
    if self.op.hvparams:
1024 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1025 7352d33b Thomas Thrainer
    if self.op.os_hvp:
1026 7352d33b Thomas Thrainer
      self.cluster.os_hvp = self.new_os_hvp
1027 7352d33b Thomas Thrainer
    if self.op.enabled_hypervisors is not None:
1028 7352d33b Thomas Thrainer
      self.cluster.hvparams = self.new_hvparams
1029 7352d33b Thomas Thrainer
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
1030 7352d33b Thomas Thrainer
    if self.op.beparams:
1031 7352d33b Thomas Thrainer
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
1032 7352d33b Thomas Thrainer
    if self.op.nicparams:
1033 7352d33b Thomas Thrainer
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
1034 7352d33b Thomas Thrainer
    if self.op.ipolicy:
1035 7352d33b Thomas Thrainer
      self.cluster.ipolicy = self.new_ipolicy
1036 7352d33b Thomas Thrainer
    if self.op.osparams:
1037 7352d33b Thomas Thrainer
      self.cluster.osparams = self.new_osp
1038 7352d33b Thomas Thrainer
    if self.op.ndparams:
1039 7352d33b Thomas Thrainer
      self.cluster.ndparams = self.new_ndparams
1040 7352d33b Thomas Thrainer
    if self.op.diskparams:
1041 7352d33b Thomas Thrainer
      self.cluster.diskparams = self.new_diskparams
1042 7352d33b Thomas Thrainer
    if self.op.hv_state:
1043 7352d33b Thomas Thrainer
      self.cluster.hv_state_static = self.new_hv_state
1044 7352d33b Thomas Thrainer
    if self.op.disk_state:
1045 7352d33b Thomas Thrainer
      self.cluster.disk_state_static = self.new_disk_state
1046 7352d33b Thomas Thrainer
1047 7352d33b Thomas Thrainer
    if self.op.candidate_pool_size is not None:
1048 7352d33b Thomas Thrainer
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
1049 7352d33b Thomas Thrainer
      # we need to update the pool size here, otherwise the save will fail
1050 5eacbcae Thomas Thrainer
      AdjustCandidatePool(self, [])
1051 7352d33b Thomas Thrainer
1052 7352d33b Thomas Thrainer
    if self.op.maintain_node_health is not None:
1053 7352d33b Thomas Thrainer
      if self.op.maintain_node_health and not constants.ENABLE_CONFD:
1054 7352d33b Thomas Thrainer
        feedback_fn("Note: CONFD was disabled at build time, node health"
1055 7352d33b Thomas Thrainer
                    " maintenance is not useful (still enabling it)")
1056 7352d33b Thomas Thrainer
      self.cluster.maintain_node_health = self.op.maintain_node_health
1057 7352d33b Thomas Thrainer
1058 75f2ff7d Michele Tartara
    if self.op.modify_etc_hosts is not None:
1059 75f2ff7d Michele Tartara
      self.cluster.modify_etc_hosts = self.op.modify_etc_hosts
1060 75f2ff7d Michele Tartara
1061 7352d33b Thomas Thrainer
    if self.op.prealloc_wipe_disks is not None:
1062 7352d33b Thomas Thrainer
      self.cluster.prealloc_wipe_disks = self.op.prealloc_wipe_disks
1063 7352d33b Thomas Thrainer
1064 7352d33b Thomas Thrainer
    if self.op.add_uids is not None:
1065 7352d33b Thomas Thrainer
      uidpool.AddToUidPool(self.cluster.uid_pool, self.op.add_uids)
1066 7352d33b Thomas Thrainer
1067 7352d33b Thomas Thrainer
    if self.op.remove_uids is not None:
1068 7352d33b Thomas Thrainer
      uidpool.RemoveFromUidPool(self.cluster.uid_pool, self.op.remove_uids)
1069 7352d33b Thomas Thrainer
1070 7352d33b Thomas Thrainer
    if self.op.uid_pool is not None:
1071 7352d33b Thomas Thrainer
      self.cluster.uid_pool = self.op.uid_pool
1072 7352d33b Thomas Thrainer
1073 7352d33b Thomas Thrainer
    if self.op.default_iallocator is not None:
1074 7352d33b Thomas Thrainer
      self.cluster.default_iallocator = self.op.default_iallocator
1075 7352d33b Thomas Thrainer
1076 7352d33b Thomas Thrainer
    if self.op.reserved_lvs is not None:
1077 7352d33b Thomas Thrainer
      self.cluster.reserved_lvs = self.op.reserved_lvs
1078 7352d33b Thomas Thrainer
1079 7352d33b Thomas Thrainer
    if self.op.use_external_mip_script is not None:
1080 7352d33b Thomas Thrainer
      self.cluster.use_external_mip_script = self.op.use_external_mip_script
1081 7352d33b Thomas Thrainer
1082 7352d33b Thomas Thrainer
    def helper_os(aname, mods, desc):
1083 7352d33b Thomas Thrainer
      desc += " OS list"
1084 7352d33b Thomas Thrainer
      lst = getattr(self.cluster, aname)
1085 7352d33b Thomas Thrainer
      for key, val in mods:
1086 7352d33b Thomas Thrainer
        if key == constants.DDM_ADD:
1087 7352d33b Thomas Thrainer
          if val in lst:
1088 7352d33b Thomas Thrainer
            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
1089 7352d33b Thomas Thrainer
          else:
1090 7352d33b Thomas Thrainer
            lst.append(val)
1091 7352d33b Thomas Thrainer
        elif key == constants.DDM_REMOVE:
1092 7352d33b Thomas Thrainer
          if val in lst:
1093 7352d33b Thomas Thrainer
            lst.remove(val)
1094 7352d33b Thomas Thrainer
          else:
1095 7352d33b Thomas Thrainer
            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
1096 7352d33b Thomas Thrainer
        else:
1097 7352d33b Thomas Thrainer
          raise errors.ProgrammerError("Invalid modification '%s'" % key)
1098 7352d33b Thomas Thrainer
1099 7352d33b Thomas Thrainer
    if self.op.hidden_os:
1100 7352d33b Thomas Thrainer
      helper_os("hidden_os", self.op.hidden_os, "hidden")
1101 7352d33b Thomas Thrainer
1102 7352d33b Thomas Thrainer
    if self.op.blacklisted_os:
1103 7352d33b Thomas Thrainer
      helper_os("blacklisted_os", self.op.blacklisted_os, "blacklisted")
1104 7352d33b Thomas Thrainer
1105 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1106 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1107 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1108 7352d33b Thomas Thrainer
      feedback_fn("Shutting down master ip on the current netdev (%s)" %
1109 7352d33b Thomas Thrainer
                  self.cluster.master_netdev)
1110 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_deactivate_master_ip(master_params.uuid,
1111 7352d33b Thomas Thrainer
                                                       master_params, ems)
1112 e5c92cfb Klaus Aehlig
      if not self.op.force:
1113 e5c92cfb Klaus Aehlig
        result.Raise("Could not disable the master ip")
1114 e5c92cfb Klaus Aehlig
      else:
1115 e5c92cfb Klaus Aehlig
        if result.fail_msg:
1116 e5c92cfb Klaus Aehlig
          msg = ("Could not disable the master ip (continuing anyway): %s" %
1117 e5c92cfb Klaus Aehlig
                 result.fail_msg)
1118 e5c92cfb Klaus Aehlig
          feedback_fn(msg)
1119 7352d33b Thomas Thrainer
      feedback_fn("Changing master_netdev from %s to %s" %
1120 7352d33b Thomas Thrainer
                  (master_params.netdev, self.op.master_netdev))
1121 7352d33b Thomas Thrainer
      self.cluster.master_netdev = self.op.master_netdev
1122 7352d33b Thomas Thrainer
1123 7352d33b Thomas Thrainer
    if self.op.master_netmask:
1124 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1125 7352d33b Thomas Thrainer
      feedback_fn("Changing master IP netmask to %s" % self.op.master_netmask)
1126 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_change_master_netmask(
1127 1c3231aa Thomas Thrainer
                 master_params.uuid, master_params.netmask,
1128 1c3231aa Thomas Thrainer
                 self.op.master_netmask, master_params.ip,
1129 1c3231aa Thomas Thrainer
                 master_params.netdev)
1130 c7dd65be Klaus Aehlig
      result.Warn("Could not change the master IP netmask", feedback_fn)
1131 7352d33b Thomas Thrainer
      self.cluster.master_netmask = self.op.master_netmask
1132 7352d33b Thomas Thrainer
1133 7352d33b Thomas Thrainer
    self.cfg.Update(self.cluster, feedback_fn)
1134 7352d33b Thomas Thrainer
1135 7352d33b Thomas Thrainer
    if self.op.master_netdev:
1136 7352d33b Thomas Thrainer
      master_params = self.cfg.GetMasterNetworkParameters()
1137 7352d33b Thomas Thrainer
      feedback_fn("Starting the master ip on the new master netdev (%s)" %
1138 7352d33b Thomas Thrainer
                  self.op.master_netdev)
1139 7352d33b Thomas Thrainer
      ems = self.cfg.GetUseExternalMipScript()
1140 1c3231aa Thomas Thrainer
      result = self.rpc.call_node_activate_master_ip(master_params.uuid,
1141 7352d33b Thomas Thrainer
                                                     master_params, ems)
1142 c7dd65be Klaus Aehlig
      result.Warn("Could not re-enable the master ip on the master,"
1143 c7dd65be Klaus Aehlig
                  " please restart manually", self.LogWarning)
1144 7352d33b Thomas Thrainer
1145 7352d33b Thomas Thrainer
1146 7352d33b Thomas Thrainer
class LUClusterVerify(NoHooksLU):
1147 7352d33b Thomas Thrainer
  """Submits all jobs necessary to verify the cluster.
1148 7352d33b Thomas Thrainer

1149 7352d33b Thomas Thrainer
  """
1150 7352d33b Thomas Thrainer
  REQ_BGL = False
1151 7352d33b Thomas Thrainer
1152 7352d33b Thomas Thrainer
  def ExpandNames(self):
1153 7352d33b Thomas Thrainer
    self.needed_locks = {}
1154 7352d33b Thomas Thrainer
1155 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1156 7352d33b Thomas Thrainer
    jobs = []
1157 7352d33b Thomas Thrainer
1158 7352d33b Thomas Thrainer
    if self.op.group_name:
1159 7352d33b Thomas Thrainer
      groups = [self.op.group_name]
1160 7352d33b Thomas Thrainer
      depends_fn = lambda: None
1161 7352d33b Thomas Thrainer
    else:
1162 7352d33b Thomas Thrainer
      groups = self.cfg.GetNodeGroupList()
1163 7352d33b Thomas Thrainer
1164 7352d33b Thomas Thrainer
      # Verify global configuration
1165 7352d33b Thomas Thrainer
      jobs.append([
1166 7352d33b Thomas Thrainer
        opcodes.OpClusterVerifyConfig(ignore_errors=self.op.ignore_errors),
1167 7352d33b Thomas Thrainer
        ])
1168 7352d33b Thomas Thrainer
1169 7352d33b Thomas Thrainer
      # Always depend on global verification
1170 7352d33b Thomas Thrainer
      depends_fn = lambda: [(-len(jobs), [])]
1171 7352d33b Thomas Thrainer
1172 7352d33b Thomas Thrainer
    jobs.extend(
1173 7352d33b Thomas Thrainer
      [opcodes.OpClusterVerifyGroup(group_name=group,
1174 7352d33b Thomas Thrainer
                                    ignore_errors=self.op.ignore_errors,
1175 7352d33b Thomas Thrainer
                                    depends=depends_fn())]
1176 7352d33b Thomas Thrainer
      for group in groups)
1177 7352d33b Thomas Thrainer
1178 7352d33b Thomas Thrainer
    # Fix up all parameters
1179 7352d33b Thomas Thrainer
    for op in itertools.chain(*jobs): # pylint: disable=W0142
1180 7352d33b Thomas Thrainer
      op.debug_simulate_errors = self.op.debug_simulate_errors
1181 7352d33b Thomas Thrainer
      op.verbose = self.op.verbose
1182 7352d33b Thomas Thrainer
      op.error_codes = self.op.error_codes
1183 7352d33b Thomas Thrainer
      try:
1184 7352d33b Thomas Thrainer
        op.skip_checks = self.op.skip_checks
1185 7352d33b Thomas Thrainer
      except AttributeError:
1186 7352d33b Thomas Thrainer
        assert not isinstance(op, opcodes.OpClusterVerifyGroup)
1187 7352d33b Thomas Thrainer
1188 7352d33b Thomas Thrainer
    return ResultWithJobs(jobs)
1189 7352d33b Thomas Thrainer
1190 7352d33b Thomas Thrainer
1191 7352d33b Thomas Thrainer
class _VerifyErrors(object):
1192 7352d33b Thomas Thrainer
  """Mix-in for cluster/group verify LUs.
1193 7352d33b Thomas Thrainer

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

1197 7352d33b Thomas Thrainer
  """
1198 7352d33b Thomas Thrainer
1199 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1200 7352d33b Thomas Thrainer
  ETYPE_ERROR = "ERROR"
1201 7352d33b Thomas Thrainer
  ETYPE_WARNING = "WARNING"
1202 7352d33b Thomas Thrainer
1203 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1204 7352d33b Thomas Thrainer
    """Format an error message.
1205 7352d33b Thomas Thrainer

1206 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1207 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1208 7352d33b Thomas Thrainer

1209 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1210 7352d33b Thomas Thrainer

1211 7352d33b Thomas Thrainer
    """
1212 7352d33b Thomas Thrainer
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1213 7352d33b Thomas Thrainer
    itype, etxt, _ = ecode
1214 7352d33b Thomas Thrainer
    # If the error code is in the list of ignored errors, demote the error to a
1215 7352d33b Thomas Thrainer
    # warning
1216 7352d33b Thomas Thrainer
    if etxt in self.op.ignore_errors:     # pylint: disable=E1101
1217 7352d33b Thomas Thrainer
      ltype = self.ETYPE_WARNING
1218 7352d33b Thomas Thrainer
    # first complete the msg
1219 7352d33b Thomas Thrainer
    if args:
1220 7352d33b Thomas Thrainer
      msg = msg % args
1221 7352d33b Thomas Thrainer
    # then format the whole message
1222 7352d33b Thomas Thrainer
    if self.op.error_codes: # This is a mix-in. pylint: disable=E1101
1223 7352d33b Thomas Thrainer
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1224 7352d33b Thomas Thrainer
    else:
1225 7352d33b Thomas Thrainer
      if item:
1226 7352d33b Thomas Thrainer
        item = " " + item
1227 7352d33b Thomas Thrainer
      else:
1228 7352d33b Thomas Thrainer
        item = ""
1229 7352d33b Thomas Thrainer
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1230 7352d33b Thomas Thrainer
    # and finally report it via the feedback_fn
1231 7352d33b Thomas Thrainer
    self._feedback_fn("  - %s" % msg) # Mix-in. pylint: disable=E1101
1232 7352d33b Thomas Thrainer
    # do not mark the operation as failed for WARN cases only
1233 7352d33b Thomas Thrainer
    if ltype == self.ETYPE_ERROR:
1234 7352d33b Thomas Thrainer
      self.bad = True
1235 7352d33b Thomas Thrainer
1236 7352d33b Thomas Thrainer
  def _ErrorIf(self, cond, *args, **kwargs):
1237 7352d33b Thomas Thrainer
    """Log an error message if the passed condition is True.
1238 7352d33b Thomas Thrainer

1239 7352d33b Thomas Thrainer
    """
1240 7352d33b Thomas Thrainer
    if (bool(cond)
1241 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1242 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1243 7352d33b Thomas Thrainer
1244 7352d33b Thomas Thrainer
1245 7352d33b Thomas Thrainer
def _VerifyCertificate(filename):
1246 7352d33b Thomas Thrainer
  """Verifies a certificate for L{LUClusterVerifyConfig}.
1247 7352d33b Thomas Thrainer

1248 7352d33b Thomas Thrainer
  @type filename: string
1249 7352d33b Thomas Thrainer
  @param filename: Path to PEM file
1250 7352d33b Thomas Thrainer

1251 7352d33b Thomas Thrainer
  """
1252 7352d33b Thomas Thrainer
  try:
1253 7352d33b Thomas Thrainer
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
1254 7352d33b Thomas Thrainer
                                           utils.ReadFile(filename))
1255 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
1256 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR,
1257 7352d33b Thomas Thrainer
            "Failed to load X509 certificate %s: %s" % (filename, err))
1258 7352d33b Thomas Thrainer
1259 7352d33b Thomas Thrainer
  (errcode, msg) = \
1260 7352d33b Thomas Thrainer
    utils.VerifyX509Certificate(cert, constants.SSL_CERT_EXPIRATION_WARN,
1261 7352d33b Thomas Thrainer
                                constants.SSL_CERT_EXPIRATION_ERROR)
1262 7352d33b Thomas Thrainer
1263 7352d33b Thomas Thrainer
  if msg:
1264 7352d33b Thomas Thrainer
    fnamemsg = "While verifying %s: %s" % (filename, msg)
1265 7352d33b Thomas Thrainer
  else:
1266 7352d33b Thomas Thrainer
    fnamemsg = None
1267 7352d33b Thomas Thrainer
1268 7352d33b Thomas Thrainer
  if errcode is None:
1269 7352d33b Thomas Thrainer
    return (None, fnamemsg)
1270 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_WARNING:
1271 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_WARNING, fnamemsg)
1272 7352d33b Thomas Thrainer
  elif errcode == utils.CERT_ERROR:
1273 7352d33b Thomas Thrainer
    return (LUClusterVerifyConfig.ETYPE_ERROR, fnamemsg)
1274 7352d33b Thomas Thrainer
1275 7352d33b Thomas Thrainer
  raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode)
1276 7352d33b Thomas Thrainer
1277 7352d33b Thomas Thrainer
1278 7352d33b Thomas Thrainer
def _GetAllHypervisorParameters(cluster, instances):
1279 7352d33b Thomas Thrainer
  """Compute the set of all hypervisor parameters.
1280 7352d33b Thomas Thrainer

1281 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1282 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1283 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1284 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1285 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1286 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1287 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1288 7352d33b Thomas Thrainer

1289 7352d33b Thomas Thrainer
  """
1290 7352d33b Thomas Thrainer
  hvp_data = []
1291 7352d33b Thomas Thrainer
1292 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1293 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1294 7352d33b Thomas Thrainer
1295 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1296 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1297 7352d33b Thomas Thrainer
      if hv_params:
1298 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1299 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1300 7352d33b Thomas Thrainer
1301 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1302 7352d33b Thomas Thrainer
  for instance in instances:
1303 7352d33b Thomas Thrainer
    if instance.hvparams:
1304 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1305 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1306 7352d33b Thomas Thrainer
1307 7352d33b Thomas Thrainer
  return hvp_data
1308 7352d33b Thomas Thrainer
1309 7352d33b Thomas Thrainer
1310 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1311 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1312 7352d33b Thomas Thrainer

1313 7352d33b Thomas Thrainer
  """
1314 7352d33b Thomas Thrainer
  REQ_BGL = False
1315 7352d33b Thomas Thrainer
1316 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1317 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1318 7352d33b Thomas Thrainer

1319 7352d33b Thomas Thrainer
    """
1320 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1321 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1322 7352d33b Thomas Thrainer
             (item, hv_name))
1323 7352d33b Thomas Thrainer
      try:
1324 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1325 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1326 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1327 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1328 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1329 7352d33b Thomas Thrainer
1330 7352d33b Thomas Thrainer
  def ExpandNames(self):
1331 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1332 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1333 7352d33b Thomas Thrainer
1334 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1335 7352d33b Thomas Thrainer
    """Check prerequisites.
1336 7352d33b Thomas Thrainer

1337 7352d33b Thomas Thrainer
    """
1338 7352d33b Thomas Thrainer
    # Retrieve all information
1339 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1340 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1341 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1342 7352d33b Thomas Thrainer
1343 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1344 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1345 7352d33b Thomas Thrainer

1346 7352d33b Thomas Thrainer
    """
1347 7352d33b Thomas Thrainer
    self.bad = False
1348 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
1349 7352d33b Thomas Thrainer
1350 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster config")
1351 7352d33b Thomas Thrainer
1352 7352d33b Thomas Thrainer
    for msg in self.cfg.VerifyConfig():
1353 7352d33b Thomas Thrainer
      self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg)
1354 7352d33b Thomas Thrainer
1355 7352d33b Thomas Thrainer
    feedback_fn("* Verifying cluster certificate files")
1356 7352d33b Thomas Thrainer
1357 7352d33b Thomas Thrainer
    for cert_filename in pathutils.ALL_CERT_FILES:
1358 7352d33b Thomas Thrainer
      (errcode, msg) = _VerifyCertificate(cert_filename)
1359 7352d33b Thomas Thrainer
      self._ErrorIf(errcode, constants.CV_ECLUSTERCERT, None, msg, code=errcode)
1360 7352d33b Thomas Thrainer
1361 69ac3b74 Michele Tartara
    self._ErrorIf(not utils.CanRead(constants.CONFD_USER,
1362 69ac3b74 Michele Tartara
                                    pathutils.NODED_CERT_FILE),
1363 69ac3b74 Michele Tartara
                  constants.CV_ECLUSTERCERT,
1364 69ac3b74 Michele Tartara
                  None,
1365 69ac3b74 Michele Tartara
                  pathutils.NODED_CERT_FILE + " must be accessible by the " +
1366 69ac3b74 Michele Tartara
                    constants.CONFD_USER + " user")
1367 69ac3b74 Michele Tartara
1368 7352d33b Thomas Thrainer
    feedback_fn("* Verifying hypervisor parameters")
1369 7352d33b Thomas Thrainer
1370 7352d33b Thomas Thrainer
    self._VerifyHVP(_GetAllHypervisorParameters(self.cfg.GetClusterInfo(),
1371 7352d33b Thomas Thrainer
                                                self.all_inst_info.values()))
1372 7352d33b Thomas Thrainer
1373 7352d33b Thomas Thrainer
    feedback_fn("* Verifying all nodes belong to an existing group")
1374 7352d33b Thomas Thrainer
1375 7352d33b Thomas Thrainer
    # We do this verification here because, should this bogus circumstance
1376 7352d33b Thomas Thrainer
    # occur, it would never be caught by VerifyGroup, which only acts on
1377 7352d33b Thomas Thrainer
    # nodes/instances reachable from existing node groups.
1378 7352d33b Thomas Thrainer
1379 1c3231aa Thomas Thrainer
    dangling_nodes = set(node for node in self.all_node_info.values()
1380 7352d33b Thomas Thrainer
                         if node.group not in self.all_group_info)
1381 7352d33b Thomas Thrainer
1382 7352d33b Thomas Thrainer
    dangling_instances = {}
1383 7352d33b Thomas Thrainer
    no_node_instances = []
1384 7352d33b Thomas Thrainer
1385 7352d33b Thomas Thrainer
    for inst in self.all_inst_info.values():
1386 1c3231aa Thomas Thrainer
      if inst.primary_node in [node.uuid for node in dangling_nodes]:
1387 da4a52a3 Thomas Thrainer
        dangling_instances.setdefault(inst.primary_node, []).append(inst)
1388 7352d33b Thomas Thrainer
      elif inst.primary_node not in self.all_node_info:
1389 da4a52a3 Thomas Thrainer
        no_node_instances.append(inst)
1390 7352d33b Thomas Thrainer
1391 7352d33b Thomas Thrainer
    pretty_dangling = [
1392 7352d33b Thomas Thrainer
        "%s (%s)" %
1393 7352d33b Thomas Thrainer
        (node.name,
1394 da4a52a3 Thomas Thrainer
         utils.CommaJoin(
1395 da4a52a3 Thomas Thrainer
           self.cfg.GetInstanceNames(
1396 da4a52a3 Thomas Thrainer
             dangling_instances.get(node.uuid, ["no instances"]))))
1397 7352d33b Thomas Thrainer
        for node in dangling_nodes]
1398 7352d33b Thomas Thrainer
1399 7352d33b Thomas Thrainer
    self._ErrorIf(bool(dangling_nodes), constants.CV_ECLUSTERDANGLINGNODES,
1400 7352d33b Thomas Thrainer
                  None,
1401 7352d33b Thomas Thrainer
                  "the following nodes (and their instances) belong to a non"
1402 7352d33b Thomas Thrainer
                  " existing group: %s", utils.CommaJoin(pretty_dangling))
1403 7352d33b Thomas Thrainer
1404 7352d33b Thomas Thrainer
    self._ErrorIf(bool(no_node_instances), constants.CV_ECLUSTERDANGLINGINST,
1405 7352d33b Thomas Thrainer
                  None,
1406 7352d33b Thomas Thrainer
                  "the following instances have a non-existing primary-node:"
1407 da4a52a3 Thomas Thrainer
                  " %s", utils.CommaJoin(
1408 da4a52a3 Thomas Thrainer
                           self.cfg.GetInstanceNames(no_node_instances)))
1409 7352d33b Thomas Thrainer
1410 7352d33b Thomas Thrainer
    return not self.bad
1411 7352d33b Thomas Thrainer
1412 7352d33b Thomas Thrainer
1413 7352d33b Thomas Thrainer
class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
1414 7352d33b Thomas Thrainer
  """Verifies the status of a node group.
1415 7352d33b Thomas Thrainer

1416 7352d33b Thomas Thrainer
  """
1417 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1418 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1419 7352d33b Thomas Thrainer
  REQ_BGL = False
1420 7352d33b Thomas Thrainer
1421 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1422 7352d33b Thomas Thrainer
1423 7352d33b Thomas Thrainer
  class NodeImage(object):
1424 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1425 7352d33b Thomas Thrainer

1426 1c3231aa Thomas Thrainer
    @type uuid: string
1427 1c3231aa Thomas Thrainer
    @ivar uuid: the node UUID to which this object refers
1428 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1429 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1430 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1431 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1432 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1433 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1434 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1435 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1436 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1437 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1438 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1439 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1440 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1441 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1442 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1443 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1444 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1445 7352d33b Thomas Thrainer
    @type ghost: boolean
1446 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1447 7352d33b Thomas Thrainer
    @type os_fail: boolean
1448 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1449 7352d33b Thomas Thrainer
    @type oslist: list
1450 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1451 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1452 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1453 7352d33b Thomas Thrainer
    @type pv_min: float
1454 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1455 7352d33b Thomas Thrainer
    @type pv_max: float
1456 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1457 7352d33b Thomas Thrainer

1458 7352d33b Thomas Thrainer
    """
1459 1c3231aa Thomas Thrainer
    def __init__(self, offline=False, uuid=None, vm_capable=True):
1460 1c3231aa Thomas Thrainer
      self.uuid = uuid
1461 7352d33b Thomas Thrainer
      self.volumes = {}
1462 7352d33b Thomas Thrainer
      self.instances = []
1463 7352d33b Thomas Thrainer
      self.pinst = []
1464 7352d33b Thomas Thrainer
      self.sinst = []
1465 7352d33b Thomas Thrainer
      self.sbp = {}
1466 7352d33b Thomas Thrainer
      self.mfree = 0
1467 7352d33b Thomas Thrainer
      self.dfree = 0
1468 7352d33b Thomas Thrainer
      self.offline = offline
1469 7352d33b Thomas Thrainer
      self.vm_capable = vm_capable
1470 7352d33b Thomas Thrainer
      self.rpc_fail = False
1471 7352d33b Thomas Thrainer
      self.lvm_fail = False
1472 7352d33b Thomas Thrainer
      self.hyp_fail = False
1473 7352d33b Thomas Thrainer
      self.ghost = False
1474 7352d33b Thomas Thrainer
      self.os_fail = False
1475 7352d33b Thomas Thrainer
      self.oslist = {}
1476 7352d33b Thomas Thrainer
      self.pv_min = None
1477 7352d33b Thomas Thrainer
      self.pv_max = None
1478 7352d33b Thomas Thrainer
1479 7352d33b Thomas Thrainer
  def ExpandNames(self):
1480 7352d33b Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
1481 7352d33b Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
1482 7352d33b Thomas Thrainer
1483 7352d33b Thomas Thrainer
    # Get instances in node group; this is unsafe and needs verification later
1484 da4a52a3 Thomas Thrainer
    inst_uuids = \
1485 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1486 7352d33b Thomas Thrainer
1487 7352d33b Thomas Thrainer
    self.needed_locks = {
1488 da4a52a3 Thomas Thrainer
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceNames(inst_uuids),
1489 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
1490 7352d33b Thomas Thrainer
      locking.LEVEL_NODE: [],
1491 7352d33b Thomas Thrainer
1492 7352d33b Thomas Thrainer
      # This opcode is run by watcher every five minutes and acquires all nodes
1493 7352d33b Thomas Thrainer
      # for a group. It doesn't run for a long time, so it's better to acquire
1494 7352d33b Thomas Thrainer
      # the node allocation lock as well.
1495 7352d33b Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1496 7352d33b Thomas Thrainer
      }
1497 7352d33b Thomas Thrainer
1498 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1499 7352d33b Thomas Thrainer
1500 7352d33b Thomas Thrainer
  def DeclareLocks(self, level):
1501 7352d33b Thomas Thrainer
    if level == locking.LEVEL_NODE:
1502 7352d33b Thomas Thrainer
      # Get members of node group; this is unsafe and needs verification later
1503 7352d33b Thomas Thrainer
      nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members)
1504 7352d33b Thomas Thrainer
1505 7352d33b Thomas Thrainer
      # In Exec(), we warn about mirrored instances that have primary and
1506 7352d33b Thomas Thrainer
      # secondary living in separate node groups. To fully verify that
1507 7352d33b Thomas Thrainer
      # volumes for these instances are healthy, we will need to do an
1508 7352d33b Thomas Thrainer
      # extra call to their secondaries. We ensure here those nodes will
1509 7352d33b Thomas Thrainer
      # be locked.
1510 da4a52a3 Thomas Thrainer
      for inst_name in self.owned_locks(locking.LEVEL_INSTANCE):
1511 7352d33b Thomas Thrainer
        # Important: access only the instances whose lock is owned
1512 da4a52a3 Thomas Thrainer
        instance = self.cfg.GetInstanceInfoByName(inst_name)
1513 da4a52a3 Thomas Thrainer
        if instance.disk_template in constants.DTS_INT_MIRROR:
1514 da4a52a3 Thomas Thrainer
          nodes.update(instance.secondary_nodes)
1515 7352d33b Thomas Thrainer
1516 7352d33b Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodes
1517 7352d33b Thomas Thrainer
1518 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1519 7352d33b Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
1520 7352d33b Thomas Thrainer
    self.group_info = self.cfg.GetNodeGroup(self.group_uuid)
1521 7352d33b Thomas Thrainer
1522 1c3231aa Thomas Thrainer
    group_node_uuids = set(self.group_info.members)
1523 da4a52a3 Thomas Thrainer
    group_inst_uuids = \
1524 7352d33b Thomas Thrainer
      self.cfg.GetNodeGroupInstances(self.group_uuid, primary_only=True)
1525 7352d33b Thomas Thrainer
1526 1c3231aa Thomas Thrainer
    unlocked_node_uuids = \
1527 1c3231aa Thomas Thrainer
        group_node_uuids.difference(self.owned_locks(locking.LEVEL_NODE))
1528 7352d33b Thomas Thrainer
1529 da4a52a3 Thomas Thrainer
    unlocked_inst_uuids = \
1530 da4a52a3 Thomas Thrainer
        group_inst_uuids.difference(
1531 da4a52a3 Thomas Thrainer
          [self.cfg.GetInstanceInfoByName(name).uuid
1532 da4a52a3 Thomas Thrainer
           for name in self.owned_locks(locking.LEVEL_INSTANCE)])
1533 7352d33b Thomas Thrainer
1534 1c3231aa Thomas Thrainer
    if unlocked_node_uuids:
1535 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError(
1536 1c3231aa Thomas Thrainer
        "Missing lock for nodes: %s" %
1537 1c3231aa Thomas Thrainer
        utils.CommaJoin(self.cfg.GetNodeNames(unlocked_node_uuids)),
1538 1c3231aa Thomas Thrainer
        errors.ECODE_STATE)
1539 7352d33b Thomas Thrainer
1540 da4a52a3 Thomas Thrainer
    if unlocked_inst_uuids:
1541 da4a52a3 Thomas Thrainer
      raise errors.OpPrereqError(
1542 da4a52a3 Thomas Thrainer
        "Missing lock for instances: %s" %
1543 da4a52a3 Thomas Thrainer
        utils.CommaJoin(self.cfg.GetInstanceNames(unlocked_inst_uuids)),
1544 da4a52a3 Thomas Thrainer
        errors.ECODE_STATE)
1545 7352d33b Thomas Thrainer
1546 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1547 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1548 7352d33b Thomas Thrainer
1549 1c3231aa Thomas Thrainer
    self.my_node_uuids = group_node_uuids
1550 1c3231aa Thomas Thrainer
    self.my_node_info = dict((node_uuid, self.all_node_info[node_uuid])
1551 1c3231aa Thomas Thrainer
                             for node_uuid in group_node_uuids)
1552 7352d33b Thomas Thrainer
1553 da4a52a3 Thomas Thrainer
    self.my_inst_uuids = group_inst_uuids
1554 da4a52a3 Thomas Thrainer
    self.my_inst_info = dict((inst_uuid, self.all_inst_info[inst_uuid])
1555 da4a52a3 Thomas Thrainer
                             for inst_uuid in group_inst_uuids)
1556 7352d33b Thomas Thrainer
1557 7352d33b Thomas Thrainer
    # We detect here the nodes that will need the extra RPC calls for verifying
1558 7352d33b Thomas Thrainer
    # split LV volumes; they should be locked.
1559 7352d33b Thomas Thrainer
    extra_lv_nodes = set()
1560 7352d33b Thomas Thrainer
1561 7352d33b Thomas Thrainer
    for inst in self.my_inst_info.values():
1562 7352d33b Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1563 1c3231aa Thomas Thrainer
        for nuuid in inst.all_nodes:
1564 1c3231aa Thomas Thrainer
          if self.all_node_info[nuuid].group != self.group_uuid:
1565 1c3231aa Thomas Thrainer
            extra_lv_nodes.add(nuuid)
1566 7352d33b Thomas Thrainer
1567 7352d33b Thomas Thrainer
    unlocked_lv_nodes = \
1568 7352d33b Thomas Thrainer
        extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE))
1569 7352d33b Thomas Thrainer
1570 7352d33b Thomas Thrainer
    if unlocked_lv_nodes:
1571 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Missing node locks for LV check: %s" %
1572 7352d33b Thomas Thrainer
                                 utils.CommaJoin(unlocked_lv_nodes),
1573 7352d33b Thomas Thrainer
                                 errors.ECODE_STATE)
1574 7352d33b Thomas Thrainer
    self.extra_lv_nodes = list(extra_lv_nodes)
1575 7352d33b Thomas Thrainer
1576 7352d33b Thomas Thrainer
  def _VerifyNode(self, ninfo, nresult):
1577 7352d33b Thomas Thrainer
    """Perform some basic validation on data returned from a node.
1578 7352d33b Thomas Thrainer

1579 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1580 7352d33b Thomas Thrainer
        mandatory fields
1581 7352d33b Thomas Thrainer
      - check ganeti version
1582 7352d33b Thomas Thrainer

1583 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1584 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1585 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1586 7352d33b Thomas Thrainer
    @rtype: boolean
1587 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1588 7352d33b Thomas Thrainer
         reasonable values in the respose)
1589 7352d33b Thomas Thrainer

1590 7352d33b Thomas Thrainer
    """
1591 7352d33b Thomas Thrainer
    # main result, nresult should be a non-empty dict
1592 7352d33b Thomas Thrainer
    test = not nresult or not isinstance(nresult, dict)
1593 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1594 7352d33b Thomas Thrainer
                  "unable to verify node: no data returned")
1595 7352d33b Thomas Thrainer
    if test:
1596 7352d33b Thomas Thrainer
      return False
1597 7352d33b Thomas Thrainer
1598 7352d33b Thomas Thrainer
    # compares ganeti version
1599 7352d33b Thomas Thrainer
    local_version = constants.PROTOCOL_VERSION
1600 7352d33b Thomas Thrainer
    remote_version = nresult.get("version", None)
1601 7352d33b Thomas Thrainer
    test = not (remote_version and
1602 7352d33b Thomas Thrainer
                isinstance(remote_version, (list, tuple)) and
1603 7352d33b Thomas Thrainer
                len(remote_version) == 2)
1604 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODERPC, ninfo.name,
1605 d0d7d7cf Thomas Thrainer
                  "connection to node returned invalid data")
1606 7352d33b Thomas Thrainer
    if test:
1607 7352d33b Thomas Thrainer
      return False
1608 7352d33b Thomas Thrainer
1609 7352d33b Thomas Thrainer
    test = local_version != remote_version[0]
1610 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEVERSION, ninfo.name,
1611 d0d7d7cf Thomas Thrainer
                  "incompatible protocol versions: master %s,"
1612 d0d7d7cf Thomas Thrainer
                  " node %s", local_version, remote_version[0])
1613 7352d33b Thomas Thrainer
    if test:
1614 7352d33b Thomas Thrainer
      return False
1615 7352d33b Thomas Thrainer
1616 7352d33b Thomas Thrainer
    # node seems compatible, we can actually try to look into its results
1617 7352d33b Thomas Thrainer
1618 7352d33b Thomas Thrainer
    # full package version
1619 7352d33b Thomas Thrainer
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1620 d0d7d7cf Thomas Thrainer
                  constants.CV_ENODEVERSION, ninfo.name,
1621 7352d33b Thomas Thrainer
                  "software version mismatch: master %s, node %s",
1622 7352d33b Thomas Thrainer
                  constants.RELEASE_VERSION, remote_version[1],
1623 7352d33b Thomas Thrainer
                  code=self.ETYPE_WARNING)
1624 7352d33b Thomas Thrainer
1625 7352d33b Thomas Thrainer
    hyp_result = nresult.get(constants.NV_HYPERVISOR, None)
1626 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hyp_result, dict):
1627 7352d33b Thomas Thrainer
      for hv_name, hv_result in hyp_result.iteritems():
1628 7352d33b Thomas Thrainer
        test = hv_result is not None
1629 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
1630 d0d7d7cf Thomas Thrainer
                      "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1631 7352d33b Thomas Thrainer
1632 7352d33b Thomas Thrainer
    hvp_result = nresult.get(constants.NV_HVPARAMS, None)
1633 7352d33b Thomas Thrainer
    if ninfo.vm_capable and isinstance(hvp_result, list):
1634 7352d33b Thomas Thrainer
      for item, hv_name, hv_result in hvp_result:
1635 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEHV, ninfo.name,
1636 d0d7d7cf Thomas Thrainer
                      "hypervisor %s parameter verify failure (source %s): %s",
1637 d0d7d7cf Thomas Thrainer
                      hv_name, item, hv_result)
1638 7352d33b Thomas Thrainer
1639 7352d33b Thomas Thrainer
    test = nresult.get(constants.NV_NODESETUP,
1640 7352d33b Thomas Thrainer
                       ["Missing NODESETUP results"])
1641 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESETUP, ninfo.name,
1642 d0d7d7cf Thomas Thrainer
                  "node setup error: %s", "; ".join(test))
1643 7352d33b Thomas Thrainer
1644 7352d33b Thomas Thrainer
    return True
1645 7352d33b Thomas Thrainer
1646 7352d33b Thomas Thrainer
  def _VerifyNodeTime(self, ninfo, nresult,
1647 7352d33b Thomas Thrainer
                      nvinfo_starttime, nvinfo_endtime):
1648 7352d33b Thomas Thrainer
    """Check the node time.
1649 7352d33b Thomas Thrainer

1650 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1651 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1652 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1653 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1654 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1655 7352d33b Thomas Thrainer

1656 7352d33b Thomas Thrainer
    """
1657 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1658 7352d33b Thomas Thrainer
    try:
1659 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1660 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1661 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODETIME, ninfo.name,
1662 d0d7d7cf Thomas Thrainer
                    "Node returned invalid time")
1663 7352d33b Thomas Thrainer
      return
1664 7352d33b Thomas Thrainer
1665 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1666 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1667 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1668 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1669 7352d33b Thomas Thrainer
    else:
1670 7352d33b Thomas Thrainer
      ntime_diff = None
1671 7352d33b Thomas Thrainer
1672 d0d7d7cf Thomas Thrainer
    self._ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, ninfo.name,
1673 d0d7d7cf Thomas Thrainer
                  "Node time diverges by at least %s from master node time",
1674 d0d7d7cf Thomas Thrainer
                  ntime_diff)
1675 7352d33b Thomas Thrainer
1676 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1677 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1678 7352d33b Thomas Thrainer

1679 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1680 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1681 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1682 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1683 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1684 7352d33b Thomas Thrainer
    @param nimg: node image
1685 7352d33b Thomas Thrainer

1686 7352d33b Thomas Thrainer
    """
1687 7352d33b Thomas Thrainer
    if vg_name is None:
1688 7352d33b Thomas Thrainer
      return
1689 7352d33b Thomas Thrainer
1690 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1691 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1692 7352d33b Thomas Thrainer
    test = not vglist
1693 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
1694 d0d7d7cf Thomas Thrainer
                  "unable to check volume groups")
1695 7352d33b Thomas Thrainer
    if not test:
1696 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1697 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1698 d0d7d7cf Thomas Thrainer
      self._ErrorIf(vgstatus, constants.CV_ENODELVM, ninfo.name, vgstatus)
1699 7352d33b Thomas Thrainer
1700 7352d33b Thomas Thrainer
    # Check PVs
1701 5eacbcae Thomas Thrainer
    (errmsgs, pvminmax) = CheckNodePVs(nresult, self._exclusive_storage)
1702 7352d33b Thomas Thrainer
    for em in errmsgs:
1703 d0d7d7cf Thomas Thrainer
      self._Error(constants.CV_ENODELVM, ninfo.name, em)
1704 7352d33b Thomas Thrainer
    if pvminmax is not None:
1705 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1706 7352d33b Thomas Thrainer
1707 1bb99a33 Bernardo Dal Seno
  def _VerifyGroupDRBDVersion(self, node_verify_infos):
1708 1bb99a33 Bernardo Dal Seno
    """Check cross-node DRBD version consistency.
1709 1bb99a33 Bernardo Dal Seno

1710 1bb99a33 Bernardo Dal Seno
    @type node_verify_infos: dict
1711 1bb99a33 Bernardo Dal Seno
    @param node_verify_infos: infos about nodes as returned from the
1712 1bb99a33 Bernardo Dal Seno
      node_verify call.
1713 1bb99a33 Bernardo Dal Seno

1714 1bb99a33 Bernardo Dal Seno
    """
1715 1bb99a33 Bernardo Dal Seno
    node_versions = {}
1716 1c3231aa Thomas Thrainer
    for node_uuid, ndata in node_verify_infos.items():
1717 1bb99a33 Bernardo Dal Seno
      nresult = ndata.payload
1718 1bb99a33 Bernardo Dal Seno
      version = nresult.get(constants.NV_DRBDVERSION, "Missing DRBD version")
1719 1c3231aa Thomas Thrainer
      node_versions[node_uuid] = version
1720 1bb99a33 Bernardo Dal Seno
1721 1bb99a33 Bernardo Dal Seno
    if len(set(node_versions.values())) > 1:
1722 1c3231aa Thomas Thrainer
      for node_uuid, version in sorted(node_versions.items()):
1723 1bb99a33 Bernardo Dal Seno
        msg = "DRBD version mismatch: %s" % version
1724 1c3231aa Thomas Thrainer
        self._Error(constants.CV_ENODEDRBDHELPER, node_uuid, msg,
1725 1bb99a33 Bernardo Dal Seno
                    code=self.ETYPE_WARNING)
1726 1bb99a33 Bernardo Dal Seno
1727 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
1728 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
1729 7352d33b Thomas Thrainer

1730 7352d33b Thomas Thrainer
    @type node_image: dict
1731 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
1732 7352d33b Thomas Thrainer
      L{NodeImage} objects
1733 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1734 7352d33b Thomas Thrainer

1735 7352d33b Thomas Thrainer
    """
1736 7352d33b Thomas Thrainer
    if vg_name is None:
1737 7352d33b Thomas Thrainer
      return
1738 7352d33b Thomas Thrainer
1739 bb935d8d Thomas Thrainer
    # Only exclusive storage needs this kind of checks
1740 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
1741 7352d33b Thomas Thrainer
      return
1742 7352d33b Thomas Thrainer
1743 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
1744 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
1745 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
1746 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
1747 7352d33b Thomas Thrainer
    if not vals:
1748 7352d33b Thomas Thrainer
      return
1749 bb935d8d Thomas Thrainer
    (pvmin, minnode_uuid) = min((ni.pv_min, ni.uuid) for ni in vals)
1750 bb935d8d Thomas Thrainer
    (pvmax, maxnode_uuid) = max((ni.pv_max, ni.uuid) for ni in vals)
1751 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
1752 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
1753 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
1754 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
1755 bb935d8d Thomas Thrainer
                  pvmin, self.cfg.GetNodeName(minnode_uuid),
1756 bb935d8d Thomas Thrainer
                  pvmax, self.cfg.GetNodeName(maxnode_uuid))
1757 7352d33b Thomas Thrainer
1758 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
1759 7352d33b Thomas Thrainer
    """Check the node bridges.
1760 7352d33b Thomas Thrainer

1761 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1762 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1763 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1764 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
1765 7352d33b Thomas Thrainer

1766 7352d33b Thomas Thrainer
    """
1767 7352d33b Thomas Thrainer
    if not bridges:
1768 7352d33b Thomas Thrainer
      return
1769 7352d33b Thomas Thrainer
1770 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
1771 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
1772 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1773 d0d7d7cf Thomas Thrainer
                  "did not return valid bridge information")
1774 7352d33b Thomas Thrainer
    if not test:
1775 d0d7d7cf Thomas Thrainer
      self._ErrorIf(bool(missing), constants.CV_ENODENET, ninfo.name,
1776 d0d7d7cf Thomas Thrainer
                    "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
1777 7352d33b Thomas Thrainer
1778 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
1779 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
1780 7352d33b Thomas Thrainer

1781 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1782 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1783 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1784 7352d33b Thomas Thrainer

1785 7352d33b Thomas Thrainer
    """
1786 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
1787 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
1788 7352d33b Thomas Thrainer
                  "did not return user scripts information")
1789 7352d33b Thomas Thrainer
1790 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
1791 7352d33b Thomas Thrainer
    if not test:
1792 d0d7d7cf Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, ninfo.name,
1793 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
1794 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
1795 7352d33b Thomas Thrainer
1796 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
1797 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
1798 7352d33b Thomas Thrainer

1799 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1800 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1801 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1802 7352d33b Thomas Thrainer

1803 7352d33b Thomas Thrainer
    """
1804 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
1805 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODESSH, ninfo.name,
1806 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node ssh connectivity data")
1807 7352d33b Thomas Thrainer
    if not test:
1808 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
1809 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
1810 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODESSH, ninfo.name,
1811 d0d7d7cf Thomas Thrainer
                        "ssh communication with node '%s': %s", a_node, a_msg)
1812 7352d33b Thomas Thrainer
1813 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
1814 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1815 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node tcp connectivity data")
1816 7352d33b Thomas Thrainer
    if not test:
1817 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
1818 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
1819 7352d33b Thomas Thrainer
        for anode in nlist:
1820 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODENET, ninfo.name,
1821 d0d7d7cf Thomas Thrainer
                        "tcp communication with node '%s': %s",
1822 d0d7d7cf Thomas Thrainer
                        anode, nresult[constants.NV_NODENETTEST][anode])
1823 7352d33b Thomas Thrainer
1824 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
1825 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODENET, ninfo.name,
1826 d0d7d7cf Thomas Thrainer
                  "node hasn't returned node master IP reachability data")
1827 7352d33b Thomas Thrainer
    if not test:
1828 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
1829 1c3231aa Thomas Thrainer
        if ninfo.uuid == self.master_node:
1830 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
1831 7352d33b Thomas Thrainer
        else:
1832 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
1833 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODENET, ninfo.name, msg)
1834 7352d33b Thomas Thrainer
1835 da4a52a3 Thomas Thrainer
  def _VerifyInstance(self, instance, node_image, diskstatus):
1836 7352d33b Thomas Thrainer
    """Verify an instance.
1837 7352d33b Thomas Thrainer

1838 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
1839 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
1840 7352d33b Thomas Thrainer
    state.
1841 7352d33b Thomas Thrainer

1842 7352d33b Thomas Thrainer
    """
1843 da4a52a3 Thomas Thrainer
    pnode_uuid = instance.primary_node
1844 da4a52a3 Thomas Thrainer
    pnode_img = node_image[pnode_uuid]
1845 7352d33b Thomas Thrainer
    groupinfo = self.cfg.GetAllNodeGroupsInfo()
1846 7352d33b Thomas Thrainer
1847 7352d33b Thomas Thrainer
    node_vol_should = {}
1848 da4a52a3 Thomas Thrainer
    instance.MapLVsByNode(node_vol_should)
1849 7352d33b Thomas Thrainer
1850 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1851 7352d33b Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
1852 7352d33b Thomas Thrainer
                                                            self.group_info)
1853 da4a52a3 Thomas Thrainer
    err = ComputeIPolicyInstanceViolation(ipolicy, instance, self.cfg)
1854 da4a52a3 Thomas Thrainer
    self._ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance.name,
1855 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(err), code=self.ETYPE_WARNING)
1856 7352d33b Thomas Thrainer
1857 da4a52a3 Thomas Thrainer
    for node_uuid in node_vol_should:
1858 da4a52a3 Thomas Thrainer
      n_img = node_image[node_uuid]
1859 7352d33b Thomas Thrainer
      if n_img.offline or n_img.rpc_fail or n_img.lvm_fail:
1860 7352d33b Thomas Thrainer
        # ignore missing volumes on offline or broken nodes
1861 7352d33b Thomas Thrainer
        continue
1862 da4a52a3 Thomas Thrainer
      for volume in node_vol_should[node_uuid]:
1863 7352d33b Thomas Thrainer
        test = volume not in n_img.volumes
1864 da4a52a3 Thomas Thrainer
        self._ErrorIf(test, constants.CV_EINSTANCEMISSINGDISK, instance.name,
1865 d0d7d7cf Thomas Thrainer
                      "volume %s missing on node %s", volume,
1866 da4a52a3 Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid))
1867 7352d33b Thomas Thrainer
1868 da4a52a3 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
1869 da4a52a3 Thomas Thrainer
      test = instance.uuid not in pnode_img.instances and not pnode_img.offline
1870 da4a52a3 Thomas Thrainer
      self._ErrorIf(test, constants.CV_EINSTANCEDOWN, instance.name,
1871 d0d7d7cf Thomas Thrainer
                    "instance not running on its primary node %s",
1872 da4a52a3 Thomas Thrainer
                     self.cfg.GetNodeName(pnode_uuid))
1873 da4a52a3 Thomas Thrainer
      self._ErrorIf(pnode_img.offline, constants.CV_EINSTANCEBADNODE,
1874 da4a52a3 Thomas Thrainer
                    instance.name, "instance is marked as running and lives on"
1875 da4a52a3 Thomas Thrainer
                    " offline node %s", self.cfg.GetNodeName(pnode_uuid))
1876 7352d33b Thomas Thrainer
1877 7352d33b Thomas Thrainer
    diskdata = [(nname, success, status, idx)
1878 7352d33b Thomas Thrainer
                for (nname, disks) in diskstatus.items()
1879 7352d33b Thomas Thrainer
                for idx, (success, status) in enumerate(disks)]
1880 7352d33b Thomas Thrainer
1881 7352d33b Thomas Thrainer
    for nname, success, bdev_status, idx in diskdata:
1882 7352d33b Thomas Thrainer
      # the 'ghost node' construction in Exec() ensures that we have a
1883 7352d33b Thomas Thrainer
      # node here
1884 7352d33b Thomas Thrainer
      snode = node_image[nname]
1885 7352d33b Thomas Thrainer
      bad_snode = snode.ghost or snode.offline
1886 da4a52a3 Thomas Thrainer
      self._ErrorIf(instance.disks_active and
1887 d0d7d7cf Thomas Thrainer
                    not success and not bad_snode,
1888 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEFAULTYDISK, instance.name,
1889 d0d7d7cf Thomas Thrainer
                    "couldn't retrieve status for disk/%s on %s: %s",
1890 d0d7d7cf Thomas Thrainer
                    idx, self.cfg.GetNodeName(nname), bdev_status)
1891 9b0e86e2 Thomas Thrainer
1892 da4a52a3 Thomas Thrainer
      if instance.disks_active and success and \
1893 9b0e86e2 Thomas Thrainer
         (bdev_status.is_degraded or
1894 9b0e86e2 Thomas Thrainer
          bdev_status.ldisk_status != constants.LDS_OKAY):
1895 9b0e86e2 Thomas Thrainer
        msg = "disk/%s on %s" % (idx, self.cfg.GetNodeName(nname))
1896 9b0e86e2 Thomas Thrainer
        if bdev_status.is_degraded:
1897 9b0e86e2 Thomas Thrainer
          msg += " is degraded"
1898 9b0e86e2 Thomas Thrainer
        if bdev_status.ldisk_status != constants.LDS_OKAY:
1899 9b0e86e2 Thomas Thrainer
          msg += "; state is '%s'" % \
1900 9b0e86e2 Thomas Thrainer
                 constants.LDS_NAMES[bdev_status.ldisk_status]
1901 9b0e86e2 Thomas Thrainer
1902 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEFAULTYDISK, instance.name, msg)
1903 d0d7d7cf Thomas Thrainer
1904 d0d7d7cf Thomas Thrainer
    self._ErrorIf(pnode_img.rpc_fail and not pnode_img.offline,
1905 da4a52a3 Thomas Thrainer
                  constants.CV_ENODERPC, self.cfg.GetNodeName(pnode_uuid),
1906 da4a52a3 Thomas Thrainer
                  "instance %s, connection to primary node failed",
1907 da4a52a3 Thomas Thrainer
                  instance.name)
1908 da4a52a3 Thomas Thrainer
1909 da4a52a3 Thomas Thrainer
    self._ErrorIf(len(instance.secondary_nodes) > 1,
1910 da4a52a3 Thomas Thrainer
                  constants.CV_EINSTANCELAYOUT, instance.name,
1911 da4a52a3 Thomas Thrainer
                  "instance has multiple secondary nodes: %s",
1912 da4a52a3 Thomas Thrainer
                  utils.CommaJoin(instance.secondary_nodes),
1913 d0d7d7cf Thomas Thrainer
                  code=self.ETYPE_WARNING)
1914 7352d33b Thomas Thrainer
1915 da4a52a3 Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg, instance.all_nodes)
1916 c69b147d Bernardo Dal Seno
    if any(es_flags.values()):
1917 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_EXCL_STORAGE:
1918 c69b147d Bernardo Dal Seno
        # Disk template not compatible with exclusive_storage: no instance
1919 c69b147d Bernardo Dal Seno
        # node should have the flag set
1920 c69b147d Bernardo Dal Seno
        es_nodes = [n
1921 c69b147d Bernardo Dal Seno
                    for (n, es) in es_flags.items()
1922 c69b147d Bernardo Dal Seno
                    if es]
1923 da4a52a3 Thomas Thrainer
        self._Error(constants.CV_EINSTANCEUNSUITABLENODE, instance.name,
1924 c69b147d Bernardo Dal Seno
                    "instance has template %s, which is not supported on nodes"
1925 c69b147d Bernardo Dal Seno
                    " that have exclusive storage set: %s",
1926 da4a52a3 Thomas Thrainer
                    instance.disk_template,
1927 1c3231aa Thomas Thrainer
                    utils.CommaJoin(self.cfg.GetNodeNames(es_nodes)))
1928 da4a52a3 Thomas Thrainer
      for (idx, disk) in enumerate(instance.disks):
1929 d0d7d7cf Thomas Thrainer
        self._ErrorIf(disk.spindles is None,
1930 da4a52a3 Thomas Thrainer
                      constants.CV_EINSTANCEMISSINGCFGPARAMETER, instance.name,
1931 d0d7d7cf Thomas Thrainer
                      "number of spindles not configured for disk %s while"
1932 d0d7d7cf Thomas Thrainer
                      " exclusive storage is enabled, try running"
1933 d0d7d7cf Thomas Thrainer
                      " gnt-cluster repair-disk-sizes", idx)
1934 7352d33b Thomas Thrainer
1935 da4a52a3 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
1936 da4a52a3 Thomas Thrainer
      instance_nodes = utils.NiceSort(instance.all_nodes)
1937 7352d33b Thomas Thrainer
      instance_groups = {}
1938 7352d33b Thomas Thrainer
1939 da4a52a3 Thomas Thrainer
      for node_uuid in instance_nodes:
1940 da4a52a3 Thomas Thrainer
        instance_groups.setdefault(self.all_node_info[node_uuid].group,
1941 da4a52a3 Thomas Thrainer
                                   []).append(node_uuid)
1942 7352d33b Thomas Thrainer
1943 7352d33b Thomas Thrainer
      pretty_list = [
1944 1c3231aa Thomas Thrainer
        "%s (group %s)" % (utils.CommaJoin(self.cfg.GetNodeNames(nodes)),
1945 1c3231aa Thomas Thrainer
                           groupinfo[group].name)
1946 7352d33b Thomas Thrainer
        # Sort so that we always list the primary node first.
1947 7352d33b Thomas Thrainer
        for group, nodes in sorted(instance_groups.items(),
1948 da4a52a3 Thomas Thrainer
                                   key=lambda (_, nodes): pnode_uuid in nodes,
1949 7352d33b Thomas Thrainer
                                   reverse=True)]
1950 7352d33b Thomas Thrainer
1951 7352d33b Thomas Thrainer
      self._ErrorIf(len(instance_groups) > 1,
1952 7352d33b Thomas Thrainer
                    constants.CV_EINSTANCESPLITGROUPS,
1953 da4a52a3 Thomas Thrainer
                    instance.name, "instance has primary and secondary nodes in"
1954 7352d33b Thomas Thrainer
                    " different groups: %s", utils.CommaJoin(pretty_list),
1955 7352d33b Thomas Thrainer
                    code=self.ETYPE_WARNING)
1956 7352d33b Thomas Thrainer
1957 7352d33b Thomas Thrainer
    inst_nodes_offline = []
1958 da4a52a3 Thomas Thrainer
    for snode in instance.secondary_nodes:
1959 7352d33b Thomas Thrainer
      s_img = node_image[snode]
1960 d0d7d7cf Thomas Thrainer
      self._ErrorIf(s_img.rpc_fail and not s_img.offline, constants.CV_ENODERPC,
1961 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(snode),
1962 da4a52a3 Thomas Thrainer
                    "instance %s, connection to secondary node failed",
1963 da4a52a3 Thomas Thrainer
                    instance.name)
1964 7352d33b Thomas Thrainer
1965 7352d33b Thomas Thrainer
      if s_img.offline:
1966 7352d33b Thomas Thrainer
        inst_nodes_offline.append(snode)
1967 7352d33b Thomas Thrainer
1968 7352d33b Thomas Thrainer
    # warn that the instance lives on offline nodes
1969 da4a52a3 Thomas Thrainer
    self._ErrorIf(inst_nodes_offline, constants.CV_EINSTANCEBADNODE,
1970 da4a52a3 Thomas Thrainer
                  instance.name, "instance has offline secondary node(s) %s",
1971 d0d7d7cf Thomas Thrainer
                  utils.CommaJoin(self.cfg.GetNodeNames(inst_nodes_offline)))
1972 7352d33b Thomas Thrainer
    # ... or ghost/non-vm_capable nodes
1973 da4a52a3 Thomas Thrainer
    for node_uuid in instance.all_nodes:
1974 da4a52a3 Thomas Thrainer
      self._ErrorIf(node_image[node_uuid].ghost, constants.CV_EINSTANCEBADNODE,
1975 da4a52a3 Thomas Thrainer
                    instance.name, "instance lives on ghost node %s",
1976 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
1977 da4a52a3 Thomas Thrainer
      self._ErrorIf(not node_image[node_uuid].vm_capable,
1978 da4a52a3 Thomas Thrainer
                    constants.CV_EINSTANCEBADNODE, instance.name,
1979 d0d7d7cf Thomas Thrainer
                    "instance lives on non-vm_capable node %s",
1980 da4a52a3 Thomas Thrainer
                    self.cfg.GetNodeName(node_uuid))
1981 7352d33b Thomas Thrainer
1982 7352d33b Thomas Thrainer
  def _VerifyOrphanVolumes(self, node_vol_should, node_image, reserved):
1983 7352d33b Thomas Thrainer
    """Verify if there are any unknown volumes in the cluster.
1984 7352d33b Thomas Thrainer

1985 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
1986 7352d33b Thomas Thrainer
    reported as unknown.
1987 7352d33b Thomas Thrainer

1988 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
1989 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
1990 7352d33b Thomas Thrainer

1991 7352d33b Thomas Thrainer
    """
1992 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
1993 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
1994 1c3231aa Thomas Thrainer
          self.all_node_info[node_uuid].group != self.group_uuid):
1995 7352d33b Thomas Thrainer
        # skip non-healthy nodes
1996 7352d33b Thomas Thrainer
        continue
1997 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
1998 1c3231aa Thomas Thrainer
        test = ((node_uuid not in node_vol_should or
1999 1c3231aa Thomas Thrainer
                volume not in node_vol_should[node_uuid]) and
2000 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
2001 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV,
2002 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2003 7352d33b Thomas Thrainer
                      "volume %s is unknown", volume)
2004 7352d33b Thomas Thrainer
2005 da4a52a3 Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, all_insts):
2006 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
2007 7352d33b Thomas Thrainer

2008 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
2009 7352d33b Thomas Thrainer
    instances it was primary for.
2010 7352d33b Thomas Thrainer

2011 7352d33b Thomas Thrainer
    """
2012 7352d33b Thomas Thrainer
    cluster_info = self.cfg.GetClusterInfo()
2013 1c3231aa Thomas Thrainer
    for node_uuid, n_img in node_image.items():
2014 7352d33b Thomas Thrainer
      # This code checks that every node which is now listed as
2015 7352d33b Thomas Thrainer
      # secondary has enough memory to host all instances it is
2016 7352d33b Thomas Thrainer
      # supposed to should a single other node in the cluster fail.
2017 7352d33b Thomas Thrainer
      # FIXME: not ready for failover to an arbitrary node
2018 7352d33b Thomas Thrainer
      # FIXME: does not support file-backed instances
2019 7352d33b Thomas Thrainer
      # WARNING: we currently take into account down instances as well
2020 7352d33b Thomas Thrainer
      # as up ones, considering that even if they're down someone
2021 7352d33b Thomas Thrainer
      # might want to start them even in the event of a node failure.
2022 1c3231aa Thomas Thrainer
      if n_img.offline or \
2023 1c3231aa Thomas Thrainer
         self.all_node_info[node_uuid].group != self.group_uuid:
2024 7352d33b Thomas Thrainer
        # we're skipping nodes marked offline and nodes in other groups from
2025 7352d33b Thomas Thrainer
        # the N+1 warning, since most likely we don't have good memory
2026 7352d33b Thomas Thrainer
        # infromation from them; we already list instances living on such
2027 7352d33b Thomas Thrainer
        # nodes, and that's enough warning
2028 7352d33b Thomas Thrainer
        continue
2029 7352d33b Thomas Thrainer
      #TODO(dynmem): also consider ballooning out other instances
2030 da4a52a3 Thomas Thrainer
      for prinode, inst_uuids in n_img.sbp.items():
2031 7352d33b Thomas Thrainer
        needed_mem = 0
2032 da4a52a3 Thomas Thrainer
        for inst_uuid in inst_uuids:
2033 da4a52a3 Thomas Thrainer
          bep = cluster_info.FillBE(all_insts[inst_uuid])
2034 7352d33b Thomas Thrainer
          if bep[constants.BE_AUTO_BALANCE]:
2035 7352d33b Thomas Thrainer
            needed_mem += bep[constants.BE_MINMEM]
2036 7352d33b Thomas Thrainer
        test = n_img.mfree < needed_mem
2037 1c3231aa Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEN1,
2038 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(node_uuid),
2039 7352d33b Thomas Thrainer
                      "not enough memory to accomodate instance failovers"
2040 7352d33b Thomas Thrainer
                      " should node %s fail (%dMiB needed, %dMiB available)",
2041 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeName(prinode), needed_mem, n_img.mfree)
2042 7352d33b Thomas Thrainer
2043 1c3231aa Thomas Thrainer
  def _VerifyFiles(self, nodes, master_node_uuid, all_nvinfo,
2044 7352d33b Thomas Thrainer
                   (files_all, files_opt, files_mc, files_vm)):
2045 7352d33b Thomas Thrainer
    """Verifies file checksums collected from all nodes.
2046 7352d33b Thomas Thrainer

2047 1c3231aa Thomas Thrainer
    @param nodes: List of L{objects.Node} objects
2048 1c3231aa Thomas Thrainer
    @param master_node_uuid: UUID of master node
2049 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
2050 7352d33b Thomas Thrainer

2051 7352d33b Thomas Thrainer
    """
2052 7352d33b Thomas Thrainer
    # Define functions determining which nodes to consider for a file
2053 7352d33b Thomas Thrainer
    files2nodefn = [
2054 7352d33b Thomas Thrainer
      (files_all, None),
2055 7352d33b Thomas Thrainer
      (files_mc, lambda node: (node.master_candidate or
2056 1c3231aa Thomas Thrainer
                               node.uuid == master_node_uuid)),
2057 7352d33b Thomas Thrainer
      (files_vm, lambda node: node.vm_capable),
2058 7352d33b Thomas Thrainer
      ]
2059 7352d33b Thomas Thrainer
2060 7352d33b Thomas Thrainer
    # Build mapping from filename to list of nodes which should have the file
2061 7352d33b Thomas Thrainer
    nodefiles = {}
2062 7352d33b Thomas Thrainer
    for (files, fn) in files2nodefn:
2063 7352d33b Thomas Thrainer
      if fn is None:
2064 1c3231aa Thomas Thrainer
        filenodes = nodes
2065 7352d33b Thomas Thrainer
      else:
2066 1c3231aa Thomas Thrainer
        filenodes = filter(fn, nodes)
2067 7352d33b Thomas Thrainer
      nodefiles.update((filename,
2068 1c3231aa Thomas Thrainer
                        frozenset(map(operator.attrgetter("uuid"), filenodes)))
2069 7352d33b Thomas Thrainer
                       for filename in files)
2070 7352d33b Thomas Thrainer
2071 7352d33b Thomas Thrainer
    assert set(nodefiles) == (files_all | files_mc | files_vm)
2072 7352d33b Thomas Thrainer
2073 7352d33b Thomas Thrainer
    fileinfo = dict((filename, {}) for filename in nodefiles)
2074 7352d33b Thomas Thrainer
    ignore_nodes = set()
2075 7352d33b Thomas Thrainer
2076 1c3231aa Thomas Thrainer
    for node in nodes:
2077 7352d33b Thomas Thrainer
      if node.offline:
2078 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2079 7352d33b Thomas Thrainer
        continue
2080 7352d33b Thomas Thrainer
2081 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node.uuid]
2082 7352d33b Thomas Thrainer
2083 7352d33b Thomas Thrainer
      if nresult.fail_msg or not nresult.payload:
2084 7352d33b Thomas Thrainer
        node_files = None
2085 7352d33b Thomas Thrainer
      else:
2086 7352d33b Thomas Thrainer
        fingerprints = nresult.payload.get(constants.NV_FILELIST, None)
2087 7352d33b Thomas Thrainer
        node_files = dict((vcluster.LocalizeVirtualPath(key), value)
2088 7352d33b Thomas Thrainer
                          for (key, value) in fingerprints.items())
2089 7352d33b Thomas Thrainer
        del fingerprints
2090 7352d33b Thomas Thrainer
2091 7352d33b Thomas Thrainer
      test = not (node_files and isinstance(node_files, dict))
2092 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEFILECHECK, node.name,
2093 1c3231aa Thomas Thrainer
                    "Node did not return file checksum data")
2094 7352d33b Thomas Thrainer
      if test:
2095 1c3231aa Thomas Thrainer
        ignore_nodes.add(node.uuid)
2096 7352d33b Thomas Thrainer
        continue
2097 7352d33b Thomas Thrainer
2098 7352d33b Thomas Thrainer
      # Build per-checksum mapping from filename to nodes having it
2099 7352d33b Thomas Thrainer
      for (filename, checksum) in node_files.items():
2100 7352d33b Thomas Thrainer
        assert filename in nodefiles
2101 1c3231aa Thomas Thrainer
        fileinfo[filename].setdefault(checksum, set()).add(node.uuid)
2102 7352d33b Thomas Thrainer
2103 7352d33b Thomas Thrainer
    for (filename, checksums) in fileinfo.items():
2104 7352d33b Thomas Thrainer
      assert compat.all(len(i) > 10 for i in checksums), "Invalid checksum"
2105 7352d33b Thomas Thrainer
2106 7352d33b Thomas Thrainer
      # Nodes having the file
2107 1c3231aa Thomas Thrainer
      with_file = frozenset(node_uuid
2108 1c3231aa Thomas Thrainer
                            for node_uuids in fileinfo[filename].values()
2109 1c3231aa Thomas Thrainer
                            for node_uuid in node_uuids) - ignore_nodes
2110 7352d33b Thomas Thrainer
2111 7352d33b Thomas Thrainer
      expected_nodes = nodefiles[filename] - ignore_nodes
2112 7352d33b Thomas Thrainer
2113 7352d33b Thomas Thrainer
      # Nodes missing file
2114 7352d33b Thomas Thrainer
      missing_file = expected_nodes - with_file
2115 7352d33b Thomas Thrainer
2116 7352d33b Thomas Thrainer
      if filename in files_opt:
2117 7352d33b Thomas Thrainer
        # All or no nodes
2118 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file and missing_file != expected_nodes,
2119 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2120 1c3231aa Thomas Thrainer
                      "File %s is optional, but it must exist on all or no"
2121 1c3231aa Thomas Thrainer
                      " nodes (not found on %s)",
2122 1c3231aa Thomas Thrainer
                      filename,
2123 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2124 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2125 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2126 7352d33b Thomas Thrainer
      else:
2127 1c3231aa Thomas Thrainer
        self._ErrorIf(missing_file, constants.CV_ECLUSTERFILECHECK, None,
2128 1c3231aa Thomas Thrainer
                      "File %s is missing from node(s) %s", filename,
2129 1c3231aa Thomas Thrainer
                      utils.CommaJoin(
2130 1c3231aa Thomas Thrainer
                        utils.NiceSort(
2131 1c3231aa Thomas Thrainer
                          map(self.cfg.GetNodeName, missing_file))))
2132 7352d33b Thomas Thrainer
2133 7352d33b Thomas Thrainer
        # Warn if a node has a file it shouldn't
2134 7352d33b Thomas Thrainer
        unexpected = with_file - expected_nodes
2135 1c3231aa Thomas Thrainer
        self._ErrorIf(unexpected,
2136 1c3231aa Thomas Thrainer
                      constants.CV_ECLUSTERFILECHECK, None,
2137 1c3231aa Thomas Thrainer
                      "File %s should not exist on node(s) %s",
2138 1c3231aa Thomas Thrainer
                      filename, utils.CommaJoin(
2139 1c3231aa Thomas Thrainer
                        utils.NiceSort(map(self.cfg.GetNodeName, unexpected))))
2140 7352d33b Thomas Thrainer
2141 7352d33b Thomas Thrainer
      # See if there are multiple versions of the file
2142 7352d33b Thomas Thrainer
      test = len(checksums) > 1
2143 7352d33b Thomas Thrainer
      if test:
2144 7352d33b Thomas Thrainer
        variants = ["variant %s on %s" %
2145 1c3231aa Thomas Thrainer
                    (idx + 1,
2146 1c3231aa Thomas Thrainer
                     utils.CommaJoin(utils.NiceSort(
2147 1c3231aa Thomas Thrainer
                       map(self.cfg.GetNodeName, node_uuids))))
2148 1c3231aa Thomas Thrainer
                    for (idx, (checksum, node_uuids)) in
2149 7352d33b Thomas Thrainer
                      enumerate(sorted(checksums.items()))]
2150 7352d33b Thomas Thrainer
      else:
2151 7352d33b Thomas Thrainer
        variants = []
2152 7352d33b Thomas Thrainer
2153 1c3231aa Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERFILECHECK, None,
2154 1c3231aa Thomas Thrainer
                    "File %s found with %s different checksums (%s)",
2155 1c3231aa Thomas Thrainer
                    filename, len(checksums), "; ".join(variants))
2156 7352d33b Thomas Thrainer
2157 7352d33b Thomas Thrainer
  def _VerifyNodeDrbd(self, ninfo, nresult, instanceinfo, drbd_helper,
2158 7352d33b Thomas Thrainer
                      drbd_map):
2159 7352d33b Thomas Thrainer
    """Verifies and the node DRBD status.
2160 7352d33b Thomas Thrainer

2161 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2162 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2163 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2164 7352d33b Thomas Thrainer
    @param instanceinfo: the dict of instances
2165 7352d33b Thomas Thrainer
    @param drbd_helper: the configured DRBD usermode helper
2166 7352d33b Thomas Thrainer
    @param drbd_map: the DRBD map as returned by
2167 7352d33b Thomas Thrainer
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2168 7352d33b Thomas Thrainer

2169 7352d33b Thomas Thrainer
    """
2170 7352d33b Thomas Thrainer
    if drbd_helper:
2171 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2172 7352d33b Thomas Thrainer
      test = (helper_result is None)
2173 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2174 d0d7d7cf Thomas Thrainer
                    "no drbd usermode helper returned")
2175 7352d33b Thomas Thrainer
      if helper_result:
2176 7352d33b Thomas Thrainer
        status, payload = helper_result
2177 7352d33b Thomas Thrainer
        test = not status
2178 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2179 d0d7d7cf Thomas Thrainer
                      "drbd usermode helper check unsuccessful: %s", payload)
2180 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2181 d0d7d7cf Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEDRBDHELPER, ninfo.name,
2182 d0d7d7cf Thomas Thrainer
                      "wrong drbd usermode helper: %s", payload)
2183 7352d33b Thomas Thrainer
2184 7352d33b Thomas Thrainer
    # compute the DRBD minors
2185 7352d33b Thomas Thrainer
    node_drbd = {}
2186 da4a52a3 Thomas Thrainer
    for minor, inst_uuid in drbd_map[ninfo.uuid].items():
2187 da4a52a3 Thomas Thrainer
      test = inst_uuid not in instanceinfo
2188 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2189 da4a52a3 Thomas Thrainer
                    "ghost instance '%s' in temporary DRBD map", inst_uuid)
2190 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2191 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2192 7352d33b Thomas Thrainer
        # unallocated minor in use)
2193 7352d33b Thomas Thrainer
      if test:
2194 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, False)
2195 7352d33b Thomas Thrainer
      else:
2196 da4a52a3 Thomas Thrainer
        instance = instanceinfo[inst_uuid]
2197 da4a52a3 Thomas Thrainer
        node_drbd[minor] = (inst_uuid, instance.disks_active)
2198 7352d33b Thomas Thrainer
2199 7352d33b Thomas Thrainer
    # and now check them
2200 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2201 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2202 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2203 d0d7d7cf Thomas Thrainer
                  "cannot parse drbd status file: %s", str(used_minors))
2204 7352d33b Thomas Thrainer
    if test:
2205 7352d33b Thomas Thrainer
      # we cannot check drbd status
2206 7352d33b Thomas Thrainer
      return
2207 7352d33b Thomas Thrainer
2208 da4a52a3 Thomas Thrainer
    for minor, (inst_uuid, must_exist) in node_drbd.items():
2209 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2210 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2211 da4a52a3 Thomas Thrainer
                    "drbd minor %d of instance %s is not active", minor,
2212 da4a52a3 Thomas Thrainer
                    self.cfg.GetInstanceName(inst_uuid))
2213 7352d33b Thomas Thrainer
    for minor in used_minors:
2214 7352d33b Thomas Thrainer
      test = minor not in node_drbd
2215 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEDRBD, ninfo.name,
2216 d0d7d7cf Thomas Thrainer
                    "unallocated drbd minor %d is in use", minor)
2217 7352d33b Thomas Thrainer
2218 7352d33b Thomas Thrainer
  def _UpdateNodeOS(self, ninfo, nresult, nimg):
2219 7352d33b Thomas Thrainer
    """Builds the node OS structures.
2220 7352d33b Thomas Thrainer

2221 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2222 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2223 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2224 7352d33b Thomas Thrainer
    @param nimg: the node image object
2225 7352d33b Thomas Thrainer

2226 7352d33b Thomas Thrainer
    """
2227 7352d33b Thomas Thrainer
    remote_os = nresult.get(constants.NV_OSLIST, None)
2228 7352d33b Thomas Thrainer
    test = (not isinstance(remote_os, list) or
2229 7352d33b Thomas Thrainer
            not compat.all(isinstance(v, list) and len(v) == 7
2230 7352d33b Thomas Thrainer
                           for v in remote_os))
2231 7352d33b Thomas Thrainer
2232 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2233 d0d7d7cf Thomas Thrainer
                  "node hasn't returned valid OS data")
2234 7352d33b Thomas Thrainer
2235 7352d33b Thomas Thrainer
    nimg.os_fail = test
2236 7352d33b Thomas Thrainer
2237 7352d33b Thomas Thrainer
    if test:
2238 7352d33b Thomas Thrainer
      return
2239 7352d33b Thomas Thrainer
2240 7352d33b Thomas Thrainer
    os_dict = {}
2241 7352d33b Thomas Thrainer
2242 7352d33b Thomas Thrainer
    for (name, os_path, status, diagnose,
2243 7352d33b Thomas Thrainer
         variants, parameters, api_ver) in nresult[constants.NV_OSLIST]:
2244 7352d33b Thomas Thrainer
2245 7352d33b Thomas Thrainer
      if name not in os_dict:
2246 7352d33b Thomas Thrainer
        os_dict[name] = []
2247 7352d33b Thomas Thrainer
2248 7352d33b Thomas Thrainer
      # parameters is a list of lists instead of list of tuples due to
2249 7352d33b Thomas Thrainer
      # JSON lacking a real tuple type, fix it:
2250 7352d33b Thomas Thrainer
      parameters = [tuple(v) for v in parameters]
2251 7352d33b Thomas Thrainer
      os_dict[name].append((os_path, status, diagnose,
2252 7352d33b Thomas Thrainer
                            set(variants), set(parameters), set(api_ver)))
2253 7352d33b Thomas Thrainer
2254 7352d33b Thomas Thrainer
    nimg.oslist = os_dict
2255 7352d33b Thomas Thrainer
2256 7352d33b Thomas Thrainer
  def _VerifyNodeOS(self, ninfo, nimg, base):
2257 7352d33b Thomas Thrainer
    """Verifies the node OS list.
2258 7352d33b Thomas Thrainer

2259 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2260 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2261 7352d33b Thomas Thrainer
    @param nimg: the node image object
2262 7352d33b Thomas Thrainer
    @param base: the 'template' node we match against (e.g. from the master)
2263 7352d33b Thomas Thrainer

2264 7352d33b Thomas Thrainer
    """
2265 7352d33b Thomas Thrainer
    assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?"
2266 7352d33b Thomas Thrainer
2267 7352d33b Thomas Thrainer
    beautify_params = lambda l: ["%s: %s" % (k, v) for (k, v) in l]
2268 7352d33b Thomas Thrainer
    for os_name, os_data in nimg.oslist.items():
2269 7352d33b Thomas Thrainer
      assert os_data, "Empty OS status for OS %s?!" % os_name
2270 7352d33b Thomas Thrainer
      f_path, f_status, f_diag, f_var, f_param, f_api = os_data[0]
2271 d0d7d7cf Thomas Thrainer
      self._ErrorIf(not f_status, constants.CV_ENODEOS, ninfo.name,
2272 d0d7d7cf Thomas Thrainer
                    "Invalid OS %s (located at %s): %s",
2273 d0d7d7cf Thomas Thrainer
                    os_name, f_path, f_diag)
2274 d0d7d7cf Thomas Thrainer
      self._ErrorIf(len(os_data) > 1, constants.CV_ENODEOS, ninfo.name,
2275 d0d7d7cf Thomas Thrainer
                    "OS '%s' has multiple entries"
2276 d0d7d7cf Thomas Thrainer
                    " (first one shadows the rest): %s",
2277 d0d7d7cf Thomas Thrainer
                    os_name, utils.CommaJoin([v[0] for v in os_data]))
2278 7352d33b Thomas Thrainer
      # comparisons with the 'base' image
2279 7352d33b Thomas Thrainer
      test = os_name not in base.oslist
2280 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODEOS, ninfo.name,
2281 d0d7d7cf Thomas Thrainer
                    "Extra OS %s not present on reference node (%s)",
2282 d0d7d7cf Thomas Thrainer
                    os_name, self.cfg.GetNodeName(base.uuid))
2283 7352d33b Thomas Thrainer
      if test:
2284 7352d33b Thomas Thrainer
        continue
2285 7352d33b Thomas Thrainer
      assert base.oslist[os_name], "Base node has empty OS status?"
2286 7352d33b Thomas Thrainer
      _, b_status, _, b_var, b_param, b_api = base.oslist[os_name][0]
2287 7352d33b Thomas Thrainer
      if not b_status:
2288 7352d33b Thomas Thrainer
        # base OS is invalid, skipping
2289 7352d33b Thomas Thrainer
        continue
2290 7352d33b Thomas Thrainer
      for kind, a, b in [("API version", f_api, b_api),
2291 7352d33b Thomas Thrainer
                         ("variants list", f_var, b_var),
2292 7352d33b Thomas Thrainer
                         ("parameters", beautify_params(f_param),
2293 7352d33b Thomas Thrainer
                          beautify_params(b_param))]:
2294 d0d7d7cf Thomas Thrainer
        self._ErrorIf(a != b, constants.CV_ENODEOS, ninfo.name,
2295 d0d7d7cf Thomas Thrainer
                      "OS %s for %s differs from reference node %s:"
2296 d0d7d7cf Thomas Thrainer
                      " [%s] vs. [%s]", kind, os_name,
2297 d0d7d7cf Thomas Thrainer
                      self.cfg.GetNodeName(base.uuid),
2298 d0d7d7cf Thomas Thrainer
                      utils.CommaJoin(sorted(a)), utils.CommaJoin(sorted(b)))
2299 7352d33b Thomas Thrainer
2300 7352d33b Thomas Thrainer
    # check any missing OSes
2301 7352d33b Thomas Thrainer
    missing = set(base.oslist.keys()).difference(nimg.oslist.keys())
2302 d0d7d7cf Thomas Thrainer
    self._ErrorIf(missing, constants.CV_ENODEOS, ninfo.name,
2303 d0d7d7cf Thomas Thrainer
                  "OSes present on reference node %s"
2304 d0d7d7cf Thomas Thrainer
                  " but missing on this node: %s",
2305 d0d7d7cf Thomas Thrainer
                  self.cfg.GetNodeName(base.uuid), utils.CommaJoin(missing))
2306 7352d33b Thomas Thrainer
2307 13a6c760 Helga Velroyen
  def _VerifyAcceptedFileStoragePaths(self, ninfo, nresult, is_master):
2308 7352d33b Thomas Thrainer
    """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
2309 7352d33b Thomas Thrainer

2310 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2311 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2312 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2313 7352d33b Thomas Thrainer
    @type is_master: bool
2314 7352d33b Thomas Thrainer
    @param is_master: Whether node is the master node
2315 7352d33b Thomas Thrainer

2316 7352d33b Thomas Thrainer
    """
2317 13a6c760 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2318 7352d33b Thomas Thrainer
    if (is_master and
2319 13a6c760 Helga Velroyen
        (cluster.IsFileStorageEnabled() or
2320 13a6c760 Helga Velroyen
         cluster.IsSharedFileStorageEnabled())):
2321 7352d33b Thomas Thrainer
      try:
2322 13a6c760 Helga Velroyen
        fspaths = nresult[constants.NV_ACCEPTED_STORAGE_PATHS]
2323 7352d33b Thomas Thrainer
      except KeyError:
2324 7352d33b Thomas Thrainer
        # This should never happen
2325 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2326 7352d33b Thomas Thrainer
                      "Node did not return forbidden file storage paths")
2327 7352d33b Thomas Thrainer
      else:
2328 d0d7d7cf Thomas Thrainer
        self._ErrorIf(fspaths, constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2329 7352d33b Thomas Thrainer
                      "Found forbidden file storage paths: %s",
2330 7352d33b Thomas Thrainer
                      utils.CommaJoin(fspaths))
2331 7352d33b Thomas Thrainer
    else:
2332 13a6c760 Helga Velroyen
      self._ErrorIf(constants.NV_ACCEPTED_STORAGE_PATHS in nresult,
2333 d0d7d7cf Thomas Thrainer
                    constants.CV_ENODEFILESTORAGEPATHS, ninfo.name,
2334 7352d33b Thomas Thrainer
                    "Node should not have returned forbidden file storage"
2335 7352d33b Thomas Thrainer
                    " paths")
2336 7352d33b Thomas Thrainer
2337 9c1c3c19 Helga Velroyen
  def _VerifyStoragePaths(self, ninfo, nresult):
2338 9c1c3c19 Helga Velroyen
    """Verifies (file) storage paths.
2339 9c1c3c19 Helga Velroyen

2340 9c1c3c19 Helga Velroyen
    @type ninfo: L{objects.Node}
2341 9c1c3c19 Helga Velroyen
    @param ninfo: the node to check
2342 9c1c3c19 Helga Velroyen
    @param nresult: the remote results for the node
2343 9c1c3c19 Helga Velroyen

2344 9c1c3c19 Helga Velroyen
    """
2345 9c1c3c19 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
2346 9c1c3c19 Helga Velroyen
    if cluster.IsFileStorageEnabled():
2347 9c1c3c19 Helga Velroyen
      self._ErrorIf(
2348 9c1c3c19 Helga Velroyen
          constants.NV_FILE_STORAGE_PATH in nresult,
2349 9c1c3c19 Helga Velroyen
          constants.CV_ENODEFILESTORAGEPATHUNUSABLE, ninfo.name,
2350 9c1c3c19 Helga Velroyen
          "The configured file storage path is unusable: %s" %
2351 9c1c3c19 Helga Velroyen
          nresult.get(constants.NV_FILE_STORAGE_PATH))
2352 9c1c3c19 Helga Velroyen
2353 7352d33b Thomas Thrainer
  def _VerifyOob(self, ninfo, nresult):
2354 7352d33b Thomas Thrainer
    """Verifies out of band functionality of a node.
2355 7352d33b Thomas Thrainer

2356 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2357 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2358 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2359 7352d33b Thomas Thrainer

2360 7352d33b Thomas Thrainer
    """
2361 7352d33b Thomas Thrainer
    # We just have to verify the paths on master and/or master candidates
2362 7352d33b Thomas Thrainer
    # as the oob helper is invoked on the master
2363 7352d33b Thomas Thrainer
    if ((ninfo.master_candidate or ninfo.master_capable) and
2364 7352d33b Thomas Thrainer
        constants.NV_OOB_PATHS in nresult):
2365 7352d33b Thomas Thrainer
      for path_result in nresult[constants.NV_OOB_PATHS]:
2366 1c3231aa Thomas Thrainer
        self._ErrorIf(path_result, constants.CV_ENODEOOBPATH,
2367 d0d7d7cf Thomas Thrainer
                      ninfo.name, path_result)
2368 7352d33b Thomas Thrainer
2369 7352d33b Thomas Thrainer
  def _UpdateNodeVolumes(self, ninfo, nresult, nimg, vg_name):
2370 7352d33b Thomas Thrainer
    """Verifies and updates the node volume data.
2371 7352d33b Thomas Thrainer

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

2375 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2376 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2377 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2378 7352d33b Thomas Thrainer
    @param nimg: the node image object
2379 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2380 7352d33b Thomas Thrainer

2381 7352d33b Thomas Thrainer
    """
2382 7352d33b Thomas Thrainer
    nimg.lvm_fail = True
2383 7352d33b Thomas Thrainer
    lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
2384 7352d33b Thomas Thrainer
    if vg_name is None:
2385 7352d33b Thomas Thrainer
      pass
2386 7352d33b Thomas Thrainer
    elif isinstance(lvdata, basestring):
2387 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2388 d0d7d7cf Thomas Thrainer
                    "LVM problem on node: %s", utils.SafeEncode(lvdata))
2389 7352d33b Thomas Thrainer
    elif not isinstance(lvdata, dict):
2390 d0d7d7cf Thomas Thrainer
      self._ErrorIf(True, constants.CV_ENODELVM, ninfo.name,
2391 d0d7d7cf Thomas Thrainer
                    "rpc call to node failed (lvlist)")
2392 7352d33b Thomas Thrainer
    else:
2393 7352d33b Thomas Thrainer
      nimg.volumes = lvdata
2394 7352d33b Thomas Thrainer
      nimg.lvm_fail = False
2395 7352d33b Thomas Thrainer
2396 7352d33b Thomas Thrainer
  def _UpdateNodeInstances(self, ninfo, nresult, nimg):
2397 7352d33b Thomas Thrainer
    """Verifies and updates the node instance list.
2398 7352d33b Thomas Thrainer

2399 7352d33b Thomas Thrainer
    If the listing was successful, then updates this node's instance
2400 7352d33b Thomas Thrainer
    list. Otherwise, it marks the RPC call as failed for the instance
2401 7352d33b Thomas Thrainer
    list key.
2402 7352d33b Thomas Thrainer

2403 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2404 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2405 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2406 7352d33b Thomas Thrainer
    @param nimg: the node image object
2407 7352d33b Thomas Thrainer

2408 7352d33b Thomas Thrainer
    """
2409 7352d33b Thomas Thrainer
    idata = nresult.get(constants.NV_INSTANCELIST, None)
2410 7352d33b Thomas Thrainer
    test = not isinstance(idata, list)
2411 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2412 7352d33b Thomas Thrainer
                  "rpc call to node failed (instancelist): %s",
2413 7352d33b Thomas Thrainer
                  utils.SafeEncode(str(idata)))
2414 7352d33b Thomas Thrainer
    if test:
2415 7352d33b Thomas Thrainer
      nimg.hyp_fail = True
2416 7352d33b Thomas Thrainer
    else:
2417 da4a52a3 Thomas Thrainer
      nimg.instances = [inst.uuid for (_, inst) in
2418 da4a52a3 Thomas Thrainer
                        self.cfg.GetMultiInstanceInfoByName(idata)]
2419 7352d33b Thomas Thrainer
2420 7352d33b Thomas Thrainer
  def _UpdateNodeInfo(self, ninfo, nresult, nimg, vg_name):
2421 7352d33b Thomas Thrainer
    """Verifies and computes a node information map
2422 7352d33b Thomas Thrainer

2423 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2424 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2425 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2426 7352d33b Thomas Thrainer
    @param nimg: the node image object
2427 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
2428 7352d33b Thomas Thrainer

2429 7352d33b Thomas Thrainer
    """
2430 7352d33b Thomas Thrainer
    # try to read free memory (from the hypervisor)
2431 7352d33b Thomas Thrainer
    hv_info = nresult.get(constants.NV_HVINFO, None)
2432 7352d33b Thomas Thrainer
    test = not isinstance(hv_info, dict) or "memory_free" not in hv_info
2433 d0d7d7cf Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEHV, ninfo.name,
2434 d0d7d7cf Thomas Thrainer
                  "rpc call to node failed (hvinfo)")
2435 7352d33b Thomas Thrainer
    if not test:
2436 7352d33b Thomas Thrainer
      try:
2437 7352d33b Thomas Thrainer
        nimg.mfree = int(hv_info["memory_free"])
2438 7352d33b Thomas Thrainer
      except (ValueError, TypeError):
2439 d0d7d7cf Thomas Thrainer
        self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2440 d0d7d7cf Thomas Thrainer
                      "node returned invalid nodeinfo, check hypervisor")
2441 7352d33b Thomas Thrainer
2442 7352d33b Thomas Thrainer
    # FIXME: devise a free space model for file based instances as well
2443 7352d33b Thomas Thrainer
    if vg_name is not None:
2444 7352d33b Thomas Thrainer
      test = (constants.NV_VGLIST not in nresult or
2445 7352d33b Thomas Thrainer
              vg_name not in nresult[constants.NV_VGLIST])
2446 d0d7d7cf Thomas Thrainer
      self._ErrorIf(test, constants.CV_ENODELVM, ninfo.name,
2447 d0d7d7cf Thomas Thrainer
                    "node didn't return data for the volume group '%s'"
2448 d0d7d7cf Thomas Thrainer
                    " - it is either missing or broken", vg_name)
2449 7352d33b Thomas Thrainer
      if not test:
2450 7352d33b Thomas Thrainer
        try:
2451 7352d33b Thomas Thrainer
          nimg.dfree = int(nresult[constants.NV_VGLIST][vg_name])
2452 7352d33b Thomas Thrainer
        except (ValueError, TypeError):
2453 d0d7d7cf Thomas Thrainer
          self._ErrorIf(True, constants.CV_ENODERPC, ninfo.name,
2454 d0d7d7cf Thomas Thrainer
                        "node returned invalid LVM info, check LVM status")
2455 7352d33b Thomas Thrainer
2456 1c3231aa Thomas Thrainer
  def _CollectDiskInfo(self, node_uuids, node_image, instanceinfo):
2457 7352d33b Thomas Thrainer
    """Gets per-disk status information for all instances.
2458 7352d33b Thomas Thrainer

2459 1c3231aa Thomas Thrainer
    @type node_uuids: list of strings
2460 1c3231aa Thomas Thrainer
    @param node_uuids: Node UUIDs
2461 da4a52a3 Thomas Thrainer
    @type node_image: dict of (UUID, L{objects.Node})
2462 7352d33b Thomas Thrainer
    @param node_image: Node objects
2463 da4a52a3 Thomas Thrainer
    @type instanceinfo: dict of (UUID, L{objects.Instance})
2464 7352d33b Thomas Thrainer
    @param instanceinfo: Instance objects
2465 7352d33b Thomas Thrainer
    @rtype: {instance: {node: [(succes, payload)]}}
2466 7352d33b Thomas Thrainer
    @return: a dictionary of per-instance dictionaries with nodes as
2467 7352d33b Thomas Thrainer
        keys and disk information as values; the disk information is a
2468 7352d33b Thomas Thrainer
        list of tuples (success, payload)
2469 7352d33b Thomas Thrainer

2470 7352d33b Thomas Thrainer
    """
2471 7352d33b Thomas Thrainer
    node_disks = {}
2472 7352d33b Thomas Thrainer
    node_disks_devonly = {}
2473 7352d33b Thomas Thrainer
    diskless_instances = set()
2474 7352d33b Thomas Thrainer
    diskless = constants.DT_DISKLESS
2475 7352d33b Thomas Thrainer
2476 1c3231aa Thomas Thrainer
    for nuuid in node_uuids:
2477 da4a52a3 Thomas Thrainer
      node_inst_uuids = list(itertools.chain(node_image[nuuid].pinst,
2478 da4a52a3 Thomas Thrainer
                                             node_image[nuuid].sinst))
2479 da4a52a3 Thomas Thrainer
      diskless_instances.update(uuid for uuid in node_inst_uuids
2480 da4a52a3 Thomas Thrainer
                                if instanceinfo[uuid].disk_template == diskless)
2481 da4a52a3 Thomas Thrainer
      disks = [(inst_uuid, disk)
2482 da4a52a3 Thomas Thrainer
               for inst_uuid in node_inst_uuids
2483 da4a52a3 Thomas Thrainer
               for disk in instanceinfo[inst_uuid].disks]
2484 7352d33b Thomas Thrainer
2485 7352d33b Thomas Thrainer
      if not disks:
2486 7352d33b Thomas Thrainer
        # No need to collect data
2487 7352d33b Thomas Thrainer
        continue
2488 7352d33b Thomas Thrainer
2489 1c3231aa Thomas Thrainer
      node_disks[nuuid] = disks
2490 7352d33b Thomas Thrainer
2491 7352d33b Thomas Thrainer
      # _AnnotateDiskParams makes already copies of the disks
2492 7352d33b Thomas Thrainer
      devonly = []
2493 da4a52a3 Thomas Thrainer
      for (inst_uuid, dev) in disks:
2494 da4a52a3 Thomas Thrainer
        (anno_disk,) = AnnotateDiskParams(instanceinfo[inst_uuid], [dev],
2495 da4a52a3 Thomas Thrainer
                                          self.cfg)
2496 1c3231aa Thomas Thrainer
        self.cfg.SetDiskID(anno_disk, nuuid)
2497 7352d33b Thomas Thrainer
        devonly.append(anno_disk)
2498 7352d33b Thomas Thrainer
2499 1c3231aa Thomas Thrainer
      node_disks_devonly[nuuid] = devonly
2500 7352d33b Thomas Thrainer
2501 7352d33b Thomas Thrainer
    assert len(node_disks) == len(node_disks_devonly)
2502 7352d33b Thomas Thrainer
2503 7352d33b Thomas Thrainer
    # Collect data from all nodes with disks
2504 7352d33b Thomas Thrainer
    result = self.rpc.call_blockdev_getmirrorstatus_multi(node_disks.keys(),
2505 7352d33b Thomas Thrainer
                                                          node_disks_devonly)
2506 7352d33b Thomas Thrainer
2507 7352d33b Thomas Thrainer
    assert len(result) == len(node_disks)
2508 7352d33b Thomas Thrainer
2509 7352d33b Thomas Thrainer
    instdisk = {}
2510 7352d33b Thomas Thrainer
2511 1c3231aa Thomas Thrainer
    for (nuuid, nres) in result.items():
2512 1c3231aa Thomas Thrainer
      node = self.cfg.GetNodeInfo(nuuid)
2513 1c3231aa Thomas Thrainer
      disks = node_disks[node.uuid]
2514 7352d33b Thomas Thrainer
2515 7352d33b Thomas Thrainer
      if nres.offline:
2516 7352d33b Thomas Thrainer
        # No data from this node
2517 7352d33b Thomas Thrainer
        data = len(disks) * [(False, "node offline")]
2518 7352d33b Thomas Thrainer
      else:
2519 7352d33b Thomas Thrainer
        msg = nres.fail_msg
2520 1c3231aa Thomas Thrainer
        self._ErrorIf(msg, constants.CV_ENODERPC, node.name,
2521 1c3231aa Thomas Thrainer
                      "while getting disk information: %s", msg)
2522 7352d33b Thomas Thrainer
        if msg:
2523 7352d33b Thomas Thrainer
          # No data from this node
2524 7352d33b Thomas Thrainer
          data = len(disks) * [(False, msg)]
2525 7352d33b Thomas Thrainer
        else:
2526 7352d33b Thomas Thrainer
          data = []
2527 7352d33b Thomas Thrainer
          for idx, i in enumerate(nres.payload):
2528 7352d33b Thomas Thrainer
            if isinstance(i, (tuple, list)) and len(i) == 2:
2529 7352d33b Thomas Thrainer
              data.append(i)
2530 7352d33b Thomas Thrainer
            else:
2531 7352d33b Thomas Thrainer
              logging.warning("Invalid result from node %s, entry %d: %s",
2532 1c3231aa Thomas Thrainer
                              node.name, idx, i)
2533 7352d33b Thomas Thrainer
              data.append((False, "Invalid result from the remote node"))
2534 7352d33b Thomas Thrainer
2535 da4a52a3 Thomas Thrainer
      for ((inst_uuid, _), status) in zip(disks, data):
2536 da4a52a3 Thomas Thrainer
        instdisk.setdefault(inst_uuid, {}).setdefault(node.uuid, []) \
2537 da4a52a3 Thomas Thrainer
          .append(status)
2538 7352d33b Thomas Thrainer
2539 7352d33b Thomas Thrainer
    # Add empty entries for diskless instances.
2540 da4a52a3 Thomas Thrainer
    for inst_uuid in diskless_instances:
2541 da4a52a3 Thomas Thrainer
      assert inst_uuid not in instdisk
2542 da4a52a3 Thomas Thrainer
      instdisk[inst_uuid] = {}
2543 7352d33b Thomas Thrainer
2544 7352d33b Thomas Thrainer
    assert compat.all(len(statuses) == len(instanceinfo[inst].disks) and
2545 1c3231aa Thomas Thrainer
                      len(nuuids) <= len(instanceinfo[inst].all_nodes) and
2546 7352d33b Thomas Thrainer
                      compat.all(isinstance(s, (tuple, list)) and
2547 7352d33b Thomas Thrainer
                                 len(s) == 2 for s in statuses)
2548 1c3231aa Thomas Thrainer
                      for inst, nuuids in instdisk.items()
2549 1c3231aa Thomas Thrainer
                      for nuuid, statuses in nuuids.items())
2550 7352d33b Thomas Thrainer
    if __debug__:
2551 7352d33b Thomas Thrainer
      instdisk_keys = set(instdisk)
2552 7352d33b Thomas Thrainer
      instanceinfo_keys = set(instanceinfo)
2553 7352d33b Thomas Thrainer
      assert instdisk_keys == instanceinfo_keys, \
2554 7352d33b Thomas Thrainer
        ("instdisk keys (%s) do not match instanceinfo keys (%s)" %
2555 7352d33b Thomas Thrainer
         (instdisk_keys, instanceinfo_keys))
2556 7352d33b Thomas Thrainer
2557 7352d33b Thomas Thrainer
    return instdisk
2558 7352d33b Thomas Thrainer
2559 7352d33b Thomas Thrainer
  @staticmethod
2560 7352d33b Thomas Thrainer
  def _SshNodeSelector(group_uuid, all_nodes):
2561 7352d33b Thomas Thrainer
    """Create endless iterators for all potential SSH check hosts.
2562 7352d33b Thomas Thrainer

2563 7352d33b Thomas Thrainer
    """
2564 7352d33b Thomas Thrainer
    nodes = [node for node in all_nodes
2565 7352d33b Thomas Thrainer
             if (node.group != group_uuid and
2566 7352d33b Thomas Thrainer
                 not node.offline)]
2567 7352d33b Thomas Thrainer
    keyfunc = operator.attrgetter("group")
2568 7352d33b Thomas Thrainer
2569 7352d33b Thomas Thrainer
    return map(itertools.cycle,
2570 7352d33b Thomas Thrainer
               [sorted(map(operator.attrgetter("name"), names))
2571 7352d33b Thomas Thrainer
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2572 7352d33b Thomas Thrainer
                                                  keyfunc)])
2573 7352d33b Thomas Thrainer
2574 7352d33b Thomas Thrainer
  @classmethod
2575 7352d33b Thomas Thrainer
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2576 7352d33b Thomas Thrainer
    """Choose which nodes should talk to which other nodes.
2577 7352d33b Thomas Thrainer

2578 7352d33b Thomas Thrainer
    We will make nodes contact all nodes in their group, and one node from
2579 7352d33b Thomas Thrainer
    every other group.
2580 7352d33b Thomas Thrainer

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

2585 7352d33b Thomas Thrainer
    """
2586 7352d33b Thomas Thrainer
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2587 7352d33b Thomas Thrainer
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2588 7352d33b Thomas Thrainer
2589 7352d33b Thomas Thrainer
    return (online_nodes,
2590 7352d33b Thomas Thrainer
            dict((name, sorted([i.next() for i in sel]))
2591 7352d33b Thomas Thrainer
                 for name in online_nodes))
2592 7352d33b Thomas Thrainer
2593 7352d33b Thomas Thrainer
  def BuildHooksEnv(self):
2594 7352d33b Thomas Thrainer
    """Build hooks env.
2595 7352d33b Thomas Thrainer

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

2599 7352d33b Thomas Thrainer
    """
2600 7352d33b Thomas Thrainer
    env = {
2601 7352d33b Thomas Thrainer
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags()),
2602 7352d33b Thomas Thrainer
      }
2603 7352d33b Thomas Thrainer
2604 7352d33b Thomas Thrainer
    env.update(("NODE_TAGS_%s" % node.name, " ".join(node.GetTags()))
2605 7352d33b Thomas Thrainer
               for node in self.my_node_info.values())
2606 7352d33b Thomas Thrainer
2607 7352d33b Thomas Thrainer
    return env
2608 7352d33b Thomas Thrainer
2609 7352d33b Thomas Thrainer
  def BuildHooksNodes(self):
2610 7352d33b Thomas Thrainer
    """Build hooks nodes.
2611 7352d33b Thomas Thrainer

2612 7352d33b Thomas Thrainer
    """
2613 1c3231aa Thomas Thrainer
    return ([], list(self.my_node_info.keys()))
2614 7352d33b Thomas Thrainer
2615 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
2616 7352d33b Thomas Thrainer
    """Verify integrity of the node group, performing various test on nodes.
2617 7352d33b Thomas Thrainer

2618 7352d33b Thomas Thrainer
    """
2619 7352d33b Thomas Thrainer
    # This method has too many local variables. pylint: disable=R0914
2620 7352d33b Thomas Thrainer
    feedback_fn("* Verifying group '%s'" % self.group_info.name)
2621 7352d33b Thomas Thrainer
2622 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
2623 7352d33b Thomas Thrainer
      # empty node group
2624 7352d33b Thomas Thrainer
      feedback_fn("* Empty node group, skipping verification")
2625 7352d33b Thomas Thrainer
      return True
2626 7352d33b Thomas Thrainer
2627 7352d33b Thomas Thrainer
    self.bad = False
2628 7352d33b Thomas Thrainer
    verbose = self.op.verbose
2629 7352d33b Thomas Thrainer
    self._feedback_fn = feedback_fn
2630 7352d33b Thomas Thrainer
2631 7352d33b Thomas Thrainer
    vg_name = self.cfg.GetVGName()
2632 7352d33b Thomas Thrainer
    drbd_helper = self.cfg.GetDRBDHelper()
2633 7352d33b Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2634 7352d33b Thomas Thrainer
    hypervisors = cluster.enabled_hypervisors
2635 1c3231aa Thomas Thrainer
    node_data_list = self.my_node_info.values()
2636 7352d33b Thomas Thrainer
2637 7352d33b Thomas Thrainer
    i_non_redundant = [] # Non redundant instances
2638 7352d33b Thomas Thrainer
    i_non_a_balanced = [] # Non auto-balanced instances
2639 7352d33b Thomas Thrainer
    i_offline = 0 # Count of offline instances
2640 7352d33b Thomas Thrainer
    n_offline = 0 # Count of offline nodes
2641 7352d33b Thomas Thrainer
    n_drained = 0 # Count of nodes being drained
2642 7352d33b Thomas Thrainer
    node_vol_should = {}
2643 7352d33b Thomas Thrainer
2644 7352d33b Thomas Thrainer
    # FIXME: verify OS list
2645 7352d33b Thomas Thrainer
2646 7352d33b Thomas Thrainer
    # File verification
2647 5eacbcae Thomas Thrainer
    filemap = ComputeAncillaryFiles(cluster, False)
2648 7352d33b Thomas Thrainer
2649 7352d33b Thomas Thrainer
    # do local checksums
2650 1c3231aa Thomas Thrainer
    master_node_uuid = self.master_node = self.cfg.GetMasterNode()
2651 7352d33b Thomas Thrainer
    master_ip = self.cfg.GetMasterIP()
2652 7352d33b Thomas Thrainer
2653 1c3231aa Thomas Thrainer
    feedback_fn("* Gathering data (%d nodes)" % len(self.my_node_uuids))
2654 7352d33b Thomas Thrainer
2655 7352d33b Thomas Thrainer
    user_scripts = []
2656 7352d33b Thomas Thrainer
    if self.cfg.GetUseExternalMipScript():
2657 7352d33b Thomas Thrainer
      user_scripts.append(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
2658 7352d33b Thomas Thrainer
2659 7352d33b Thomas Thrainer
    node_verify_param = {
2660 7352d33b Thomas Thrainer
      constants.NV_FILELIST:
2661 7352d33b Thomas Thrainer
        map(vcluster.MakeVirtualPath,
2662 7352d33b Thomas Thrainer
            utils.UniqueSequence(filename
2663 7352d33b Thomas Thrainer
                                 for files in filemap
2664 7352d33b Thomas Thrainer
                                 for filename in files)),
2665 7352d33b Thomas Thrainer
      constants.NV_NODELIST:
2666 7352d33b Thomas Thrainer
        self._SelectSshCheckNodes(node_data_list, self.group_uuid,
2667 7352d33b Thomas Thrainer
                                  self.all_node_info.values()),
2668 7352d33b Thomas Thrainer
      constants.NV_HYPERVISOR: hypervisors,
2669 7352d33b Thomas Thrainer
      constants.NV_HVPARAMS:
2670 7352d33b Thomas Thrainer
        _GetAllHypervisorParameters(cluster, self.all_inst_info.values()),
2671 7352d33b Thomas Thrainer
      constants.NV_NODENETTEST: [(node.name, node.primary_ip, node.secondary_ip)
2672 7352d33b Thomas Thrainer
                                 for node in node_data_list
2673 7352d33b Thomas Thrainer
                                 if not node.offline],
2674 7352d33b Thomas Thrainer
      constants.NV_INSTANCELIST: hypervisors,
2675 7352d33b Thomas Thrainer
      constants.NV_VERSION: None,
2676 7352d33b Thomas Thrainer
      constants.NV_HVINFO: self.cfg.GetHypervisorType(),
2677 7352d33b Thomas Thrainer
      constants.NV_NODESETUP: None,
2678 7352d33b Thomas Thrainer
      constants.NV_TIME: None,
2679 1c3231aa Thomas Thrainer
      constants.NV_MASTERIP: (self.cfg.GetMasterNodeName(), master_ip),
2680 7352d33b Thomas Thrainer
      constants.NV_OSLIST: None,
2681 7352d33b Thomas Thrainer
      constants.NV_VMNODES: self.cfg.GetNonVmCapableNodeList(),
2682 7352d33b Thomas Thrainer
      constants.NV_USERSCRIPTS: user_scripts,
2683 7352d33b Thomas Thrainer
      }
2684 7352d33b Thomas Thrainer
2685 7352d33b Thomas Thrainer
    if vg_name is not None:
2686 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_VGLIST] = None
2687 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_LVLIST] = vg_name
2688 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_PVLIST] = [vg_name]
2689 7352d33b Thomas Thrainer
2690 7352d33b Thomas Thrainer
    if drbd_helper:
2691 1bb99a33 Bernardo Dal Seno
      node_verify_param[constants.NV_DRBDVERSION] = None
2692 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_DRBDLIST] = None
2693 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_DRBDHELPER] = drbd_helper
2694 7352d33b Thomas Thrainer
2695 850c53f1 Helga Velroyen
    if cluster.IsFileStorageEnabled() or \
2696 850c53f1 Helga Velroyen
        cluster.IsSharedFileStorageEnabled():
2697 7352d33b Thomas Thrainer
      # Load file storage paths only from master node
2698 13a6c760 Helga Velroyen
      node_verify_param[constants.NV_ACCEPTED_STORAGE_PATHS] = \
2699 1c3231aa Thomas Thrainer
        self.cfg.GetMasterNodeName()
2700 13a6c760 Helga Velroyen
      if cluster.IsFileStorageEnabled():
2701 13a6c760 Helga Velroyen
        node_verify_param[constants.NV_FILE_STORAGE_PATH] = \
2702 13a6c760 Helga Velroyen
          cluster.file_storage_dir
2703 7352d33b Thomas Thrainer
2704 7352d33b Thomas Thrainer
    # bridge checks
2705 7352d33b Thomas Thrainer
    # FIXME: this needs to be changed per node-group, not cluster-wide
2706 7352d33b Thomas Thrainer
    bridges = set()
2707 7352d33b Thomas Thrainer
    default_nicpp = cluster.nicparams[constants.PP_DEFAULT]
2708 7352d33b Thomas Thrainer
    if default_nicpp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2709 7352d33b Thomas Thrainer
      bridges.add(default_nicpp[constants.NIC_LINK])
2710 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_info.values():
2711 da4a52a3 Thomas Thrainer
      for nic in inst_uuid.nics:
2712 7352d33b Thomas Thrainer
        full_nic = cluster.SimpleFillNIC(nic.nicparams)
2713 7352d33b Thomas Thrainer
        if full_nic[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2714 7352d33b Thomas Thrainer
          bridges.add(full_nic[constants.NIC_LINK])
2715 7352d33b Thomas Thrainer
2716 7352d33b Thomas Thrainer
    if bridges:
2717 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_BRIDGES] = list(bridges)
2718 7352d33b Thomas Thrainer
2719 7352d33b Thomas Thrainer
    # Build our expected cluster state
2720 1c3231aa Thomas Thrainer
    node_image = dict((node.uuid, self.NodeImage(offline=node.offline,
2721 1c3231aa Thomas Thrainer
                                                 uuid=node.uuid,
2722 7352d33b Thomas Thrainer
                                                 vm_capable=node.vm_capable))
2723 7352d33b Thomas Thrainer
                      for node in node_data_list)
2724 7352d33b Thomas Thrainer
2725 7352d33b Thomas Thrainer
    # Gather OOB paths
2726 7352d33b Thomas Thrainer
    oob_paths = []
2727 7352d33b Thomas Thrainer
    for node in self.all_node_info.values():
2728 5eacbcae Thomas Thrainer
      path = SupportsOob(self.cfg, node)
2729 7352d33b Thomas Thrainer
      if path and path not in oob_paths:
2730 7352d33b Thomas Thrainer
        oob_paths.append(path)
2731 7352d33b Thomas Thrainer
2732 7352d33b Thomas Thrainer
    if oob_paths:
2733 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_OOB_PATHS] = oob_paths
2734 7352d33b Thomas Thrainer
2735 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
2736 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
2737 da4a52a3 Thomas Thrainer
      if instance.admin_state == constants.ADMINST_OFFLINE:
2738 7352d33b Thomas Thrainer
        i_offline += 1
2739 7352d33b Thomas Thrainer
2740 da4a52a3 Thomas Thrainer
      for nuuid in instance.all_nodes:
2741 1c3231aa Thomas Thrainer
        if nuuid not in node_image:
2742 1c3231aa Thomas Thrainer
          gnode = self.NodeImage(uuid=nuuid)
2743 1c3231aa Thomas Thrainer
          gnode.ghost = (nuuid not in self.all_node_info)
2744 1c3231aa Thomas Thrainer
          node_image[nuuid] = gnode
2745 7352d33b Thomas Thrainer
2746 da4a52a3 Thomas Thrainer
      instance.MapLVsByNode(node_vol_should)
2747 7352d33b Thomas Thrainer
2748 da4a52a3 Thomas Thrainer
      pnode = instance.primary_node
2749 da4a52a3 Thomas Thrainer
      node_image[pnode].pinst.append(instance.uuid)
2750 7352d33b Thomas Thrainer
2751 da4a52a3 Thomas Thrainer
      for snode in instance.secondary_nodes:
2752 7352d33b Thomas Thrainer
        nimg = node_image[snode]
2753 da4a52a3 Thomas Thrainer
        nimg.sinst.append(instance.uuid)
2754 7352d33b Thomas Thrainer
        if pnode not in nimg.sbp:
2755 7352d33b Thomas Thrainer
          nimg.sbp[pnode] = []
2756 da4a52a3 Thomas Thrainer
        nimg.sbp[pnode].append(instance.uuid)
2757 7352d33b Thomas Thrainer
2758 1c3231aa Thomas Thrainer
    es_flags = rpc.GetExclusiveStorageForNodes(self.cfg,
2759 1c3231aa Thomas Thrainer
                                               self.my_node_info.keys())
2760 7352d33b Thomas Thrainer
    # The value of exclusive_storage should be the same across the group, so if
2761 7352d33b Thomas Thrainer
    # it's True for at least a node, we act as if it were set for all the nodes
2762 7352d33b Thomas Thrainer
    self._exclusive_storage = compat.any(es_flags.values())
2763 7352d33b Thomas Thrainer
    if self._exclusive_storage:
2764 7352d33b Thomas Thrainer
      node_verify_param[constants.NV_EXCLUSIVEPVS] = True
2765 7352d33b Thomas Thrainer
2766 7352d33b Thomas Thrainer
    # At this point, we have the in-memory data structures complete,
2767 7352d33b Thomas Thrainer
    # except for the runtime information, which we'll gather next
2768 7352d33b Thomas Thrainer
2769 7352d33b Thomas Thrainer
    # Due to the way our RPC system works, exact response times cannot be
2770 7352d33b Thomas Thrainer
    # guaranteed (e.g. a broken node could run into a timeout). By keeping the
2771 7352d33b Thomas Thrainer
    # time before and after executing the request, we can at least have a time
2772 7352d33b Thomas Thrainer
    # window.
2773 7352d33b Thomas Thrainer
    nvinfo_starttime = time.time()
2774 1c3231aa Thomas Thrainer
    all_nvinfo = self.rpc.call_node_verify(self.my_node_uuids,
2775 7352d33b Thomas Thrainer
                                           node_verify_param,
2776 5b0dfcef Helga Velroyen
                                           self.cfg.GetClusterName(),
2777 5b0dfcef Helga Velroyen
                                           self.cfg.GetClusterInfo().hvparams)
2778 7352d33b Thomas Thrainer
    nvinfo_endtime = time.time()
2779 7352d33b Thomas Thrainer
2780 7352d33b Thomas Thrainer
    if self.extra_lv_nodes and vg_name is not None:
2781 7352d33b Thomas Thrainer
      extra_lv_nvinfo = \
2782 7352d33b Thomas Thrainer
          self.rpc.call_node_verify(self.extra_lv_nodes,
2783 7352d33b Thomas Thrainer
                                    {constants.NV_LVLIST: vg_name},
2784 5b0dfcef Helga Velroyen
                                    self.cfg.GetClusterName(),
2785 5b0dfcef Helga Velroyen
                                    self.cfg.GetClusterInfo().hvparams)
2786 7352d33b Thomas Thrainer
    else:
2787 7352d33b Thomas Thrainer
      extra_lv_nvinfo = {}
2788 7352d33b Thomas Thrainer
2789 7352d33b Thomas Thrainer
    all_drbd_map = self.cfg.ComputeDRBDMap()
2790 7352d33b Thomas Thrainer
2791 7352d33b Thomas Thrainer
    feedback_fn("* Gathering disk information (%s nodes)" %
2792 1c3231aa Thomas Thrainer
                len(self.my_node_uuids))
2793 1c3231aa Thomas Thrainer
    instdisk = self._CollectDiskInfo(self.my_node_info.keys(), node_image,
2794 7352d33b Thomas Thrainer
                                     self.my_inst_info)
2795 7352d33b Thomas Thrainer
2796 7352d33b Thomas Thrainer
    feedback_fn("* Verifying configuration file consistency")
2797 7352d33b Thomas Thrainer
2798 7352d33b Thomas Thrainer
    # If not all nodes are being checked, we need to make sure the master node
2799 7352d33b Thomas Thrainer
    # and a non-checked vm_capable node are in the list.
2800 1c3231aa Thomas Thrainer
    absent_node_uuids = set(self.all_node_info).difference(self.my_node_info)
2801 1c3231aa Thomas Thrainer
    if absent_node_uuids:
2802 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo.copy()
2803 7352d33b Thomas Thrainer
      vf_node_info = list(self.my_node_info.values())
2804 1c3231aa Thomas Thrainer
      additional_node_uuids = []
2805 1c3231aa Thomas Thrainer
      if master_node_uuid not in self.my_node_info:
2806 1c3231aa Thomas Thrainer
        additional_node_uuids.append(master_node_uuid)
2807 1c3231aa Thomas Thrainer
        vf_node_info.append(self.all_node_info[master_node_uuid])
2808 7352d33b Thomas Thrainer
      # Add the first vm_capable node we find which is not included,
2809 7352d33b Thomas Thrainer
      # excluding the master node (which we already have)
2810 1c3231aa Thomas Thrainer
      for node_uuid in absent_node_uuids:
2811 1c3231aa Thomas Thrainer
        nodeinfo = self.all_node_info[node_uuid]
2812 7352d33b Thomas Thrainer
        if (nodeinfo.vm_capable and not nodeinfo.offline and
2813 1c3231aa Thomas Thrainer
            node_uuid != master_node_uuid):
2814 1c3231aa Thomas Thrainer
          additional_node_uuids.append(node_uuid)
2815 1c3231aa Thomas Thrainer
          vf_node_info.append(self.all_node_info[node_uuid])
2816 7352d33b Thomas Thrainer
          break
2817 7352d33b Thomas Thrainer
      key = constants.NV_FILELIST
2818 5b0dfcef Helga Velroyen
      vf_nvinfo.update(self.rpc.call_node_verify(
2819 1c3231aa Thomas Thrainer
         additional_node_uuids, {key: node_verify_param[key]},
2820 5b0dfcef Helga Velroyen
         self.cfg.GetClusterName(), self.cfg.GetClusterInfo().hvparams))
2821 7352d33b Thomas Thrainer
    else:
2822 7352d33b Thomas Thrainer
      vf_nvinfo = all_nvinfo
2823 7352d33b Thomas Thrainer
      vf_node_info = self.my_node_info.values()
2824 7352d33b Thomas Thrainer
2825 1c3231aa Thomas Thrainer
    self._VerifyFiles(vf_node_info, master_node_uuid, vf_nvinfo, filemap)
2826 7352d33b Thomas Thrainer
2827 7352d33b Thomas Thrainer
    feedback_fn("* Verifying node status")
2828 7352d33b Thomas Thrainer
2829 7352d33b Thomas Thrainer
    refos_img = None
2830 7352d33b Thomas Thrainer
2831 7352d33b Thomas Thrainer
    for node_i in node_data_list:
2832 1c3231aa Thomas Thrainer
      nimg = node_image[node_i.uuid]
2833 7352d33b Thomas Thrainer
2834 7352d33b Thomas Thrainer
      if node_i.offline:
2835 7352d33b Thomas Thrainer
        if verbose:
2836 1c3231aa Thomas Thrainer
          feedback_fn("* Skipping offline node %s" % (node_i.name,))
2837 7352d33b Thomas Thrainer
        n_offline += 1
2838 7352d33b Thomas Thrainer
        continue
2839 7352d33b Thomas Thrainer
2840 1c3231aa Thomas Thrainer
      if node_i.uuid == master_node_uuid:
2841 7352d33b Thomas Thrainer
        ntype = "master"
2842 7352d33b Thomas Thrainer
      elif node_i.master_candidate:
2843 7352d33b Thomas Thrainer
        ntype = "master candidate"
2844 7352d33b Thomas Thrainer
      elif node_i.drained:
2845 7352d33b Thomas Thrainer
        ntype = "drained"
2846 7352d33b Thomas Thrainer
        n_drained += 1
2847 7352d33b Thomas Thrainer
      else:
2848 7352d33b Thomas Thrainer
        ntype = "regular"
2849 7352d33b Thomas Thrainer
      if verbose:
2850 1c3231aa Thomas Thrainer
        feedback_fn("* Verifying node %s (%s)" % (node_i.name, ntype))
2851 7352d33b Thomas Thrainer
2852 1c3231aa Thomas Thrainer
      msg = all_nvinfo[node_i.uuid].fail_msg
2853 d0d7d7cf Thomas Thrainer
      self._ErrorIf(msg, constants.CV_ENODERPC, node_i.name,
2854 d0d7d7cf Thomas Thrainer
                    "while contacting node: %s", msg)
2855 7352d33b Thomas Thrainer
      if msg:
2856 7352d33b Thomas Thrainer
        nimg.rpc_fail = True
2857 7352d33b Thomas Thrainer
        continue
2858 7352d33b Thomas Thrainer
2859 1c3231aa Thomas Thrainer
      nresult = all_nvinfo[node_i.uuid].payload
2860 7352d33b Thomas Thrainer
2861 7352d33b Thomas Thrainer
      nimg.call_ok = self._VerifyNode(node_i, nresult)
2862 7352d33b Thomas Thrainer
      self._VerifyNodeTime(node_i, nresult, nvinfo_starttime, nvinfo_endtime)
2863 7352d33b Thomas Thrainer
      self._VerifyNodeNetwork(node_i, nresult)
2864 7352d33b Thomas Thrainer
      self._VerifyNodeUserScripts(node_i, nresult)
2865 7352d33b Thomas Thrainer
      self._VerifyOob(node_i, nresult)
2866 13a6c760 Helga Velroyen
      self._VerifyAcceptedFileStoragePaths(node_i, nresult,
2867 13a6c760 Helga Velroyen
                                           node_i.uuid == master_node_uuid)
2868 9c1c3c19 Helga Velroyen
      self._VerifyStoragePaths(node_i, nresult)
2869 7352d33b Thomas Thrainer
2870 7352d33b Thomas Thrainer
      if nimg.vm_capable:
2871 7352d33b Thomas Thrainer
        self._UpdateVerifyNodeLVM(node_i, nresult, vg_name, nimg)
2872 7352d33b Thomas Thrainer
        self._VerifyNodeDrbd(node_i, nresult, self.all_inst_info, drbd_helper,
2873 7352d33b Thomas Thrainer
                             all_drbd_map)
2874 7352d33b Thomas Thrainer
2875 7352d33b Thomas Thrainer
        self._UpdateNodeVolumes(node_i, nresult, nimg, vg_name)
2876 7352d33b Thomas Thrainer
        self._UpdateNodeInstances(node_i, nresult, nimg)
2877 7352d33b Thomas Thrainer
        self._UpdateNodeInfo(node_i, nresult, nimg, vg_name)
2878 7352d33b Thomas Thrainer
        self._UpdateNodeOS(node_i, nresult, nimg)
2879 7352d33b Thomas Thrainer
2880 7352d33b Thomas Thrainer
        if not nimg.os_fail:
2881 7352d33b Thomas Thrainer
          if refos_img is None:
2882 7352d33b Thomas Thrainer
            refos_img = nimg
2883 7352d33b Thomas Thrainer
          self._VerifyNodeOS(node_i, nimg, refos_img)
2884 7352d33b Thomas Thrainer
        self._VerifyNodeBridges(node_i, nresult, bridges)
2885 7352d33b Thomas Thrainer
2886 da4a52a3 Thomas Thrainer
        # Check whether all running instances are primary for the node. (This
2887 7352d33b Thomas Thrainer
        # can no longer be done from _VerifyInstance below, since some of the
2888 7352d33b Thomas Thrainer
        # wrong instances could be from other node groups.)
2889 da4a52a3 Thomas Thrainer
        non_primary_inst_uuids = set(nimg.instances).difference(nimg.pinst)
2890 7352d33b Thomas Thrainer
2891 da4a52a3 Thomas Thrainer
        for inst_uuid in non_primary_inst_uuids:
2892 da4a52a3 Thomas Thrainer
          test = inst_uuid in self.all_inst_info
2893 da4a52a3 Thomas Thrainer
          self._ErrorIf(test, constants.CV_EINSTANCEWRONGNODE,
2894 da4a52a3 Thomas Thrainer
                        self.cfg.GetInstanceName(inst_uuid),
2895 d0d7d7cf Thomas Thrainer
                        "instance should not run on node %s", node_i.name)
2896 d0d7d7cf Thomas Thrainer
          self._ErrorIf(not test, constants.CV_ENODEORPHANINSTANCE, node_i.name,
2897 da4a52a3 Thomas Thrainer
                        "node is running unknown instance %s", inst_uuid)
2898 7352d33b Thomas Thrainer
2899 1bb99a33 Bernardo Dal Seno
    self._VerifyGroupDRBDVersion(all_nvinfo)
2900 7352d33b Thomas Thrainer
    self._VerifyGroupLVM(node_image, vg_name)
2901 7352d33b Thomas Thrainer
2902 1c3231aa Thomas Thrainer
    for node_uuid, result in extra_lv_nvinfo.items():
2903 1c3231aa Thomas Thrainer
      self._UpdateNodeVolumes(self.all_node_info[node_uuid], result.payload,
2904 1c3231aa Thomas Thrainer
                              node_image[node_uuid], vg_name)
2905 7352d33b Thomas Thrainer
2906 7352d33b Thomas Thrainer
    feedback_fn("* Verifying instance status")
2907 da4a52a3 Thomas Thrainer
    for inst_uuid in self.my_inst_uuids:
2908 da4a52a3 Thomas Thrainer
      instance = self.my_inst_info[inst_uuid]
2909 7352d33b Thomas Thrainer
      if verbose:
2910 da4a52a3 Thomas Thrainer
        feedback_fn("* Verifying instance %s" % instance.name)
2911 da4a52a3 Thomas Thrainer
      self._VerifyInstance(instance, node_image, instdisk[inst_uuid])
2912 7352d33b Thomas Thrainer
2913 7352d33b Thomas Thrainer
      # If the instance is non-redundant we cannot survive losing its primary
2914 7352d33b Thomas Thrainer
      # node, so we are not N+1 compliant.
2915 da4a52a3 Thomas Thrainer
      if instance.disk_template not in constants.DTS_MIRRORED:
2916 7352d33b Thomas Thrainer
        i_non_redundant.append(instance)
2917 7352d33b Thomas Thrainer
2918 da4a52a3 Thomas Thrainer
      if not cluster.FillBE(instance)[constants.BE_AUTO_BALANCE]:
2919 7352d33b Thomas Thrainer
        i_non_a_balanced.append(instance)
2920 7352d33b Thomas Thrainer
2921 7352d33b Thomas Thrainer
    feedback_fn("* Verifying orphan volumes")
2922 7352d33b Thomas Thrainer
    reserved = utils.FieldSet(*cluster.reserved_lvs)
2923 7352d33b Thomas Thrainer
2924 7352d33b Thomas Thrainer
    # We will get spurious "unknown volume" warnings if any node of this group
2925 7352d33b Thomas Thrainer
    # is secondary for an instance whose primary is in another group. To avoid
2926 7352d33b Thomas Thrainer
    # them, we find these instances and add their volumes to node_vol_should.
2927 da4a52a3 Thomas Thrainer
    for instance in self.all_inst_info.values():
2928 da4a52a3 Thomas Thrainer
      for secondary in instance.secondary_nodes:
2929 7352d33b Thomas Thrainer
        if (secondary in self.my_node_info
2930 da4a52a3 Thomas Thrainer
            and instance.name not in self.my_inst_info):
2931 da4a52a3 Thomas Thrainer
          instance.MapLVsByNode(node_vol_should)
2932 7352d33b Thomas Thrainer
          break
2933 7352d33b Thomas Thrainer
2934 7352d33b Thomas Thrainer
    self._VerifyOrphanVolumes(node_vol_should, node_image, reserved)
2935 7352d33b Thomas Thrainer
2936 7352d33b Thomas Thrainer
    if constants.VERIFY_NPLUSONE_MEM not in self.op.skip_checks:
2937 7352d33b Thomas Thrainer
      feedback_fn("* Verifying N+1 Memory redundancy")
2938 7352d33b Thomas Thrainer
      self._VerifyNPlusOneMemory(node_image, self.my_inst_info)
2939 7352d33b Thomas Thrainer
2940 7352d33b Thomas Thrainer
    feedback_fn("* Other Notes")
2941 7352d33b Thomas Thrainer
    if i_non_redundant:
2942 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-redundant instance(s) found."
2943 7352d33b Thomas Thrainer
                  % len(i_non_redundant))
2944 7352d33b Thomas Thrainer
2945 7352d33b Thomas Thrainer
    if i_non_a_balanced:
2946 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d non-auto-balanced instance(s) found."
2947 7352d33b Thomas Thrainer
                  % len(i_non_a_balanced))
2948 7352d33b Thomas Thrainer
2949 7352d33b Thomas Thrainer
    if i_offline:
2950 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline instance(s) found." % i_offline)
2951 7352d33b Thomas Thrainer
2952 7352d33b Thomas Thrainer
    if n_offline:
2953 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d offline node(s) found." % n_offline)
2954 7352d33b Thomas Thrainer
2955 7352d33b Thomas Thrainer
    if n_drained:
2956 7352d33b Thomas Thrainer
      feedback_fn("  - NOTICE: %d drained node(s) found." % n_drained)
2957 7352d33b Thomas Thrainer
2958 7352d33b Thomas Thrainer
    return not self.bad
2959 7352d33b Thomas Thrainer
2960 7352d33b Thomas Thrainer
  def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
2961 7352d33b Thomas Thrainer
    """Analyze the post-hooks' result
2962 7352d33b Thomas Thrainer

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

2966 7352d33b Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
2967 7352d33b Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
2968 7352d33b Thomas Thrainer
    @param hooks_results: the results of the multi-node hooks rpc call
2969 7352d33b Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
2970 7352d33b Thomas Thrainer
    @param lu_result: previous Exec result
2971 7352d33b Thomas Thrainer
    @return: the new Exec result, based on the previous result
2972 7352d33b Thomas Thrainer
        and hook results
2973 7352d33b Thomas Thrainer

2974 7352d33b Thomas Thrainer
    """
2975 7352d33b Thomas Thrainer
    # We only really run POST phase hooks, only for non-empty groups,
2976 7352d33b Thomas Thrainer
    # and are only interested in their results
2977 1c3231aa Thomas Thrainer
    if not self.my_node_uuids:
2978 7352d33b Thomas Thrainer
      # empty node group
2979 7352d33b Thomas Thrainer
      pass
2980 7352d33b Thomas Thrainer
    elif phase == constants.HOOKS_PHASE_POST:
2981 7352d33b Thomas Thrainer
      # Used to change hooks' output to proper indentation
2982 7352d33b Thomas Thrainer
      feedback_fn("* Hooks Results")
2983 7352d33b Thomas Thrainer
      assert hooks_results, "invalid result from hooks"
2984 7352d33b Thomas Thrainer
2985 7352d33b Thomas Thrainer
      for node_name in hooks_results:
2986 7352d33b Thomas Thrainer
        res = hooks_results[node_name]
2987 7352d33b Thomas Thrainer
        msg = res.fail_msg
2988 7352d33b Thomas Thrainer
        test = msg and not res.offline
2989 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
2990 7352d33b Thomas Thrainer
                      "Communication failure in hooks execution: %s", msg)
2991 7352d33b Thomas Thrainer
        if res.offline or msg:
2992 7352d33b Thomas Thrainer
          # No need to investigate payload if node is offline or gave
2993 7352d33b Thomas Thrainer
          # an error.
2994 7352d33b Thomas Thrainer
          continue
2995 7352d33b Thomas Thrainer
        for script, hkr, output in res.payload:
2996 7352d33b Thomas Thrainer
          test = hkr == constants.HKR_FAIL
2997 7352d33b Thomas Thrainer
          self._ErrorIf(test, constants.CV_ENODEHOOKS, node_name,
2998 7352d33b Thomas Thrainer
                        "Script %s failed, output:", script)
2999 7352d33b Thomas Thrainer
          if test:
3000 7352d33b Thomas Thrainer
            output = self._HOOKS_INDENT_RE.sub("      ", output)
3001 7352d33b Thomas Thrainer
            feedback_fn("%s" % output)
3002 7352d33b Thomas Thrainer
            lu_result = False
3003 7352d33b Thomas Thrainer
3004 7352d33b Thomas Thrainer
    return lu_result
3005 7352d33b Thomas Thrainer
3006 7352d33b Thomas Thrainer
3007 7352d33b Thomas Thrainer
class LUClusterVerifyDisks(NoHooksLU):
3008 7352d33b Thomas Thrainer
  """Verifies the cluster disks status.
3009 7352d33b Thomas Thrainer

3010 7352d33b Thomas Thrainer
  """
3011 7352d33b Thomas Thrainer
  REQ_BGL = False
3012 7352d33b Thomas Thrainer
3013 7352d33b Thomas Thrainer
  def ExpandNames(self):
3014 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3015 7352d33b Thomas Thrainer
    self.needed_locks = {
3016 7352d33b Thomas Thrainer
      locking.LEVEL_NODEGROUP: locking.ALL_SET,
3017 7352d33b Thomas Thrainer
      }
3018 7352d33b Thomas Thrainer
3019 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
3020 7352d33b Thomas Thrainer
    group_names = self.owned_locks(locking.LEVEL_NODEGROUP)
3021 7352d33b Thomas Thrainer
3022 7352d33b Thomas Thrainer
    # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
3023 7352d33b Thomas Thrainer
    return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
3024 7352d33b Thomas Thrainer
                           for group in group_names])