Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / cluster.py @ 7352d33b

History | View | Annotate | Download (104.1 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 7352d33b Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, _QueryBase, LogicalUnit, \
53 7352d33b Thomas Thrainer
  ResultWithJobs
54 7352d33b Thomas Thrainer
from ganeti.cmdlib.common import _ShareAll, _RunPostHook, \
55 7352d33b Thomas Thrainer
  _ComputeAncillaryFiles, _RedistributeAncillaryFiles, _UploadHelper, \
56 7352d33b Thomas Thrainer
  _GetWantedInstances, _MergeAndVerifyHvState, _MergeAndVerifyDiskState, \
57 7352d33b Thomas Thrainer
  _GetUpdatedIPolicy, _ComputeNewInstanceViolations, _GetUpdatedParams, \
58 7352d33b Thomas Thrainer
  _CheckOSParams, _CheckHVParams, _AdjustCandidatePool, _CheckNodePVs, \
59 7352d33b Thomas Thrainer
  _ComputeIPolicyInstanceViolation, _AnnotateDiskParams, \
60 7352d33b Thomas Thrainer
  _SupportsOob
61 7352d33b Thomas Thrainer
62 7352d33b Thomas Thrainer
import ganeti.masterd.instance
63 7352d33b Thomas Thrainer
64 7352d33b Thomas Thrainer
65 7352d33b Thomas Thrainer
class LUClusterActivateMasterIp(NoHooksLU):
66 7352d33b Thomas Thrainer
  """Activate the master IP on the master node.
67 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1110 7352d33b Thomas Thrainer
  """
1111 7352d33b Thomas Thrainer
1112 7352d33b Thomas Thrainer
  ETYPE_FIELD = "code"
1113 7352d33b Thomas Thrainer
  ETYPE_ERROR = "ERROR"
1114 7352d33b Thomas Thrainer
  ETYPE_WARNING = "WARNING"
1115 7352d33b Thomas Thrainer
1116 7352d33b Thomas Thrainer
  def _Error(self, ecode, item, msg, *args, **kwargs):
1117 7352d33b Thomas Thrainer
    """Format an error message.
1118 7352d33b Thomas Thrainer

1119 7352d33b Thomas Thrainer
    Based on the opcode's error_codes parameter, either format a
1120 7352d33b Thomas Thrainer
    parseable error code, or a simpler error string.
1121 7352d33b Thomas Thrainer

1122 7352d33b Thomas Thrainer
    This must be called only from Exec and functions called from Exec.
1123 7352d33b Thomas Thrainer

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

1152 7352d33b Thomas Thrainer
    """
1153 7352d33b Thomas Thrainer
    if (bool(cond)
1154 7352d33b Thomas Thrainer
        or self.op.debug_simulate_errors): # pylint: disable=E1101
1155 7352d33b Thomas Thrainer
      self._Error(*args, **kwargs)
1156 7352d33b Thomas Thrainer
1157 7352d33b Thomas Thrainer
1158 7352d33b Thomas Thrainer
def _VerifyCertificate(filename):
1159 7352d33b Thomas Thrainer
  """Verifies a certificate for L{LUClusterVerifyConfig}.
1160 7352d33b Thomas Thrainer

1161 7352d33b Thomas Thrainer
  @type filename: string
1162 7352d33b Thomas Thrainer
  @param filename: Path to PEM file
1163 7352d33b Thomas Thrainer

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

1194 7352d33b Thomas Thrainer
  @type cluster: L{objects.Cluster}
1195 7352d33b Thomas Thrainer
  @param cluster: the cluster object
1196 7352d33b Thomas Thrainer
  @param instances: list of L{objects.Instance}
1197 7352d33b Thomas Thrainer
  @param instances: additional instances from which to obtain parameters
1198 7352d33b Thomas Thrainer
  @rtype: list of (origin, hypervisor, parameters)
1199 7352d33b Thomas Thrainer
  @return: a list with all parameters found, indicating the hypervisor they
1200 7352d33b Thomas Thrainer
       apply to, and the origin (can be "cluster", "os X", or "instance Y")
1201 7352d33b Thomas Thrainer

1202 7352d33b Thomas Thrainer
  """
1203 7352d33b Thomas Thrainer
  hvp_data = []
1204 7352d33b Thomas Thrainer
1205 7352d33b Thomas Thrainer
  for hv_name in cluster.enabled_hypervisors:
1206 7352d33b Thomas Thrainer
    hvp_data.append(("cluster", hv_name, cluster.GetHVDefaults(hv_name)))
1207 7352d33b Thomas Thrainer
1208 7352d33b Thomas Thrainer
  for os_name, os_hvp in cluster.os_hvp.items():
1209 7352d33b Thomas Thrainer
    for hv_name, hv_params in os_hvp.items():
1210 7352d33b Thomas Thrainer
      if hv_params:
1211 7352d33b Thomas Thrainer
        full_params = cluster.GetHVDefaults(hv_name, os_name=os_name)
1212 7352d33b Thomas Thrainer
        hvp_data.append(("os %s" % os_name, hv_name, full_params))
1213 7352d33b Thomas Thrainer
1214 7352d33b Thomas Thrainer
  # TODO: collapse identical parameter values in a single one
1215 7352d33b Thomas Thrainer
  for instance in instances:
1216 7352d33b Thomas Thrainer
    if instance.hvparams:
1217 7352d33b Thomas Thrainer
      hvp_data.append(("instance %s" % instance.name, instance.hypervisor,
1218 7352d33b Thomas Thrainer
                       cluster.FillHV(instance)))
1219 7352d33b Thomas Thrainer
1220 7352d33b Thomas Thrainer
  return hvp_data
1221 7352d33b Thomas Thrainer
1222 7352d33b Thomas Thrainer
1223 7352d33b Thomas Thrainer
class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors):
1224 7352d33b Thomas Thrainer
  """Verifies the cluster config.
1225 7352d33b Thomas Thrainer

1226 7352d33b Thomas Thrainer
  """
1227 7352d33b Thomas Thrainer
  REQ_BGL = False
1228 7352d33b Thomas Thrainer
1229 7352d33b Thomas Thrainer
  def _VerifyHVP(self, hvp_data):
1230 7352d33b Thomas Thrainer
    """Verifies locally the syntax of the hypervisor parameters.
1231 7352d33b Thomas Thrainer

1232 7352d33b Thomas Thrainer
    """
1233 7352d33b Thomas Thrainer
    for item, hv_name, hv_params in hvp_data:
1234 7352d33b Thomas Thrainer
      msg = ("hypervisor %s parameters syntax check (source %s): %%s" %
1235 7352d33b Thomas Thrainer
             (item, hv_name))
1236 7352d33b Thomas Thrainer
      try:
1237 7352d33b Thomas Thrainer
        hv_class = hypervisor.GetHypervisorClass(hv_name)
1238 7352d33b Thomas Thrainer
        utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
1239 7352d33b Thomas Thrainer
        hv_class.CheckParameterSyntax(hv_params)
1240 7352d33b Thomas Thrainer
      except errors.GenericError, err:
1241 7352d33b Thomas Thrainer
        self._ErrorIf(True, constants.CV_ECLUSTERCFG, None, msg % str(err))
1242 7352d33b Thomas Thrainer
1243 7352d33b Thomas Thrainer
  def ExpandNames(self):
1244 7352d33b Thomas Thrainer
    self.needed_locks = dict.fromkeys(locking.LEVELS, locking.ALL_SET)
1245 7352d33b Thomas Thrainer
    self.share_locks = _ShareAll()
1246 7352d33b Thomas Thrainer
1247 7352d33b Thomas Thrainer
  def CheckPrereq(self):
1248 7352d33b Thomas Thrainer
    """Check prerequisites.
1249 7352d33b Thomas Thrainer

1250 7352d33b Thomas Thrainer
    """
1251 7352d33b Thomas Thrainer
    # Retrieve all information
1252 7352d33b Thomas Thrainer
    self.all_group_info = self.cfg.GetAllNodeGroupsInfo()
1253 7352d33b Thomas Thrainer
    self.all_node_info = self.cfg.GetAllNodesInfo()
1254 7352d33b Thomas Thrainer
    self.all_inst_info = self.cfg.GetAllInstancesInfo()
1255 7352d33b Thomas Thrainer
1256 7352d33b Thomas Thrainer
  def Exec(self, feedback_fn):
1257 7352d33b Thomas Thrainer
    """Verify integrity of cluster, performing various test on nodes.
1258 7352d33b Thomas Thrainer

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

1320 7352d33b Thomas Thrainer
  """
1321 7352d33b Thomas Thrainer
  HPATH = "cluster-verify"
1322 7352d33b Thomas Thrainer
  HTYPE = constants.HTYPE_CLUSTER
1323 7352d33b Thomas Thrainer
  REQ_BGL = False
1324 7352d33b Thomas Thrainer
1325 7352d33b Thomas Thrainer
  _HOOKS_INDENT_RE = re.compile("^", re.M)
1326 7352d33b Thomas Thrainer
1327 7352d33b Thomas Thrainer
  class NodeImage(object):
1328 7352d33b Thomas Thrainer
    """A class representing the logical and physical status of a node.
1329 7352d33b Thomas Thrainer

1330 7352d33b Thomas Thrainer
    @type name: string
1331 7352d33b Thomas Thrainer
    @ivar name: the node name to which this object refers
1332 7352d33b Thomas Thrainer
    @ivar volumes: a structure as returned from
1333 7352d33b Thomas Thrainer
        L{ganeti.backend.GetVolumeList} (runtime)
1334 7352d33b Thomas Thrainer
    @ivar instances: a list of running instances (runtime)
1335 7352d33b Thomas Thrainer
    @ivar pinst: list of configured primary instances (config)
1336 7352d33b Thomas Thrainer
    @ivar sinst: list of configured secondary instances (config)
1337 7352d33b Thomas Thrainer
    @ivar sbp: dictionary of {primary-node: list of instances} for all
1338 7352d33b Thomas Thrainer
        instances for which this node is secondary (config)
1339 7352d33b Thomas Thrainer
    @ivar mfree: free memory, as reported by hypervisor (runtime)
1340 7352d33b Thomas Thrainer
    @ivar dfree: free disk, as reported by the node (runtime)
1341 7352d33b Thomas Thrainer
    @ivar offline: the offline status (config)
1342 7352d33b Thomas Thrainer
    @type rpc_fail: boolean
1343 7352d33b Thomas Thrainer
    @ivar rpc_fail: whether the RPC verify call was successfull (overall,
1344 7352d33b Thomas Thrainer
        not whether the individual keys were correct) (runtime)
1345 7352d33b Thomas Thrainer
    @type lvm_fail: boolean
1346 7352d33b Thomas Thrainer
    @ivar lvm_fail: whether the RPC call didn't return valid LVM data
1347 7352d33b Thomas Thrainer
    @type hyp_fail: boolean
1348 7352d33b Thomas Thrainer
    @ivar hyp_fail: whether the RPC call didn't return the instance list
1349 7352d33b Thomas Thrainer
    @type ghost: boolean
1350 7352d33b Thomas Thrainer
    @ivar ghost: whether this is a known node or not (config)
1351 7352d33b Thomas Thrainer
    @type os_fail: boolean
1352 7352d33b Thomas Thrainer
    @ivar os_fail: whether the RPC call didn't return valid OS data
1353 7352d33b Thomas Thrainer
    @type oslist: list
1354 7352d33b Thomas Thrainer
    @ivar oslist: list of OSes as diagnosed by DiagnoseOS
1355 7352d33b Thomas Thrainer
    @type vm_capable: boolean
1356 7352d33b Thomas Thrainer
    @ivar vm_capable: whether the node can host instances
1357 7352d33b Thomas Thrainer
    @type pv_min: float
1358 7352d33b Thomas Thrainer
    @ivar pv_min: size in MiB of the smallest PVs
1359 7352d33b Thomas Thrainer
    @type pv_max: float
1360 7352d33b Thomas Thrainer
    @ivar pv_max: size in MiB of the biggest PVs
1361 7352d33b Thomas Thrainer

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

1481 7352d33b Thomas Thrainer
      - check the result data structure is well formed and has all the
1482 7352d33b Thomas Thrainer
        mandatory fields
1483 7352d33b Thomas Thrainer
      - check ganeti version
1484 7352d33b Thomas Thrainer

1485 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1486 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1487 7352d33b Thomas Thrainer
    @param nresult: the results from the node
1488 7352d33b Thomas Thrainer
    @rtype: boolean
1489 7352d33b Thomas Thrainer
    @return: whether overall this call was successful (and we can expect
1490 7352d33b Thomas Thrainer
         reasonable values in the respose)
1491 7352d33b Thomas Thrainer

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

1555 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1556 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1557 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1558 7352d33b Thomas Thrainer
    @param nvinfo_starttime: the start time of the RPC call
1559 7352d33b Thomas Thrainer
    @param nvinfo_endtime: the end time of the RPC call
1560 7352d33b Thomas Thrainer

1561 7352d33b Thomas Thrainer
    """
1562 7352d33b Thomas Thrainer
    node = ninfo.name
1563 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1564 7352d33b Thomas Thrainer
1565 7352d33b Thomas Thrainer
    ntime = nresult.get(constants.NV_TIME, None)
1566 7352d33b Thomas Thrainer
    try:
1567 7352d33b Thomas Thrainer
      ntime_merged = utils.MergeTime(ntime)
1568 7352d33b Thomas Thrainer
    except (ValueError, TypeError):
1569 7352d33b Thomas Thrainer
      _ErrorIf(True, constants.CV_ENODETIME, node, "Node returned invalid time")
1570 7352d33b Thomas Thrainer
      return
1571 7352d33b Thomas Thrainer
1572 7352d33b Thomas Thrainer
    if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1573 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1574 7352d33b Thomas Thrainer
    elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1575 7352d33b Thomas Thrainer
      ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1576 7352d33b Thomas Thrainer
    else:
1577 7352d33b Thomas Thrainer
      ntime_diff = None
1578 7352d33b Thomas Thrainer
1579 7352d33b Thomas Thrainer
    _ErrorIf(ntime_diff is not None, constants.CV_ENODETIME, node,
1580 7352d33b Thomas Thrainer
             "Node time diverges by at least %s from master node time",
1581 7352d33b Thomas Thrainer
             ntime_diff)
1582 7352d33b Thomas Thrainer
1583 7352d33b Thomas Thrainer
  def _UpdateVerifyNodeLVM(self, ninfo, nresult, vg_name, nimg):
1584 7352d33b Thomas Thrainer
    """Check the node LVM results and update info for cross-node checks.
1585 7352d33b Thomas Thrainer

1586 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1587 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1588 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1589 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1590 7352d33b Thomas Thrainer
    @type nimg: L{NodeImage}
1591 7352d33b Thomas Thrainer
    @param nimg: node image
1592 7352d33b Thomas Thrainer

1593 7352d33b Thomas Thrainer
    """
1594 7352d33b Thomas Thrainer
    if vg_name is None:
1595 7352d33b Thomas Thrainer
      return
1596 7352d33b Thomas Thrainer
1597 7352d33b Thomas Thrainer
    node = ninfo.name
1598 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1599 7352d33b Thomas Thrainer
1600 7352d33b Thomas Thrainer
    # checks vg existence and size > 20G
1601 7352d33b Thomas Thrainer
    vglist = nresult.get(constants.NV_VGLIST, None)
1602 7352d33b Thomas Thrainer
    test = not vglist
1603 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODELVM, node, "unable to check volume groups")
1604 7352d33b Thomas Thrainer
    if not test:
1605 7352d33b Thomas Thrainer
      vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1606 7352d33b Thomas Thrainer
                                            constants.MIN_VG_SIZE)
1607 7352d33b Thomas Thrainer
      _ErrorIf(vgstatus, constants.CV_ENODELVM, node, vgstatus)
1608 7352d33b Thomas Thrainer
1609 7352d33b Thomas Thrainer
    # Check PVs
1610 7352d33b Thomas Thrainer
    (errmsgs, pvminmax) = _CheckNodePVs(nresult, self._exclusive_storage)
1611 7352d33b Thomas Thrainer
    for em in errmsgs:
1612 7352d33b Thomas Thrainer
      self._Error(constants.CV_ENODELVM, node, em)
1613 7352d33b Thomas Thrainer
    if pvminmax is not None:
1614 7352d33b Thomas Thrainer
      (nimg.pv_min, nimg.pv_max) = pvminmax
1615 7352d33b Thomas Thrainer
1616 7352d33b Thomas Thrainer
  def _VerifyGroupLVM(self, node_image, vg_name):
1617 7352d33b Thomas Thrainer
    """Check cross-node consistency in LVM.
1618 7352d33b Thomas Thrainer

1619 7352d33b Thomas Thrainer
    @type node_image: dict
1620 7352d33b Thomas Thrainer
    @param node_image: info about nodes, mapping from node to names to
1621 7352d33b Thomas Thrainer
      L{NodeImage} objects
1622 7352d33b Thomas Thrainer
    @param vg_name: the configured VG name
1623 7352d33b Thomas Thrainer

1624 7352d33b Thomas Thrainer
    """
1625 7352d33b Thomas Thrainer
    if vg_name is None:
1626 7352d33b Thomas Thrainer
      return
1627 7352d33b Thomas Thrainer
1628 7352d33b Thomas Thrainer
    # Only exlcusive storage needs this kind of checks
1629 7352d33b Thomas Thrainer
    if not self._exclusive_storage:
1630 7352d33b Thomas Thrainer
      return
1631 7352d33b Thomas Thrainer
1632 7352d33b Thomas Thrainer
    # exclusive_storage wants all PVs to have the same size (approximately),
1633 7352d33b Thomas Thrainer
    # if the smallest and the biggest ones are okay, everything is fine.
1634 7352d33b Thomas Thrainer
    # pv_min is None iff pv_max is None
1635 7352d33b Thomas Thrainer
    vals = filter((lambda ni: ni.pv_min is not None), node_image.values())
1636 7352d33b Thomas Thrainer
    if not vals:
1637 7352d33b Thomas Thrainer
      return
1638 7352d33b Thomas Thrainer
    (pvmin, minnode) = min((ni.pv_min, ni.name) for ni in vals)
1639 7352d33b Thomas Thrainer
    (pvmax, maxnode) = max((ni.pv_max, ni.name) for ni in vals)
1640 7352d33b Thomas Thrainer
    bad = utils.LvmExclusiveTestBadPvSizes(pvmin, pvmax)
1641 7352d33b Thomas Thrainer
    self._ErrorIf(bad, constants.CV_EGROUPDIFFERENTPVSIZE, self.group_info.name,
1642 7352d33b Thomas Thrainer
                  "PV sizes differ too much in the group; smallest (%s MB) is"
1643 7352d33b Thomas Thrainer
                  " on %s, biggest (%s MB) is on %s",
1644 7352d33b Thomas Thrainer
                  pvmin, minnode, pvmax, maxnode)
1645 7352d33b Thomas Thrainer
1646 7352d33b Thomas Thrainer
  def _VerifyNodeBridges(self, ninfo, nresult, bridges):
1647 7352d33b Thomas Thrainer
    """Check the node bridges.
1648 7352d33b Thomas Thrainer

1649 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1650 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1651 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1652 7352d33b Thomas Thrainer
    @param bridges: the expected list of bridges
1653 7352d33b Thomas Thrainer

1654 7352d33b Thomas Thrainer
    """
1655 7352d33b Thomas Thrainer
    if not bridges:
1656 7352d33b Thomas Thrainer
      return
1657 7352d33b Thomas Thrainer
1658 7352d33b Thomas Thrainer
    node = ninfo.name
1659 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1660 7352d33b Thomas Thrainer
1661 7352d33b Thomas Thrainer
    missing = nresult.get(constants.NV_BRIDGES, None)
1662 7352d33b Thomas Thrainer
    test = not isinstance(missing, list)
1663 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODENET, node,
1664 7352d33b Thomas Thrainer
             "did not return valid bridge information")
1665 7352d33b Thomas Thrainer
    if not test:
1666 7352d33b Thomas Thrainer
      _ErrorIf(bool(missing), constants.CV_ENODENET, node,
1667 7352d33b Thomas Thrainer
               "missing bridges: %s" % utils.CommaJoin(sorted(missing)))
1668 7352d33b Thomas Thrainer
1669 7352d33b Thomas Thrainer
  def _VerifyNodeUserScripts(self, ninfo, nresult):
1670 7352d33b Thomas Thrainer
    """Check the results of user scripts presence and executability on the node
1671 7352d33b Thomas Thrainer

1672 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
1673 7352d33b Thomas Thrainer
    @param ninfo: the node to check
1674 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
1675 7352d33b Thomas Thrainer

1676 7352d33b Thomas Thrainer
    """
1677 7352d33b Thomas Thrainer
    node = ninfo.name
1678 7352d33b Thomas Thrainer
1679 7352d33b Thomas Thrainer
    test = not constants.NV_USERSCRIPTS in nresult
1680 7352d33b Thomas Thrainer
    self._ErrorIf(test, constants.CV_ENODEUSERSCRIPTS, node,
1681 7352d33b Thomas Thrainer
                  "did not return user scripts information")
1682 7352d33b Thomas Thrainer
1683 7352d33b Thomas Thrainer
    broken_scripts = nresult.get(constants.NV_USERSCRIPTS, None)
1684 7352d33b Thomas Thrainer
    if not test:
1685 7352d33b Thomas Thrainer
      self._ErrorIf(broken_scripts, constants.CV_ENODEUSERSCRIPTS, node,
1686 7352d33b Thomas Thrainer
                    "user scripts not present or not executable: %s" %
1687 7352d33b Thomas Thrainer
                    utils.CommaJoin(sorted(broken_scripts)))
1688 7352d33b Thomas Thrainer
1689 7352d33b Thomas Thrainer
  def _VerifyNodeNetwork(self, ninfo, nresult):
1690 7352d33b Thomas Thrainer
    """Check the node network connectivity results.
1691 7352d33b Thomas Thrainer

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

1696 7352d33b Thomas Thrainer
    """
1697 7352d33b Thomas Thrainer
    node = ninfo.name
1698 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
1699 7352d33b Thomas Thrainer
1700 7352d33b Thomas Thrainer
    test = constants.NV_NODELIST not in nresult
1701 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODESSH, node,
1702 7352d33b Thomas Thrainer
             "node hasn't returned node ssh connectivity data")
1703 7352d33b Thomas Thrainer
    if not test:
1704 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODELIST]:
1705 7352d33b Thomas Thrainer
        for a_node, a_msg in nresult[constants.NV_NODELIST].items():
1706 7352d33b Thomas Thrainer
          _ErrorIf(True, constants.CV_ENODESSH, node,
1707 7352d33b Thomas Thrainer
                   "ssh communication with node '%s': %s", a_node, a_msg)
1708 7352d33b Thomas Thrainer
1709 7352d33b Thomas Thrainer
    test = constants.NV_NODENETTEST not in nresult
1710 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODENET, node,
1711 7352d33b Thomas Thrainer
             "node hasn't returned node tcp connectivity data")
1712 7352d33b Thomas Thrainer
    if not test:
1713 7352d33b Thomas Thrainer
      if nresult[constants.NV_NODENETTEST]:
1714 7352d33b Thomas Thrainer
        nlist = utils.NiceSort(nresult[constants.NV_NODENETTEST].keys())
1715 7352d33b Thomas Thrainer
        for anode in nlist:
1716 7352d33b Thomas Thrainer
          _ErrorIf(True, constants.CV_ENODENET, node,
1717 7352d33b Thomas Thrainer
                   "tcp communication with node '%s': %s",
1718 7352d33b Thomas Thrainer
                   anode, nresult[constants.NV_NODENETTEST][anode])
1719 7352d33b Thomas Thrainer
1720 7352d33b Thomas Thrainer
    test = constants.NV_MASTERIP not in nresult
1721 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODENET, node,
1722 7352d33b Thomas Thrainer
             "node hasn't returned node master IP reachability data")
1723 7352d33b Thomas Thrainer
    if not test:
1724 7352d33b Thomas Thrainer
      if not nresult[constants.NV_MASTERIP]:
1725 7352d33b Thomas Thrainer
        if node == self.master_node:
1726 7352d33b Thomas Thrainer
          msg = "the master node cannot reach the master IP (not configured?)"
1727 7352d33b Thomas Thrainer
        else:
1728 7352d33b Thomas Thrainer
          msg = "cannot reach the master IP"
1729 7352d33b Thomas Thrainer
        _ErrorIf(True, constants.CV_ENODENET, node, msg)
1730 7352d33b Thomas Thrainer
1731 7352d33b Thomas Thrainer
  def _VerifyInstance(self, instance, inst_config, node_image,
1732 7352d33b Thomas Thrainer
                      diskstatus):
1733 7352d33b Thomas Thrainer
    """Verify an instance.
1734 7352d33b Thomas Thrainer

1735 7352d33b Thomas Thrainer
    This function checks to see if the required block devices are
1736 7352d33b Thomas Thrainer
    available on the instance's node, and that the nodes are in the correct
1737 7352d33b Thomas Thrainer
    state.
1738 7352d33b Thomas Thrainer

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

1860 7352d33b Thomas Thrainer
    The .os, .swap and backup volumes are ignored. All other volumes are
1861 7352d33b Thomas Thrainer
    reported as unknown.
1862 7352d33b Thomas Thrainer

1863 7352d33b Thomas Thrainer
    @type reserved: L{ganeti.utils.FieldSet}
1864 7352d33b Thomas Thrainer
    @param reserved: a FieldSet of reserved volume names
1865 7352d33b Thomas Thrainer

1866 7352d33b Thomas Thrainer
    """
1867 7352d33b Thomas Thrainer
    for node, n_img in node_image.items():
1868 7352d33b Thomas Thrainer
      if (n_img.offline or n_img.rpc_fail or n_img.lvm_fail or
1869 7352d33b Thomas Thrainer
          self.all_node_info[node].group != self.group_uuid):
1870 7352d33b Thomas Thrainer
        # skip non-healthy nodes
1871 7352d33b Thomas Thrainer
        continue
1872 7352d33b Thomas Thrainer
      for volume in n_img.volumes:
1873 7352d33b Thomas Thrainer
        test = ((node not in node_vol_should or
1874 7352d33b Thomas Thrainer
                volume not in node_vol_should[node]) and
1875 7352d33b Thomas Thrainer
                not reserved.Matches(volume))
1876 7352d33b Thomas Thrainer
        self._ErrorIf(test, constants.CV_ENODEORPHANLV, node,
1877 7352d33b Thomas Thrainer
                      "volume %s is unknown", volume)
1878 7352d33b Thomas Thrainer
1879 7352d33b Thomas Thrainer
  def _VerifyNPlusOneMemory(self, node_image, instance_cfg):
1880 7352d33b Thomas Thrainer
    """Verify N+1 Memory Resilience.
1881 7352d33b Thomas Thrainer

1882 7352d33b Thomas Thrainer
    Check that if one single node dies we can still start all the
1883 7352d33b Thomas Thrainer
    instances it was primary for.
1884 7352d33b Thomas Thrainer

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

1920 7352d33b Thomas Thrainer
    @param errorif: Callback for reporting errors
1921 7352d33b Thomas Thrainer
    @param nodeinfo: List of L{objects.Node} objects
1922 7352d33b Thomas Thrainer
    @param master_node: Name of master node
1923 7352d33b Thomas Thrainer
    @param all_nvinfo: RPC results
1924 7352d33b Thomas Thrainer

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

2027 7352d33b Thomas Thrainer
    @type ninfo: L{objects.Node}
2028 7352d33b Thomas Thrainer
    @param ninfo: the node to check
2029 7352d33b Thomas Thrainer
    @param nresult: the remote results for the node
2030 7352d33b Thomas Thrainer
    @param instanceinfo: the dict of instances
2031 7352d33b Thomas Thrainer
    @param drbd_helper: the configured DRBD usermode helper
2032 7352d33b Thomas Thrainer
    @param drbd_map: the DRBD map as returned by
2033 7352d33b Thomas Thrainer
        L{ganeti.config.ConfigWriter.ComputeDRBDMap}
2034 7352d33b Thomas Thrainer

2035 7352d33b Thomas Thrainer
    """
2036 7352d33b Thomas Thrainer
    node = ninfo.name
2037 7352d33b Thomas Thrainer
    _ErrorIf = self._ErrorIf # pylint: disable=C0103
2038 7352d33b Thomas Thrainer
2039 7352d33b Thomas Thrainer
    if drbd_helper:
2040 7352d33b Thomas Thrainer
      helper_result = nresult.get(constants.NV_DRBDHELPER, None)
2041 7352d33b Thomas Thrainer
      test = (helper_result is None)
2042 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODEDRBDHELPER, node,
2043 7352d33b Thomas Thrainer
               "no drbd usermode helper returned")
2044 7352d33b Thomas Thrainer
      if helper_result:
2045 7352d33b Thomas Thrainer
        status, payload = helper_result
2046 7352d33b Thomas Thrainer
        test = not status
2047 7352d33b Thomas Thrainer
        _ErrorIf(test, constants.CV_ENODEDRBDHELPER, node,
2048 7352d33b Thomas Thrainer
                 "drbd usermode helper check unsuccessful: %s", payload)
2049 7352d33b Thomas Thrainer
        test = status and (payload != drbd_helper)
2050 7352d33b Thomas Thrainer
        _ErrorIf(test, constants.CV_ENODEDRBDHELPER, node,
2051 7352d33b Thomas Thrainer
                 "wrong drbd usermode helper: %s", payload)
2052 7352d33b Thomas Thrainer
2053 7352d33b Thomas Thrainer
    # compute the DRBD minors
2054 7352d33b Thomas Thrainer
    node_drbd = {}
2055 7352d33b Thomas Thrainer
    for minor, instance in drbd_map[node].items():
2056 7352d33b Thomas Thrainer
      test = instance not in instanceinfo
2057 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ECLUSTERCFG, None,
2058 7352d33b Thomas Thrainer
               "ghost instance '%s' in temporary DRBD map", instance)
2059 7352d33b Thomas Thrainer
        # ghost instance should not be running, but otherwise we
2060 7352d33b Thomas Thrainer
        # don't give double warnings (both ghost instance and
2061 7352d33b Thomas Thrainer
        # unallocated minor in use)
2062 7352d33b Thomas Thrainer
      if test:
2063 7352d33b Thomas Thrainer
        node_drbd[minor] = (instance, False)
2064 7352d33b Thomas Thrainer
      else:
2065 7352d33b Thomas Thrainer
        instance = instanceinfo[instance]
2066 7352d33b Thomas Thrainer
        node_drbd[minor] = (instance.name,
2067 7352d33b Thomas Thrainer
                            instance.admin_state == constants.ADMINST_UP)
2068 7352d33b Thomas Thrainer
2069 7352d33b Thomas Thrainer
    # and now check them
2070 7352d33b Thomas Thrainer
    used_minors = nresult.get(constants.NV_DRBDLIST, [])
2071 7352d33b Thomas Thrainer
    test = not isinstance(used_minors, (tuple, list))
2072 7352d33b Thomas Thrainer
    _ErrorIf(test, constants.CV_ENODEDRBD, node,
2073 7352d33b Thomas Thrainer
             "cannot parse drbd status file: %s", str(used_minors))
2074 7352d33b Thomas Thrainer
    if test:
2075 7352d33b Thomas Thrainer
      # we cannot check drbd status
2076 7352d33b Thomas Thrainer
      return
2077 7352d33b Thomas Thrainer
2078 7352d33b Thomas Thrainer
    for minor, (iname, must_exist) in node_drbd.items():
2079 7352d33b Thomas Thrainer
      test = minor not in used_minors and must_exist
2080 7352d33b Thomas Thrainer
      _ErrorIf(test, constants.CV_ENODEDRBD, node,
2081 7352d33b